@better-t-stack/template-generator 3.31.1 → 3.33.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.mjs CHANGED
@@ -187,6 +187,7 @@ Handlebars.registerHelper("eq", (a, b) => a === b);
187
187
  Handlebars.registerHelper("ne", (a, b) => a !== b);
188
188
  Handlebars.registerHelper("and", (...args) => args.slice(0, -1).every(Boolean));
189
189
  Handlebars.registerHelper("or", (...args) => args.slice(0, -1).some(Boolean));
190
+ Handlebars.registerHelper("not", (a) => !a);
190
191
  Handlebars.registerHelper("includes", (arr, val) => Array.isArray(arr) && arr.includes(val));
191
192
  function processTemplateString(content, context) {
192
193
  return Handlebars.compile(content)(context);
@@ -199,6 +200,7 @@ function transformFilename(filename) {
199
200
  const basename = result.split("/").pop() || result;
200
201
  if (basename === "_gitignore") result = result.replace(/_gitignore$/, ".gitignore");
201
202
  else if (basename === "_npmrc") result = result.replace(/_npmrc$/, ".npmrc");
203
+ else if (basename === "_dockerignore") result = result.replace(/_dockerignore$/, ".dockerignore");
202
204
  return result;
203
205
  }
204
206
  function processFileContent(filePath, content, context) {
@@ -380,6 +382,157 @@ function updatePackageJsonsWithCatalogs(vfs, packagesInfo, catalog) {
380
382
  }
381
383
  }
382
384
  //#endregion
385
+ //#region src/utils/add-deps.ts
386
+ const dependencyVersionMap = {
387
+ typescript: "^6",
388
+ "better-auth": "1.6.11",
389
+ "@better-auth/expo": "1.6.11",
390
+ "@clerk/backend": "^3.2.1",
391
+ "@clerk/express": "^2.0.5",
392
+ "@clerk/fastify": "^3.1.3",
393
+ "@clerk/nextjs": "^7.0.5",
394
+ "@clerk/react": "^6.1.1",
395
+ "@clerk/react-router": "^3.0.5",
396
+ "@clerk/tanstack-react-start": "^1.1.3",
397
+ "@clerk/expo": "^3.1.3",
398
+ "drizzle-orm": "^0.45.1",
399
+ "drizzle-kit": "^0.31.8",
400
+ "@planetscale/database": "^1.19.0",
401
+ "@libsql/client": "0.15.15",
402
+ libsql: "0.5.22",
403
+ "@neondatabase/serverless": "^1.0.2",
404
+ pg: "^8.17.1",
405
+ "@types/pg": "^8.16.0",
406
+ "@types/ws": "^8.18.1",
407
+ ws: "^8.18.3",
408
+ mysql2: "^3.14.0",
409
+ "@prisma/client": "^7.8.0",
410
+ prisma: "^7.8.0",
411
+ "@prisma/adapter-d1": "^7.8.0",
412
+ "@prisma/adapter-neon": "^7.8.0",
413
+ "@prisma/adapter-mariadb": "^7.8.0",
414
+ "@prisma/adapter-libsql": "^7.8.0",
415
+ "@prisma/adapter-better-sqlite3": "^7.8.0",
416
+ "@prisma/adapter-pg": "^7.8.0",
417
+ "@prisma/adapter-planetscale": "^7.8.0",
418
+ mongoose: "^9.6.2",
419
+ mongodb: "^7.2.0",
420
+ "vite-plugin-pwa": "^1.3.0",
421
+ "@vite-pwa/assets-generator": "^1.0.2",
422
+ "@tauri-apps/cli": "^2.11.2",
423
+ "@biomejs/biome": "^2.4.16",
424
+ oxlint: "^1.68.0",
425
+ oxfmt: "^0.53.0",
426
+ husky: "^9.1.7",
427
+ lefthook: "^2.1.9",
428
+ "lint-staged": "^17.0.7",
429
+ tsx: "^4.19.2",
430
+ "@types/node": "^22.13.14",
431
+ "@types/bun": "^1.3.4",
432
+ "@elysiajs/node": "^1.4.5",
433
+ "@elysiajs/cors": "^1.4.1",
434
+ "@elysiajs/trpc": "^1.1.0",
435
+ elysia: "^1.4.28",
436
+ "@sinclair/typebox": "^0.34.49",
437
+ "@hono/node-server": "^1.14.4",
438
+ "@hono/trpc-server": "^0.4.0",
439
+ hono: "^4.8.2",
440
+ cors: "^2.8.5",
441
+ express: "^5.1.0",
442
+ "@types/express": "^5.0.1",
443
+ "@types/cors": "^2.8.17",
444
+ fastify: "^5.3.3",
445
+ "@fastify/cors": "^11.0.1",
446
+ turbo: "^2.9.16",
447
+ nx: "^22.7.5",
448
+ "vite-plus": "0.1.24",
449
+ rolldown: "1.1.0",
450
+ ai: "^6.0.3",
451
+ "@ai-sdk/google": "^3.0.1",
452
+ "@ai-sdk/vue": "^3.0.3",
453
+ "@ai-sdk/svelte": "^4.0.3",
454
+ "@ai-sdk/react": "^3.0.3",
455
+ "@ai-sdk/devtools": "^0.0.2",
456
+ streamdown: "^1.6.10",
457
+ shiki: "^3.20.0",
458
+ "@orpc/server": "^1.13.14",
459
+ "@orpc/client": "^1.13.14",
460
+ "@orpc/openapi": "^1.13.14",
461
+ "@orpc/zod": "^1.13.14",
462
+ "@orpc/tanstack-query": "^1.13.14",
463
+ "@trpc/tanstack-react-query": "^11.16.0",
464
+ "@trpc/server": "^11.16.0",
465
+ "@trpc/client": "^11.16.0",
466
+ next: "^16.2.0",
467
+ nitro: "^3.0.260429-beta",
468
+ convex: "^1.33.1",
469
+ "@convex-dev/react-query": "^0.1.0",
470
+ "@convex-dev/agent": "^0.3.2",
471
+ "@convex-dev/polar": "^0.9.1",
472
+ "convex-svelte": "^0.0.12",
473
+ "convex-nuxt": "0.1.5",
474
+ "convex-vue": "^0.1.5",
475
+ "@convex-dev/better-auth": "^0.12.2",
476
+ "@tanstack/svelte-query": "^5.85.3",
477
+ "@tanstack/svelte-query-devtools": "^5.85.3",
478
+ "@tanstack/vue-query-devtools": "^6.1.5",
479
+ "@tanstack/vue-query": "^5.92.9",
480
+ "@tanstack/react-query-devtools": "^5.91.1",
481
+ "@tanstack/react-query": "^5.90.12",
482
+ "@tanstack/react-form": "^1.28.0",
483
+ "@tanstack/react-router-ssr-query": "^1.166.11",
484
+ "@tanstack/solid-form": "^1.28.0",
485
+ "@tanstack/svelte-form": "^1.28.0",
486
+ "@tanstack/solid-query": "^5.99.1",
487
+ "@tanstack/solid-query-devtools": "^5.99.1",
488
+ "@tanstack/solid-router-devtools": "^1.166.13",
489
+ wrangler: "^4.77.0",
490
+ "@cloudflare/vite-plugin": "^1.17.1",
491
+ "@opennextjs/cloudflare": "^1.17.3",
492
+ "nitro-cloudflare-dev": "^0.2.2",
493
+ "@sveltejs/adapter-cloudflare": "^7.2.8",
494
+ "@sveltejs/adapter-node": "^5.5.4",
495
+ "@cloudflare/workers-types": "^4.20251213.0",
496
+ "@astrojs/cloudflare": "^13.0.1",
497
+ "@astrojs/node": "^10.0.0-beta.9",
498
+ alchemy: "^0.91.2",
499
+ dotenv: "^17.2.2",
500
+ tsdown: "^0.21.9",
501
+ zod: "^4.1.13",
502
+ "@t3-oss/env-core": "^0.13.1",
503
+ "@t3-oss/env-nextjs": "^0.13.1",
504
+ "@t3-oss/env-nuxt": "^0.13.1",
505
+ "@polar-sh/better-auth": "^1.8.4",
506
+ "@polar-sh/checkout": "^0.2.1",
507
+ "@polar-sh/sdk": "^0.47.1",
508
+ "@stripe/react-stripe-js": "^4.0.2",
509
+ "@stripe/stripe-js": "^7.9.0",
510
+ evlog: "^2.18.1"
511
+ };
512
+ /**
513
+ * Add dependencies to a package.json file in the VFS
514
+ */
515
+ function addPackageDependency(options) {
516
+ const { vfs, packagePath, dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {} } = options;
517
+ const pkgJson = vfs.readJson(packagePath);
518
+ if (!pkgJson) return;
519
+ pkgJson.dependencies = pkgJson.dependencies || {};
520
+ pkgJson.devDependencies = pkgJson.devDependencies || {};
521
+ for (const dep of dependencies) if (!pkgJson.dependencies[dep]) {
522
+ const version = dependencyVersionMap[dep];
523
+ if (!version) throw new Error(`Missing version for dependency: ${dep}. Add it to dependencyVersionMap in add-deps.ts`);
524
+ pkgJson.dependencies[dep] = version;
525
+ }
526
+ for (const dep of devDependencies) if (!pkgJson.devDependencies[dep]) {
527
+ const version = dependencyVersionMap[dep];
528
+ if (!version) throw new Error(`Missing version for devDependency: ${dep}. Add it to dependencyVersionMap in add-deps.ts`);
529
+ pkgJson.devDependencies[dep] = version;
530
+ }
531
+ for (const [dep, version] of Object.entries(customDependencies)) pkgJson.dependencies[dep] = version;
532
+ for (const [dep, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[dep] = version;
533
+ vfs.writeJson(packagePath, pkgJson);
534
+ }
535
+ //#endregion
383
536
  //#region src/utils/db-scripts.ts
384
537
  function getDbScriptSupport(config) {
385
538
  const isD1Alchemy = config.dbSetup === "d1" && (config.serverDeploy === "cloudflare" || config.backend === "self" && config.webDeploy === "cloudflare");
@@ -406,6 +559,7 @@ function getDbScriptSupport(config) {
406
559
  * Package.json configuration post-processor
407
560
  * Updates package names, scripts, and workspaces after template generation
408
561
  */
562
+ const VITE_PLUS_VERSION = dependencyVersionMap["vite-plus"];
409
563
  /**
410
564
  * Update all package.json files with proper names, scripts, and workspaces
411
565
  */
@@ -417,6 +571,7 @@ function processPackageConfigs(vfs, config) {
417
571
  updateInfraPackageJson(vfs, config);
418
572
  updateDesktopPackageJson(vfs, config);
419
573
  renameDevScriptsForAlchemy(vfs, config);
574
+ updateVitePlusPackageScripts(vfs, config);
420
575
  if (config.backend === "convex") updateConvexPackageJson(vfs, config);
421
576
  else if (config.backend !== "none") {
422
577
  updateDbPackageJson(vfs, config);
@@ -429,10 +584,8 @@ function updateRootPackageJson(vfs, config) {
429
584
  if (!pkgJson) return;
430
585
  pkgJson.name = config.projectName;
431
586
  pkgJson.scripts = pkgJson.scripts || {};
432
- let workspaces = [];
433
- if (Array.isArray(pkgJson.workspaces)) workspaces = pkgJson.workspaces;
434
- else if (pkgJson.workspaces && typeof pkgJson.workspaces === "object" && pkgJson.workspaces.packages) workspaces = pkgJson.workspaces.packages;
435
- pkgJson.workspaces = workspaces;
587
+ const existingWorkspaces = pkgJson.workspaces;
588
+ const workspaces = getWorkspacePackages(existingWorkspaces);
436
589
  const scripts = pkgJson.scripts;
437
590
  const { projectName, packageManager, backend, database, orm, dbSetup, addons, frontend } = config;
438
591
  const hasWebApp = frontend.some((item) => desktopWebFrontends.includes(item));
@@ -445,16 +598,27 @@ function updateRootPackageJson(vfs, config) {
445
598
  const dbPackageName = `@${projectName}/db`;
446
599
  const hasTurborepo = addons.includes("turborepo");
447
600
  const hasNx = addons.includes("nx");
601
+ const hasVitePlus = addons.includes("vite-plus");
602
+ const hasVitePlusNativeHooks = hasVitePlus && !addons.includes("husky") && !addons.includes("lefthook");
448
603
  const dbSupport = getDbScriptSupport(config);
449
604
  const needsDbScripts = dbSupport.hasDbScripts;
450
605
  const isD1Alchemy = dbSupport.isD1Alchemy;
451
606
  const pmConfig = getPackageManagerConfig(packageManager, {
452
607
  hasTurborepo,
453
- hasNx
608
+ hasNx,
609
+ hasVitePlus
454
610
  });
455
611
  scripts.dev = pmConfig.dev;
456
612
  scripts.build = pmConfig.build;
457
613
  scripts["check-types"] = pmConfig.checkTypes;
614
+ if (hasVitePlus) {
615
+ scripts.check = "vp check && vp run -r check-types";
616
+ scripts.lint = "vp lint";
617
+ scripts.format = "vp fmt";
618
+ scripts.staged = "vp staged";
619
+ if (hasVitePlusNativeHooks) scripts["hooks:setup"] = "vp config";
620
+ else delete scripts["hooks:setup"];
621
+ }
458
622
  if (hasNativeApp) scripts["dev:native"] = pmConfig.filter("native", "dev");
459
623
  if (hasWebApp) scripts["dev:web"] = pmConfig.filter("web", "dev");
460
624
  if (addons.includes("electrobun")) {
@@ -477,7 +641,13 @@ function updateRootPackageJson(vfs, config) {
477
641
  }
478
642
  }
479
643
  if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = pmConfig.filter(dbPackageName, "db:local");
480
- if (dbSetup === "docker") {
644
+ const hasDockerDeployScripts = config.webDeploy === "docker" || config.serverDeploy === "docker";
645
+ if (dbSetup === "docker") if (hasDockerDeployScripts) {
646
+ scripts["db:start"] = `docker compose up -d ${database}`;
647
+ scripts["db:watch"] = `docker compose up ${database}`;
648
+ scripts["db:stop"] = `docker compose stop ${database}`;
649
+ scripts["db:down"] = `docker compose down ${database}`;
650
+ } else {
481
651
  scripts["db:start"] = pmConfig.filter(dbPackageName, "db:start");
482
652
  scripts["db:watch"] = pmConfig.filter(dbPackageName, "db:watch");
483
653
  scripts["db:stop"] = pmConfig.filter(dbPackageName, "db:stop");
@@ -488,11 +658,22 @@ function updateRootPackageJson(vfs, config) {
488
658
  scripts.deploy = pmConfig.filter(infraPackageName, "deploy");
489
659
  scripts.destroy = pmConfig.filter(infraPackageName, "destroy");
490
660
  }
661
+ if (config.webDeploy === "docker" || config.serverDeploy === "docker") {
662
+ scripts["docker:build"] = "docker compose build";
663
+ scripts["docker:up"] = "docker compose up -d --build";
664
+ scripts["docker:down"] = "docker compose down";
665
+ scripts["docker:logs"] = "docker compose logs -f";
666
+ }
491
667
  pkgJson.packageManager = `${packageManager}@latest`;
492
668
  if (config.api === "orpc" && config.frontend.includes("nuxt")) pkgJson.overrides = {
493
669
  ...pkgJson.overrides,
494
670
  "@vue/devtools-api": "^8.0.7"
495
671
  };
672
+ if (hasVitePlus) pkgJson.overrides = {
673
+ ...pkgJson.overrides,
674
+ vite: `npm:@voidzero-dev/vite-plus-core@${VITE_PLUS_VERSION}`,
675
+ vitest: `npm:@voidzero-dev/vite-plus-test@${VITE_PLUS_VERSION}`
676
+ };
496
677
  if (backend === "convex") {
497
678
  if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
498
679
  if ((config.frontend.length > 0 || addons.includes("starlight")) && !workspaces.includes("apps/*")) workspaces.push("apps/*");
@@ -500,8 +681,21 @@ function updateRootPackageJson(vfs, config) {
500
681
  if (!workspaces.includes("apps/*")) workspaces.push("apps/*");
501
682
  if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
502
683
  }
684
+ pkgJson.workspaces = getUpdatedWorkspaces(existingWorkspaces, workspaces);
503
685
  vfs.writeJson("package.json", pkgJson);
504
686
  }
687
+ function getWorkspacePackages(workspaces) {
688
+ if (Array.isArray(workspaces)) return workspaces;
689
+ if (workspaces && typeof workspaces === "object" && workspaces.packages) return workspaces.packages;
690
+ return [];
691
+ }
692
+ function getUpdatedWorkspaces(existingWorkspaces, packages) {
693
+ if (existingWorkspaces && !Array.isArray(existingWorkspaces) && typeof existingWorkspaces === "object" && existingWorkspaces.catalog) return {
694
+ ...existingWorkspaces,
695
+ packages
696
+ };
697
+ return packages;
698
+ }
505
699
  function getPackageManagerConfig(packageManager, options) {
506
700
  if (options.hasTurborepo) return {
507
701
  dev: "turbo dev",
@@ -515,6 +709,12 @@ function getPackageManagerConfig(packageManager, options) {
515
709
  checkTypes: "nx run-many -t check-types",
516
710
  filter: (workspace, script) => `nx run-many -t ${script} --projects=${workspace}`
517
711
  };
712
+ if (options.hasVitePlus) return {
713
+ dev: "vp run -r dev",
714
+ build: "vp run -r build",
715
+ checkTypes: "vp run -r check-types",
716
+ filter: (workspace, script) => `vp run --filter ${workspace} ${script}`
717
+ };
518
718
  switch (packageManager) {
519
719
  case "pnpm": return {
520
720
  dev: "pnpm -r dev",
@@ -542,14 +742,17 @@ function updateDesktopPackageJson(vfs, config) {
542
742
  const { packageManager, addons, frontend } = config;
543
743
  const hasTurborepo = addons.includes("turborepo");
544
744
  const hasNx = addons.includes("nx");
745
+ const hasVitePlus = addons.includes("vite-plus");
545
746
  const desktopBuildScript = frontend.includes("nuxt") ? "generate" : "build";
546
747
  const webBuildCommand = getDesktopWebCommand(packageManager, {
547
748
  hasTurborepo,
548
- hasNx
749
+ hasNx,
750
+ hasVitePlus
549
751
  }, desktopBuildScript);
550
752
  const webDevCommand = getDesktopWebCommand(packageManager, {
551
753
  hasTurborepo,
552
- hasNx
754
+ hasNx,
755
+ hasVitePlus
553
756
  }, "dev");
554
757
  const localRunCommand = getLocalRunCommand(packageManager);
555
758
  pkgJson.scripts = {
@@ -568,6 +771,7 @@ function updateDesktopPackageJson(vfs, config) {
568
771
  function getDesktopWebCommand(packageManager, options, script) {
569
772
  if (options.hasTurborepo) return `turbo -F web ${script}`;
570
773
  if (options.hasNx) return `nx run-many -t ${script} --projects=web`;
774
+ if (options.hasVitePlus) return `vp run --filter web ${script}`;
571
775
  switch (packageManager) {
572
776
  case "npm": return `npm run ${script} --workspace web`;
573
777
  case "pnpm": return `pnpm -w --filter web ${script}`;
@@ -607,7 +811,8 @@ function updateDbPackageJson(vfs, config) {
607
811
  }
608
812
  }
609
813
  }
610
- if (dbSetup === "docker") {
814
+ const hasDockerDeploy = config.webDeploy === "docker" || config.serverDeploy === "docker";
815
+ if (dbSetup === "docker" && !hasDockerDeploy) {
611
816
  scripts["db:start"] = "docker compose up -d";
612
817
  scripts["db:watch"] = "docker compose up";
613
818
  scripts["db:stop"] = "docker compose stop";
@@ -691,153 +896,21 @@ function renameDevScriptsForAlchemy(vfs, config) {
691
896
  }
692
897
  }
693
898
  }
694
- //#endregion
695
- //#region src/utils/add-deps.ts
696
- const dependencyVersionMap = {
697
- typescript: "^6",
698
- "better-auth": "1.6.11",
699
- "@better-auth/expo": "1.6.11",
700
- "@clerk/backend": "^3.2.1",
701
- "@clerk/express": "^2.0.5",
702
- "@clerk/fastify": "^3.1.3",
703
- "@clerk/nextjs": "^7.0.5",
704
- "@clerk/react": "^6.1.1",
705
- "@clerk/react-router": "^3.0.5",
706
- "@clerk/tanstack-react-start": "^1.1.3",
707
- "@clerk/expo": "^3.1.3",
708
- "drizzle-orm": "^0.45.1",
709
- "drizzle-kit": "^0.31.8",
710
- "@planetscale/database": "^1.19.0",
711
- "@libsql/client": "0.15.15",
712
- libsql: "0.5.22",
713
- "@neondatabase/serverless": "^1.0.2",
714
- pg: "^8.17.1",
715
- "@types/pg": "^8.16.0",
716
- "@types/ws": "^8.18.1",
717
- ws: "^8.18.3",
718
- mysql2: "^3.14.0",
719
- "@prisma/client": "^7.8.0",
720
- prisma: "^7.8.0",
721
- "@prisma/adapter-d1": "^7.8.0",
722
- "@prisma/adapter-neon": "^7.8.0",
723
- "@prisma/adapter-mariadb": "^7.8.0",
724
- "@prisma/adapter-libsql": "^7.8.0",
725
- "@prisma/adapter-better-sqlite3": "^7.8.0",
726
- "@prisma/adapter-pg": "^7.8.0",
727
- "@prisma/adapter-planetscale": "^7.8.0",
728
- mongoose: "^9.6.2",
729
- mongodb: "^7.2.0",
730
- "vite-plugin-pwa": "^1.2.0",
731
- "@vite-pwa/assets-generator": "^1.0.2",
732
- "@tauri-apps/cli": "^2.4.0",
733
- "@biomejs/biome": "^2.2.0",
734
- oxlint: "^1.61.0",
735
- oxfmt: "^0.46.0",
736
- husky: "^9.1.7",
737
- lefthook: "^2.0.13",
738
- "lint-staged": "^16.1.2",
739
- tsx: "^4.19.2",
740
- "@types/node": "^22.13.14",
741
- "@types/bun": "^1.3.4",
742
- "@elysiajs/node": "^1.4.5",
743
- "@elysiajs/cors": "^1.4.1",
744
- "@elysiajs/trpc": "^1.1.0",
745
- elysia: "^1.4.28",
746
- "@sinclair/typebox": "^0.34.49",
747
- "@hono/node-server": "^1.14.4",
748
- "@hono/trpc-server": "^0.4.0",
749
- hono: "^4.8.2",
750
- cors: "^2.8.5",
751
- express: "^5.1.0",
752
- "@types/express": "^5.0.1",
753
- "@types/cors": "^2.8.17",
754
- fastify: "^5.3.3",
755
- "@fastify/cors": "^11.0.1",
756
- turbo: "^2.8.12",
757
- nx: "^21.5.2",
758
- ai: "^6.0.3",
759
- "@ai-sdk/google": "^3.0.1",
760
- "@ai-sdk/vue": "^3.0.3",
761
- "@ai-sdk/svelte": "^4.0.3",
762
- "@ai-sdk/react": "^3.0.3",
763
- "@ai-sdk/devtools": "^0.0.2",
764
- streamdown: "^1.6.10",
765
- shiki: "^3.20.0",
766
- "@orpc/server": "^1.13.14",
767
- "@orpc/client": "^1.13.14",
768
- "@orpc/openapi": "^1.13.14",
769
- "@orpc/zod": "^1.13.14",
770
- "@orpc/tanstack-query": "^1.13.14",
771
- "@trpc/tanstack-react-query": "^11.16.0",
772
- "@trpc/server": "^11.16.0",
773
- "@trpc/client": "^11.16.0",
774
- next: "^16.2.0",
775
- nitro: "^3.0.260429-beta",
776
- convex: "^1.33.1",
777
- "@convex-dev/react-query": "^0.1.0",
778
- "@convex-dev/agent": "^0.3.2",
779
- "@convex-dev/polar": "^0.9.1",
780
- "convex-svelte": "^0.0.12",
781
- "convex-nuxt": "0.1.5",
782
- "convex-vue": "^0.1.5",
783
- "@convex-dev/better-auth": "^0.12.2",
784
- "@tanstack/svelte-query": "^5.85.3",
785
- "@tanstack/svelte-query-devtools": "^5.85.3",
786
- "@tanstack/vue-query-devtools": "^6.1.5",
787
- "@tanstack/vue-query": "^5.92.9",
788
- "@tanstack/react-query-devtools": "^5.91.1",
789
- "@tanstack/react-query": "^5.90.12",
790
- "@tanstack/react-form": "^1.28.0",
791
- "@tanstack/react-router-ssr-query": "^1.166.11",
792
- "@tanstack/solid-form": "^1.28.0",
793
- "@tanstack/svelte-form": "^1.28.0",
794
- "@tanstack/solid-query": "^5.99.1",
795
- "@tanstack/solid-query-devtools": "^5.99.1",
796
- "@tanstack/solid-router-devtools": "^1.166.13",
797
- wrangler: "^4.77.0",
798
- "@cloudflare/vite-plugin": "^1.17.1",
799
- "@opennextjs/cloudflare": "^1.17.3",
800
- "nitro-cloudflare-dev": "^0.2.2",
801
- "@sveltejs/adapter-cloudflare": "^7.2.8",
802
- "@cloudflare/workers-types": "^4.20251213.0",
803
- "@astrojs/cloudflare": "^13.0.1",
804
- "@astrojs/node": "^10.0.0-beta.9",
805
- alchemy: "^0.91.2",
806
- dotenv: "^17.2.2",
807
- tsdown: "^0.21.9",
808
- zod: "^4.1.13",
809
- "@t3-oss/env-core": "^0.13.1",
810
- "@t3-oss/env-nextjs": "^0.13.1",
811
- "@t3-oss/env-nuxt": "^0.13.1",
812
- "@polar-sh/better-auth": "^1.8.4",
813
- "@polar-sh/checkout": "^0.2.1",
814
- "@polar-sh/sdk": "^0.47.1",
815
- "@stripe/react-stripe-js": "^4.0.2",
816
- "@stripe/stripe-js": "^7.9.0",
817
- evlog: "^2.14.1"
818
- };
819
- /**
820
- * Add dependencies to a package.json file in the VFS
821
- */
822
- function addPackageDependency(options) {
823
- const { vfs, packagePath, dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {} } = options;
824
- const pkgJson = vfs.readJson(packagePath);
825
- if (!pkgJson) return;
826
- pkgJson.dependencies = pkgJson.dependencies || {};
827
- pkgJson.devDependencies = pkgJson.devDependencies || {};
828
- for (const dep of dependencies) if (!pkgJson.dependencies[dep]) {
829
- const version = dependencyVersionMap[dep];
830
- if (!version) throw new Error(`Missing version for dependency: ${dep}. Add it to dependencyVersionMap in add-deps.ts`);
831
- pkgJson.dependencies[dep] = version;
832
- }
833
- for (const dep of devDependencies) if (!pkgJson.devDependencies[dep]) {
834
- const version = dependencyVersionMap[dep];
835
- if (!version) throw new Error(`Missing version for devDependency: ${dep}. Add it to dependencyVersionMap in add-deps.ts`);
836
- pkgJson.devDependencies[dep] = version;
837
- }
838
- for (const [dep, version] of Object.entries(customDependencies)) pkgJson.dependencies[dep] = version;
839
- for (const [dep, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[dep] = version;
840
- vfs.writeJson(packagePath, pkgJson);
899
+ function updateVitePlusPackageScripts(vfs, config) {
900
+ if (!config.addons.includes("vite-plus")) return;
901
+ const webPkgPath = "apps/web/package.json";
902
+ const webPkg = vfs.readJson(webPkgPath);
903
+ if (!webPkg?.scripts) return;
904
+ const viteScriptReplacements = {
905
+ vite: "vp dev",
906
+ "vite dev": "vp dev",
907
+ "vite build": "vp build",
908
+ "vite preview": "vp preview",
909
+ "vitest run": "vp test",
910
+ "vite build && tsc --noEmit": "vp build && tsc --noEmit"
911
+ };
912
+ for (const [scriptName, command] of Object.entries(webPkg.scripts)) webPkg.scripts[scriptName] = viteScriptReplacements[command] ?? command;
913
+ vfs.writeJson(webPkgPath, webPkg);
841
914
  }
842
915
  //#endregion
843
916
  //#region src/processors/addons-deps.ts
@@ -863,6 +936,11 @@ function processAddonsDeps(vfs, config) {
863
936
  packagePath: "package.json",
864
937
  devDependencies: ["nx"]
865
938
  });
939
+ if (config.addons.includes("vite-plus")) addPackageDependency({
940
+ vfs,
941
+ packagePath: "package.json",
942
+ devDependencies: ["vite-plus", "rolldown"]
943
+ });
866
944
  if (config.addons.includes("evlog")) {
867
945
  const serverPkgPath = "apps/server/package.json";
868
946
  if (vfs.exists(serverPkgPath) && config.backend !== "self" && config.backend !== "none") addPackageDependency({
@@ -1959,8 +2037,24 @@ function processDeployDeps(vfs, config) {
1959
2037
  const { webDeploy, serverDeploy, frontend, backend } = config;
1960
2038
  const isCloudflareWeb = webDeploy === "cloudflare";
1961
2039
  const isCloudflareServer = serverDeploy === "cloudflare";
2040
+ const isDockerWeb = webDeploy === "docker";
1962
2041
  const isBackendSelf = backend === "self";
1963
- if (!isCloudflareWeb && !isCloudflareServer) return;
2042
+ if (!isCloudflareWeb && !isCloudflareServer && !isDockerWeb) return;
2043
+ if (isDockerWeb) {
2044
+ const webPkgPath = "apps/web/package.json";
2045
+ if (vfs.exists(webPkgPath)) {
2046
+ if (frontend.includes("svelte")) addPackageDependency({
2047
+ vfs,
2048
+ packagePath: webPkgPath,
2049
+ devDependencies: ["@sveltejs/adapter-node"]
2050
+ });
2051
+ else if (frontend.includes("tanstack-start")) addPackageDependency({
2052
+ vfs,
2053
+ packagePath: webPkgPath,
2054
+ dependencies: ["nitro"]
2055
+ });
2056
+ }
2057
+ }
1964
2058
  if (isCloudflareWeb || isCloudflareServer) addPackageDependency({
1965
2059
  vfs,
1966
2060
  packagePath: "package.json",
@@ -2568,6 +2662,86 @@ function processInfraDeps(vfs, config) {
2568
2662
  });
2569
2663
  }
2570
2664
  //#endregion
2665
+ //#region src/utils/generated-ignore-patterns.ts
2666
+ const FRONTEND_GENERATED_PATTERNS = {
2667
+ "tanstack-router": [
2668
+ "apps/web/dist/**",
2669
+ "apps/web/.tanstack/**",
2670
+ "apps/web/src/routeTree.gen.ts"
2671
+ ],
2672
+ "react-router": ["apps/web/build/**", "apps/web/.react-router/**"],
2673
+ "tanstack-start": [
2674
+ "apps/web/dist/**",
2675
+ "apps/web/.vinxi/**",
2676
+ "apps/web/.tanstack/**",
2677
+ "apps/web/src/routeTree.gen.ts"
2678
+ ],
2679
+ next: ["apps/web/.next/**", "apps/web/out/**"],
2680
+ nuxt: [
2681
+ "apps/web/.nuxt/**",
2682
+ "apps/web/.output/**",
2683
+ "apps/web/.data/**",
2684
+ "apps/web/.nitro/**"
2685
+ ],
2686
+ svelte: [
2687
+ "apps/web/.svelte-kit/**",
2688
+ "apps/web/build/**",
2689
+ "apps/web/.output/**"
2690
+ ],
2691
+ solid: [
2692
+ "apps/web/dist/**",
2693
+ "apps/web/.tanstack/**",
2694
+ "apps/web/src/routeTree.gen.ts"
2695
+ ],
2696
+ astro: ["apps/web/dist/**", "apps/web/.astro/**"],
2697
+ "native-bare": [
2698
+ "apps/native/.expo/**",
2699
+ "apps/native/dist/**",
2700
+ "apps/native/web-build/**"
2701
+ ],
2702
+ "native-uniwind": [
2703
+ "apps/native/.expo/**",
2704
+ "apps/native/dist/**",
2705
+ "apps/native/web-build/**"
2706
+ ],
2707
+ "native-unistyles": [
2708
+ "apps/native/.expo/**",
2709
+ "apps/native/dist/**",
2710
+ "apps/native/web-build/**",
2711
+ "apps/native/ios/**",
2712
+ "apps/native/android/**"
2713
+ ]
2714
+ };
2715
+ const SERVER_BUILD_BACKENDS = [
2716
+ "hono",
2717
+ "express",
2718
+ "fastify",
2719
+ "elysia"
2720
+ ];
2721
+ function getStackGeneratedIgnorePatterns(config) {
2722
+ const patterns = /* @__PURE__ */ new Set();
2723
+ for (const frontend of config.frontend) {
2724
+ const frontendPatterns = FRONTEND_GENERATED_PATTERNS[frontend];
2725
+ if (!frontendPatterns) continue;
2726
+ for (const pattern of frontendPatterns) patterns.add(pattern);
2727
+ }
2728
+ if (SERVER_BUILD_BACKENDS.includes(config.backend)) patterns.add("apps/server/dist/**");
2729
+ if (config.database !== "none" && config.orm !== "none") patterns.add("packages/db/dist/**");
2730
+ if (config.database === "sqlite" && config.dbSetup !== "d1" && config.orm !== "none") patterns.add("packages/db/local.db*");
2731
+ if (config.orm === "prisma") {
2732
+ patterns.add("packages/db/prisma/generated/**");
2733
+ if (config.database === "sqlite" && config.dbSetup === "turso") patterns.add("packages/db/prisma/**/*.db*");
2734
+ }
2735
+ if (config.backend === "convex") patterns.add("packages/backend/convex/_generated/**");
2736
+ if (config.runtime === "workers" || config.dbSetup === "d1" || config.webDeploy === "cloudflare" || config.serverDeploy === "cloudflare") {
2737
+ patterns.add(".alchemy/**");
2738
+ patterns.add(".wrangler/**");
2739
+ patterns.add("**/.wrangler/**");
2740
+ if (config.frontend.includes("next")) patterns.add("apps/web/.open-next/**");
2741
+ }
2742
+ return [...patterns];
2743
+ }
2744
+ //#endregion
2571
2745
  //#region src/processors/nx-generator.ts
2572
2746
  function processNxConfig(vfs, config) {
2573
2747
  if (!config.addons.includes("nx")) return;
@@ -2582,35 +2756,40 @@ function generateNxConfig(config) {
2582
2756
  const isDocker = dbSetup === "docker";
2583
2757
  const isSqliteLocal = database === "sqlite" && dbSetup !== "d1" && hasDatabase;
2584
2758
  const hasCloudflare = webDeploy === "cloudflare" || serverDeploy === "cloudflare";
2759
+ const targetDefaults = {
2760
+ build: {
2761
+ dependsOn: ["^build"],
2762
+ inputs: ["production", "^production"]
2763
+ },
2764
+ "check-types": {
2765
+ dependsOn: ["^check-types"],
2766
+ inputs: ["default", "^default"]
2767
+ },
2768
+ dev: { cache: false },
2769
+ ...isConvex ? getConvexTargets() : {},
2770
+ ...!isConvex && hasDatabase ? getDatabaseTargets(dbSupport) : {},
2771
+ ...isDocker ? getDockerTargets() : {},
2772
+ ...isSqliteLocal ? getSqliteLocalTarget() : {},
2773
+ ...hasCloudflare ? getDeployTargets() : {}
2774
+ };
2585
2775
  return {
2586
2776
  $schema: "./node_modules/nx/schemas/nx-schema.json",
2587
2777
  namedInputs: {
2588
2778
  default: ["{projectRoot}/**/*", "sharedGlobals"],
2589
2779
  production: [
2590
2780
  "default",
2781
+ ...getNxProductionInputExclusions(config),
2591
2782
  "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
2592
2783
  "!{projectRoot}/tsconfig.spec.json"
2593
2784
  ],
2594
2785
  sharedGlobals: []
2595
2786
  },
2596
- targetDefaults: {
2597
- build: {
2598
- dependsOn: ["^build"],
2599
- inputs: ["production", "^production"]
2600
- },
2601
- "check-types": {
2602
- dependsOn: ["^check-types"],
2603
- inputs: ["default", "^default"]
2604
- },
2605
- dev: { cache: false },
2606
- ...isConvex ? getConvexTargets() : {},
2607
- ...!isConvex && hasDatabase ? getDatabaseTargets(dbSupport) : {},
2608
- ...isDocker ? getDockerTargets() : {},
2609
- ...isSqliteLocal ? getSqliteLocalTarget() : {},
2610
- ...hasCloudflare ? getDeployTargets() : {}
2611
- }
2787
+ targetDefaults
2612
2788
  };
2613
2789
  }
2790
+ function getNxProductionInputExclusions(config) {
2791
+ return getStackGeneratedIgnorePatterns(config).map((pattern) => `!{workspaceRoot}/${pattern}`);
2792
+ }
2614
2793
  function getConvexTargets() {
2615
2794
  return { "dev:setup": { cache: false } };
2616
2795
  }
@@ -3102,7 +3281,8 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
3102
3281
  husky: "- **Husky** - Git hooks for code quality",
3103
3282
  starlight: "- **Starlight** - Documentation site with Astro",
3104
3283
  turborepo: "- **Turborepo** - Optimized monorepo build system",
3105
- nx: "- **Nx** - Smart monorepo task orchestration and caching"
3284
+ nx: "- **Nx** - Smart monorepo task orchestration and caching",
3285
+ "vite-plus": "- **Vite+** - Unified Vite toolchain, workspace task runner, linting, and formatting"
3106
3286
  };
3107
3287
  for (const addon of addons) if (addonFeatures[addon]) features.push(addonFeatures[addon]);
3108
3288
  return features.join("\n");
@@ -3173,7 +3353,7 @@ ${packageManagerRunCmd} db:push
3173
3353
  return setup;
3174
3354
  }
3175
3355
  function generateScriptsList(packageManagerRunCmd, config, hasNative) {
3176
- const { database, addons, backend, dbSetup, frontend } = config;
3356
+ const { database, addons, backend, dbSetup, frontend, webDeploy, serverDeploy } = config;
3177
3357
  const isConvex = backend === "convex";
3178
3358
  const isBackendSelf = backend === "self";
3179
3359
  const hasWeb = frontend.some((f) => [
@@ -3201,8 +3381,15 @@ function generateScriptsList(packageManagerRunCmd, config, hasNative) {
3201
3381
  if (dbSupport.hasDbStudio) scripts += `\n- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
3202
3382
  }
3203
3383
  if (database === "sqlite" && dbSetup !== "d1" && dbSupport.hasDbScripts) scripts += `\n- \`${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
3204
- if (addons.includes("biome")) scripts += `\n- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
3205
- if (addons.includes("oxlint")) scripts += `\n- \`${packageManagerRunCmd} check\`: Run Oxlint and Oxfmt`;
3384
+ if (addons.includes("vite-plus")) {
3385
+ const hasVitePlusNativeHooks = !addons.includes("husky") && !addons.includes("lefthook");
3386
+ scripts += `\n- \`${packageManagerRunCmd} check\`: Run Vite+ format/lint checks and workspace TypeScript checks
3387
+ - \`${packageManagerRunCmd} lint\`: Run Vite+ lint checks
3388
+ - \`${packageManagerRunCmd} format\`: Run Vite+ formatting
3389
+ - \`${packageManagerRunCmd} staged\`: Run Vite+ checks against staged files`;
3390
+ if (hasVitePlusNativeHooks) scripts += `\n- \`${packageManagerRunCmd} hooks:setup\`: Install Vite+ native Git hooks with \`vp config\``;
3391
+ } else if (addons.includes("biome")) scripts += `\n- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
3392
+ else if (addons.includes("oxlint")) scripts += `\n- \`${packageManagerRunCmd} check\`: Run Oxlint and Oxfmt`;
3206
3393
  if (addons.includes("pwa")) scripts += `\n- \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`;
3207
3394
  if (addons.includes("tauri")) {
3208
3395
  scripts += `\n- \`cd apps/web && ${packageManagerRunCmd} desktop:dev\`: Start Tauri desktop app in development
@@ -3219,23 +3406,38 @@ function generateScriptsList(packageManagerRunCmd, config, hasNative) {
3219
3406
  }
3220
3407
  if (addons.includes("starlight")) scripts += `\n- \`cd apps/docs && ${packageManagerRunCmd} dev\`: Start documentation site
3221
3408
  - \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
3409
+ if (webDeploy === "docker" || serverDeploy === "docker") scripts += `\n- \`${packageManagerRunCmd} docker:build\`: Build the Docker Compose images
3410
+ - \`${packageManagerRunCmd} docker:up\`: Build and start the Docker Compose stack
3411
+ - \`${packageManagerRunCmd} docker:logs\`: Tail logs from the Docker Compose stack
3412
+ - \`${packageManagerRunCmd} docker:down\`: Stop the Docker Compose stack`;
3222
3413
  return scripts;
3223
3414
  }
3224
3415
  function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy, backend) {
3225
- if (webDeploy !== "cloudflare" && serverDeploy !== "cloudflare") return "";
3226
- const lines = ["## Deployment (Cloudflare via Alchemy)"];
3227
- const targetLabel = webDeploy === "cloudflare" && (serverDeploy === "cloudflare" || backend === "self") ? "web + server" : webDeploy === "cloudflare" ? "web" : "server";
3228
- lines.push(`- Target: ${targetLabel}`, `- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
3229
- lines.push("", "For more details, see the guide on [Deploying to Cloudflare with Alchemy](https://www.better-t-stack.dev/docs/guides/cloudflare-alchemy).");
3416
+ const hasCloudflare = webDeploy === "cloudflare" || serverDeploy === "cloudflare";
3417
+ const hasDocker = webDeploy === "docker" || serverDeploy === "docker";
3418
+ if (!hasCloudflare && !hasDocker) return "";
3419
+ const lines = ["## Deployment"];
3420
+ if (hasCloudflare) {
3421
+ const targetLabel = webDeploy === "cloudflare" && (serverDeploy === "cloudflare" || backend === "self") ? "web + server" : webDeploy === "cloudflare" ? "web" : "server";
3422
+ lines.push("", "### Cloudflare via Alchemy", "", `- Target: ${targetLabel}`, `- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`, "", "For more details, see the guide on [Deploying to Cloudflare with Alchemy](https://www.better-t-stack.dev/docs/guides/cloudflare-alchemy).");
3423
+ }
3424
+ if (hasDocker) {
3425
+ const targetLabel = webDeploy === "docker" && (serverDeploy === "docker" || backend === "self") ? "web + server" : webDeploy === "docker" ? "web" : "server";
3426
+ lines.push("", "### Docker Compose", "", `- Target: ${targetLabel}`, "- Config: `docker-compose.yml` (app Dockerfiles live in `apps/*/Dockerfile`)", `- Build images: ${packageManagerRunCmd} docker:build`, `- Start: ${packageManagerRunCmd} docker:up`, `- Logs: ${packageManagerRunCmd} docker:logs`, `- Stop: ${packageManagerRunCmd} docker:down`, "", "Environment variables are read from each app's `.env` file (baked into web builds for public variables) and overridden in `docker-compose.yml` for container networking.");
3427
+ }
3230
3428
  return `${lines.join("\n")}\n`;
3231
3429
  }
3232
3430
  function generateGitHooksSection(packageManagerRunCmd, addons) {
3233
3431
  const hasHusky = addons.includes("husky");
3234
- const hasLinting = addons.includes("biome") || addons.includes("oxlint");
3432
+ const hasLefthook = addons.includes("lefthook");
3433
+ const hasVitePlus = addons.includes("vite-plus");
3434
+ const hasVitePlusNativeHooks = hasVitePlus && !hasHusky && !hasLefthook;
3435
+ const hasLinting = addons.includes("biome") || addons.includes("oxlint") || hasVitePlus;
3235
3436
  if (!hasHusky && !hasLinting) return "";
3236
3437
  const lines = ["## Git Hooks and Formatting", ""];
3237
3438
  if (hasHusky) lines.push(`- Initialize hooks: \`${packageManagerRunCmd} prepare\``);
3238
- if (hasLinting) lines.push(`- Format and lint fix: \`${packageManagerRunCmd} check\``);
3439
+ if (hasVitePlusNativeHooks) lines.push(`- Optional native Vite+ hooks: \`${packageManagerRunCmd} hooks:setup\``, "- Docs: [Vite+ commit hooks](https://viteplus.dev/guide/commit-hooks)");
3440
+ if (hasLinting) lines.push(`- Run checks: \`${packageManagerRunCmd} check\``);
3239
3441
  return `${lines.join("\n")}\n\n`;
3240
3442
  }
3241
3443
  //#endregion
@@ -3369,6 +3571,51 @@ function getDeployTasks() {
3369
3571
  };
3370
3572
  }
3371
3573
  //#endregion
3574
+ //#region src/processors/vite-plus-generator.ts
3575
+ const BASE_IGNORE_PATTERNS = ["node_modules/**", "**/node_modules/**"];
3576
+ const STAGED_PATTERN = "*.{js,ts,jsx,tsx,vue,svelte,json,jsonc,css,md}";
3577
+ function processVitePlusConfig(vfs, config) {
3578
+ if (!config.addons.includes("vite-plus")) return;
3579
+ vfs.writeFile("vite.config.ts", generateVitePlusConfig(config));
3580
+ }
3581
+ function formatStringArray(values, indent = 4) {
3582
+ const spaces = " ".repeat(indent);
3583
+ return values.map((value) => `${spaces}${JSON.stringify(value)},`).join("\n");
3584
+ }
3585
+ function getVitePlusIgnorePatterns(config) {
3586
+ const patterns = new Set(BASE_IGNORE_PATTERNS);
3587
+ for (const pattern of getStackGeneratedIgnorePatterns(config)) patterns.add(pattern);
3588
+ return [...patterns];
3589
+ }
3590
+ function generateVitePlusConfig(config) {
3591
+ const ignorePatterns = formatStringArray(getVitePlusIgnorePatterns(config), 6);
3592
+ return `import { defineConfig } from "vite-plus";
3593
+
3594
+ export default defineConfig({
3595
+ lint: {
3596
+ ignorePatterns: [
3597
+ ${ignorePatterns}
3598
+ ],
3599
+ options: {
3600
+ typeAware: false,
3601
+ typeCheck: false,
3602
+ },
3603
+ },
3604
+ fmt: {
3605
+ ignorePatterns: [
3606
+ ${ignorePatterns}
3607
+ ],
3608
+ singleQuote: false,
3609
+ semi: true,
3610
+ sortPackageJson: true,
3611
+ },
3612
+ staged: {
3613
+ ${JSON.stringify(STAGED_PATTERN)}: "vp check --fix",
3614
+ },
3615
+ });
3616
+ `;
3617
+ }
3618
+ //#endregion
3372
3619
  //#region src/processors/workspace-deps.ts
3373
3620
  function processWorkspaceDeps(vfs, config) {
3374
3621
  const { projectName, packageManager, runtime, backend, database, auth, api, serverDeploy, webDeploy } = config;
@@ -3533,6 +3780,7 @@ function processDependencies(vfs, config) {
3533
3780
  processExamplesDeps(vfs, config);
3534
3781
  processTurboConfig(vfs, config);
3535
3782
  processNxConfig(vfs, config);
3783
+ processVitePlusConfig(vfs, config);
3536
3784
  }
3537
3785
  //#endregion
3538
3786
  //#region src/template-handlers/utils.ts
@@ -3626,7 +3874,8 @@ async function processDbTemplates(vfs, templates, config) {
3626
3874
  processTemplatesFromPrefix(vfs, templates, "db/base", "packages/db", config);
3627
3875
  processTemplatesFromPrefix(vfs, templates, `db/${config.orm}/base`, "packages/db", config);
3628
3876
  processTemplatesFromPrefix(vfs, templates, `db/${config.orm}/${config.database}`, "packages/db", config);
3629
- if (config.dbSetup === "docker") processTemplatesFromPrefix(vfs, templates, `db-setup/docker-compose/${config.database}`, "packages/db", config);
3877
+ const hasDockerDeploy = config.webDeploy === "docker" || config.serverDeploy === "docker";
3878
+ if (config.dbSetup === "docker" && !hasDockerDeploy) processTemplatesFromPrefix(vfs, templates, `db-setup/docker-compose/${config.database}`, "packages/db", config);
3630
3879
  }
3631
3880
  //#endregion
3632
3881
  //#region src/template-handlers/api.ts
@@ -3842,7 +4091,7 @@ async function processAddonTemplates(vfs, templates, config) {
3842
4091
  if (!config.addons || config.addons.length === 0) return;
3843
4092
  for (const addon of config.addons) {
3844
4093
  if (addon === "none") continue;
3845
- if (addon === "turborepo" || addon === "nx") continue;
4094
+ if (addon === "turborepo" || addon === "nx" || addon === "vite-plus") continue;
3846
4095
  if (addon === "pwa") {
3847
4096
  if (config.frontend.includes("next")) processTemplatesFromPrefix(vfs, templates, "addons/pwa/apps/web/next", "apps/web", config);
3848
4097
  else if (config.frontend.some((f) => [
@@ -3927,6 +4176,7 @@ async function processExtrasTemplates(vfs, templates, config) {
3927
4176
  async function processDeployTemplates(vfs, templates, config) {
3928
4177
  const isBackendSelf = config.backend === "self";
3929
4178
  if (config.webDeploy === "cloudflare" || config.serverDeploy === "cloudflare") processTemplatesFromPrefix(vfs, templates, "packages/infra", "packages/infra", config);
4179
+ if (config.webDeploy === "docker" || config.serverDeploy === "docker") processTemplatesFromPrefix(vfs, templates, "deploy/docker/compose", "", config);
3930
4180
  if (config.webDeploy !== "none" && config.webDeploy !== "cloudflare") {
3931
4181
  const templateMap = {
3932
4182
  "tanstack-router": "react/tanstack-router",
@@ -3935,7 +4185,8 @@ async function processDeployTemplates(vfs, templates, config) {
3935
4185
  solid: "solid",
3936
4186
  next: "react/next",
3937
4187
  nuxt: "nuxt",
3938
- svelte: "svelte"
4188
+ svelte: "svelte",
4189
+ astro: "astro"
3939
4190
  };
3940
4191
  for (const f of config.frontend) if (templateMap[f]) processTemplatesFromPrefix(vfs, templates, `deploy/${config.webDeploy}/web/${templateMap[f]}`, "apps/web", config);
3941
4192
  }
@@ -4195,11 +4446,11 @@ export default {
4195
4446
  "type": "module",
4196
4447
  "scripts": {},
4197
4448
  "dependencies": {
4198
- "electrobun": "^1.15.1"
4449
+ "electrobun": "^1.18.1"
4199
4450
  },
4200
4451
  "devDependencies": {
4201
- "@types/bun": "^1.3.4",
4202
- "concurrently": "^9.1.0",
4452
+ "@types/bun": "^1.3.14",
4453
+ "concurrently": "^10.0.3",
4203
4454
  "typescript": "^6"
4204
4455
  }
4205
4456
  }
@@ -4277,6 +4528,10 @@ pre-commit:
4277
4528
  - name: oxfmt
4278
4529
  run: {{packageManager}} oxfmt --write {staged_files}
4279
4530
  stage_fixed: true
4531
+ {{else if (includes addons "vite-plus")}}
4532
+ - name: vite-plus
4533
+ run: {{packageManager}} vp staged
4534
+ stage_fixed: true
4280
4535
  {{else}}
4281
4536
  # Add your pre-commit commands here
4282
4537
  # Example:
@@ -5319,7 +5574,9 @@ import { createTanstackQueryUtils } from "@orpc/tanstack-query";
5319
5574
 
5320
5575
  export default defineNuxtPlugin(() => {
5321
5576
  const config = useRuntimeConfig();
5322
- const rpcUrl = \`\${config.public.serverUrl}/rpc\`;
5577
+ const serverUrl =
5578
+ (import.meta.server && config.serverUrl) || config.public.serverUrl;
5579
+ const rpcUrl = \`\${serverUrl}/rpc\`;
5323
5580
 
5324
5581
  const rpcLink = new RPCLink({
5325
5582
  url: rpcUrl,
@@ -13565,7 +13822,7 @@ export default defineNuxtPlugin(() => {
13565
13822
 
13566
13823
  const authClient = createAuthClient({
13567
13824
  {{#if (ne backend "self")}}
13568
- baseURL: config.public.serverUrl,
13825
+ baseURL: (import.meta.server && config.serverUrl) || config.public.serverUrl,
13569
13826
  {{/if}}
13570
13827
  {{#if (eq payments "polar")}}
13571
13828
  plugins: [polarClient()],
@@ -18800,7 +19057,7 @@ fastify.get('/', async () => {
18800
19057
  return 'OK';
18801
19058
  });
18802
19059
 
18803
- fastify.listen({ port: 3000 }, (err) => {
19060
+ fastify.listen({ port: 3000{{#if (eq serverDeploy "docker")}}, host: "0.0.0.0"{{/if}} }, (err) => {
18804
19061
  if (err) {
18805
19062
  fastify.log.error(err);
18806
19063
  process.exit(1);
@@ -19028,6 +19285,9 @@ dist
19028
19285
  build
19029
19286
  *.tsbuildinfo
19030
19287
 
19288
+ # Generated files
19289
+ apps/web/src/routeTree.gen.ts
19290
+
19031
19291
  # Environment variables
19032
19292
  .env
19033
19293
  .env*.local
@@ -19876,6 +20136,710 @@ const prisma = createPrismaClient();
19876
20136
  export default prisma;
19877
20137
  {{/if}}
19878
20138
  {{/if}}
20139
+ `],
20140
+ ["deploy/docker/compose/_dockerignore", `**/node_modules
20141
+ .git
20142
+
20143
+ **/dist
20144
+ **/build
20145
+ **/.next
20146
+ **/.nuxt
20147
+ **/.output
20148
+ **/.svelte-kit
20149
+ **/.astro
20150
+ **/.turbo
20151
+ .turbo
20152
+
20153
+ **/.wrangler
20154
+ **/.alchemy
20155
+ **/.expo
20156
+ **/.vercel
20157
+ *.log
20158
+
20159
+ Dockerfile
20160
+ **/Dockerfile
20161
+ docker-compose.yml
20162
+
20163
+ # Secrets stay out of image layers; runtime env comes from compose env_file,
20164
+ # build-time public values come from compose build args
20165
+ **/.env
20166
+ **/.env.*
20167
+ !**/.env.example
20168
+ `],
20169
+ ["deploy/docker/compose/docker-compose.yml.hbs", `name: {{projectName}}
20170
+
20171
+ services:
20172
+ {{#if (eq webDeploy "docker")}}
20173
+ web:
20174
+ build:
20175
+ context: .
20176
+ dockerfile: apps/web/Dockerfile
20177
+ {{#if (and (not (includes frontend "nuxt")) (or (and (ne backend "self") (ne backend "none") (ne backend "convex")) (eq backend "convex") (and (eq auth "clerk") (or (includes frontend "next") (includes frontend "react-router") (includes frontend "tanstack-router") (includes frontend "tanstack-start")))))}}
20178
+ args:
20179
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20180
+ {{#if (includes frontend "next")}}NEXT_PUBLIC_SERVER_URL{{else if (or (includes frontend "svelte") (includes frontend "astro"))}}PUBLIC_SERVER_URL{{else}}VITE_SERVER_URL{{/if}}: http://localhost:3000
20181
+ {{/if}}
20182
+ {{#if (eq backend "convex")}}
20183
+ {{#if (includes frontend "next")}}NEXT_PUBLIC_CONVEX_URL{{else if (or (includes frontend "svelte") (includes frontend "astro"))}}PUBLIC_CONVEX_URL{{else}}VITE_CONVEX_URL{{/if}}: \${CONVEX_URL:-}
20184
+ {{/if}}
20185
+ {{#if (and (eq auth "clerk") (or (includes frontend "next") (includes frontend "react-router") (includes frontend "tanstack-router") (includes frontend "tanstack-start")))}}
20186
+ {{#if (includes frontend "next")}}NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY{{else}}VITE_CLERK_PUBLISHABLE_KEY{{/if}}: \${CLERK_PUBLISHABLE_KEY:-}
20187
+ {{/if}}
20188
+ {{/if}}
20189
+ init: true
20190
+ ports:
20191
+ {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "solid"))}}
20192
+ - "3001:80"
20193
+ {{else}}
20194
+ - "3001:3001"
20195
+ {{/if}}
20196
+ env_file:
20197
+ - path: apps/web/.env
20198
+ required: false
20199
+ {{#if (eq backend "self")}}
20200
+ {{#if (or (eq dbSetup "docker") (eq auth "better-auth"))}}
20201
+ environment:
20202
+ {{#if (eq auth "better-auth")}}
20203
+ BETTER_AUTH_URL: http://localhost:3001
20204
+ CORS_ORIGIN: http://localhost:3001
20205
+ {{/if}}
20206
+ {{#if (and (eq dbSetup "docker") (eq database "postgres"))}}
20207
+ DATABASE_URL: postgresql://postgres:\${POSTGRES_PASSWORD:-password}@postgres:5432/{{projectName}}
20208
+ {{/if}}
20209
+ {{#if (and (eq dbSetup "docker") (eq database "mysql"))}}
20210
+ DATABASE_URL: mysql://user:\${MYSQL_PASSWORD:-password}@mysql:3306/{{projectName}}
20211
+ {{/if}}
20212
+ {{#if (and (eq dbSetup "docker") (eq database "mongodb"))}}
20213
+ DATABASE_URL: mongodb://root:\${MONGO_PASSWORD:-password}@mongodb:27017/{{projectName}}?authSource=admin
20214
+ {{/if}}
20215
+ {{/if}}
20216
+ {{#if (eq dbSetup "docker")}}
20217
+ depends_on:
20218
+ {{database}}:
20219
+ condition: service_healthy
20220
+ {{/if}}
20221
+ {{else}}
20222
+ {{#if (eq serverDeploy "docker")}}
20223
+ {{#if (or (includes frontend "next") (includes frontend "nuxt"))}}
20224
+ environment:
20225
+ {{#if (includes frontend "next")}}
20226
+ NEXT_PUBLIC_SERVER_URL: http://server:3000
20227
+ {{/if}}
20228
+ {{#if (includes frontend "nuxt")}}
20229
+ NUXT_SERVER_URL: http://server:3000
20230
+ {{/if}}
20231
+ {{/if}}
20232
+ depends_on:
20233
+ server:
20234
+ condition: service_healthy
20235
+ {{/if}}
20236
+ {{/if}}
20237
+ restart: unless-stopped
20238
+
20239
+ {{/if}}
20240
+ {{#if (and (eq serverDeploy "docker") (ne backend "self"))}}
20241
+ server:
20242
+ build:
20243
+ context: .
20244
+ dockerfile: apps/server/Dockerfile
20245
+ init: true
20246
+ ports:
20247
+ - "3000:3000"
20248
+ env_file:
20249
+ - path: apps/server/.env
20250
+ required: false
20251
+ {{#if (or (eq webDeploy "docker") (eq dbSetup "docker"))}}
20252
+ environment:
20253
+ {{#if (eq webDeploy "docker")}}
20254
+ CORS_ORIGIN: http://localhost:3001
20255
+ {{/if}}
20256
+ {{#if (and (eq dbSetup "docker") (eq database "postgres"))}}
20257
+ DATABASE_URL: postgresql://postgres:\${POSTGRES_PASSWORD:-password}@postgres:5432/{{projectName}}
20258
+ {{/if}}
20259
+ {{#if (and (eq dbSetup "docker") (eq database "mysql"))}}
20260
+ DATABASE_URL: mysql://user:\${MYSQL_PASSWORD:-password}@mysql:3306/{{projectName}}
20261
+ {{/if}}
20262
+ {{#if (and (eq dbSetup "docker") (eq database "mongodb"))}}
20263
+ DATABASE_URL: mongodb://root:\${MONGO_PASSWORD:-password}@mongodb:27017/{{projectName}}?authSource=admin
20264
+ {{/if}}
20265
+ {{/if}}
20266
+ healthcheck:
20267
+ test:
20268
+ [
20269
+ "CMD",
20270
+ "node",
20271
+ "-e",
20272
+ "fetch('http://localhost:3000/').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))",
20273
+ ]
20274
+ interval: 10s
20275
+ timeout: 5s
20276
+ retries: 5
20277
+ start_period: 10s
20278
+ {{#if (eq dbSetup "docker")}}
20279
+ depends_on:
20280
+ {{database}}:
20281
+ condition: service_healthy
20282
+ {{/if}}
20283
+ restart: unless-stopped
20284
+
20285
+ {{/if}}
20286
+ {{#if (eq dbSetup "docker")}}
20287
+ {{#if (eq database "postgres")}}
20288
+ postgres:
20289
+ image: postgres
20290
+ container_name: {{projectName}}-postgres
20291
+ environment:
20292
+ POSTGRES_DB: {{projectName}}
20293
+ POSTGRES_USER: postgres
20294
+ POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-password}
20295
+ ports:
20296
+ - "5432:5432"
20297
+ volumes:
20298
+ - {{projectName}}_postgres_data:/var/lib/postgresql
20299
+ healthcheck:
20300
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
20301
+ interval: 10s
20302
+ timeout: 5s
20303
+ retries: 5
20304
+ restart: unless-stopped
20305
+ {{/if}}
20306
+ {{#if (eq database "mysql")}}
20307
+ mysql:
20308
+ image: mysql
20309
+ container_name: {{projectName}}-mysql
20310
+ environment:
20311
+ MYSQL_ROOT_PASSWORD: \${MYSQL_ROOT_PASSWORD:-password}
20312
+ MYSQL_DATABASE: {{projectName}}
20313
+ MYSQL_USER: user
20314
+ MYSQL_PASSWORD: \${MYSQL_PASSWORD:-password}
20315
+ ports:
20316
+ - "3306:3306"
20317
+ volumes:
20318
+ - {{projectName}}_mysql_data:/var/lib/mysql
20319
+ healthcheck:
20320
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
20321
+ interval: 10s
20322
+ timeout: 5s
20323
+ retries: 5
20324
+ restart: unless-stopped
20325
+ {{/if}}
20326
+ {{#if (eq database "mongodb")}}
20327
+ mongodb:
20328
+ image: mongo
20329
+ container_name: {{projectName}}-mongodb
20330
+ environment:
20331
+ MONGO_INITDB_ROOT_USERNAME: root
20332
+ MONGO_INITDB_ROOT_PASSWORD: \${MONGO_PASSWORD:-password}
20333
+ MONGO_INITDB_DATABASE: {{projectName}}
20334
+ ports:
20335
+ - "27017:27017"
20336
+ volumes:
20337
+ - {{projectName}}_mongodb_data:/data/db
20338
+ healthcheck:
20339
+ test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
20340
+ interval: 10s
20341
+ timeout: 5s
20342
+ retries: 5
20343
+ restart: unless-stopped
20344
+ {{/if}}
20345
+
20346
+ volumes:
20347
+ {{projectName}}_{{database}}_data:
20348
+ {{/if}}
20349
+ `],
20350
+ ["deploy/docker/server/Dockerfile.hbs", `FROM node:24-slim AS base
20351
+ {{#if (or (eq packageManager "bun") (eq runtime "bun"))}}
20352
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20353
+ {{/if}}
20354
+ {{#if (eq packageManager "pnpm")}}
20355
+ RUN npm install -g pnpm
20356
+ {{/if}}
20357
+ WORKDIR /app
20358
+ ENV SKIP_ENV_VALIDATION=1
20359
+ {{#if (eq orm "prisma")}}
20360
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20361
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20362
+ {{/if}}
20363
+
20364
+ COPY . .
20365
+ {{#if (eq packageManager "bun")}}
20366
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20367
+ {{else if (eq packageManager "pnpm")}}
20368
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20369
+ {{else}}
20370
+ RUN --mount=type=cache,target=/root/.npm npm install
20371
+ {{/if}}
20372
+
20373
+ ENV NODE_ENV=production
20374
+ RUN cd apps/server && {{packageManager}} run build
20375
+ ENV SKIP_ENV_VALIDATION=
20376
+ {{#if (eq orm "prisma")}}
20377
+ ENV DATABASE_URL=
20378
+ {{/if}}
20379
+
20380
+ EXPOSE 3000
20381
+
20382
+ WORKDIR /app/apps/server
20383
+ {{#if (eq runtime "bun")}}
20384
+ CMD ["bun", "dist/index.mjs"]
20385
+ {{else}}
20386
+ CMD ["node", "dist/index.mjs"]
20387
+ {{/if}}
20388
+ `],
20389
+ ["deploy/docker/web/astro/Dockerfile.hbs", `FROM node:24-slim AS base
20390
+ {{#if (eq packageManager "bun")}}
20391
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20392
+ {{/if}}
20393
+ {{#if (eq packageManager "pnpm")}}
20394
+ RUN npm install -g pnpm
20395
+ {{/if}}
20396
+ WORKDIR /app
20397
+ ENV SKIP_ENV_VALIDATION=1
20398
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20399
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20400
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20401
+ {{/if}}
20402
+ {{#if (eq orm "prisma")}}
20403
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20404
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20405
+ {{/if}}
20406
+
20407
+ COPY . .
20408
+ {{#if (eq packageManager "bun")}}
20409
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20410
+ {{else if (eq packageManager "pnpm")}}
20411
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20412
+ {{else}}
20413
+ RUN --mount=type=cache,target=/root/.npm npm install
20414
+ {{/if}}
20415
+
20416
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20417
+ ARG PUBLIC_SERVER_URL
20418
+ ENV PUBLIC_SERVER_URL=\${PUBLIC_SERVER_URL}
20419
+ {{/if}}
20420
+ {{#if (eq backend "convex")}}
20421
+ ARG PUBLIC_CONVEX_URL
20422
+ ENV PUBLIC_CONVEX_URL=\${PUBLIC_CONVEX_URL}
20423
+ {{/if}}
20424
+ ENV NODE_ENV=production
20425
+ RUN cd apps/web && {{packageManager}} run build
20426
+ ENV SKIP_ENV_VALIDATION=
20427
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20428
+ ENV BETTER_AUTH_SECRET=
20429
+ {{/if}}
20430
+ {{#if (eq orm "prisma")}}
20431
+ ENV DATABASE_URL=
20432
+ {{/if}}
20433
+
20434
+ ENV HOST=0.0.0.0
20435
+ ENV PORT=3001
20436
+ EXPOSE 3001
20437
+
20438
+ WORKDIR /app/apps/web
20439
+ CMD ["node", "dist/server/entry.mjs"]
20440
+ `],
20441
+ ["deploy/docker/web/nuxt/Dockerfile.hbs", `FROM node:24-slim AS builder
20442
+ {{#if (eq packageManager "bun")}}
20443
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20444
+ {{/if}}
20445
+ {{#if (eq packageManager "pnpm")}}
20446
+ RUN npm install -g pnpm
20447
+ {{/if}}
20448
+ WORKDIR /app
20449
+ ENV SKIP_ENV_VALIDATION=1
20450
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20451
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20452
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20453
+ {{/if}}
20454
+ {{#if (eq orm "prisma")}}
20455
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20456
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20457
+ {{/if}}
20458
+
20459
+ COPY . .
20460
+ {{#if (eq packageManager "bun")}}
20461
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20462
+ {{else if (eq packageManager "pnpm")}}
20463
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20464
+ {{else}}
20465
+ RUN --mount=type=cache,target=/root/.npm npm install
20466
+ {{/if}}
20467
+
20468
+ ENV NODE_ENV=production
20469
+ RUN cd apps/web && {{packageManager}} run build
20470
+
20471
+ FROM node:24-slim AS runner
20472
+ WORKDIR /app
20473
+ ENV NODE_ENV=production
20474
+
20475
+ COPY --from=builder /app/apps/web/.output ./
20476
+
20477
+ ENV HOST=0.0.0.0
20478
+ ENV PORT=3001
20479
+ EXPOSE 3001
20480
+
20481
+ CMD ["node", "server/index.mjs"]
20482
+ `],
20483
+ ["deploy/docker/web/react/next/Dockerfile.hbs", `FROM node:24-slim AS builder
20484
+ {{#if (eq packageManager "bun")}}
20485
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20486
+ {{/if}}
20487
+ {{#if (eq packageManager "pnpm")}}
20488
+ RUN npm install -g pnpm
20489
+ {{/if}}
20490
+ WORKDIR /app
20491
+ ENV SKIP_ENV_VALIDATION=1
20492
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20493
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20494
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20495
+ {{/if}}
20496
+ {{#if (eq orm "prisma")}}
20497
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20498
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20499
+ {{/if}}
20500
+
20501
+ COPY . .
20502
+ {{#if (eq packageManager "bun")}}
20503
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20504
+ {{else if (eq packageManager "pnpm")}}
20505
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20506
+ {{else}}
20507
+ RUN --mount=type=cache,target=/root/.npm npm install
20508
+ {{/if}}
20509
+
20510
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20511
+ ARG NEXT_PUBLIC_SERVER_URL
20512
+ ENV NEXT_PUBLIC_SERVER_URL=\${NEXT_PUBLIC_SERVER_URL}
20513
+ {{/if}}
20514
+ {{#if (eq backend "convex")}}
20515
+ ARG NEXT_PUBLIC_CONVEX_URL
20516
+ ENV NEXT_PUBLIC_CONVEX_URL=\${NEXT_PUBLIC_CONVEX_URL}
20517
+ {{/if}}
20518
+ {{#if (eq auth "clerk")}}
20519
+ ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
20520
+ ENV NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=\${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
20521
+ {{/if}}
20522
+ ENV NODE_ENV=production
20523
+ RUN cd apps/web && {{packageManager}} run build
20524
+
20525
+ FROM node:24-slim AS runner
20526
+ WORKDIR /app
20527
+ ENV NODE_ENV=production
20528
+
20529
+ COPY --from=builder /app/apps/web/.next/standalone ./
20530
+ COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
20531
+
20532
+ ENV HOSTNAME=0.0.0.0
20533
+ ENV PORT=3001
20534
+ EXPOSE 3001
20535
+
20536
+ CMD ["node", "apps/web/server.js"]
20537
+ `],
20538
+ ["deploy/docker/web/react/react-router/Dockerfile.hbs", `FROM node:24-slim AS builder
20539
+ {{#if (eq packageManager "bun")}}
20540
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20541
+ {{/if}}
20542
+ {{#if (eq packageManager "pnpm")}}
20543
+ RUN npm install -g pnpm
20544
+ {{/if}}
20545
+ WORKDIR /app
20546
+ ENV SKIP_ENV_VALIDATION=1
20547
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20548
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20549
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20550
+ {{/if}}
20551
+ {{#if (eq orm "prisma")}}
20552
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20553
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20554
+ {{/if}}
20555
+
20556
+ COPY . .
20557
+ {{#if (eq packageManager "bun")}}
20558
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20559
+ {{else if (eq packageManager "pnpm")}}
20560
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20561
+ {{else}}
20562
+ RUN --mount=type=cache,target=/root/.npm npm install
20563
+ {{/if}}
20564
+
20565
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20566
+ ARG VITE_SERVER_URL
20567
+ ENV VITE_SERVER_URL=\${VITE_SERVER_URL}
20568
+ {{/if}}
20569
+ {{#if (eq backend "convex")}}
20570
+ ARG VITE_CONVEX_URL
20571
+ ENV VITE_CONVEX_URL=\${VITE_CONVEX_URL}
20572
+ {{/if}}
20573
+ {{#if (eq auth "clerk")}}
20574
+ ARG VITE_CLERK_PUBLISHABLE_KEY
20575
+ ENV VITE_CLERK_PUBLISHABLE_KEY=\${VITE_CLERK_PUBLISHABLE_KEY}
20576
+ {{/if}}
20577
+ ENV NODE_ENV=production
20578
+ RUN cd apps/web && {{packageManager}} run build
20579
+
20580
+ FROM nginx:alpine AS runner
20581
+ COPY --from=builder /app/apps/web/build/client /usr/share/nginx/html
20582
+ COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf
20583
+ EXPOSE 80
20584
+ CMD ["nginx", "-g", "daemon off;"]
20585
+ `],
20586
+ ["deploy/docker/web/react/react-router/nginx.conf", `server {
20587
+ listen 80;
20588
+ server_name _;
20589
+ root /usr/share/nginx/html;
20590
+ index index.html;
20591
+
20592
+ gzip on;
20593
+ gzip_types text/plain text/css application/json application/javascript image/svg+xml;
20594
+
20595
+ location /assets/ {
20596
+ add_header Cache-Control "public, max-age=31536000, immutable";
20597
+ }
20598
+
20599
+ # SPA fallback
20600
+ location / {
20601
+ try_files $uri $uri/ /index.html;
20602
+ }
20603
+ }
20604
+ `],
20605
+ ["deploy/docker/web/react/tanstack-router/Dockerfile.hbs", `FROM node:24-slim AS builder
20606
+ {{#if (eq packageManager "bun")}}
20607
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20608
+ {{/if}}
20609
+ {{#if (eq packageManager "pnpm")}}
20610
+ RUN npm install -g pnpm
20611
+ {{/if}}
20612
+ WORKDIR /app
20613
+ ENV SKIP_ENV_VALIDATION=1
20614
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20615
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20616
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20617
+ {{/if}}
20618
+ {{#if (eq orm "prisma")}}
20619
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20620
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20621
+ {{/if}}
20622
+
20623
+ COPY . .
20624
+ {{#if (eq packageManager "bun")}}
20625
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20626
+ {{else if (eq packageManager "pnpm")}}
20627
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20628
+ {{else}}
20629
+ RUN --mount=type=cache,target=/root/.npm npm install
20630
+ {{/if}}
20631
+
20632
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20633
+ ARG VITE_SERVER_URL
20634
+ ENV VITE_SERVER_URL=\${VITE_SERVER_URL}
20635
+ {{/if}}
20636
+ {{#if (eq backend "convex")}}
20637
+ ARG VITE_CONVEX_URL
20638
+ ENV VITE_CONVEX_URL=\${VITE_CONVEX_URL}
20639
+ {{/if}}
20640
+ {{#if (eq auth "clerk")}}
20641
+ ARG VITE_CLERK_PUBLISHABLE_KEY
20642
+ ENV VITE_CLERK_PUBLISHABLE_KEY=\${VITE_CLERK_PUBLISHABLE_KEY}
20643
+ {{/if}}
20644
+ ENV NODE_ENV=production
20645
+ RUN cd apps/web && {{packageManager}} run build
20646
+
20647
+ FROM nginx:alpine AS runner
20648
+ COPY --from=builder /app/apps/web/dist /usr/share/nginx/html
20649
+ COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf
20650
+ EXPOSE 80
20651
+ CMD ["nginx", "-g", "daemon off;"]
20652
+ `],
20653
+ ["deploy/docker/web/react/tanstack-router/nginx.conf", `server {
20654
+ listen 80;
20655
+ server_name _;
20656
+ root /usr/share/nginx/html;
20657
+ index index.html;
20658
+
20659
+ gzip on;
20660
+ gzip_types text/plain text/css application/json application/javascript image/svg+xml;
20661
+
20662
+ location /assets/ {
20663
+ add_header Cache-Control "public, max-age=31536000, immutable";
20664
+ }
20665
+
20666
+ # SPA fallback
20667
+ location / {
20668
+ try_files $uri $uri/ /index.html;
20669
+ }
20670
+ }
20671
+ `],
20672
+ ["deploy/docker/web/react/tanstack-start/Dockerfile.hbs", `FROM node:24-slim AS base
20673
+ {{#if (eq packageManager "bun")}}
20674
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20675
+ {{/if}}
20676
+ {{#if (eq packageManager "pnpm")}}
20677
+ RUN npm install -g pnpm
20678
+ {{/if}}
20679
+ WORKDIR /app
20680
+ ENV SKIP_ENV_VALIDATION=1
20681
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20682
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20683
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20684
+ {{/if}}
20685
+ {{#if (eq orm "prisma")}}
20686
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20687
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20688
+ {{/if}}
20689
+
20690
+ COPY . .
20691
+ {{#if (eq packageManager "bun")}}
20692
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20693
+ {{else if (eq packageManager "pnpm")}}
20694
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20695
+ {{else}}
20696
+ RUN --mount=type=cache,target=/root/.npm npm install
20697
+ {{/if}}
20698
+
20699
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20700
+ ARG VITE_SERVER_URL
20701
+ ENV VITE_SERVER_URL=\${VITE_SERVER_URL}
20702
+ {{/if}}
20703
+ {{#if (eq backend "convex")}}
20704
+ ARG VITE_CONVEX_URL
20705
+ ENV VITE_CONVEX_URL=\${VITE_CONVEX_URL}
20706
+ {{/if}}
20707
+ {{#if (eq auth "clerk")}}
20708
+ ARG VITE_CLERK_PUBLISHABLE_KEY
20709
+ ENV VITE_CLERK_PUBLISHABLE_KEY=\${VITE_CLERK_PUBLISHABLE_KEY}
20710
+ {{/if}}
20711
+ ENV NODE_ENV=production
20712
+ RUN cd apps/web && {{packageManager}} run build
20713
+ ENV SKIP_ENV_VALIDATION=
20714
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20715
+ ENV BETTER_AUTH_SECRET=
20716
+ {{/if}}
20717
+ {{#if (eq orm "prisma")}}
20718
+ ENV DATABASE_URL=
20719
+ {{/if}}
20720
+
20721
+ ENV HOST=0.0.0.0
20722
+ ENV PORT=3001
20723
+ EXPOSE 3001
20724
+
20725
+ # SSR chunks require() externals at runtime; must run from the workspace
20726
+ WORKDIR /app/apps/web
20727
+ CMD ["node", ".output/server/index.mjs"]
20728
+ `],
20729
+ ["deploy/docker/web/solid/Dockerfile.hbs", `FROM node:24-slim AS builder
20730
+ {{#if (eq packageManager "bun")}}
20731
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20732
+ {{/if}}
20733
+ {{#if (eq packageManager "pnpm")}}
20734
+ RUN npm install -g pnpm
20735
+ {{/if}}
20736
+ WORKDIR /app
20737
+ ENV SKIP_ENV_VALIDATION=1
20738
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20739
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20740
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20741
+ {{/if}}
20742
+ {{#if (eq orm "prisma")}}
20743
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20744
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20745
+ {{/if}}
20746
+
20747
+ COPY . .
20748
+ {{#if (eq packageManager "bun")}}
20749
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20750
+ {{else if (eq packageManager "pnpm")}}
20751
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20752
+ {{else}}
20753
+ RUN --mount=type=cache,target=/root/.npm npm install
20754
+ {{/if}}
20755
+
20756
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20757
+ ARG VITE_SERVER_URL
20758
+ ENV VITE_SERVER_URL=\${VITE_SERVER_URL}
20759
+ {{/if}}
20760
+ {{#if (eq backend "convex")}}
20761
+ ARG VITE_CONVEX_URL
20762
+ ENV VITE_CONVEX_URL=\${VITE_CONVEX_URL}
20763
+ {{/if}}
20764
+ ENV NODE_ENV=production
20765
+ RUN cd apps/web && {{packageManager}} run build
20766
+
20767
+ FROM nginx:alpine AS runner
20768
+ COPY --from=builder /app/apps/web/dist /usr/share/nginx/html
20769
+ COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf
20770
+ EXPOSE 80
20771
+ CMD ["nginx", "-g", "daemon off;"]
20772
+ `],
20773
+ ["deploy/docker/web/solid/nginx.conf", `server {
20774
+ listen 80;
20775
+ server_name _;
20776
+ root /usr/share/nginx/html;
20777
+ index index.html;
20778
+
20779
+ gzip on;
20780
+ gzip_types text/plain text/css application/json application/javascript image/svg+xml;
20781
+
20782
+ location /assets/ {
20783
+ add_header Cache-Control "public, max-age=31536000, immutable";
20784
+ }
20785
+
20786
+ # SPA fallback
20787
+ location / {
20788
+ try_files $uri $uri/ /index.html;
20789
+ }
20790
+ }
20791
+ `],
20792
+ ["deploy/docker/web/svelte/Dockerfile.hbs", `FROM node:24-slim AS base
20793
+ {{#if (eq packageManager "bun")}}
20794
+ COPY --from=oven/bun:1 /usr/local/bin/bun /usr/local/bin/bun
20795
+ {{/if}}
20796
+ {{#if (eq packageManager "pnpm")}}
20797
+ RUN npm install -g pnpm
20798
+ {{/if}}
20799
+ WORKDIR /app
20800
+ ENV SKIP_ENV_VALIDATION=1
20801
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20802
+ # the build evaluates the auth config; the real secret comes from compose at runtime
20803
+ ENV BETTER_AUTH_SECRET=build-time-placeholder-secret-not-used-at-runtime
20804
+ {{/if}}
20805
+ {{#if (eq orm "prisma")}}
20806
+ # prisma generate resolves DATABASE_URL at install time; the real value comes from compose at runtime
20807
+ ENV DATABASE_URL={{#if (eq database "mysql")}}mysql://build:build@localhost:3306/build{{else if (eq database "mongodb")}}mongodb://localhost:27017/build{{else if (eq database "sqlite")}}file:./build.db{{else}}postgresql://build:build@localhost:5432/build{{/if}}
20808
+ {{/if}}
20809
+
20810
+ COPY . .
20811
+ {{#if (eq packageManager "bun")}}
20812
+ RUN --mount=type=cache,target=/root/.bun/install/cache bun install
20813
+ {{else if (eq packageManager "pnpm")}}
20814
+ RUN --mount=type=cache,target=/pnpm-store pnpm install --store-dir /pnpm-store
20815
+ {{else}}
20816
+ RUN --mount=type=cache,target=/root/.npm npm install
20817
+ {{/if}}
20818
+
20819
+ {{#if (and (ne backend "self") (ne backend "none") (ne backend "convex"))}}
20820
+ ARG PUBLIC_SERVER_URL
20821
+ ENV PUBLIC_SERVER_URL=\${PUBLIC_SERVER_URL}
20822
+ {{/if}}
20823
+ {{#if (eq backend "convex")}}
20824
+ ARG PUBLIC_CONVEX_URL
20825
+ ENV PUBLIC_CONVEX_URL=\${PUBLIC_CONVEX_URL}
20826
+ {{/if}}
20827
+ ENV NODE_ENV=production
20828
+ RUN cd apps/web && {{packageManager}} run build
20829
+ ENV SKIP_ENV_VALIDATION=
20830
+ {{#if (and (eq backend "self") (eq auth "better-auth"))}}
20831
+ ENV BETTER_AUTH_SECRET=
20832
+ {{/if}}
20833
+ {{#if (eq orm "prisma")}}
20834
+ ENV DATABASE_URL=
20835
+ {{/if}}
20836
+
20837
+ ENV HOST=0.0.0.0
20838
+ ENV PORT=3001
20839
+ EXPOSE 3001
20840
+
20841
+ WORKDIR /app/apps/web
20842
+ CMD ["node", "build/index.js"]
19879
20843
  `],
19880
20844
  ["examples/ai/convex/packages/backend/convex/agent.ts.hbs", `import { Agent } from "@convex-dev/agent";
19881
20845
  import { google } from "@ai-sdk/google";
@@ -26408,12 +27372,12 @@ declare module "cloudflare:workers" {
26408
27372
  ["extras/pnpm-workspace.yaml.hbs", `packages:
26409
27373
  - "apps/*"
26410
27374
  - "packages/*"
26411
- {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (eq orm "prisma") (includes addons "lefthook") (includes addons "nx") (includes addons "pwa") (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start") (includes frontend "next") (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
27375
+ {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (eq webDeploy "docker") (eq serverDeploy "docker") (eq orm "prisma") (includes addons "lefthook") (includes addons "nx") (includes addons "pwa") (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start") (includes frontend "next") (includes frontend "nuxt") (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
26412
27376
 
26413
27377
  # pnpm 11 blocks dependency lifecycle scripts unless they are approved here.
26414
27378
  # Entries are scoped to packages this generated stack can pull in.
26415
27379
  allowBuilds:
26416
- {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (includes frontend "tanstack-start"))}}
27380
+ {{#if (or (eq runtime "node") (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (eq webDeploy "docker") (eq serverDeploy "docker") (includes frontend "tanstack-start"))}}
26417
27381
  esbuild: true
26418
27382
  {{/if}}
26419
27383
  {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start") (includes frontend "next"))}}
@@ -26422,7 +27386,11 @@ allowBuilds:
26422
27386
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
26423
27387
  msgpackr-extract: true
26424
27388
  {{/if}}
26425
- {{#if (or (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (includes addons "pwa"))}}
27389
+ {{#if (includes frontend "nuxt")}}
27390
+ "@parcel/watcher": true
27391
+ vue-demi: true
27392
+ {{/if}}
27393
+ {{#if (or (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare") (eq webDeploy "docker") (includes addons "pwa"))}}
26426
27394
  sharp: true
26427
27395
  {{/if}}
26428
27396
  {{#if (or (eq webDeploy "cloudflare") (eq serverDeploy "cloudflare"))}}
@@ -30606,6 +31574,8 @@ export default defineNuxtConfig({
30606
31574
  },
30607
31575
  {{else if (and (ne backend "self") (ne backend "none"))}}
30608
31576
  runtimeConfig: {
31577
+ // server-side override for SSR fetches (NUXT_SERVER_URL); falls back to the public URL
31578
+ serverUrl: "",
30609
31579
  public: {
30610
31580
  serverUrl: process.env.NUXT_PUBLIC_SERVER_URL ?? "",
30611
31581
  }
@@ -30626,7 +31596,8 @@ export default defineNuxtConfig({
30626
31596
  },
30627
31597
  "dependencies": {
30628
31598
  "@nuxt/ui": "^4.5.1",
30629
- "nuxt": "^4.4.4"
31599
+ "nuxt": "^4.4.4",
31600
+ "vue": "^3.5.38"
30630
31601
  },
30631
31602
  "devDependencies": {
30632
31603
  "tailwindcss": "^4.2.1",
@@ -30676,6 +31647,9 @@ import type { NextConfig } from "next";
30676
31647
  const nextConfig: NextConfig = {
30677
31648
  typedRoutes: true,
30678
31649
  reactCompiler: true,
31650
+ {{#if (eq webDeploy "docker")}}
31651
+ output: "standalone",
31652
+ {{/if}}
30679
31653
  {{#if (includes examples "ai")}}
30680
31654
  transpilePackages: ["shiki"],
30681
31655
  {{/if}}
@@ -31603,7 +32577,7 @@ export default function Home() {
31603
32577
  `],
31604
32578
  ["frontend/react/react-router/vite.config.ts.hbs", `import { reactRouter } from "@react-router/dev/vite";
31605
32579
  import tailwindcss from "@tailwindcss/vite";
31606
- import { defineConfig } from "vite";
32580
+ import { defineConfig } from "{{#if (includes addons "vite-plus")}}vite-plus{{else}}vite{{/if}}";
31607
32581
  import tsconfigPaths from "vite-tsconfig-paths";
31608
32582
 
31609
32583
  export default defineConfig({
@@ -31612,7 +32586,8 @@ export default defineConfig({
31612
32586
  reactRouter(),
31613
32587
  tsconfigPaths(),
31614
32588
  ],
31615
- });`],
32589
+ });
32590
+ `],
31616
32591
  ["frontend/react/tanstack-router/index.html.hbs", `<!DOCTYPE html>
31617
32592
  <html lang="en">
31618
32593
  <head>
@@ -32055,7 +33030,7 @@ function HomeComponent() {
32055
33030
  ["frontend/react/tanstack-router/vite.config.ts.hbs", `import tailwindcss from "@tailwindcss/vite";
32056
33031
  import { tanstackRouter } from "@tanstack/router-plugin/vite";
32057
33032
  import react from "@vitejs/plugin-react";
32058
- import { defineConfig } from "vite";
33033
+ import { defineConfig } from "{{#if (includes addons "vite-plus")}}vite-plus{{else}}vite{{/if}}";
32059
33034
 
32060
33035
  export default defineConfig({
32061
33036
  server: {
@@ -32643,8 +33618,11 @@ function HomeComponent() {
32643
33618
  }
32644
33619
  }
32645
33620
  `],
32646
- ["frontend/react/tanstack-start/vite.config.ts.hbs", `import { defineConfig } from "vite";
33621
+ ["frontend/react/tanstack-start/vite.config.ts.hbs", `import { defineConfig } from "{{#if (includes addons "vite-plus")}}vite-plus{{else}}vite{{/if}}";
32647
33622
  import { tanstackStart } from "@tanstack/react-start/plugin/vite";
33623
+ {{#if (eq webDeploy "docker")}}
33624
+ import { nitro } from "nitro/vite";
33625
+ {{/if}}
32648
33626
  import tailwindcss from "@tailwindcss/vite";
32649
33627
  import viteReact from "@vitejs/plugin-react";
32650
33628
 
@@ -32658,6 +33636,9 @@ export default defineConfig({
32658
33636
  plugins: [
32659
33637
  tailwindcss(),
32660
33638
  tanstackStart(),
33639
+ {{#if (eq webDeploy "docker")}}
33640
+ nitro(),
33641
+ {{/if}}
32661
33642
  viteReact(),
32662
33643
  ],
32663
33644
  {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
@@ -32891,6 +33872,7 @@ dist-ssr
32891
33872
  "tailwindcss": "^4.2.2"
32892
33873
  },
32893
33874
  "devDependencies": {
33875
+ "@tanstack/solid-router-devtools": "^1.166.13",
32894
33876
  "vite": "^8.0.8",
32895
33877
  "vite-plugin-solid": "^2.11.12"
32896
33878
  }
@@ -33134,7 +34116,7 @@ body {
33134
34116
  }
33135
34117
  }
33136
34118
  `],
33137
- ["frontend/solid/vite.config.ts.hbs", `import { defineConfig } from "vite";
34119
+ ["frontend/solid/vite.config.ts.hbs", `import { defineConfig } from "{{#if (includes addons "vite-plus")}}vite-plus{{else}}vite{{/if}}";
33138
34120
  import { tanstackRouter } from "@tanstack/router-plugin/vite";
33139
34121
  import solidPlugin from "vite-plugin-solid";
33140
34122
  import tailwindcss from "@tailwindcss/vite";
@@ -33154,7 +34136,8 @@ export default defineConfig({
33154
34136
  server: {
33155
34137
  port: 3001,
33156
34138
  },
33157
- });`],
34139
+ });
34140
+ `],
33158
34141
  ["frontend/svelte/_gitignore", `node_modules
33159
34142
 
33160
34143
  # Output
@@ -33446,6 +34429,8 @@ const TITLE_TEXT = \`
33446
34429
  ["frontend/svelte/static/favicon.png", `[Binary file]`],
33447
34430
  ["frontend/svelte/svelte.config.js.hbs", `{{#if (eq webDeploy "cloudflare")}}
33448
34431
  import alchemy from 'alchemy/cloudflare/sveltekit';
34432
+ {{else if (eq webDeploy "docker")}}
34433
+ import adapter from '@sveltejs/adapter-node';
33449
34434
  {{else}}
33450
34435
  import adapter from '@sveltejs/adapter-auto';
33451
34436
  {{/if}}
@@ -33461,6 +34446,9 @@ const config = {
33461
34446
  {{#if (eq webDeploy "cloudflare")}}
33462
34447
  // Alchemy's adapter wraps SvelteKit's Cloudflare adapter for local platform.env and Worker builds.
33463
34448
  adapter: alchemy()
34449
+ {{else if (eq webDeploy "docker")}}
34450
+ // adapter-node builds a standalone Node server (run with \`node build/index.js\`).
34451
+ adapter: adapter()
33464
34452
  {{else}}
33465
34453
  // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
33466
34454
  // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
@@ -33495,7 +34483,7 @@ export default config;
33495
34483
  `],
33496
34484
  ["frontend/svelte/vite.config.ts.hbs", `import tailwindcss from "@tailwindcss/vite";
33497
34485
  import { sveltekit } from "@sveltejs/kit/vite";
33498
- import { defineConfig } from "vite";
34486
+ import { defineConfig } from "{{#if (includes addons "vite-plus")}}vite-plus{{else}}vite{{/if}}";
33499
34487
 
33500
34488
  export default defineConfig({
33501
34489
  plugins: [tailwindcss(), sveltekit()],
@@ -33716,6 +34704,7 @@ export const env = createEnv({
33716
34704
  NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
33717
34705
  },
33718
34706
  runtimeEnv: process.env,
34707
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
33719
34708
  emptyStringAsUndefined: true,
33720
34709
  });
33721
34710
  {{/if}}
@@ -33841,6 +34830,7 @@ export const env = createEnv({
33841
34830
  runtimeEnv: (import.meta as any).env,
33842
34831
  {{/if}}
33843
34832
  {{/if}}
34833
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
33844
34834
  emptyStringAsUndefined: true,
33845
34835
  });
33846
34836
  `],
@@ -35297,8 +36287,8 @@ function SuccessPage() {
35297
36287
  </div>
35298
36288
  `]
35299
36289
  ]);
35300
- const TEMPLATE_COUNT = 483;
36290
+ const TEMPLATE_COUNT = 497;
35301
36291
  //#endregion
35302
- export { EMBEDDED_TEMPLATES, GeneratorError, Handlebars, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generate, generateReproducibleCommand, isBinaryFile, processAddonTemplates, processAddonsDeps, processFileContent, processTemplateString, transformFilename, writeBtsConfigToVfs };
36292
+ export { EMBEDDED_TEMPLATES, GeneratorError, Handlebars, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generate, generateReproducibleCommand, isBinaryFile, processAddonTemplates, processAddonsDeps, processFileContent, processNxConfig, processPackageConfigs, processTemplateString, processTurboConfig, processVitePlusConfig, transformFilename, writeBtsConfigToVfs };
35303
36293
 
35304
36294
  //# sourceMappingURL=index.mjs.map