create-enterprise-next 0.1.0 → 0.2.0

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
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command as Command2 } from "commander";
5
- import fs3 from "fs";
6
- import path3 from "path";
5
+ import fs4 from "fs";
6
+ import path4 from "path";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  // src/cli/flags.ts
@@ -47,6 +47,20 @@ var projectConfigSchema = z.object({
47
47
  path: ["database"]
48
48
  });
49
49
  }
50
+ if (data.orm === "drizzle" && data.database === "mongo") {
51
+ ctx.addIssue({
52
+ code: z.ZodIssueCode.custom,
53
+ message: "Drizzle ORM does not support MongoDB.",
54
+ path: ["database"]
55
+ });
56
+ }
57
+ if (data.monorepo && data.packageManager !== "pnpm") {
58
+ ctx.addIssue({
59
+ code: z.ZodIssueCode.custom,
60
+ message: "Monorepo setup is only supported with pnpm package manager.",
61
+ path: ["packageManager"]
62
+ });
63
+ }
50
64
  });
51
65
 
52
66
  // src/cli/flags.ts
@@ -426,13 +440,1761 @@ async function runInteractivePrompts(partial) {
426
440
  };
427
441
  }
428
442
 
429
- // src/utils/logger.ts
443
+ // src/generate/generate-project.ts
444
+ import * as clack2 from "@clack/prompts";
445
+
446
+ // src/generate/context.ts
447
+ function createGenerationContext(config, cwd = process.cwd()) {
448
+ return {
449
+ config,
450
+ cwd,
451
+ targetDir: config.projectName.startsWith("/") ? config.projectName : `${cwd}/${config.projectName}`,
452
+ manifest: {
453
+ dependencies: {},
454
+ devDependencies: {},
455
+ scripts: {},
456
+ rootDependencies: {},
457
+ rootDevDependencies: {},
458
+ rootScripts: {},
459
+ env: {},
460
+ files: /* @__PURE__ */ new Map()
461
+ }
462
+ };
463
+ }
464
+ function addDependency(context, name, version) {
465
+ context.manifest.dependencies[name] = version;
466
+ }
467
+ function addDevDependency(context, name, version) {
468
+ context.manifest.devDependencies[name] = version;
469
+ }
470
+ function addScript(context, name, command) {
471
+ context.manifest.scripts[name] = command;
472
+ }
473
+ function addRootDevDependency(context, name, version) {
474
+ context.manifest.rootDevDependencies[name] = version;
475
+ }
476
+ function addEnv(context, key, defaultValue = "") {
477
+ context.manifest.env[key] = defaultValue;
478
+ }
479
+ function addAppFile(context, relativePath, content) {
480
+ const normalizedPath = context.config.monorepo ? `apps/web/${relativePath}` : relativePath;
481
+ context.manifest.files.set(normalizedPath, content);
482
+ }
483
+ function addProjectFile(context, relativePath, content) {
484
+ context.manifest.files.set(relativePath, content);
485
+ }
486
+
487
+ // src/generate/filesystem.ts
488
+ import fs3 from "fs/promises";
489
+ import { existsSync } from "fs";
490
+ import path3 from "path";
491
+ function safeResolve(targetDir, relativePath) {
492
+ const absoluteTarget = path3.resolve(targetDir);
493
+ const resolved = path3.resolve(absoluteTarget, relativePath);
494
+ const relative = path3.relative(absoluteTarget, resolved);
495
+ if (relative.startsWith("..") || path3.isAbsolute(relative)) {
496
+ throw new Error(`Directory traversal detected: "${relativePath}" resolves outside of target directory "${absoluteTarget}".`);
497
+ }
498
+ return resolved;
499
+ }
500
+ async function ensureDirectory(targetDir, relativePath) {
501
+ const resolved = safeResolve(targetDir, relativePath);
502
+ await fs3.mkdir(resolved, { recursive: true });
503
+ return resolved;
504
+ }
505
+ async function writeFile(targetDir, relativePath, content) {
506
+ const resolved = safeResolve(targetDir, relativePath);
507
+ await fs3.mkdir(path3.dirname(resolved), { recursive: true });
508
+ await fs3.writeFile(resolved, content, "utf-8");
509
+ }
510
+ async function directoryIsEmpty(targetDir, relativePath) {
511
+ const resolved = safeResolve(targetDir, relativePath);
512
+ if (!existsSync(resolved)) {
513
+ return true;
514
+ }
515
+ const files = await fs3.readdir(resolved);
516
+ const nonHiddenFiles = files.filter((f) => !f.startsWith("."));
517
+ return nonHiddenFiles.length === 0;
518
+ }
519
+
520
+ // src/generate/dependencies.ts
521
+ var dependencyVersions = {
522
+ // Base
523
+ next: "^15.1.0",
524
+ react: "^19.0.0",
525
+ "react-dom": "^19.0.0",
526
+ typescript: "^5.7.2",
527
+ "@types/react": "^19.0.0",
528
+ "@types/react-dom": "^19.0.0",
529
+ "@types/node": "^20.11.30",
530
+ eslint: "^9.16.0",
531
+ "eslint-config-next": "^15.1.0",
532
+ // Styling
533
+ tailwindcss: "^3.4.15",
534
+ postcss: "^8.4.49",
535
+ autoprefixer: "^10.4.20",
536
+ "styled-components": "^6.1.13",
537
+ // Auth
538
+ "@clerk/nextjs": "^6.9.6",
539
+ "next-auth": "^5.0.0-beta.25",
540
+ "@auth0/nextjs-auth0": "^3.5.0",
541
+ // ORM
542
+ prisma: "^6.1.0",
543
+ "@prisma/client": "^6.1.0",
544
+ "drizzle-orm": "^0.38.2",
545
+ "drizzle-kit": "^0.30.1",
546
+ pg: "^8.13.1",
547
+ "@types/pg": "^8.11.10",
548
+ mysql2: "^3.11.5",
549
+ "better-sqlite3": "^11.7.0",
550
+ "@types/better-sqlite3": "^11.6.0",
551
+ // State Management
552
+ zustand: "^5.0.2",
553
+ "@reduxjs/toolkit": "^2.5.0",
554
+ "react-redux": "^9.2.0",
555
+ jotai: "^2.11.0",
556
+ // Testing
557
+ vitest: "^2.1.8",
558
+ "@vitejs/plugin-react": "^4.3.4",
559
+ jsdom: "^25.0.1",
560
+ jest: "^29.7.0",
561
+ "jest-environment-jsdom": "^29.7.0",
562
+ "@testing-library/react": "^16.1.0",
563
+ "@testing-library/jest-dom": "^6.6.3",
564
+ "ts-jest": "^29.2.5",
565
+ "ts-node": "^10.9.2",
566
+ "@types/jest": "^29.5.14",
567
+ "@playwright/test": "^1.49.1",
568
+ cypress: "^13.16.1",
569
+ // Monorepo
570
+ turbo: "^2.3.3"
571
+ };
572
+
573
+ // src/generate/base/index.ts
574
+ async function generateBaseProject(context) {
575
+ const { config } = context;
576
+ const ext = config.typescript ? "tsx" : "jsx";
577
+ const jsExt = config.typescript ? "ts" : "js";
578
+ addDependency(context, "next", dependencyVersions["next"]);
579
+ addDependency(context, "react", dependencyVersions["react"]);
580
+ addDependency(context, "react-dom", dependencyVersions["react-dom"]);
581
+ addDevDependency(context, "eslint", dependencyVersions["eslint"]);
582
+ addDevDependency(context, "eslint-config-next", dependencyVersions["eslint-config-next"]);
583
+ if (config.typescript) {
584
+ addDevDependency(context, "typescript", dependencyVersions["typescript"]);
585
+ addDevDependency(context, "@types/react", dependencyVersions["@types/react"]);
586
+ addDevDependency(context, "@types/react-dom", dependencyVersions["@types/react-dom"]);
587
+ addDevDependency(context, "@types/node", dependencyVersions["@types/node"]);
588
+ }
589
+ if (config.typescript) {
590
+ addAppFile(context, "next-env.d.ts", `/// <reference types="next" />
591
+ /// <reference types="next/image-types/global" />
592
+
593
+ // NOTE: This file should not be edited
594
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information
595
+ `);
596
+ }
597
+ if (config.typescript) {
598
+ addAppFile(
599
+ context,
600
+ "tsconfig.json",
601
+ JSON.stringify(
602
+ {
603
+ compilerOptions: {
604
+ target: "ES2022",
605
+ lib: ["dom", "dom.iterable", "esnext"],
606
+ allowJs: true,
607
+ skipLibCheck: true,
608
+ strict: true,
609
+ noEmit: true,
610
+ esModuleInterop: true,
611
+ module: "esnext",
612
+ moduleResolution: "bundler",
613
+ resolveJsonModule: true,
614
+ isolatedModules: true,
615
+ jsx: "preserve",
616
+ incremental: true,
617
+ plugins: [
618
+ {
619
+ name: "next"
620
+ }
621
+ ],
622
+ paths: {
623
+ "@/*": ["./src/*"]
624
+ }
625
+ },
626
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
627
+ exclude: ["node_modules"]
628
+ },
629
+ null,
630
+ 2
631
+ ) + "\n"
632
+ );
633
+ }
634
+ const nextConfigOptions = [];
635
+ if (config.docker) {
636
+ nextConfigOptions.push(' output: "standalone",');
637
+ }
638
+ if (config.styling === "styled-components") {
639
+ nextConfigOptions.push(" compiler: {\n styledComponents: true,\n },");
640
+ }
641
+ const nextConfigContent = config.typescript ? `import type { NextConfig } from "next";
642
+
643
+ const nextConfig: NextConfig = {
644
+ ${nextConfigOptions.join("\n")}
645
+ };
646
+
647
+ export default nextConfig;
648
+ ` : `/** @type {import('next').NextConfig} */
649
+ const nextConfig = {
650
+ ${nextConfigOptions.join("\n")}
651
+ };
652
+
653
+ export default nextConfig;
654
+ `;
655
+ addAppFile(context, `next.config.${jsExt}`, nextConfigContent);
656
+ addAppFile(
657
+ context,
658
+ "eslint.config.mjs",
659
+ `import { dirname } from "path";
660
+ import { fileURLToPath } from "url";
661
+ import { FlatCompat } from "@eslint/eslintrc";
662
+
663
+ const __filename = fileURLToPath(import.meta.url);
664
+ const __dirname = dirname(__filename);
665
+
666
+ const compat = new FlatCompat({
667
+ baseDirectory: __dirname,
668
+ });
669
+
670
+ const eslintConfig = [
671
+ ...compat.extends("next/core-web-vitals"),
672
+ ${config.typescript ? '...compat.extends("next/typescript"),' : ""}
673
+ ];
674
+
675
+ export default eslintConfig;
676
+ `
677
+ );
678
+ addProjectFile(
679
+ context,
680
+ ".gitignore",
681
+ `# dependencies
682
+ /node_modules
683
+ /.pnpm-store
684
+
685
+ # testing
686
+ /coverage
687
+ /cypress/videos
688
+ /cypress/screenshots
689
+ /playwright-report
690
+ /test-results
691
+
692
+ # next.js
693
+ /.next/
694
+ /out/
695
+
696
+ # production
697
+ /build
698
+
699
+ # debug
700
+ npm-debug.log*
701
+ yarn-debug.log*
702
+ yarn-error.log*
703
+ pnpm-debug.log*
704
+
705
+ # local env files
706
+ .env*.local
707
+ .env
708
+
709
+ # vercel
710
+ .vercel
711
+
712
+ # typescript
713
+ *.tsbuildinfo
714
+ next-env.d.ts
715
+ `
716
+ );
717
+ const layoutImports = [];
718
+ let childrenWrapped = "{children}";
719
+ if (config.styling === "styled-components") {
720
+ layoutImports.push("import StyledComponentsRegistry from '@/lib/registry';");
721
+ childrenWrapped = `<StyledComponentsRegistry>${childrenWrapped}</StyledComponentsRegistry>`;
722
+ }
723
+ if (config.auth === "clerk") {
724
+ layoutImports.push("import { ClerkProvider } from '@clerk/nextjs';");
725
+ childrenWrapped = `<ClerkProvider>${childrenWrapped}</ClerkProvider>`;
726
+ } else if (config.auth === "auth0") {
727
+ layoutImports.push("import { UserProvider } from '@auth0/nextjs-auth0/client';");
728
+ childrenWrapped = `<UserProvider>${childrenWrapped}</UserProvider>`;
729
+ }
730
+ if (config.stateManagement === "redux") {
731
+ layoutImports.push("import StoreProvider from './StoreProvider';");
732
+ childrenWrapped = `<StoreProvider>${childrenWrapped}</StoreProvider>`;
733
+ } else if (config.stateManagement === "jotai") {
734
+ layoutImports.push("import JotaiProvider from './JotaiProvider';");
735
+ childrenWrapped = `<JotaiProvider>${childrenWrapped}</JotaiProvider>`;
736
+ }
737
+ const layoutContent = `${config.typescript ? 'import type { Metadata } from "next";\n' : ""}import "./globals.css";
738
+ ${layoutImports.join("\n")}
739
+
740
+ export const metadata${config.typescript ? ": Metadata" : ""} = {
741
+ title: "${config.projectName}",
742
+ description: "Generated by create-enterprise-next",
743
+ };
744
+
745
+ export default function RootLayout({
746
+ children,
747
+ }: Readonly<{
748
+ children: React.ReactNode;
749
+ }>) {
750
+ return (
751
+ <html lang="en">
752
+ <body>
753
+ ${childrenWrapped}
754
+ </body>
755
+ </html>
756
+ );
757
+ }
758
+ `;
759
+ addAppFile(context, `src/app/layout.${ext}`, layoutContent);
760
+ const testingLabel = config.testing.length > 0 ? config.testing.join(", ") : "None";
761
+ const modules = [
762
+ `<li><strong>Styling:</strong> ${config.styling}</li>`,
763
+ `<li><strong>Authentication:</strong> ${config.auth}</li>`,
764
+ `<li><strong>ORM:</strong> ${config.orm} (${config.database || "None"})</li>`,
765
+ `<li><strong>Testing:</strong> ${testingLabel}</li>`,
766
+ `<li><strong>State Management:</strong> ${config.stateManagement}</li>`,
767
+ `<li><strong>CI Setup:</strong> ${config.ci}</li>`,
768
+ `<li><strong>Docker Configuration:</strong> ${config.docker ? "Enabled" : "Disabled"}</li>`,
769
+ `<li><strong>Monorepo Structure:</strong> ${config.monorepo ? "Enabled" : "Disabled"}</li>`,
770
+ `<li><strong>Package Manager:</strong> ${config.packageManager}</li>`
771
+ ].join("\n ");
772
+ let pageContent = "";
773
+ if (config.styling === "tailwind") {
774
+ pageContent = `export default function Page() {
775
+ return (
776
+ <main className="flex min-h-screen flex-col items-center justify-center bg-slate-950 text-white p-6">
777
+ <div className="max-w-2xl text-center space-y-6">
778
+ <h1 className="text-4xl font-extrabold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-emerald-400">
779
+ create-enterprise-next
780
+ </h1>
781
+ <p className="text-lg text-slate-400">
782
+ Your enterprise Next.js foundation is ready.
783
+ </p>
784
+ <div className="mt-8 p-6 bg-slate-900 rounded-xl border border-slate-800 text-left">
785
+ <h2 className="text-xl font-semibold mb-4 text-slate-200">Enabled Modules</h2>
786
+ <ul className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm text-slate-400">
787
+ ${modules}
788
+ </ul>
789
+ </div>
790
+ </div>
791
+ </main>
792
+ );
793
+ }
794
+ `;
795
+ } else if (config.styling === "styled-components") {
796
+ pageContent = `import styled from 'styled-components';
797
+
798
+ const Container = styled.main\`
799
+ display: flex;
800
+ flex-direction: column;
801
+ align-items: center;
802
+ justify-content: center;
803
+ min-height: 100vh;
804
+ background-color: #020617;
805
+ color: #ffffff;
806
+ padding: 24px;
807
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
808
+ \`;
809
+
810
+ const Title = styled.h1\`
811
+ font-size: 36px;
812
+ font-weight: 800;
813
+ margin-bottom: 12px;
814
+ background: linear-gradient(to right, #60a5fa, #34d399);
815
+ -webkit-background-clip: text;
816
+ -webkit-text-fill-color: transparent;
817
+ \`;
818
+
819
+ const Subtitle = styled.p\`
820
+ font-size: 18px;
821
+ color: #94a3b8;
822
+ margin-bottom: 32px;
823
+ \`;
824
+
825
+ const ModuleCard = styled.div\`
826
+ background-color: #0f172a;
827
+ border: 1px solid #1e293b;
828
+ border-radius: 12px;
829
+ padding: 24px;
830
+ max-width: 600px;
831
+ width: 100%;
832
+ text-align: left;
833
+ \`;
834
+
835
+ const CardTitle = styled.h2\`
836
+ font-size: 20px;
837
+ font-weight: 600;
838
+ margin-bottom: 16px;
839
+ color: #e2e8f0;
840
+ \`;
841
+
842
+ const ModuleList = styled.ul\`
843
+ display: grid;
844
+ grid-template-columns: 1fr;
845
+ gap: 12px;
846
+ list-style: none;
847
+ padding: 0;
848
+ margin: 0;
849
+ color: #94a3b8;
850
+ font-size: 14px;
851
+
852
+ @media (min-width: 768px) {
853
+ grid-template-columns: 1fr 1fr;
854
+ }
855
+ \`;
856
+
857
+ export default function Page() {
858
+ return (
859
+ <Container>
860
+ <Title>create-enterprise-next</Title>
861
+ <Subtitle>Your enterprise Next.js foundation is ready.</Subtitle>
862
+ <ModuleCard>
863
+ <CardTitle>Enabled Modules</CardTitle>
864
+ <ModuleList>
865
+ ${modules}
866
+ </ModuleList>
867
+ </ModuleCard>
868
+ </Container>
869
+ );
870
+ }
871
+ `;
872
+ } else {
873
+ pageContent = `import styles from './page.module.css';
874
+
875
+ export default function Page() {
876
+ return (
877
+ <main className={styles.main}>
878
+ <div className={styles.container}>
879
+ <h1 className={styles.title}>create-enterprise-next</h1>
880
+ <p className={styles.subtitle}>Your enterprise Next.js foundation is ready.</p>
881
+ <div className={styles.card}>
882
+ <h2 className={styles.cardTitle}>Enabled Modules</h2>
883
+ <ul className={styles.list}>
884
+ ${modules}
885
+ </ul>
886
+ </div>
887
+ </div>
888
+ </main>
889
+ );
890
+ }
891
+ `;
892
+ }
893
+ addAppFile(context, `src/app/page.${ext}`, pageContent);
894
+ }
895
+
896
+ // src/generate/base/readme.ts
897
+ function generateReadme(context) {
898
+ const { config } = context;
899
+ const pm = config.packageManager;
900
+ const pmRun = pm === "yarn" ? "yarn" : `${pm} run`;
901
+ const pmInstall = pm === "yarn" ? "yarn" : `${pm} install`;
902
+ let content = `# ${config.projectName}
903
+
904
+ `;
905
+ content += `This is an enterprise-grade Next.js application scaffolded with \`create-enterprise-next\`.
906
+
907
+ `;
908
+ content += `## Tech Stack
909
+
910
+ `;
911
+ content += `- **Framework:** Next.js (App Router)
912
+ `;
913
+ content += `- **Language:** ${config.typescript ? "TypeScript" : "JavaScript"}
914
+ `;
915
+ content += `- **Styling:** ${config.styling}
916
+ `;
917
+ content += `- **Authentication:** ${config.auth}
918
+ `;
919
+ content += `- **ORM:** ${config.orm}${config.database ? ` (${config.database})` : ""}
920
+ `;
921
+ content += `- **State Management:** ${config.stateManagement}
922
+ `;
923
+ content += `- **Testing:** ${config.testing.join(", ") || "None"}
924
+ `;
925
+ content += `- **CI Pipeline:** ${config.ci}
926
+ `;
927
+ content += `- **Docker:** ${config.docker ? "Yes" : "No"}
928
+ `;
929
+ content += `- **Monorepo:** ${config.monorepo ? "Yes" : "No"}
930
+
931
+ `;
932
+ content += `## Getting Started
933
+
934
+ `;
935
+ content += `### 1. Install Dependencies
936
+
937
+ `;
938
+ content += `\`\`\`bash
939
+ ${pmInstall}
940
+ \`\`\`
941
+
942
+ `;
943
+ if (config.orm !== "none") {
944
+ content += `### 2. Database Setup
945
+
946
+ `;
947
+ content += `Ensure your database is running and the \`DATABASE_URL\` is defined in your \`.env\` file.
948
+
949
+ `;
950
+ content += `Generate ORM client:
951
+ `;
952
+ content += `\`\`\`bash
953
+ ${pmRun} db:generate
954
+ \`\`\`
955
+
956
+ `;
957
+ content += `Push schema / run migrations:
958
+ `;
959
+ content += `\`\`\`bash
960
+ ${pmRun} db:migrate
961
+ \`\`\`
962
+
963
+ `;
964
+ }
965
+ content += `### 3. Run Development Server
966
+
967
+ `;
968
+ content += `\`\`\`bash
969
+ ${pm} dev
970
+ \`\`\`
971
+
972
+ `;
973
+ content += `Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
974
+
975
+ `;
976
+ if (config.testing.length > 0) {
977
+ content += `## Testing
978
+
979
+ `;
980
+ if (config.testing.includes("vitest")) {
981
+ content += `- **Vitest:** Run unit/integration tests: \`${pmRun} test:vitest\` (or \`${pmRun} test\`)
982
+ `;
983
+ }
984
+ if (config.testing.includes("jest")) {
985
+ content += `- **Jest:** Run unit/integration tests: \`${pmRun} test:jest\` (or \`${pmRun} test\`)
986
+ `;
987
+ }
988
+ if (config.testing.includes("playwright")) {
989
+ content += `- **Playwright:** Run end-to-end tests: \`${pmRun} test:e2e\`
990
+ `;
991
+ }
992
+ if (config.testing.includes("cypress")) {
993
+ content += `- **Cypress:** Run Cypress: \`${pmRun} test:cypress\` / \`${pmRun} cypress:open\`
994
+ `;
995
+ }
996
+ content += `
997
+ `;
998
+ }
999
+ if (config.docker) {
1000
+ content += `## Docker
1001
+
1002
+ `;
1003
+ content += `Build the production Docker image:
1004
+ `;
1005
+ content += `\`\`\`bash
1006
+ docker build -t ${config.projectName} .
1007
+ \`\`\`
1008
+
1009
+ `;
1010
+ content += `Run the container:
1011
+ `;
1012
+ content += `\`\`\`bash
1013
+ docker run -p 3000:3000 ${config.projectName}
1014
+ \`\`\`
1015
+
1016
+ `;
1017
+ }
1018
+ if (config.auth !== "none") {
1019
+ content += `## Authentication Setup
1020
+
1021
+ `;
1022
+ if (config.auth === "clerk") {
1023
+ content += `This project uses Clerk for authentication. Configure your credentials in \`.env.local\`:
1024
+ `;
1025
+ content += `- \`NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY\`
1026
+ `;
1027
+ content += `- \`CLERK_SECRET_KEY\`
1028
+ `;
1029
+ } else if (config.auth === "next-auth") {
1030
+ content += `This project uses NextAuth.js. Configure \`AUTH_SECRET\` in \`.env.local\`.
1031
+ `;
1032
+ } else if (config.auth === "auth0") {
1033
+ content += `This project uses Auth0. Configure \`AUTH0_SECRET\`, \`AUTH0_BASE_URL\`, and client variables in \`.env.local\`.
1034
+ `;
1035
+ }
1036
+ content += `
1037
+ `;
1038
+ }
1039
+ addAppFile(context, "README.md", content);
1040
+ }
1041
+
1042
+ // src/generate/features/styling/index.ts
1043
+ async function generateStyling(context) {
1044
+ const { config } = context;
1045
+ const ext = config.typescript ? "tsx" : "jsx";
1046
+ if (config.styling === "tailwind") {
1047
+ addDevDependency(context, "tailwindcss", dependencyVersions["tailwindcss"]);
1048
+ addDevDependency(context, "postcss", dependencyVersions["postcss"]);
1049
+ addDevDependency(context, "autoprefixer", dependencyVersions["autoprefixer"]);
1050
+ addAppFile(
1051
+ context,
1052
+ "postcss.config.mjs",
1053
+ `const config = {
1054
+ plugins: {
1055
+ tailwindcss: {},
1056
+ autoprefixer: {},
1057
+ },
1058
+ };
1059
+ export default config;
1060
+ `
1061
+ );
1062
+ const tailwindConfigContent = config.typescript ? `import type { Config } from "tailwindcss";
1063
+
1064
+ const config: Config = {
1065
+ content: [
1066
+ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
1067
+ "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
1068
+ "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
1069
+ ],
1070
+ theme: {
1071
+ extend: {},
1072
+ },
1073
+ plugins: [],
1074
+ };
1075
+ export default config;
1076
+ ` : `/** @type {import('tailwindcss').Config} */
1077
+ const config = {
1078
+ content: [
1079
+ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
1080
+ "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
1081
+ "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
1082
+ ],
1083
+ theme: {
1084
+ extend: {},
1085
+ },
1086
+ plugins: [],
1087
+ };
1088
+ export default config;
1089
+ `;
1090
+ addAppFile(context, `tailwind.config.${config.typescript ? "ts" : "js"}`, tailwindConfigContent);
1091
+ addAppFile(
1092
+ context,
1093
+ "src/app/globals.css",
1094
+ `@tailwind base;
1095
+ @tailwind components;
1096
+ @tailwind utilities;
1097
+ `
1098
+ );
1099
+ } else if (config.styling === "css-modules") {
1100
+ addAppFile(
1101
+ context,
1102
+ "src/app/globals.css",
1103
+ `body {
1104
+ margin: 0;
1105
+ padding: 0;
1106
+ background-color: #020617;
1107
+ color: #ffffff;
1108
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1109
+ }
1110
+ `
1111
+ );
1112
+ addAppFile(
1113
+ context,
1114
+ "src/app/page.module.css",
1115
+ `.main {
1116
+ display: flex;
1117
+ flex-direction: column;
1118
+ align-items: center;
1119
+ justify-content: center;
1120
+ min-height: 100vh;
1121
+ background-color: #020617;
1122
+ color: #ffffff;
1123
+ padding: 24px;
1124
+ }
1125
+ .container {
1126
+ text-align: center;
1127
+ max-width: 640px;
1128
+ }
1129
+ .title {
1130
+ font-size: 36px;
1131
+ font-weight: 800;
1132
+ margin-bottom: 12px;
1133
+ background: linear-gradient(to right, #60a5fa, #34d399);
1134
+ -webkit-background-clip: text;
1135
+ -webkit-text-fill-color: transparent;
1136
+ }
1137
+ .subtitle {
1138
+ font-size: 18px;
1139
+ color: #94a3b8;
1140
+ margin-bottom: 32px;
1141
+ }
1142
+ .card {
1143
+ background-color: #0f172a;
1144
+ border: 1px solid #1e293b;
1145
+ border-radius: 12px;
1146
+ padding: 24px;
1147
+ text-align: left;
1148
+ }
1149
+ .cardTitle {
1150
+ font-size: 20px;
1151
+ font-weight: 600;
1152
+ margin-bottom: 16px;
1153
+ color: #e2e8f0;
1154
+ }
1155
+ .list {
1156
+ display: grid;
1157
+ grid-template-columns: 1fr;
1158
+ gap: 12px;
1159
+ list-style: none;
1160
+ padding: 0;
1161
+ margin: 0;
1162
+ color: #94a3b8;
1163
+ font-size: 14px;
1164
+ }
1165
+ @media (min-width: 768px) {
1166
+ .list {
1167
+ grid-template-columns: 1fr 1fr;
1168
+ }
1169
+ }
1170
+ `
1171
+ );
1172
+ } else if (config.styling === "styled-components") {
1173
+ addDependency(context, "styled-components", dependencyVersions["styled-components"]);
1174
+ addAppFile(
1175
+ context,
1176
+ "src/app/globals.css",
1177
+ `body {
1178
+ margin: 0;
1179
+ padding: 0;
1180
+ background-color: #020617;
1181
+ color: #ffffff;
1182
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1183
+ }
1184
+ `
1185
+ );
1186
+ const registryContent = config.typescript ? `'use client';
1187
+
1188
+ import React, { useState } from 'react';
1189
+ import { useServerInsertedHTML } from 'next/navigation';
1190
+ import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
1191
+
1192
+ export default function StyledComponentsRegistry({
1193
+ children,
1194
+ }: {
1195
+ children: React.ReactNode;
1196
+ }) {
1197
+ const [jsxStyleSheet] = useState(() => new ServerStyleSheet());
1198
+
1199
+ useServerInsertedHTML(() => {
1200
+ const styles = jsxStyleSheet.getStyleElement();
1201
+ jsxStyleSheet.instance.clearTag();
1202
+ return <>{styles}</>;
1203
+ });
1204
+
1205
+ if (typeof window !== 'undefined') return <>{children}</>;
1206
+
1207
+ return (
1208
+ <StyleSheetManager sheet={jsxStyleSheet.instance}>
1209
+ {children}
1210
+ </StyleSheetManager>
1211
+ );
1212
+ }
1213
+ ` : `'use client';
1214
+
1215
+ import React, { useState } from 'react';
1216
+ import { useServerInsertedHTML } from 'next/navigation';
1217
+ import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
1218
+
1219
+ export default function StyledComponentsRegistry({ children }) {
1220
+ const [jsxStyleSheet] = useState(() => new ServerStyleSheet());
1221
+
1222
+ useServerInsertedHTML(() => {
1223
+ const styles = jsxStyleSheet.getStyleElement();
1224
+ jsxStyleSheet.instance.clearTag();
1225
+ return <>{styles}</>;
1226
+ });
1227
+
1228
+ if (typeof window !== 'undefined') return <>{children}</>;
1229
+
1230
+ return (
1231
+ <StyleSheetManager sheet={jsxStyleSheet.instance}>
1232
+ {children}
1233
+ </StyleSheetManager>
1234
+ );
1235
+ }
1236
+ `;
1237
+ addAppFile(context, `src/lib/registry.${ext}`, registryContent);
1238
+ }
1239
+ }
1240
+
1241
+ // src/generate/features/auth/index.ts
1242
+ async function generateAuthentication(context) {
1243
+ const { config } = context;
1244
+ const jsExt = config.typescript ? "ts" : "js";
1245
+ if (config.auth === "none") {
1246
+ return;
1247
+ }
1248
+ if (config.auth === "clerk") {
1249
+ addDependency(context, "@clerk/nextjs", dependencyVersions["@clerk/nextjs"]);
1250
+ addEnv(context, "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY");
1251
+ addEnv(context, "CLERK_SECRET_KEY");
1252
+ addEnv(context, "NEXT_PUBLIC_CLERK_SIGN_IN_URL", "/sign-in");
1253
+ addEnv(context, "NEXT_PUBLIC_CLERK_SIGN_UP_URL", "/sign-up");
1254
+ const middlewareContent = `import { clerkMiddleware } from "@clerk/nextjs/server";
1255
+
1256
+ export default clerkMiddleware();
1257
+
1258
+ export const config = {
1259
+ matcher: [
1260
+ // Skip Next.js internals and all static files, unless found in search params
1261
+ '/((?!_next|[^?]*\\\\.[\\\\w]+$|_next/image|_next/static|favicon.ico).*)',
1262
+ // Always run for API routes
1263
+ '/(api|trpc)(.*)',
1264
+ ],
1265
+ };
1266
+ `;
1267
+ addAppFile(context, `src/middleware.${jsExt}`, middlewareContent);
1268
+ } else if (config.auth === "next-auth") {
1269
+ addDependency(context, "next-auth", dependencyVersions["next-auth"]);
1270
+ addEnv(context, "AUTH_SECRET");
1271
+ addEnv(context, "AUTH_URL", "http://localhost:3000");
1272
+ const authConfigContent = `import NextAuth from "next-auth";
1273
+
1274
+ export const { handlers, auth, signIn, signOut } = NextAuth({
1275
+ providers: [],
1276
+ });
1277
+ `;
1278
+ addAppFile(context, `src/auth.${jsExt}`, authConfigContent);
1279
+ const routeContent = `import { handlers } from "@/auth";
1280
+ export const { GET, POST } = handlers;
1281
+ `;
1282
+ addAppFile(context, `src/app/api/auth/[...nextauth]/route.${jsExt}`, routeContent);
1283
+ const middlewareContent = `export { auth as middleware } from "@/auth";
1284
+ `;
1285
+ addAppFile(context, `src/middleware.${jsExt}`, middlewareContent);
1286
+ } else if (config.auth === "auth0") {
1287
+ addDependency(context, "@auth0/nextjs-auth0", dependencyVersions["@auth0/nextjs-auth0"]);
1288
+ addEnv(context, "AUTH0_SECRET");
1289
+ addEnv(context, "AUTH0_BASE_URL", "http://localhost:3000");
1290
+ addEnv(context, "AUTH0_ISSUER_BASE_URL");
1291
+ addEnv(context, "AUTH0_CLIENT_ID");
1292
+ addEnv(context, "AUTH0_CLIENT_SECRET");
1293
+ const routeContent = `import { handleAuth } from '@auth0/nextjs-auth0';
1294
+ export const GET = handleAuth();
1295
+ `;
1296
+ addAppFile(context, `src/app/api/auth/[auth0]/route.${jsExt}`, routeContent);
1297
+ }
1298
+ }
1299
+
1300
+ // src/generate/features/orm/index.ts
1301
+ async function generateOrm(context) {
1302
+ const { config } = context;
1303
+ const jsExt = config.typescript ? "ts" : "js";
1304
+ if (config.orm === "none") {
1305
+ return;
1306
+ }
1307
+ if (config.orm === "prisma") {
1308
+ addDependency(context, "@prisma/client", dependencyVersions["@prisma/client"]);
1309
+ addDevDependency(context, "prisma", dependencyVersions["prisma"]);
1310
+ addScript(context, "db:generate", "prisma generate");
1311
+ addScript(context, "db:migrate", "prisma migrate dev");
1312
+ addScript(context, "db:studio", "prisma studio");
1313
+ let prismaProvider = "postgresql";
1314
+ let defaultDbUrl = "";
1315
+ if (config.database === "mysql") {
1316
+ prismaProvider = "mysql";
1317
+ } else if (config.database === "sqlite") {
1318
+ prismaProvider = "sqlite";
1319
+ defaultDbUrl = "file:./dev.db";
1320
+ } else if (config.database === "mongo") {
1321
+ prismaProvider = "mongodb";
1322
+ }
1323
+ addEnv(context, "DATABASE_URL", defaultDbUrl);
1324
+ const schemaContent = `generator client {
1325
+ provider = "prisma-client-js"
1326
+ }
1327
+
1328
+ datasource db {
1329
+ provider = "${prismaProvider}"
1330
+ url = env("DATABASE_URL")
1331
+ }
1332
+ `;
1333
+ addProjectFile(context, "prisma/schema.prisma", schemaContent);
1334
+ const clientContent = config.typescript ? `import { PrismaClient } from '@prisma/client';
1335
+
1336
+ const globalForPrisma = globalThis as unknown as {
1337
+ prisma: PrismaClient | undefined;
1338
+ };
1339
+
1340
+ export const db = globalForPrisma.prisma ?? new PrismaClient();
1341
+
1342
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
1343
+ ` : `import { PrismaClient } from '@prisma/client';
1344
+
1345
+ const globalForPrisma = globalThis;
1346
+
1347
+ export const db = globalForPrisma.prisma ?? new PrismaClient();
1348
+
1349
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
1350
+ `;
1351
+ addAppFile(context, `src/lib/db.${jsExt}`, clientContent);
1352
+ }
1353
+ if (config.orm === "drizzle") {
1354
+ addDependency(context, "drizzle-orm", dependencyVersions["drizzle-orm"]);
1355
+ addDevDependency(context, "drizzle-kit", dependencyVersions["drizzle-kit"]);
1356
+ addScript(context, "db:generate", "drizzle-kit generate");
1357
+ addScript(context, "db:migrate", "drizzle-kit migrate");
1358
+ addScript(context, "db:studio", "drizzle-kit studio");
1359
+ let drizzleDialect = "postgresql";
1360
+ let defaultDbUrl = "";
1361
+ if (config.database === "postgres") {
1362
+ drizzleDialect = "postgresql";
1363
+ addDependency(context, "pg", dependencyVersions["pg"]);
1364
+ if (config.typescript) {
1365
+ addDevDependency(context, "@types/pg", dependencyVersions["@types/pg"]);
1366
+ }
1367
+ } else if (config.database === "mysql") {
1368
+ drizzleDialect = "mysql";
1369
+ addDependency(context, "mysql2", dependencyVersions["mysql2"]);
1370
+ } else if (config.database === "sqlite") {
1371
+ drizzleDialect = "sqlite";
1372
+ addDependency(context, "better-sqlite3", dependencyVersions["better-sqlite3"]);
1373
+ if (config.typescript) {
1374
+ addDevDependency(context, "@types/better-sqlite3", dependencyVersions["@types/better-sqlite3"]);
1375
+ }
1376
+ defaultDbUrl = "sqlite.db";
1377
+ }
1378
+ addEnv(context, "DATABASE_URL", defaultDbUrl);
1379
+ const drizzleConfigContent = `import { defineConfig } from 'drizzle-kit';
1380
+
1381
+ export default defineConfig({
1382
+ schema: './src/db/schema.${jsExt}',
1383
+ out: './drizzle',
1384
+ dialect: '${drizzleDialect}',
1385
+ dbCredentials: {
1386
+ url: process.env.DATABASE_URL!,
1387
+ },
1388
+ });
1389
+ `;
1390
+ addAppFile(context, `drizzle.config.${jsExt}`, drizzleConfigContent);
1391
+ addAppFile(context, `src/db/schema.${jsExt}`, "// Define your database schema here\n");
1392
+ let clientContent = "";
1393
+ if (config.database === "postgres") {
1394
+ clientContent = `import { drizzle } from 'drizzle-orm/node-postgres';
1395
+ import pg from 'pg';
1396
+ import * as schema from './schema';
1397
+
1398
+ const pool = new pg.Pool({
1399
+ connectionString: process.env.DATABASE_URL,
1400
+ });
1401
+
1402
+ export const db = drizzle(pool, { schema });
1403
+ `;
1404
+ } else if (config.database === "mysql") {
1405
+ clientContent = `import { drizzle } from 'drizzle-orm/mysql2';
1406
+ import mysql from 'mysql2/promise';
1407
+ import * as schema from './schema';
1408
+
1409
+ const pool = mysql.createPool({
1410
+ uri: process.env.DATABASE_URL,
1411
+ });
1412
+
1413
+ export const db = drizzle(pool, { schema });
1414
+ `;
1415
+ } else if (config.database === "sqlite") {
1416
+ clientContent = `import { drizzle } from 'drizzle-orm/better-sqlite3';
1417
+ import Database from 'better-sqlite3';
1418
+ import * as schema from './schema';
1419
+
1420
+ const sqlite = new Database(process.env.DATABASE_URL || 'sqlite.db');
1421
+ export const db = drizzle(sqlite, { schema });
1422
+ `;
1423
+ }
1424
+ addAppFile(context, `src/db/index.${jsExt}`, clientContent);
1425
+ }
1426
+ }
1427
+
1428
+ // src/generate/features/state-management/index.ts
1429
+ async function generateStateManagement(context) {
1430
+ const { config } = context;
1431
+ const ext = config.typescript ? "tsx" : "jsx";
1432
+ const jsExt = config.typescript ? "ts" : "js";
1433
+ if (config.stateManagement === "none") {
1434
+ return;
1435
+ }
1436
+ if (config.stateManagement === "zustand") {
1437
+ addDependency(context, "zustand", dependencyVersions["zustand"]);
1438
+ const storeContent = config.typescript ? `import { create } from 'zustand';
1439
+
1440
+ interface CounterState {
1441
+ count: number;
1442
+ increment: () => void;
1443
+ decrement: () => void;
1444
+ }
1445
+
1446
+ export const useCounterStore = create<CounterState>((set) => ({
1447
+ count: 0,
1448
+ increment: () => set((state) => ({ count: state.count + 1 })),
1449
+ decrement: () => set((state) => ({ count: state.count - 1 })),
1450
+ }));
1451
+ ` : `import { create } from 'zustand';
1452
+
1453
+ export const useCounterStore = create((set) => ({
1454
+ count: 0,
1455
+ increment: () => set((state) => ({ count: state.count + 1 })),
1456
+ decrement: () => set((state) => ({ count: state.count - 1 })),
1457
+ }));
1458
+ `;
1459
+ addAppFile(context, `src/stores/counter-store.${jsExt}`, storeContent);
1460
+ } else if (config.stateManagement === "redux") {
1461
+ addDependency(context, "@reduxjs/toolkit", dependencyVersions["@reduxjs/toolkit"]);
1462
+ addDependency(context, "react-redux", dependencyVersions["react-redux"]);
1463
+ const storeContent = config.typescript ? `import { configureStore } from '@reduxjs/toolkit';
1464
+
1465
+ export const makeStore = () => {
1466
+ return configureStore({
1467
+ reducer: {},
1468
+ });
1469
+ };
1470
+
1471
+ export type AppStore = ReturnType<typeof makeStore>;
1472
+ export type RootState = ReturnType<AppStore['getState']>;
1473
+ export type AppDispatch = AppStore['dispatch'];
1474
+ ` : `import { configureStore } from '@reduxjs/toolkit';
1475
+
1476
+ export const makeStore = () => {
1477
+ return configureStore({
1478
+ reducer: {},
1479
+ });
1480
+ };
1481
+ `;
1482
+ addAppFile(context, `src/lib/store.${jsExt}`, storeContent);
1483
+ const providerContent = config.typescript ? `'use client';
1484
+ import { useRef } from 'react';
1485
+ import { Provider } from 'react-redux';
1486
+ import { makeStore, AppStore } from '../lib/store';
1487
+
1488
+ export default function StoreProvider({
1489
+ children,
1490
+ }: {
1491
+ children: React.ReactNode;
1492
+ }) {
1493
+ const storeRef = useRef<AppStore>(undefined);
1494
+ if (!storeRef.current) {
1495
+ storeRef.current = makeStore();
1496
+ }
1497
+
1498
+ return <Provider store={storeRef.current}>{children}</Provider>;
1499
+ }
1500
+ ` : `'use client';
1501
+ import { useRef } from 'react';
1502
+ import { Provider } from 'react-redux';
1503
+ import { makeStore } from '../lib/store';
1504
+
1505
+ export default function StoreProvider({ children }) {
1506
+ const storeRef = useRef();
1507
+ if (!storeRef.current) {
1508
+ storeRef.current = makeStore();
1509
+ }
1510
+
1511
+ return <Provider store={storeRef.current}>{children}</Provider>;
1512
+ }
1513
+ `;
1514
+ addAppFile(context, `src/app/StoreProvider.${ext}`, providerContent);
1515
+ } else if (config.stateManagement === "jotai") {
1516
+ addDependency(context, "jotai", dependencyVersions["jotai"]);
1517
+ const providerContent = config.typescript ? `'use client';
1518
+ import { Provider } from 'jotai';
1519
+
1520
+ export default function JotaiProvider({
1521
+ children,
1522
+ }: {
1523
+ children: React.ReactNode;
1524
+ }) {
1525
+ return <Provider>{children}</Provider>;
1526
+ }
1527
+ ` : `'use client';
1528
+ import { Provider } from 'jotai';
1529
+
1530
+ export default function JotaiProvider({ children }) {
1531
+ return <Provider>{children}</Provider>;
1532
+ }
1533
+ `;
1534
+ addAppFile(context, `src/app/JotaiProvider.${ext}`, providerContent);
1535
+ }
1536
+ }
1537
+
1538
+ // src/generate/features/testing/index.ts
1539
+ async function generateTesting(context) {
1540
+ const { config } = context;
1541
+ const ext = config.typescript ? "tsx" : "jsx";
1542
+ const jsExt = config.typescript ? "ts" : "js";
1543
+ const hasVitest = config.testing.includes("vitest");
1544
+ const hasJest = config.testing.includes("jest");
1545
+ const hasPlaywright = config.testing.includes("playwright");
1546
+ const hasCypress = config.testing.includes("cypress");
1547
+ if (config.testing.length === 0) {
1548
+ return;
1549
+ }
1550
+ const pmRun = config.packageManager === "yarn" ? "yarn" : `${config.packageManager} run`;
1551
+ if (hasVitest) {
1552
+ addDevDependency(context, "vitest", dependencyVersions["vitest"]);
1553
+ addDevDependency(context, "@vitejs/plugin-react", dependencyVersions["@vitejs/plugin-react"]);
1554
+ addDevDependency(context, "jsdom", dependencyVersions["jsdom"]);
1555
+ addDevDependency(context, "@testing-library/react", dependencyVersions["@testing-library/react"]);
1556
+ addDevDependency(context, "@testing-library/jest-dom", dependencyVersions["@testing-library/jest-dom"]);
1557
+ addScript(context, "test:vitest", "vitest run");
1558
+ const vitestConfig = `import { defineConfig } from 'vitest/config';
1559
+ import react from '@vitejs/plugin-react';
1560
+ import path from 'path';
1561
+
1562
+ export default defineConfig({
1563
+ plugins: [react()],
1564
+ test: {
1565
+ environment: 'jsdom',
1566
+ globals: true,
1567
+ setupFiles: ['./vitest.setup.${jsExt}'],
1568
+ },
1569
+ resolve: {
1570
+ alias: {
1571
+ '@': path.resolve(__dirname, './src'),
1572
+ },
1573
+ },
1574
+ });
1575
+ `;
1576
+ addAppFile(context, `vitest.config.${jsExt}`, vitestConfig);
1577
+ addAppFile(context, `vitest.setup.${jsExt}`, `import '@testing-library/jest-dom';
1578
+ `);
1579
+ }
1580
+ if (hasJest) {
1581
+ addDevDependency(context, "jest", dependencyVersions["jest"]);
1582
+ addDevDependency(context, "jest-environment-jsdom", dependencyVersions["jest-environment-jsdom"]);
1583
+ addDevDependency(context, "@testing-library/react", dependencyVersions["@testing-library/react"]);
1584
+ addDevDependency(context, "@testing-library/jest-dom", dependencyVersions["@testing-library/jest-dom"]);
1585
+ if (config.typescript) {
1586
+ addDevDependency(context, "ts-jest", dependencyVersions["ts-jest"]);
1587
+ addDevDependency(context, "ts-node", dependencyVersions["ts-node"]);
1588
+ addDevDependency(context, "@types/jest", dependencyVersions["@types/jest"]);
1589
+ }
1590
+ addScript(context, "test:jest", "jest");
1591
+ const jestConfig = config.typescript ? `import type { Config } from 'jest';
1592
+
1593
+ const config: Config = {
1594
+ preset: 'ts-jest',
1595
+ testEnvironment: 'jest-environment-jsdom',
1596
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
1597
+ moduleNameMapper: {
1598
+ '^@/(.*)$': '<rootDir>/src/$1',
1599
+ '\\\\.css$': 'identity-obj-proxy',
1600
+ },
1601
+ };
1602
+
1603
+ export default config;
1604
+ ` : `/** @type {import('jest').Config} */
1605
+ const config = {
1606
+ testEnvironment: 'jest-environment-jsdom',
1607
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
1608
+ moduleNameMapper: {
1609
+ '^@/(.*)$': '<rootDir>/src/$1',
1610
+ '\\\\.css$': 'identity-obj-proxy',
1611
+ },
1612
+ };
1613
+
1614
+ export default config;
1615
+ `;
1616
+ addAppFile(context, `jest.config.${jsExt}`, jestConfig);
1617
+ addAppFile(context, `jest.setup.${jsExt}`, `import '@testing-library/jest-dom';
1618
+ `);
1619
+ }
1620
+ if (hasVitest || hasJest) {
1621
+ const cleanSmokeTest = `import { render, screen } from '@testing-library/react';
1622
+ import Page from './page';
1623
+
1624
+ test('renders welcome page', () => {
1625
+ render(<Page />);
1626
+ const heading = screen.getByRole('heading', { level: 1 });
1627
+ expect(heading).toBeInTheDocument();
1628
+ });
1629
+ `;
1630
+ addAppFile(context, `src/app/page.test.${ext}`, cleanSmokeTest);
1631
+ }
1632
+ if (hasPlaywright) {
1633
+ addDevDependency(context, "@playwright/test", dependencyVersions["@playwright/test"]);
1634
+ addScript(context, "test:e2e", "playwright test");
1635
+ const devCommand = config.packageManager === "npm" ? "npm run dev" : `${config.packageManager} dev`;
1636
+ const playwrightConfig = `import { defineConfig, devices } from '@playwright/test';
1637
+
1638
+ export default defineConfig({
1639
+ testDir: './tests/e2e',
1640
+ fullyParallel: true,
1641
+ forbidOnly: !!process.env.CI,
1642
+ retries: process.env.CI ? 2 : 0,
1643
+ workers: process.env.CI ? 1 : undefined,
1644
+ reporter: 'html',
1645
+ use: {
1646
+ baseURL: 'http://localhost:3000',
1647
+ trace: 'on-first-retry',
1648
+ },
1649
+ projects: [
1650
+ {
1651
+ name: 'chromium',
1652
+ use: { ...devices['Desktop Chrome'] },
1653
+ },
1654
+ ],
1655
+ webServer: {
1656
+ command: '${devCommand}',
1657
+ url: 'http://localhost:3000',
1658
+ reuseExistingServer: !process.env.CI,
1659
+ },
1660
+ });
1661
+ `;
1662
+ addAppFile(context, `playwright.config.${jsExt}`, playwrightConfig);
1663
+ const playwrightSmokeTest = `import { test, expect } from '@playwright/test';
1664
+
1665
+ test('should display home page', async ({ page }) => {
1666
+ await page.goto('/');
1667
+ await expect(page.locator('h1')).toContainText('create-enterprise-next');
1668
+ });
1669
+ `;
1670
+ addAppFile(context, `tests/e2e/smoke.spec.${jsExt}`, playwrightSmokeTest);
1671
+ }
1672
+ if (hasCypress) {
1673
+ addDevDependency(context, "cypress", dependencyVersions["cypress"]);
1674
+ addScript(context, "test:cypress", "cypress run");
1675
+ addScript(context, "cypress:open", "cypress open");
1676
+ const cypressConfig = `import { defineConfig } from 'cypress';
1677
+
1678
+ export default defineConfig({
1679
+ e2e: {
1680
+ baseUrl: 'http://localhost:3000',
1681
+ supportFile: false,
1682
+ },
1683
+ });
1684
+ `;
1685
+ addAppFile(context, `cypress.config.${jsExt}`, cypressConfig);
1686
+ const cypressSmokeTest = `describe('Smoke Test', () => {
1687
+ it('should display home page', () => {
1688
+ cy.visit('/');
1689
+ cy.get('h1').should('contain', 'create-enterprise-next');
1690
+ });
1691
+ });
1692
+ `;
1693
+ addAppFile(context, `cypress/e2e/smoke.cy.${jsExt}`, cypressSmokeTest);
1694
+ }
1695
+ if (hasVitest && hasJest) {
1696
+ addScript(context, "test", `${pmRun} test:vitest && ${pmRun} test:jest`);
1697
+ } else if (hasVitest) {
1698
+ addScript(context, "test", `${pmRun} test:vitest`);
1699
+ } else if (hasJest) {
1700
+ addScript(context, "test", `${pmRun} test:jest`);
1701
+ }
1702
+ }
1703
+
1704
+ // src/generate/features/ci/index.ts
1705
+ async function generateCi(context) {
1706
+ const { config } = context;
1707
+ if (config.ci === "none") {
1708
+ return;
1709
+ }
1710
+ const pm = config.packageManager;
1711
+ const hasTesting = config.testing.length > 0;
1712
+ let installCmd = "npm ci";
1713
+ let runPrefix = "npm run";
1714
+ let cacheName = "npm";
1715
+ if (pm === "pnpm") {
1716
+ installCmd = "pnpm install";
1717
+ runPrefix = "pnpm run";
1718
+ cacheName = "pnpm";
1719
+ } else if (pm === "yarn") {
1720
+ installCmd = "yarn install";
1721
+ runPrefix = "yarn";
1722
+ cacheName = "yarn";
1723
+ } else if (pm === "bun") {
1724
+ installCmd = "bun install";
1725
+ runPrefix = "bun run";
1726
+ cacheName = "bun";
1727
+ }
1728
+ if (config.ci === "github-actions") {
1729
+ let steps = ` - name: Checkout repository
1730
+ uses: actions/checkout@v4
1731
+
1732
+ - name: Setup Node.js
1733
+ uses: actions/setup-node@v4
1734
+ with:
1735
+ node-version: 20
1736
+ `;
1737
+ if (pm === "pnpm") {
1738
+ steps += `
1739
+ - name: Setup pnpm
1740
+ uses: pnpm/action-setup@v3
1741
+ with:
1742
+ version: 9
1743
+ `;
1744
+ } else if (pm === "bun") {
1745
+ steps += `
1746
+ - name: Setup Bun
1747
+ uses: oven-sh/setup-bun@v1
1748
+ with:
1749
+ bun-version: latest
1750
+ `;
1751
+ }
1752
+ if (pm === "npm" || pm === "yarn") {
1753
+ steps += ` cache: '${cacheName}'
1754
+ `;
1755
+ }
1756
+ steps += `
1757
+ - name: Install dependencies
1758
+ run: ${installCmd}
1759
+
1760
+ - name: Lint
1761
+ run: ${runPrefix} lint
1762
+ `;
1763
+ if (config.typescript) {
1764
+ steps += `
1765
+ - name: Typecheck
1766
+ run: ${runPrefix} typecheck
1767
+ `;
1768
+ }
1769
+ if (hasTesting) {
1770
+ steps += `
1771
+ - name: Run tests
1772
+ run: ${runPrefix} test
1773
+ `;
1774
+ }
1775
+ steps += `
1776
+ - name: Build
1777
+ run: ${runPrefix} build
1778
+ `;
1779
+ const workflowContent = `name: CI
1780
+
1781
+ on:
1782
+ push:
1783
+ branches: [ main, master ]
1784
+ pull_request:
1785
+ branches: [ main, master ]
1786
+
1787
+ jobs:
1788
+ validate:
1789
+ runs-on: ubuntu-latest
1790
+
1791
+ steps:
1792
+ ${steps}
1793
+ `;
1794
+ addProjectFile(context, ".github/workflows/ci.yml", workflowContent);
1795
+ }
1796
+ if (config.ci === "gitlab-ci") {
1797
+ let beforeScript = "";
1798
+ let image = "node:20";
1799
+ if (pm === "pnpm") {
1800
+ beforeScript = ` - corepack enable pnpm
1801
+ - pnpm install
1802
+ `;
1803
+ } else if (pm === "bun") {
1804
+ image = "oven/bun:latest";
1805
+ beforeScript = ` - bun install
1806
+ `;
1807
+ } else if (pm === "yarn") {
1808
+ beforeScript = ` - yarn install
1809
+ `;
1810
+ } else {
1811
+ beforeScript = ` - npm ci
1812
+ `;
1813
+ }
1814
+ let scriptSteps = ` - ${runPrefix} lint
1815
+ `;
1816
+ if (config.typescript) {
1817
+ scriptSteps += ` - ${runPrefix} typecheck
1818
+ `;
1819
+ }
1820
+ if (hasTesting) {
1821
+ scriptSteps += ` - ${runPrefix} test
1822
+ `;
1823
+ }
1824
+ scriptSteps += ` - ${runPrefix} build`;
1825
+ const gitlabContent = `image: ${image}
1826
+
1827
+ cache:
1828
+ paths:
1829
+ - node_modules/
1830
+ ${pm === "pnpm" ? "- .pnpm-store/" : ""}
1831
+
1832
+ stages:
1833
+ - validate
1834
+
1835
+ validate_job:
1836
+ stage: validate
1837
+ before_script:
1838
+ ${beforeScript}
1839
+ script:
1840
+ ${scriptSteps}
1841
+ `;
1842
+ addProjectFile(context, ".gitlab-ci.yml", gitlabContent);
1843
+ }
1844
+ }
1845
+
1846
+ // src/generate/features/docker/index.ts
1847
+ async function generateDocker(context) {
1848
+ const { config } = context;
1849
+ if (!config.docker) {
1850
+ return;
1851
+ }
1852
+ const dockerfileContent = `# Multi-stage production Dockerfile for Next.js standalone output
1853
+ FROM node:20-alpine AS base
1854
+
1855
+ # Stage 1: Install dependencies
1856
+ FROM base AS deps
1857
+ RUN apk add --no-cache libc6-compat
1858
+ WORKDIR /app
1859
+
1860
+ # Copy lockfiles and package definitions
1861
+ COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* bun.lockb* ./
1862
+
1863
+ RUN \\
1864
+ if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \\
1865
+ elif [ -f package-lock.json ]; then npm ci; \\
1866
+ elif [ -f yarn.lock ]; then yarn --frozen-lockfile; \\
1867
+ elif [ -f bun.lockb ]; then bun install --frozen-lockfile; \\
1868
+ else echo "Warning: No lockfile found. Installing normal dependencies." && npm install; \\
1869
+ fi
1870
+
1871
+ # Stage 2: Build the application
1872
+ FROM base AS builder
1873
+ WORKDIR /app
1874
+ COPY --from=deps /app/node_modules ./node_modules
1875
+ COPY . .
1876
+
1877
+ # Disable Next.js telemetry during build
1878
+ ENV NEXT_TELEMETRY_DISABLED=1
1879
+
1880
+ RUN \\
1881
+ if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \\
1882
+ elif [ -f package-lock.json ]; then npm run build; \\
1883
+ elif [ -f yarn.lock ]; then yarn build; \\
1884
+ elif [ -f bun.lockb ]; then bun run build; \\
1885
+ else npm run build; \\
1886
+ fi
1887
+
1888
+ # Stage 3: Production runner
1889
+ FROM base AS runner
1890
+ WORKDIR /app
1891
+
1892
+ ENV NODE_ENV=production
1893
+ ENV NEXT_TELEMETRY_DISABLED=1
1894
+
1895
+ RUN addgroup --system --gid 1001 nodejs
1896
+ RUN adduser --system --uid 1001 nextjs
1897
+
1898
+ # Copy static assets and public directory
1899
+ COPY --from=builder /app/public ./public
1900
+
1901
+ # Setup prerender cache directory permissions
1902
+ RUN mkdir .next
1903
+ RUN chown nextjs:nodejs .next
1904
+
1905
+ # Leverage output traces to reduce image size (standalone output copy)
1906
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
1907
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
1908
+
1909
+ USER nextjs
1910
+
1911
+ EXPOSE 3000
1912
+ ENV PORT=3000
1913
+ ENV HOSTNAME="0.0.0.0"
1914
+
1915
+ CMD ["node", "server.js"]
1916
+ `;
1917
+ const dockerignoreContent = `node_modules
1918
+ .next
1919
+ out
1920
+ build
1921
+ .dockerignore
1922
+ Dockerfile
1923
+ .git
1924
+ .github
1925
+ .gitlab-ci.yml
1926
+ README.md
1927
+ `;
1928
+ addAppFile(context, "Dockerfile", dockerfileContent);
1929
+ addAppFile(context, ".dockerignore", dockerignoreContent);
1930
+ }
1931
+
1932
+ // src/generate/features/monorepo/index.ts
1933
+ async function generateMonorepo(context) {
1934
+ const { config } = context;
1935
+ if (!config.monorepo) {
1936
+ return;
1937
+ }
1938
+ addRootDevDependency(context, "turbo", dependencyVersions["turbo"]);
1939
+ const workspaceContent = `packages:
1940
+ - 'apps/*'
1941
+ - 'packages/*'
1942
+ `;
1943
+ addProjectFile(context, "pnpm-workspace.yaml", workspaceContent);
1944
+ const turboContent = {
1945
+ $schema: "https://turbo.build/schema.json",
1946
+ tasks: {
1947
+ build: {
1948
+ dependsOn: ["^build"],
1949
+ outputs: [".next/**", "!.next/cache/**"]
1950
+ },
1951
+ lint: {},
1952
+ typecheck: {}
1953
+ }
1954
+ };
1955
+ addProjectFile(context, "turbo.json", JSON.stringify(turboContent, null, 2) + "\n");
1956
+ const configPkg = {
1957
+ name: "@enterprise/config",
1958
+ version: "0.0.0",
1959
+ private: true
1960
+ };
1961
+ addProjectFile(context, "packages/config/package.json", JSON.stringify(configPkg, null, 2) + "\n");
1962
+ const typesPkg = {
1963
+ name: "@enterprise/types",
1964
+ version: "0.0.0",
1965
+ private: true
1966
+ };
1967
+ addProjectFile(context, "packages/types/package.json", JSON.stringify(typesPkg, null, 2) + "\n");
1968
+ const uiPkg = {
1969
+ name: "@enterprise/ui",
1970
+ version: "0.0.0",
1971
+ private: true
1972
+ };
1973
+ addProjectFile(context, "packages/ui/package.json", JSON.stringify(uiPkg, null, 2) + "\n");
1974
+ }
1975
+
1976
+ // src/generate/package-json.ts
1977
+ function compilePackageJsons(context) {
1978
+ const { config, manifest } = context;
1979
+ const sortKeys = (obj) => {
1980
+ const sorted = {};
1981
+ for (const key of Object.keys(obj).sort()) {
1982
+ sorted[key] = obj[key];
1983
+ }
1984
+ return sorted;
1985
+ };
1986
+ const appScripts = {
1987
+ dev: "next dev",
1988
+ build: "next build",
1989
+ start: "next start",
1990
+ lint: "next lint",
1991
+ ...manifest.scripts
1992
+ };
1993
+ if (config.typescript) {
1994
+ appScripts.typecheck = "tsc --noEmit";
1995
+ }
1996
+ const appPkg = {
1997
+ name: config.monorepo ? "web" : config.projectName,
1998
+ version: "0.1.0",
1999
+ private: true,
2000
+ type: "module",
2001
+ scripts: sortKeys(appScripts),
2002
+ dependencies: sortKeys(manifest.dependencies),
2003
+ devDependencies: sortKeys(manifest.devDependencies)
2004
+ };
2005
+ if (config.monorepo) {
2006
+ context.manifest.files.set(
2007
+ "apps/web/package.json",
2008
+ JSON.stringify(appPkg, null, 2) + "\n"
2009
+ );
2010
+ const rootScripts = {
2011
+ dev: "turbo dev",
2012
+ build: "turbo build",
2013
+ lint: "turbo lint",
2014
+ ...manifest.rootScripts
2015
+ };
2016
+ if (config.typescript) {
2017
+ rootScripts.typecheck = "turbo typecheck";
2018
+ }
2019
+ const rootPkg = {
2020
+ name: config.projectName,
2021
+ version: "0.1.0",
2022
+ private: true,
2023
+ type: "module",
2024
+ workspaces: ["apps/*", "packages/*"],
2025
+ scripts: sortKeys(rootScripts),
2026
+ dependencies: sortKeys(manifest.rootDependencies),
2027
+ devDependencies: sortKeys(manifest.rootDevDependencies)
2028
+ };
2029
+ context.manifest.files.set(
2030
+ "package.json",
2031
+ JSON.stringify(rootPkg, null, 2) + "\n"
2032
+ );
2033
+ } else {
2034
+ context.manifest.files.set(
2035
+ "package.json",
2036
+ JSON.stringify(appPkg, null, 2) + "\n"
2037
+ );
2038
+ }
2039
+ }
2040
+
2041
+ // src/generate/summary.ts
430
2042
  import pc2 from "picocolors";
2043
+ function printGenerationSummary(context) {
2044
+ const { config } = context;
2045
+ const getTestingLabel = () => {
2046
+ if (config.testing.length === 0) return "None";
2047
+ return config.testing.map((t) => {
2048
+ if (t === "vitest") return "Vitest";
2049
+ if (t === "jest") return "Jest";
2050
+ if (t === "playwright") return "Playwright";
2051
+ if (t === "cypress") return "Cypress";
2052
+ return t;
2053
+ }).join(", ");
2054
+ };
2055
+ const getStylingLabel = () => {
2056
+ if (config.styling === "tailwind") return "Tailwind CSS";
2057
+ if (config.styling === "css-modules") return "CSS Modules";
2058
+ if (config.styling === "styled-components") return "Styled Components";
2059
+ return config.styling;
2060
+ };
2061
+ const getAuthLabel = () => {
2062
+ if (config.auth === "none") return "None";
2063
+ if (config.auth === "clerk") return "Clerk";
2064
+ if (config.auth === "next-auth") return "NextAuth.js";
2065
+ if (config.auth === "auth0") return "Auth0";
2066
+ return config.auth;
2067
+ };
2068
+ const getOrmLabel = () => {
2069
+ if (config.orm === "none") return "None";
2070
+ if (config.orm === "prisma") return "Prisma";
2071
+ if (config.orm === "drizzle") return "Drizzle";
2072
+ return config.orm;
2073
+ };
2074
+ const getDbLabel = () => {
2075
+ if (!config.database) return "None";
2076
+ if (config.database === "postgres") return "PostgreSQL";
2077
+ if (config.database === "mysql") return "MySQL";
2078
+ if (config.database === "sqlite") return "SQLite";
2079
+ if (config.database === "mongo") return "MongoDB";
2080
+ return config.database;
2081
+ };
2082
+ const getCiLabel = () => {
2083
+ if (config.ci === "none") return "None";
2084
+ if (config.ci === "github-actions") return "GitHub Actions";
2085
+ if (config.ci === "gitlab-ci") return "GitLab CI";
2086
+ return config.ci;
2087
+ };
2088
+ const getStateLabel = () => {
2089
+ if (config.stateManagement === "none") return "None";
2090
+ if (config.stateManagement === "zustand") return "Zustand";
2091
+ if (config.stateManagement === "redux") return "Redux Toolkit";
2092
+ if (config.stateManagement === "jotai") return "Jotai";
2093
+ return config.stateManagement;
2094
+ };
2095
+ console.log("\n" + pc2.green("\u2714 Project created successfully") + "\n");
2096
+ console.log(` ${pc2.bold("Project:")} ${config.projectName}`);
2097
+ console.log(` ${pc2.bold("Language:")} ${config.typescript ? "TypeScript" : "JavaScript"}`);
2098
+ console.log(` ${pc2.bold("Framework:")} Next.js (App Router)`);
2099
+ console.log(` ${pc2.bold("Styling:")} ${getStylingLabel()}`);
2100
+ console.log(` ${pc2.bold("Auth:")} ${getAuthLabel()}`);
2101
+ console.log(` ${pc2.bold("ORM:")} ${getOrmLabel()}`);
2102
+ if (config.orm !== "none") {
2103
+ console.log(` ${pc2.bold("Database:")} ${getDbLabel()}`);
2104
+ }
2105
+ console.log(` ${pc2.bold("State Mgmt:")} ${getStateLabel()}`);
2106
+ console.log(` ${pc2.bold("Testing:")} ${getTestingLabel()}`);
2107
+ console.log(` ${pc2.bold("CI:")} ${getCiLabel()}`);
2108
+ console.log(` ${pc2.bold("Docker:")} ${config.docker ? "Yes" : "No"}`);
2109
+ console.log(` ${pc2.bold("Monorepo:")} ${config.monorepo ? "Yes (Turborepo)" : "No"}`);
2110
+ console.log(` ${pc2.bold("Package Mgr:")} ${config.packageManager}`);
2111
+ console.log();
2112
+ console.log(pc2.cyan("Next steps:"));
2113
+ console.log();
2114
+ console.log(` cd ${config.projectName}`);
2115
+ const pm = config.packageManager;
2116
+ if (pm === "npm") {
2117
+ console.log(` npm install`);
2118
+ console.log(` npm run dev`);
2119
+ } else if (pm === "pnpm") {
2120
+ console.log(` pnpm install`);
2121
+ console.log(` pnpm dev`);
2122
+ } else if (pm === "yarn") {
2123
+ console.log(` yarn`);
2124
+ console.log(` yarn dev`);
2125
+ } else if (pm === "bun") {
2126
+ console.log(` bun install`);
2127
+ console.log(` bun dev`);
2128
+ }
2129
+ console.log();
2130
+ }
2131
+
2132
+ // src/generate/generate-project.ts
2133
+ function compileEnvExample(context) {
2134
+ const keys = Object.keys(context.manifest.env);
2135
+ let content = "# Environment Variables\n\n";
2136
+ if (keys.length === 0) {
2137
+ content += "# No environment variables required for this setup.\n";
2138
+ } else {
2139
+ for (const key of keys) {
2140
+ content += `${key}=${context.manifest.env[key] || ""}
2141
+ `;
2142
+ }
2143
+ }
2144
+ addAppFile(context, ".env.example", content);
2145
+ }
2146
+ async function generateProject(config, cwd) {
2147
+ const context = createGenerationContext(config, cwd);
2148
+ const { targetDir, manifest } = context;
2149
+ const s = clack2.spinner();
2150
+ s.start("Initializing project structure");
2151
+ try {
2152
+ s.message("Configuring base Next.js templates");
2153
+ await generateBaseProject(context);
2154
+ s.message("Configuring styling");
2155
+ await generateStyling(context);
2156
+ s.message("Configuring authentication");
2157
+ await generateAuthentication(context);
2158
+ s.message("Configuring database & ORM");
2159
+ await generateOrm(context);
2160
+ s.message("Configuring state management");
2161
+ await generateStateManagement(context);
2162
+ s.message("Configuring testing suites");
2163
+ await generateTesting(context);
2164
+ s.message("Configuring CI workflows");
2165
+ await generateCi(context);
2166
+ s.message("Configuring Docker environment");
2167
+ await generateDocker(context);
2168
+ s.message("Configuring monorepo structures");
2169
+ await generateMonorepo(context);
2170
+ generateReadme(context);
2171
+ compileEnvExample(context);
2172
+ compilePackageJsons(context);
2173
+ s.message("Validating destination directory");
2174
+ const isTargetEmpty = await directoryIsEmpty(targetDir, ".");
2175
+ if (!isTargetEmpty) {
2176
+ throw new Error(`Destination directory "${targetDir}" is not empty.`);
2177
+ }
2178
+ s.message("Writing files to disk");
2179
+ await ensureDirectory(targetDir, ".");
2180
+ for (const [relPath, content] of manifest.files.entries()) {
2181
+ await writeFile(targetDir, relPath, content);
2182
+ }
2183
+ s.stop("Project configuration completed successfully");
2184
+ printGenerationSummary(context);
2185
+ } catch (error) {
2186
+ s.stop("Generation failed", 1);
2187
+ throw error;
2188
+ }
2189
+ }
2190
+
2191
+ // src/utils/logger.ts
2192
+ import pc3 from "picocolors";
431
2193
  var logger = {
432
- info: (msg) => console.log(pc2.blue("\u2139 ") + msg),
433
- success: (msg) => console.log(pc2.green("\u2714 ") + msg),
434
- warn: (msg) => console.warn(pc2.yellow("\u26A0 ") + msg),
435
- error: (msg) => console.error(pc2.red("\u2716 ") + msg),
2194
+ info: (msg) => console.log(pc3.blue("\u2139 ") + msg),
2195
+ success: (msg) => console.log(pc3.green("\u2714 ") + msg),
2196
+ warn: (msg) => console.warn(pc3.yellow("\u26A0 ") + msg),
2197
+ error: (msg) => console.error(pc3.red("\u2716 ") + msg),
436
2198
  log: (msg) => console.log(msg)
437
2199
  };
438
2200
 
@@ -440,10 +2202,10 @@ var logger = {
440
2202
  function getPackageVersion() {
441
2203
  try {
442
2204
  const __filename = fileURLToPath(import.meta.url);
443
- const __dirname = path3.dirname(__filename);
444
- const pkgPath = path3.resolve(__dirname, "../package.json");
445
- if (fs3.existsSync(pkgPath)) {
446
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
2205
+ const __dirname = path4.dirname(__filename);
2206
+ const pkgPath = path4.resolve(__dirname, "../package.json");
2207
+ if (fs4.existsSync(pkgPath)) {
2208
+ const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
447
2209
  if (pkg.version) return pkg.version;
448
2210
  }
449
2211
  } catch {
@@ -479,8 +2241,7 @@ async function main() {
479
2241
  console.log(JSON.stringify(config, null, 2));
480
2242
  process.exit(0);
481
2243
  }
482
- console.log(JSON.stringify(config, null, 2));
483
- logger.success("Config resolved \u2014 scaffolding logic not yet implemented");
2244
+ await generateProject(config);
484
2245
  } catch (err) {
485
2246
  logger.error(err instanceof Error ? err.message : String(err));
486
2247
  process.exit(1);