create-better-fullstack 1.7.1 → 1.8.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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as __reExport } from "./chunk-CCII7kTE.mjs";
3
- import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-bOXo9tbL.mjs";
4
- import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CGhYT2qC.mjs";
3
+ import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-YcroedMK.mjs";
4
+ import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CUmA_nra.mjs";
5
5
  import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
6
6
  import { createRouterClient, os } from "@orpc/server";
7
7
  import pc from "picocolors";
@@ -363,7 +363,7 @@ const CreateCommandOptionsSchema = z.object({
363
363
  yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
364
364
  verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
365
365
  dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
366
- ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, go, or java)"),
366
+ ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)"),
367
367
  database: types_exports.DatabaseSchema.optional(),
368
368
  orm: types_exports.ORMSchema.optional(),
369
369
  auth: types_exports.AuthSchema.optional(),
@@ -388,6 +388,13 @@ const CreateCommandOptionsSchema = z.object({
388
388
  i18n: types_exports.I18nSchema.optional().describe("Internationalization (i18n) library"),
389
389
  search: types_exports.SearchSchema.optional().describe("Search engine solution"),
390
390
  fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
391
+ mobileNavigation: types_exports.MobileNavigationSchema.optional().describe("Mobile navigation (expo-router, react-navigation)"),
392
+ mobileUI: types_exports.MobileUISchema.optional().describe("Mobile UI (tamagui, gluestack-ui, uniwind, unistyles)"),
393
+ mobileStorage: types_exports.MobileStorageSchema.optional().describe("Mobile storage (mmkv)"),
394
+ mobileTesting: types_exports.MobileTestingSchema.optional().describe("Mobile testing (maestro, react-native-testing-library)"),
395
+ mobilePush: types_exports.MobilePushSchema.optional().describe("Mobile push notifications (expo-notifications)"),
396
+ mobileOTA: types_exports.MobileOTASchema.optional().describe("Mobile OTA updates (expo-updates)"),
397
+ mobileDeepLinking: types_exports.MobileDeepLinkingSchema.optional().describe("Mobile deep linking (expo-linking)"),
391
398
  frontend: z.array(types_exports.FrontendSchema).optional(),
392
399
  astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
393
400
  addons: z.array(types_exports.AddonsSchema).optional(),
@@ -446,6 +453,21 @@ const CreateCommandOptionsSchema = z.object({
446
453
  javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
447
454
  javaLibraries: z.array(types_exports.JavaLibrariesSchema).optional().describe("Java application libraries"),
448
455
  javaTestingLibraries: z.array(types_exports.JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
456
+ elixirWebFramework: types_exports.ElixirWebFrameworkSchema.optional().describe("Elixir web framework (phoenix, phoenix-live-view, none)"),
457
+ elixirOrm: types_exports.ElixirOrmSchema.optional().describe("Elixir ORM/database (ecto, ecto-sql, none)"),
458
+ elixirAuth: types_exports.ElixirAuthSchema.optional().describe("Elixir auth (phx-gen-auth, ueberauth, guardian, none)"),
459
+ elixirApi: types_exports.ElixirApiSchema.optional().describe("Elixir API layer (rest, absinthe, none)"),
460
+ elixirRealtime: types_exports.ElixirRealtimeSchema.optional().describe("Elixir realtime (channels, presence, pubsub, live-view-streams, none)"),
461
+ elixirJobs: types_exports.ElixirJobsSchema.optional().describe("Elixir jobs (oban, quantum, none)"),
462
+ elixirValidation: types_exports.ElixirValidationSchema.optional().describe("Elixir validation (ecto-changesets, nimble-options, none)"),
463
+ elixirHttp: types_exports.ElixirHttpSchema.optional().describe("Elixir HTTP client (req, finch, none)"),
464
+ elixirJson: types_exports.ElixirJsonSchema.optional().describe("Elixir JSON library (jason, none)"),
465
+ elixirEmail: types_exports.ElixirEmailSchema.optional().describe("Elixir email library (swoosh, none)"),
466
+ elixirCaching: types_exports.ElixirCachingSchema.optional().describe("Elixir caching (cachex, nebulex, none)"),
467
+ elixirObservability: types_exports.ElixirObservabilitySchema.optional().describe("Elixir observability (telemetry, opentelemetry, prom_ex, none)"),
468
+ elixirTesting: types_exports.ElixirTestingSchema.optional().describe("Elixir testing (ex_unit, mox, bypass, wallaby, none)"),
469
+ elixirQuality: types_exports.ElixirQualitySchema.optional().describe("Elixir code quality (credo, dialyxir, sobelow, none)"),
470
+ elixirDeploy: types_exports.ElixirDeploySchema.optional().describe("Elixir deploy target (docker, fly, gigalixir, mix-release, none)"),
449
471
  aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
450
472
  });
451
473
  const CreateCommandInputSchema = z.tuple([types_exports.ProjectNameSchema.optional(), CreateCommandOptionsSchema]);
@@ -534,6 +556,7 @@ function ensureSingleWebAndNative(frontends) {
534
556
  }
535
557
  const FULLSTACK_FRONTENDS$1 = [
536
558
  "next",
559
+ "vinext",
537
560
  "tanstack-start",
538
561
  "astro",
539
562
  "nuxt",
@@ -545,11 +568,11 @@ function validateSelfBackendCompatibility(providedFlags, options, config) {
545
568
  const frontends = config.frontend || options.frontend || [];
546
569
  if (backend === "self") {
547
570
  const { web, native } = splitFrontends$1(frontends);
548
- if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
571
+ if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
549
572
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
550
573
  }
551
574
  const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
552
- if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
575
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
553
576
  }
554
577
  const WORKERS_COMPATIBLE_BACKENDS = [
555
578
  "hono",
@@ -647,11 +670,11 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
647
670
  };
648
671
  if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
649
672
  isCompatible: false,
650
- reason: "Docker Compose currently supports Next.js, TanStack Router, React Router, React Vite, Solid, or Astro"
673
+ reason: "Docker Compose currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro"
651
674
  };
652
- if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next")) return {
675
+ if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
653
676
  isCompatible: false,
654
- reason: "Docker Compose self-backend support currently requires Next.js"
677
+ reason: "Docker Compose self-backend support currently requires Next.js or Vinext"
655
678
  };
656
679
  if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
657
680
  isCompatible: false,
@@ -1513,6 +1536,24 @@ async function runGradleTests({ projectDir }) {
1513
1536
  if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
1514
1537
  }
1515
1538
  }
1539
+ async function runMixCompile({ projectDir }) {
1540
+ const s = spinner();
1541
+ try {
1542
+ s.start("Running mix deps.get and mix compile...");
1543
+ await $({
1544
+ cwd: projectDir,
1545
+ stderr: "inherit"
1546
+ })`mix deps.get`;
1547
+ await $({
1548
+ cwd: projectDir,
1549
+ stderr: "inherit"
1550
+ })`mix compile`;
1551
+ s.stop("Elixir dependencies installed and project compiled");
1552
+ } catch (error) {
1553
+ s.stop(pc.red("mix compile failed"));
1554
+ if (error instanceof Error) consola.error(pc.red(`Mix error: ${error.message}`));
1555
+ }
1556
+ }
1516
1557
 
1517
1558
  //#endregion
1518
1559
  //#region src/helpers/core/add-handler.ts
@@ -1817,6 +1858,7 @@ function resolveAnimationPrompt(context = {}) {
1817
1858
  "react-vite",
1818
1859
  "tanstack-start",
1819
1860
  "next",
1861
+ "vinext",
1820
1862
  "redwood"
1821
1863
  ].includes(f));
1822
1864
  const isFresh = web.includes("fresh");
@@ -2054,6 +2096,7 @@ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript")
2054
2096
  //#region src/prompts/backend.ts
2055
2097
  const FULLSTACK_FRONTENDS = [
2056
2098
  "next",
2099
+ "vinext",
2057
2100
  "tanstack-start",
2058
2101
  "astro",
2059
2102
  "nuxt",
@@ -2179,6 +2222,12 @@ const CACHING_PROMPT_OPTIONS = [{
2179
2222
  hint: "Skip caching layer setup"
2180
2223
  }];
2181
2224
  function resolveCachingPrompt(context = {}) {
2225
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
2226
+ shouldPrompt: false,
2227
+ mode: "single",
2228
+ options: [],
2229
+ autoValue: "none"
2230
+ };
2182
2231
  if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
2183
2232
  shouldPrompt: false,
2184
2233
  mode: "single",
@@ -2579,6 +2628,317 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2579
2628
  return response;
2580
2629
  }
2581
2630
 
2631
+ //#endregion
2632
+ //#region src/prompts/elixir-ecosystem.ts
2633
+ function makeChoice(message, options, defaultValue, value) {
2634
+ const resolution = createStaticSinglePromptResolution(options, defaultValue, value);
2635
+ if (!resolution.shouldPrompt) return Promise.resolve(resolution.autoValue ?? defaultValue);
2636
+ return navigableSelect({
2637
+ message,
2638
+ options: resolution.options,
2639
+ initialValue: resolution.initialValue
2640
+ }).then((response) => isCancel$1(response) ? exitCancelled("Operation cancelled") : response);
2641
+ }
2642
+ const WEB_FRAMEWORK_OPTIONS = [
2643
+ {
2644
+ value: "phoenix",
2645
+ label: "Phoenix",
2646
+ hint: "Conventional Phoenix web application"
2647
+ },
2648
+ {
2649
+ value: "phoenix-live-view",
2650
+ label: "Phoenix LiveView",
2651
+ hint: "Server-rendered realtime UI"
2652
+ },
2653
+ {
2654
+ value: "none",
2655
+ label: "None",
2656
+ hint: "No Elixir web framework"
2657
+ }
2658
+ ];
2659
+ const ORM_OPTIONS = [
2660
+ {
2661
+ value: "ecto-sql",
2662
+ label: "Ecto SQL",
2663
+ hint: "Ecto plus SQL adapters and migrations"
2664
+ },
2665
+ {
2666
+ value: "ecto",
2667
+ label: "Ecto",
2668
+ hint: "Ecto schemas and changesets without SQL repo wiring"
2669
+ },
2670
+ {
2671
+ value: "none",
2672
+ label: "None",
2673
+ hint: "No database layer"
2674
+ }
2675
+ ];
2676
+ const AUTH_OPTIONS = [
2677
+ {
2678
+ value: "phx-gen-auth",
2679
+ label: "phx.gen.auth",
2680
+ hint: "Phoenix account/session scaffold"
2681
+ },
2682
+ {
2683
+ value: "ueberauth",
2684
+ label: "Ueberauth",
2685
+ hint: "OAuth strategy foundation"
2686
+ },
2687
+ {
2688
+ value: "guardian",
2689
+ label: "Guardian",
2690
+ hint: "JWT authentication foundation"
2691
+ },
2692
+ {
2693
+ value: "none",
2694
+ label: "None",
2695
+ hint: "No auth layer"
2696
+ }
2697
+ ];
2698
+ const API_OPTIONS = [
2699
+ {
2700
+ value: "rest",
2701
+ label: "Phoenix REST",
2702
+ hint: "Controllers and JSON endpoints"
2703
+ },
2704
+ {
2705
+ value: "absinthe",
2706
+ label: "Absinthe GraphQL",
2707
+ hint: "GraphQL schema and resolvers"
2708
+ },
2709
+ {
2710
+ value: "none",
2711
+ label: "None",
2712
+ hint: "No API layer"
2713
+ }
2714
+ ];
2715
+ const REALTIME_OPTIONS = [
2716
+ {
2717
+ value: "channels",
2718
+ label: "Phoenix Channels",
2719
+ hint: "WebSocket channel endpoint"
2720
+ },
2721
+ {
2722
+ value: "presence",
2723
+ label: "Phoenix Presence",
2724
+ hint: "Presence tracking over PubSub"
2725
+ },
2726
+ {
2727
+ value: "pubsub",
2728
+ label: "Phoenix PubSub",
2729
+ hint: "PubSub foundation only"
2730
+ },
2731
+ {
2732
+ value: "live-view-streams",
2733
+ label: "LiveView Streams",
2734
+ hint: "Realtime LiveView stream demo"
2735
+ },
2736
+ {
2737
+ value: "none",
2738
+ label: "None",
2739
+ hint: "No realtime feature"
2740
+ }
2741
+ ];
2742
+ const JOB_OPTIONS = [
2743
+ {
2744
+ value: "oban",
2745
+ label: "Oban",
2746
+ hint: "PostgreSQL-backed jobs and workers"
2747
+ },
2748
+ {
2749
+ value: "quantum",
2750
+ label: "Quantum",
2751
+ hint: "Cron-like scheduler"
2752
+ },
2753
+ {
2754
+ value: "none",
2755
+ label: "None",
2756
+ hint: "No jobs layer"
2757
+ }
2758
+ ];
2759
+ const VALIDATION_OPTIONS = [
2760
+ {
2761
+ value: "ecto-changesets",
2762
+ label: "Ecto Changesets",
2763
+ hint: "Data validation with Ecto"
2764
+ },
2765
+ {
2766
+ value: "nimble-options",
2767
+ label: "NimbleOptions",
2768
+ hint: "Declarative option validation"
2769
+ },
2770
+ {
2771
+ value: "none",
2772
+ label: "None",
2773
+ hint: "No extra validation helper"
2774
+ }
2775
+ ];
2776
+ const HTTP_OPTIONS = [
2777
+ {
2778
+ value: "req",
2779
+ label: "Req",
2780
+ hint: "High-level HTTP client"
2781
+ },
2782
+ {
2783
+ value: "finch",
2784
+ label: "Finch",
2785
+ hint: "Pooled HTTP client"
2786
+ },
2787
+ {
2788
+ value: "none",
2789
+ label: "None",
2790
+ hint: "No HTTP client"
2791
+ }
2792
+ ];
2793
+ const JSON_OPTIONS = [{
2794
+ value: "jason",
2795
+ label: "Jason",
2796
+ hint: "Phoenix default JSON library"
2797
+ }, {
2798
+ value: "none",
2799
+ label: "None",
2800
+ hint: "No JSON library"
2801
+ }];
2802
+ const EMAIL_OPTIONS = [{
2803
+ value: "swoosh",
2804
+ label: "Swoosh",
2805
+ hint: "Phoenix email library"
2806
+ }, {
2807
+ value: "none",
2808
+ label: "None",
2809
+ hint: "No email library"
2810
+ }];
2811
+ const CACHING_OPTIONS = [
2812
+ {
2813
+ value: "cachex",
2814
+ label: "Cachex",
2815
+ hint: "In-memory cache"
2816
+ },
2817
+ {
2818
+ value: "nebulex",
2819
+ label: "Nebulex",
2820
+ hint: "Cache abstraction"
2821
+ },
2822
+ {
2823
+ value: "none",
2824
+ label: "None",
2825
+ hint: "No cache layer"
2826
+ }
2827
+ ];
2828
+ const OBSERVABILITY_OPTIONS = [
2829
+ {
2830
+ value: "telemetry",
2831
+ label: "Telemetry",
2832
+ hint: "Phoenix telemetry metrics"
2833
+ },
2834
+ {
2835
+ value: "opentelemetry",
2836
+ label: "OpenTelemetry",
2837
+ hint: "Distributed tracing foundation"
2838
+ },
2839
+ {
2840
+ value: "prom_ex",
2841
+ label: "PromEx",
2842
+ hint: "Prometheus metrics for Phoenix"
2843
+ },
2844
+ {
2845
+ value: "none",
2846
+ label: "None",
2847
+ hint: "No observability add-on"
2848
+ }
2849
+ ];
2850
+ const TESTING_OPTIONS = [
2851
+ {
2852
+ value: "ex_unit",
2853
+ label: "ExUnit",
2854
+ hint: "Standard Elixir tests"
2855
+ },
2856
+ {
2857
+ value: "mox",
2858
+ label: "Mox",
2859
+ hint: "Concurrent-safe mocks"
2860
+ },
2861
+ {
2862
+ value: "bypass",
2863
+ label: "Bypass",
2864
+ hint: "External HTTP service fakes"
2865
+ },
2866
+ {
2867
+ value: "wallaby",
2868
+ label: "Wallaby",
2869
+ hint: "Browser acceptance testing"
2870
+ },
2871
+ {
2872
+ value: "none",
2873
+ label: "None",
2874
+ hint: "No extra test library"
2875
+ }
2876
+ ];
2877
+ const QUALITY_OPTIONS = [
2878
+ {
2879
+ value: "credo",
2880
+ label: "Credo",
2881
+ hint: "Static code analysis"
2882
+ },
2883
+ {
2884
+ value: "dialyxir",
2885
+ label: "Dialyxir",
2886
+ hint: "Dialyzer integration"
2887
+ },
2888
+ {
2889
+ value: "sobelow",
2890
+ label: "Sobelow",
2891
+ hint: "Phoenix security analysis"
2892
+ },
2893
+ {
2894
+ value: "none",
2895
+ label: "None",
2896
+ hint: "No code quality tool"
2897
+ }
2898
+ ];
2899
+ const DEPLOY_OPTIONS = [
2900
+ {
2901
+ value: "docker",
2902
+ label: "Docker",
2903
+ hint: "Dockerfile for Phoenix releases"
2904
+ },
2905
+ {
2906
+ value: "fly",
2907
+ label: "Fly.io",
2908
+ hint: "Fly.io release config"
2909
+ },
2910
+ {
2911
+ value: "gigalixir",
2912
+ label: "Gigalixir",
2913
+ hint: "Gigalixir Procfile and notes"
2914
+ },
2915
+ {
2916
+ value: "mix-release",
2917
+ label: "Mix Release",
2918
+ hint: "Release-ready runtime config"
2919
+ },
2920
+ {
2921
+ value: "none",
2922
+ label: "None",
2923
+ hint: "No deploy files"
2924
+ }
2925
+ ];
2926
+ const getElixirWebFrameworkChoice = (value) => makeChoice("Select Elixir web framework", WEB_FRAMEWORK_OPTIONS, "phoenix", value);
2927
+ const getElixirOrmChoice = (value) => makeChoice("Select Elixir database layer", ORM_OPTIONS, "ecto-sql", value);
2928
+ const getElixirAuthChoice = (value) => makeChoice("Select Elixir auth", AUTH_OPTIONS, "none", value);
2929
+ const getElixirApiChoice = (value) => makeChoice("Select Elixir API layer", API_OPTIONS, "rest", value);
2930
+ const getElixirRealtimeChoice = (value) => makeChoice("Select Elixir realtime feature", REALTIME_OPTIONS, "channels", value);
2931
+ const getElixirJobsChoice = (value) => makeChoice("Select Elixir jobs layer", JOB_OPTIONS, "none", value);
2932
+ const getElixirValidationChoice = (value) => makeChoice("Select Elixir validation", VALIDATION_OPTIONS, "ecto-changesets", value);
2933
+ const getElixirHttpChoice = (value) => makeChoice("Select Elixir HTTP client", HTTP_OPTIONS, "req", value);
2934
+ const getElixirJsonChoice = (value) => makeChoice("Select Elixir JSON library", JSON_OPTIONS, "jason", value);
2935
+ const getElixirEmailChoice = (value) => makeChoice("Select Elixir email library", EMAIL_OPTIONS, "none", value);
2936
+ const getElixirCachingChoice = (value) => makeChoice("Select Elixir caching", CACHING_OPTIONS, "none", value);
2937
+ const getElixirObservabilityChoice = (value) => makeChoice("Select Elixir observability", OBSERVABILITY_OPTIONS, "telemetry", value);
2938
+ const getElixirTestingChoice = (value) => makeChoice("Select Elixir testing", TESTING_OPTIONS, "ex_unit", value);
2939
+ const getElixirQualityChoice = (value) => makeChoice("Select Elixir code quality", QUALITY_OPTIONS, "credo", value);
2940
+ const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target", DEPLOY_OPTIONS, "none", value);
2941
+
2582
2942
  //#endregion
2583
2943
  //#region src/prompts/ecosystem.ts
2584
2944
  async function getEcosystemChoice(ecosystem) {
@@ -2589,7 +2949,12 @@ async function getEcosystemChoice(ecosystem) {
2589
2949
  {
2590
2950
  value: "typescript",
2591
2951
  label: "TypeScript",
2592
- hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
2952
+ hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
2953
+ },
2954
+ {
2955
+ value: "react-native",
2956
+ label: "React Native",
2957
+ hint: "Expo and React Native mobile apps with native integrations"
2593
2958
  },
2594
2959
  {
2595
2960
  value: "rust",
@@ -2605,6 +2970,11 @@ async function getEcosystemChoice(ecosystem) {
2605
2970
  value: "go",
2606
2971
  label: "Go",
2607
2972
  hint: "Go ecosystem with Gin, Echo, GORM, and more"
2973
+ },
2974
+ {
2975
+ value: "java",
2976
+ label: "Java",
2977
+ hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
2608
2978
  }
2609
2979
  ],
2610
2980
  initialValue: "typescript"
@@ -2693,6 +3063,12 @@ const EMAIL_PROMPT_OPTIONS = [
2693
3063
  ];
2694
3064
  const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
2695
3065
  function resolveEmailPrompt(context = {}) {
3066
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
3067
+ shouldPrompt: false,
3068
+ mode: "single",
3069
+ options: [],
3070
+ autoValue: "none"
3071
+ };
2696
3072
  const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
2697
3073
  if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
2698
3074
  shouldPrompt: false,
@@ -2866,6 +3242,7 @@ function resolveFormsPrompt(context = {}) {
2866
3242
  "react-vite",
2867
3243
  "tanstack-start",
2868
3244
  "next",
3245
+ "vinext",
2869
3246
  "redwood"
2870
3247
  ].includes(f));
2871
3248
  const isSolid = web.includes("solid");
@@ -2949,6 +3326,11 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
2949
3326
  label: "Next.js",
2950
3327
  hint: "The React Framework for the Web"
2951
3328
  },
3329
+ {
3330
+ value: "vinext",
3331
+ label: "Vinext",
3332
+ hint: "The Vite Compiler for Next.js"
3333
+ },
2952
3334
  {
2953
3335
  value: "nuxt",
2954
3336
  label: "Nuxt",
@@ -3101,6 +3483,17 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
3101
3483
  return result;
3102
3484
  }
3103
3485
  }
3486
+ async function getNativeFrontendChoice(frontendOptions) {
3487
+ if (frontendOptions !== void 0) return frontendOptions.filter((frontend) => frontend.startsWith("native-"));
3488
+ const nativeFramework = await navigableSelect({
3489
+ message: "Choose React Native app type",
3490
+ options: NATIVE_FRONTEND_PROMPT_OPTIONS,
3491
+ initialValue: "native-bare"
3492
+ });
3493
+ if (isGoBack(nativeFramework)) return GO_BACK_SYMBOL;
3494
+ if (isCancel$1(nativeFramework)) return exitCancelled("Operation cancelled");
3495
+ return [nativeFramework];
3496
+ }
3104
3497
 
3105
3498
  //#endregion
3106
3499
  //#region src/prompts/git.ts
@@ -3421,6 +3814,18 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
3421
3814
  if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3422
3815
  return response$1;
3423
3816
  }
3817
+ if (ecosystem === "elixir") {
3818
+ if (!await commandExists("mix")) {
3819
+ log.warn("Mix is not installed. Please install Elixir from https://elixir-lang.org/install.html");
3820
+ return false;
3821
+ }
3822
+ const response$1 = await navigableConfirm({
3823
+ message: "Run mix deps.get and mix compile?",
3824
+ initialValue: DEFAULT_CONFIG.install
3825
+ });
3826
+ if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3827
+ return response$1;
3828
+ }
3424
3829
  const response = await navigableConfirm({
3425
3830
  message: "Install dependencies?",
3426
3831
  initialValue: DEFAULT_CONFIG.install
@@ -3827,6 +4232,143 @@ async function getLoggingChoice(logging, backend) {
3827
4232
  return response;
3828
4233
  }
3829
4234
 
4235
+ //#endregion
4236
+ //#region src/prompts/mobile.ts
4237
+ const MOBILE_NAVIGATION_OPTIONS = [
4238
+ {
4239
+ value: "expo-router",
4240
+ label: "Expo Router",
4241
+ hint: "File-based routing for Expo apps"
4242
+ },
4243
+ {
4244
+ value: "react-navigation",
4245
+ label: "React Navigation",
4246
+ hint: "Code-defined native stacks and tabs"
4247
+ },
4248
+ {
4249
+ value: "none",
4250
+ label: "None",
4251
+ hint: "Skip navigation setup"
4252
+ }
4253
+ ];
4254
+ const MOBILE_UI_OPTIONS = [
4255
+ {
4256
+ value: "none",
4257
+ label: "None",
4258
+ hint: "Use React Native primitives"
4259
+ },
4260
+ {
4261
+ value: "tamagui",
4262
+ label: "Tamagui",
4263
+ hint: "Universal themed UI primitives"
4264
+ },
4265
+ {
4266
+ value: "gluestack-ui",
4267
+ label: "Gluestack UI",
4268
+ hint: "Accessible cross-platform components"
4269
+ },
4270
+ {
4271
+ value: "uniwind",
4272
+ label: "Uniwind",
4273
+ hint: "Tailwind-style React Native styling"
4274
+ },
4275
+ {
4276
+ value: "unistyles",
4277
+ label: "Unistyles",
4278
+ hint: "Type-safe React Native stylesheets"
4279
+ }
4280
+ ];
4281
+ const MOBILE_STORAGE_OPTIONS = [{
4282
+ value: "none",
4283
+ label: "None",
4284
+ hint: "Skip device storage helpers"
4285
+ }, {
4286
+ value: "mmkv",
4287
+ label: "MMKV",
4288
+ hint: "Fast encrypted key-value storage"
4289
+ }];
4290
+ const MOBILE_TESTING_OPTIONS = [
4291
+ {
4292
+ value: "none",
4293
+ label: "None",
4294
+ hint: "Skip mobile testing setup"
4295
+ },
4296
+ {
4297
+ value: "maestro",
4298
+ label: "Maestro",
4299
+ hint: "Mobile E2E flow files"
4300
+ },
4301
+ {
4302
+ value: "react-native-testing-library",
4303
+ label: "React Native Testing Library",
4304
+ hint: "Unit tests for native components"
4305
+ },
4306
+ {
4307
+ value: "maestro-react-native-testing-library",
4308
+ label: "Maestro + RN Testing Library",
4309
+ hint: "Mobile E2E flows and unit tests"
4310
+ }
4311
+ ];
4312
+ const MOBILE_PUSH_OPTIONS = [{
4313
+ value: "none",
4314
+ label: "None",
4315
+ hint: "Skip push notification setup"
4316
+ }, {
4317
+ value: "expo-notifications",
4318
+ label: "Expo Notifications",
4319
+ hint: "Expo push token helper"
4320
+ }];
4321
+ const MOBILE_OTA_OPTIONS = [{
4322
+ value: "none",
4323
+ label: "None",
4324
+ hint: "Skip OTA update setup"
4325
+ }, {
4326
+ value: "expo-updates",
4327
+ label: "Expo Updates",
4328
+ hint: "Runtime version and update helper"
4329
+ }];
4330
+ const MOBILE_DEEP_LINKING_OPTIONS = [{
4331
+ value: "expo-linking",
4332
+ label: "Expo Linking",
4333
+ hint: "Scheme config and redirect URI helpers"
4334
+ }, {
4335
+ value: "none",
4336
+ label: "None",
4337
+ hint: "Skip deep link helpers"
4338
+ }];
4339
+ async function promptMobileOption(options, defaultValue, selected, message) {
4340
+ const resolution = createStaticSinglePromptResolution(options, defaultValue, selected);
4341
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? defaultValue;
4342
+ const response = await navigableSelect({
4343
+ message,
4344
+ options: resolution.options,
4345
+ initialValue: resolution.initialValue
4346
+ });
4347
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4348
+ return response;
4349
+ }
4350
+ function getMobileNavigationChoice(mobileNavigation) {
4351
+ return promptMobileOption(MOBILE_NAVIGATION_OPTIONS, "expo-router", mobileNavigation, "Select mobile navigation");
4352
+ }
4353
+ function getMobileUIChoice(mobileUI) {
4354
+ return promptMobileOption(MOBILE_UI_OPTIONS, "none", mobileUI, "Select mobile UI");
4355
+ }
4356
+ function getMobileStorageChoice(mobileStorage) {
4357
+ return promptMobileOption(MOBILE_STORAGE_OPTIONS, "none", mobileStorage, "Select mobile storage");
4358
+ }
4359
+ function getMobileTestingChoice(mobileTesting) {
4360
+ return promptMobileOption(MOBILE_TESTING_OPTIONS, "none", mobileTesting, "Select mobile testing");
4361
+ }
4362
+ function getMobilePushChoice(mobilePush) {
4363
+ return promptMobileOption(MOBILE_PUSH_OPTIONS, "none", mobilePush, "Select mobile push");
4364
+ }
4365
+ function getMobileOTAChoice(mobileOTA) {
4366
+ return promptMobileOption(MOBILE_OTA_OPTIONS, "none", mobileOTA, "Select mobile OTA updates");
4367
+ }
4368
+ function getMobileDeepLinkingChoice(mobileDeepLinking) {
4369
+ return promptMobileOption(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking, "Select mobile deep linking");
4370
+ }
4371
+
3830
4372
  //#endregion
3831
4373
  //#region src/prompts/navigable-group.ts
3832
4374
  /**
@@ -3910,6 +4452,12 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
3910
4452
  ];
3911
4453
  const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
3912
4454
  function resolveObservabilityPrompt(context = {}) {
4455
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
4456
+ shouldPrompt: false,
4457
+ mode: "single",
4458
+ options: [],
4459
+ autoValue: "none"
4460
+ };
3913
4461
  const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
3914
4462
  if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
3915
4463
  shouldPrompt: false,
@@ -5006,6 +5554,12 @@ const SEARCH_PROMPT_OPTIONS = [
5006
5554
  ];
5007
5555
  const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
5008
5556
  function resolveSearchPrompt(context = {}) {
5557
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
5558
+ shouldPrompt: false,
5559
+ mode: "single",
5560
+ options: [],
5561
+ autoValue: "none"
5562
+ };
5009
5563
  const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
5010
5564
  if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
5011
5565
  shouldPrompt: false,
@@ -5479,6 +6033,7 @@ function resolveStateManagementPrompt(context = {}) {
5479
6033
  "react-vite",
5480
6034
  "tanstack-start",
5481
6035
  "next",
6036
+ "vinext",
5482
6037
  "redwood"
5483
6038
  ].includes(f));
5484
6039
  const isFresh = web.includes("fresh");
@@ -5771,6 +6326,7 @@ const WEB_FRAMEWORKS = [
5771
6326
  "react-vite",
5772
6327
  "tanstack-start",
5773
6328
  "next",
6329
+ "vinext",
5774
6330
  "nuxt",
5775
6331
  "svelte",
5776
6332
  "solid",
@@ -5830,6 +6386,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5830
6386
  const result = await navigableGroup({
5831
6387
  ecosystem: () => getEcosystemChoice(flags.ecosystem),
5832
6388
  frontend: ({ results }) => {
6389
+ if (results.ecosystem === "react-native") return getNativeFrontendChoice(flags.frontend);
5833
6390
  if (results.ecosystem !== "typescript") return Promise.resolve([]);
5834
6391
  return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
5835
6392
  },
@@ -5882,6 +6439,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5882
6439
  },
5883
6440
  auth: ({ results }) => {
5884
6441
  if (results.ecosystem === "typescript") return getAuthChoice(flags.auth, results.backend, results.frontend, "typescript");
6442
+ if (results.ecosystem === "react-native") return Promise.resolve(flags.auth ?? "none");
5885
6443
  if (results.ecosystem === "go") return getAuthChoice(flags.auth, void 0, void 0, "go");
5886
6444
  return Promise.resolve("none");
5887
6445
  },
@@ -5890,6 +6448,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5890
6448
  return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
5891
6449
  },
5892
6450
  email: ({ results }) => {
6451
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
5893
6452
  return getEmailChoice(flags.email, results.backend, results.ecosystem);
5894
6453
  },
5895
6454
  effect: ({ results }) => {
@@ -5961,6 +6520,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5961
6520
  return getLoggingChoice(flags.logging, results.backend);
5962
6521
  },
5963
6522
  observability: ({ results }) => {
6523
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
5964
6524
  return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
5965
6525
  },
5966
6526
  featureFlags: ({ results }) => {
@@ -5976,6 +6536,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5976
6536
  return getCMSChoice(flags.cms, results.backend);
5977
6537
  },
5978
6538
  caching: ({ results }) => {
6539
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
5979
6540
  return getCachingChoice(flags.caching, results.backend, results.ecosystem);
5980
6541
  },
5981
6542
  i18n: ({ results }) => {
@@ -5983,12 +6544,50 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5983
6544
  return getI18nChoice(flags.i18n, results.frontend);
5984
6545
  },
5985
6546
  search: ({ results }) => {
6547
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
5986
6548
  return getSearchChoice(flags.search, results.backend, results.ecosystem);
5987
6549
  },
5988
6550
  fileStorage: ({ results }) => {
5989
6551
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
5990
6552
  return getFileStorageChoice(flags.fileStorage, results.backend);
5991
6553
  },
6554
+ mobileNavigation: ({ results }) => {
6555
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6556
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6557
+ return getMobileNavigationChoice(flags.mobileNavigation);
6558
+ },
6559
+ mobileUI: ({ results }) => {
6560
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6561
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6562
+ if (results.frontend.includes("native-uniwind")) return Promise.resolve("uniwind");
6563
+ if (results.frontend.includes("native-unistyles")) return Promise.resolve("unistyles");
6564
+ return getMobileUIChoice(flags.mobileUI);
6565
+ },
6566
+ mobileStorage: ({ results }) => {
6567
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6568
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6569
+ return getMobileStorageChoice(flags.mobileStorage);
6570
+ },
6571
+ mobileTesting: ({ results }) => {
6572
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6573
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6574
+ return getMobileTestingChoice(flags.mobileTesting);
6575
+ },
6576
+ mobilePush: ({ results }) => {
6577
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6578
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6579
+ return getMobilePushChoice(flags.mobilePush);
6580
+ },
6581
+ mobileOTA: ({ results }) => {
6582
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6583
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6584
+ return getMobileOTAChoice(flags.mobileOTA);
6585
+ },
6586
+ mobileDeepLinking: ({ results }) => {
6587
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6588
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6589
+ return getMobileDeepLinkingChoice(flags.mobileDeepLinking);
6590
+ },
5992
6591
  rustWebFramework: ({ results }) => {
5993
6592
  if (results.ecosystem !== "rust") return Promise.resolve("none");
5994
6593
  return getRustWebFrameworkChoice(flags.rustWebFramework);
@@ -6118,10 +6717,70 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6118
6717
  if (results.javaBuildTool === "none") return Promise.resolve([]);
6119
6718
  return getJavaTestingLibrariesChoice(flags.javaTestingLibraries);
6120
6719
  },
6720
+ elixirWebFramework: ({ results }) => {
6721
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6722
+ return getElixirWebFrameworkChoice(flags.elixirWebFramework);
6723
+ },
6724
+ elixirOrm: ({ results }) => {
6725
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6726
+ return getElixirOrmChoice(flags.elixirOrm);
6727
+ },
6728
+ elixirAuth: ({ results }) => {
6729
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6730
+ return getElixirAuthChoice(flags.elixirAuth);
6731
+ },
6732
+ elixirApi: ({ results }) => {
6733
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6734
+ return getElixirApiChoice(flags.elixirApi);
6735
+ },
6736
+ elixirRealtime: ({ results }) => {
6737
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6738
+ return getElixirRealtimeChoice(flags.elixirRealtime);
6739
+ },
6740
+ elixirJobs: ({ results }) => {
6741
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6742
+ return getElixirJobsChoice(flags.elixirJobs);
6743
+ },
6744
+ elixirValidation: ({ results }) => {
6745
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6746
+ return getElixirValidationChoice(flags.elixirValidation);
6747
+ },
6748
+ elixirHttp: ({ results }) => {
6749
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6750
+ return getElixirHttpChoice(flags.elixirHttp);
6751
+ },
6752
+ elixirJson: ({ results }) => {
6753
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6754
+ return getElixirJsonChoice(flags.elixirJson);
6755
+ },
6756
+ elixirEmail: ({ results }) => {
6757
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6758
+ return getElixirEmailChoice(flags.elixirEmail);
6759
+ },
6760
+ elixirCaching: ({ results }) => {
6761
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6762
+ return getElixirCachingChoice(flags.elixirCaching);
6763
+ },
6764
+ elixirObservability: ({ results }) => {
6765
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6766
+ return getElixirObservabilityChoice(flags.elixirObservability);
6767
+ },
6768
+ elixirTesting: ({ results }) => {
6769
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6770
+ return getElixirTestingChoice(flags.elixirTesting);
6771
+ },
6772
+ elixirQuality: ({ results }) => {
6773
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6774
+ return getElixirQualityChoice(flags.elixirQuality);
6775
+ },
6776
+ elixirDeploy: ({ results }) => {
6777
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6778
+ return getElixirDeployChoice(flags.elixirDeploy);
6779
+ },
6121
6780
  aiDocs: () => getAiDocsChoice(flags.aiDocs),
6122
6781
  git: () => getGitChoice(flags.git),
6123
6782
  packageManager: ({ results }) => {
6124
- if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
6783
+ if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java" || results.ecosystem === "elixir") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
6125
6784
  return getPackageManagerChoice(flags.packageManager);
6126
6785
  },
6127
6786
  install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
@@ -6170,6 +6829,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6170
6829
  i18n: result.i18n,
6171
6830
  search: result.search,
6172
6831
  fileStorage: result.fileStorage,
6832
+ mobileNavigation: result.mobileNavigation,
6833
+ mobileUI: result.mobileUI,
6834
+ mobileStorage: result.mobileStorage,
6835
+ mobileTesting: result.mobileTesting,
6836
+ mobilePush: result.mobilePush,
6837
+ mobileOTA: result.mobileOTA,
6838
+ mobileDeepLinking: result.mobileDeepLinking,
6173
6839
  ecosystem: result.ecosystem,
6174
6840
  rustWebFramework: result.rustWebFramework,
6175
6841
  rustFrontend: result.rustFrontend,
@@ -6202,6 +6868,21 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6202
6868
  javaAuth: result.javaAuth,
6203
6869
  javaLibraries: result.javaLibraries,
6204
6870
  javaTestingLibraries: result.javaTestingLibraries,
6871
+ elixirWebFramework: result.elixirWebFramework,
6872
+ elixirOrm: result.elixirOrm,
6873
+ elixirAuth: result.elixirAuth,
6874
+ elixirApi: result.elixirApi,
6875
+ elixirRealtime: result.elixirRealtime,
6876
+ elixirJobs: result.elixirJobs,
6877
+ elixirValidation: result.elixirValidation,
6878
+ elixirHttp: result.elixirHttp,
6879
+ elixirJson: result.elixirJson,
6880
+ elixirEmail: result.elixirEmail,
6881
+ elixirCaching: result.elixirCaching,
6882
+ elixirObservability: result.elixirObservability,
6883
+ elixirTesting: result.elixirTesting,
6884
+ elixirQuality: result.elixirQuality,
6885
+ elixirDeploy: result.elixirDeploy,
6205
6886
  aiDocs: result.aiDocs
6206
6887
  };
6207
6888
  }
@@ -6358,7 +7039,17 @@ function displayConfig(config) {
6358
7039
  if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
6359
7040
  if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
6360
7041
  if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
7042
+ if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
7043
+ if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
7044
+ if (config.mobileNavigation !== void 0) configDisplay.push(`${pc.blue("Mobile Navigation:")} ${String(config.mobileNavigation)}`);
7045
+ if (config.mobileUI !== void 0) configDisplay.push(`${pc.blue("Mobile UI:")} ${String(config.mobileUI)}`);
7046
+ if (config.mobileStorage !== void 0) configDisplay.push(`${pc.blue("Mobile Storage:")} ${String(config.mobileStorage)}`);
7047
+ if (config.mobileTesting !== void 0) configDisplay.push(`${pc.blue("Mobile Testing:")} ${String(config.mobileTesting)}`);
7048
+ if (config.mobilePush !== void 0) configDisplay.push(`${pc.blue("Mobile Push:")} ${String(config.mobilePush)}`);
7049
+ if (config.mobileOTA !== void 0) configDisplay.push(`${pc.blue("Mobile OTA:")} ${String(config.mobileOTA)}`);
7050
+ if (config.mobileDeepLinking !== void 0) configDisplay.push(`${pc.blue("Mobile Deep Linking:")} ${String(config.mobileDeepLinking)}`);
6361
7051
  if (config.caching !== void 0) configDisplay.push(`${pc.blue("Caching:")} ${String(config.caching)}`);
7052
+ if (config.i18n !== void 0) configDisplay.push(`${pc.blue("i18n:")} ${String(config.i18n)}`);
6362
7053
  if (config.cms !== void 0) configDisplay.push(`${pc.blue("CMS:")} ${String(config.cms)}`);
6363
7054
  if (config.search !== void 0) configDisplay.push(`${pc.blue("Search:")} ${String(config.search)}`);
6364
7055
  if (config.fileStorage !== void 0) configDisplay.push(`${pc.blue("File Storage:")} ${String(config.fileStorage)}`);
@@ -6372,6 +7063,11 @@ function displayConfig(config) {
6372
7063
  const examplesText = examples.length > 0 && examples[0] !== void 0 ? examples.join(", ") : "none";
6373
7064
  configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`);
6374
7065
  }
7066
+ if (config.aiDocs !== void 0) {
7067
+ const aiDocs = Array.isArray(config.aiDocs) ? config.aiDocs : [config.aiDocs];
7068
+ const aiDocsText = aiDocs.length > 0 && aiDocs[0] !== void 0 ? aiDocs.join(", ") : "none";
7069
+ configDisplay.push(`${pc.blue("AI Docs:")} ${aiDocsText}`);
7070
+ }
6375
7071
  if (config.git !== void 0) {
6376
7072
  const gitText = typeof config.git === "boolean" ? config.git ? "Yes" : "No" : String(config.git);
6377
7073
  configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
@@ -6465,6 +7161,13 @@ function getTypeScriptFlags(config) {
6465
7161
  flags.push(`--cms ${config.cms}`);
6466
7162
  flags.push(`--search ${config.search}`);
6467
7163
  flags.push(`--file-storage ${config.fileStorage}`);
7164
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
7165
+ flags.push(`--mobile-ui ${config.mobileUI}`);
7166
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
7167
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
7168
+ flags.push(`--mobile-push ${config.mobilePush}`);
7169
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
7170
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
6468
7171
  if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
6469
7172
  else flags.push("--addons none");
6470
7173
  if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
@@ -6475,6 +7178,21 @@ function getTypeScriptFlags(config) {
6475
7178
  appendCommonFlags(flags, config);
6476
7179
  return flags;
6477
7180
  }
7181
+ function getReactNativeFlags(config) {
7182
+ const flags = ["--ecosystem react-native"];
7183
+ if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
7184
+ else flags.push("--frontend native-bare");
7185
+ flags.push(`--auth ${config.auth}`);
7186
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
7187
+ flags.push(`--mobile-ui ${config.mobileUI}`);
7188
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
7189
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
7190
+ flags.push(`--mobile-push ${config.mobilePush}`);
7191
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
7192
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
7193
+ appendCommonFlags(flags, config);
7194
+ return flags;
7195
+ }
6478
7196
  function getRustFlags(config) {
6479
7197
  const flags = ["--ecosystem rust"];
6480
7198
  flags.push(`--rust-web-framework ${config.rustWebFramework}`);
@@ -6531,9 +7249,32 @@ function getJavaFlags(config) {
6531
7249
  appendCommonFlags(flags, config);
6532
7250
  return flags;
6533
7251
  }
7252
+ function getElixirFlags(config) {
7253
+ const flags = ["--ecosystem elixir"];
7254
+ flags.push(`--elixir-web-framework ${config.elixirWebFramework}`);
7255
+ flags.push(`--elixir-orm ${config.elixirOrm}`);
7256
+ flags.push(`--elixir-auth ${config.elixirAuth}`);
7257
+ flags.push(`--elixir-api ${config.elixirApi}`);
7258
+ flags.push(`--elixir-realtime ${config.elixirRealtime}`);
7259
+ flags.push(`--elixir-jobs ${config.elixirJobs}`);
7260
+ flags.push(`--elixir-validation ${config.elixirValidation}`);
7261
+ flags.push(`--elixir-http ${config.elixirHttp}`);
7262
+ flags.push(`--elixir-json ${config.elixirJson}`);
7263
+ flags.push(`--elixir-email ${config.elixirEmail}`);
7264
+ flags.push(`--elixir-caching ${config.elixirCaching}`);
7265
+ flags.push(`--elixir-observability ${config.elixirObservability}`);
7266
+ flags.push(`--elixir-testing ${config.elixirTesting}`);
7267
+ flags.push(`--elixir-quality ${config.elixirQuality}`);
7268
+ flags.push(`--elixir-deploy ${config.elixirDeploy}`);
7269
+ appendCommonFlags(flags, config);
7270
+ return flags;
7271
+ }
6534
7272
  function generateReproducibleCommand(config) {
6535
7273
  let flags;
6536
7274
  switch (config.ecosystem) {
7275
+ case "react-native":
7276
+ flags = getReactNativeFlags(config);
7277
+ break;
6537
7278
  case "rust":
6538
7279
  flags = getRustFlags(config);
6539
7280
  break;
@@ -6546,6 +7287,9 @@ function generateReproducibleCommand(config) {
6546
7287
  case "java":
6547
7288
  flags = getJavaFlags(config);
6548
7289
  break;
7290
+ case "elixir":
7291
+ flags = getElixirFlags(config);
7292
+ break;
6549
7293
  case "typescript":
6550
7294
  default:
6551
7295
  flags = getTypeScriptFlags(config);
@@ -6768,7 +7512,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
6768
7512
  }],
6769
7513
  conflictsWithOptions: [{
6770
7514
  optionKey: "frontend",
6771
- values: ["next"]
7515
+ values: ["next", "vinext"]
6772
7516
  }]
6773
7517
  },
6774
7518
  {
@@ -6832,7 +7576,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
6832
7576
  }],
6833
7577
  conflictsWithOptions: [{
6834
7578
  optionKey: "frontend",
6835
- values: ["next"]
7579
+ values: ["next", "vinext"]
6836
7580
  }]
6837
7581
  }
6838
7582
  ];
@@ -7173,6 +7917,18 @@ function validateBackendConstraints(config, providedFlags, options) {
7173
7917
  function validateFrontendConstraints(config, providedFlags) {
7174
7918
  const { frontend } = config;
7175
7919
  if (frontend && frontend.length > 0) {
7920
+ if (config.ecosystem === "react-native" && frontend.some((item) => !item.startsWith("native-") && item !== "none")) incompatibilityError({
7921
+ message: "React Native ecosystem only supports native Expo frontends.",
7922
+ provided: {
7923
+ ecosystem: "react-native",
7924
+ frontend: frontend.join(" ")
7925
+ },
7926
+ suggestions: [
7927
+ "Use --frontend native-bare",
7928
+ "Use --frontend native-uniwind",
7929
+ "Use --frontend native-unistyles"
7930
+ ]
7931
+ });
7176
7932
  ensureSingleWebAndNative(frontend);
7177
7933
  if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
7178
7934
  }
@@ -7216,6 +7972,142 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
7216
7972
  suggestions: ["Use --java-build-tool maven or --java-build-tool gradle to enable JUnit/Mockito/Testcontainers", "Set --java-testing-libraries none for a source-only plain Java scaffold"]
7217
7973
  });
7218
7974
  }
7975
+ function validateElixirConstraints(config) {
7976
+ if (config.ecosystem !== "elixir") return;
7977
+ const hasPhoenix = config.elixirWebFramework !== "none";
7978
+ const hasEcto = config.elixirOrm !== "none";
7979
+ const unsupportedSelections = [
7980
+ {
7981
+ flag: "elixir-orm",
7982
+ value: config.elixirOrm,
7983
+ unsupported: ["ecto"],
7984
+ message: "Plain Ecto without SQL Repo wiring is not generated yet.",
7985
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-orm none"]
7986
+ },
7987
+ {
7988
+ flag: "elixir-auth",
7989
+ value: config.elixirAuth,
7990
+ unsupported: ["ueberauth", "guardian"],
7991
+ message: "Only phx.gen.auth currently generates Phoenix auth files.",
7992
+ suggestions: ["Use --elixir-auth phx-gen-auth", "Use --elixir-auth none"]
7993
+ },
7994
+ {
7995
+ flag: "elixir-validation",
7996
+ value: config.elixirValidation,
7997
+ unsupported: ["nimble-options"],
7998
+ message: "NimbleOptions is not generated yet.",
7999
+ suggestions: ["Use --elixir-validation ecto-changesets", "Use --elixir-validation none"]
8000
+ },
8001
+ {
8002
+ flag: "elixir-caching",
8003
+ value: config.elixirCaching,
8004
+ unsupported: ["nebulex"],
8005
+ message: "Nebulex cache modules are not generated yet.",
8006
+ suggestions: ["Use --elixir-caching cachex", "Use --elixir-caching none"]
8007
+ },
8008
+ {
8009
+ flag: "elixir-observability",
8010
+ value: config.elixirObservability,
8011
+ unsupported: ["opentelemetry", "prom_ex"],
8012
+ message: "OpenTelemetry and PromEx setup are not generated yet.",
8013
+ suggestions: ["Use --elixir-observability telemetry", "Use --elixir-observability none"]
8014
+ },
8015
+ {
8016
+ flag: "elixir-testing",
8017
+ value: config.elixirTesting,
8018
+ unsupported: [
8019
+ "mox",
8020
+ "bypass",
8021
+ "wallaby"
8022
+ ],
8023
+ message: "Generated Phoenix projects currently include ExUnit tests only.",
8024
+ suggestions: ["Use --elixir-testing ex_unit"]
8025
+ },
8026
+ {
8027
+ flag: "elixir-deploy",
8028
+ value: config.elixirDeploy,
8029
+ unsupported: ["fly", "gigalixir"],
8030
+ message: "Fly.io and Gigalixir config files are not generated yet.",
8031
+ suggestions: ["Use --elixir-deploy docker", "Use --elixir-deploy mix-release"]
8032
+ }
8033
+ ];
8034
+ for (const selection of unsupportedSelections) if (selection.value && selection.unsupported.includes(selection.value)) incompatibilityError({
8035
+ message: selection.message,
8036
+ provided: { [selection.flag]: selection.value },
8037
+ suggestions: selection.suggestions
8038
+ });
8039
+ if (!hasPhoenix) {
8040
+ const phoenixOnlySelections = [
8041
+ {
8042
+ flag: "elixir-auth",
8043
+ value: config.elixirAuth,
8044
+ message: "Elixir auth scaffolds require Phoenix."
8045
+ },
8046
+ {
8047
+ flag: "elixir-api",
8048
+ value: config.elixirApi,
8049
+ message: "Elixir API scaffolds require Phoenix."
8050
+ },
8051
+ {
8052
+ flag: "elixir-realtime",
8053
+ value: config.elixirRealtime,
8054
+ message: "Elixir realtime scaffolds require Phoenix."
8055
+ }
8056
+ ];
8057
+ for (const selection of phoenixOnlySelections) {
8058
+ if (!selection.value || selection.value === "none") continue;
8059
+ incompatibilityError({
8060
+ message: selection.message,
8061
+ provided: {
8062
+ "elixir-web-framework": config.elixirWebFramework ?? "none",
8063
+ [selection.flag]: selection.value
8064
+ },
8065
+ suggestions: [
8066
+ "Use --elixir-web-framework phoenix",
8067
+ "Use --elixir-web-framework phoenix-live-view",
8068
+ `Use --${selection.flag} none`
8069
+ ]
8070
+ });
8071
+ }
8072
+ }
8073
+ if (hasPhoenix && config.elixirJson === "none") incompatibilityError({
8074
+ message: "Phoenix JSON scaffolds require Jason.",
8075
+ provided: { "elixir-json": "none" },
8076
+ suggestions: ["Use --elixir-json jason"]
8077
+ });
8078
+ if (config.elixirAuth === "phx-gen-auth" && !hasEcto) incompatibilityError({
8079
+ message: "phx.gen.auth requires Ecto in the generated Phoenix scaffold.",
8080
+ provided: {
8081
+ "elixir-auth": "phx-gen-auth",
8082
+ "elixir-orm": config.elixirOrm ?? "none"
8083
+ },
8084
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-auth none"]
8085
+ });
8086
+ if (config.elixirJobs === "oban" && config.elixirOrm !== "ecto-sql") incompatibilityError({
8087
+ message: "Oban requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
8088
+ provided: {
8089
+ "elixir-jobs": "oban",
8090
+ "elixir-orm": config.elixirOrm ?? "none"
8091
+ },
8092
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-jobs none"]
8093
+ });
8094
+ if (config.elixirRealtime === "live-view-streams" && config.elixirWebFramework !== "phoenix-live-view") incompatibilityError({
8095
+ message: "LiveView Streams require Phoenix LiveView.",
8096
+ provided: {
8097
+ "elixir-realtime": "live-view-streams",
8098
+ "elixir-web-framework": config.elixirWebFramework ?? "none"
8099
+ },
8100
+ suggestions: ["Use --elixir-web-framework phoenix-live-view", "Use --elixir-realtime channels"]
8101
+ });
8102
+ if (config.elixirApi === "absinthe" && !hasEcto) incompatibilityError({
8103
+ message: "Absinthe GraphQL requires Ecto in the current generated Phoenix scaffold.",
8104
+ provided: {
8105
+ "elixir-api": "absinthe",
8106
+ "elixir-orm": config.elixirOrm ?? "none"
8107
+ },
8108
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-api rest"]
8109
+ });
8110
+ }
7219
8111
  function validateEmailConstraints(config) {
7220
8112
  if (!config.email || config.email === "none") return;
7221
8113
  if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
@@ -7340,6 +8232,7 @@ function validateFullConfig(config, providedFlags, options) {
7340
8232
  validateCachingConstraints(config);
7341
8233
  validateSearchConstraints(config);
7342
8234
  validateJavaConstraints(config, providedFlags);
8235
+ validateElixirConstraints(config);
7343
8236
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
7344
8237
  validateSelfBackendCompatibility(providedFlags, options, config);
7345
8238
  validateWorkersCompatibility(providedFlags, options, config);
@@ -7381,6 +8274,7 @@ function validateConfigForProgrammaticUse(config) {
7381
8274
  validateCachingConstraints(config);
7382
8275
  validateSearchConstraints(config);
7383
8276
  validateJavaConstraints(config);
8277
+ validateElixirConstraints(config);
7384
8278
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
7385
8279
  if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database);
7386
8280
  validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
@@ -8753,6 +9647,10 @@ async function displayPostInstallInstructions(config) {
8753
9647
  displayPythonInstructions(config);
8754
9648
  return;
8755
9649
  }
9650
+ if (ecosystem === "elixir") {
9651
+ displayElixirInstructions(config);
9652
+ return;
9653
+ }
8756
9654
  const isConvex = backend === "convex";
8757
9655
  const isBackendSelf = backend === "self";
8758
9656
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : packageManager === "yarn" ? "yarn" : "bun run";
@@ -8916,7 +9814,7 @@ function getBunWebNativeWarning() {
8916
9814
  }
8917
9815
  function getClerkInstructions(backend, frontend) {
8918
9816
  if (backend === "convex") return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
8919
- if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
9817
+ if (backend === "self" && (frontend.includes("next") || frontend.includes("vinext") || frontend.includes("tanstack-start"))) {
8920
9818
  const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
8921
9819
  return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Create an application in ${pc.underline("https://dashboard.clerk.com/")}\n${pc.cyan("•")} Set ${publishableKeyVar} in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Set CLERK_SECRET_KEY in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Clerk middleware and a protected dashboard route are already generated`;
8922
9820
  }
@@ -9303,6 +10201,36 @@ function displayPythonInstructions(config) {
9303
10201
  output += pc.dim("Your star helps other developers discover the project.");
9304
10202
  consola$1.box(output);
9305
10203
  }
10204
+ function displayElixirInstructions(config) {
10205
+ const { relativePath, depsInstalled, elixirWebFramework, elixirOrm, elixirApi, elixirRealtime, elixirJobs, elixirAuth, elixirDeploy } = config;
10206
+ let output = `${pc.bold("Project created successfully!")}\n\n`;
10207
+ output += `${pc.bold("Next steps:")}\n`;
10208
+ output += `${pc.cyan("1.")} cd ${relativePath}\n`;
10209
+ if (!depsInstalled) {
10210
+ output += `${pc.cyan("2.")} mix deps.get\n`;
10211
+ output += `${pc.cyan("3.")} mix ecto.setup\n`;
10212
+ output += `${pc.cyan("4.")} mix phx.server\n`;
10213
+ } else {
10214
+ output += `${pc.cyan("2.")} mix ecto.setup\n`;
10215
+ output += `${pc.cyan("3.")} mix phx.server\n`;
10216
+ }
10217
+ output += `\n${pc.bold("Selected Elixir stack:")}\n`;
10218
+ output += `${pc.cyan("•")} Web: ${elixirWebFramework}\n`;
10219
+ output += `${pc.cyan("•")} Database: ${elixirOrm}\n`;
10220
+ output += `${pc.cyan("•")} API: ${elixirApi}\n`;
10221
+ output += `${pc.cyan("•")} Realtime: ${elixirRealtime}\n`;
10222
+ output += `${pc.cyan("•")} Jobs: ${elixirJobs}\n`;
10223
+ output += `${pc.cyan("•")} Auth: ${elixirAuth}\n`;
10224
+ output += `${pc.cyan("•")} Deploy: ${elixirDeploy}\n`;
10225
+ output += `\n${pc.bold("Common Mix commands:")}\n`;
10226
+ output += `${pc.cyan("•")} Run: mix phx.server\n`;
10227
+ output += `${pc.cyan("•")} Test: mix test\n`;
10228
+ output += `${pc.cyan("•")} Format: mix format\n`;
10229
+ output += `${pc.cyan("•")} Compile: mix compile\n`;
10230
+ output += `\n${pc.bold("Your Phoenix app will be available at:")}\n`;
10231
+ output += `${pc.cyan("•")} Web: http://localhost:4000\n`;
10232
+ consola$1.box(output);
10233
+ }
9306
10234
 
9307
10235
  //#endregion
9308
10236
  //#region src/helpers/core/create-project.ts
@@ -9325,7 +10253,7 @@ async function createProject(options, cliInput = {}) {
9325
10253
  await writeBtsConfig(options);
9326
10254
  await formatProject(projectDir);
9327
10255
  if (!isSilent()) log.success("Project template successfully scaffolded!");
9328
- if (options.install && options.ecosystem === "typescript") await installDependencies({
10256
+ if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) await installDependencies({
9329
10257
  projectDir,
9330
10258
  packageManager: options.packageManager
9331
10259
  });
@@ -9334,6 +10262,7 @@ async function createProject(options, cliInput = {}) {
9334
10262
  if (options.install && options.ecosystem === "go") await runGoModTidy({ projectDir });
9335
10263
  if (options.install && options.ecosystem === "java" && options.javaBuildTool !== "none") if (options.javaBuildTool === "gradle") await runGradleTests({ projectDir });
9336
10264
  else await runMavenTests({ projectDir });
10265
+ if (options.install && options.ecosystem === "elixir") await runMixCompile({ projectDir });
9337
10266
  await initializeGit(projectDir, options.git);
9338
10267
  if (!isSilent()) await displayPostInstallInstructions({
9339
10268
  ...options,
@@ -9374,6 +10303,51 @@ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
9374
10303
 
9375
10304
  //#endregion
9376
10305
  //#region src/helpers/core/command-handlers.ts
10306
+ function getYesBaseConfig(flagConfig) {
10307
+ const baseConfig = getDefaultConfig();
10308
+ if (flagConfig.ecosystem !== "react-native") return baseConfig;
10309
+ return {
10310
+ ...baseConfig,
10311
+ backend: "none",
10312
+ runtime: "none",
10313
+ frontend: ["native-bare"],
10314
+ addons: [],
10315
+ examples: [],
10316
+ auth: "none",
10317
+ payments: "none",
10318
+ email: "none",
10319
+ fileUpload: "none",
10320
+ effect: "none",
10321
+ dbSetup: "none",
10322
+ api: "none",
10323
+ webDeploy: "none",
10324
+ serverDeploy: "none",
10325
+ cssFramework: "none",
10326
+ uiLibrary: "none",
10327
+ stateManagement: "none",
10328
+ forms: "none",
10329
+ testing: "none",
10330
+ realtime: "none",
10331
+ jobQueue: "none",
10332
+ animation: "none",
10333
+ logging: "none",
10334
+ observability: "none",
10335
+ featureFlags: "none",
10336
+ analytics: "none",
10337
+ cms: "none",
10338
+ caching: "none",
10339
+ i18n: "none",
10340
+ search: "none",
10341
+ fileStorage: "none",
10342
+ mobileNavigation: "expo-router",
10343
+ mobileUI: "none",
10344
+ mobileStorage: "none",
10345
+ mobileTesting: "none",
10346
+ mobilePush: "none",
10347
+ mobileOTA: "none",
10348
+ mobileDeepLinking: "none"
10349
+ };
10350
+ }
9377
10351
  function shouldPromptForVersionChannel(input) {
9378
10352
  if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
9379
10353
  return canPromptInteractively();
@@ -9476,6 +10450,13 @@ async function createProjectHandler(input, options = {}) {
9476
10450
  featureFlags: "none",
9477
10451
  analytics: "none",
9478
10452
  fileStorage: "none",
10453
+ mobileNavigation: "none",
10454
+ mobileUI: "none",
10455
+ mobileStorage: "none",
10456
+ mobileTesting: "none",
10457
+ mobilePush: "none",
10458
+ mobileOTA: "none",
10459
+ mobileDeepLinking: "none",
9479
10460
  pythonWebFramework: "none",
9480
10461
  pythonOrm: "none",
9481
10462
  pythonValidation: "none",
@@ -9497,6 +10478,21 @@ async function createProjectHandler(input, options = {}) {
9497
10478
  javaAuth: "none",
9498
10479
  javaLibraries: [],
9499
10480
  javaTestingLibraries: [],
10481
+ elixirWebFramework: "none",
10482
+ elixirOrm: "none",
10483
+ elixirAuth: "none",
10484
+ elixirApi: "none",
10485
+ elixirRealtime: "none",
10486
+ elixirJobs: "none",
10487
+ elixirValidation: "none",
10488
+ elixirHttp: "none",
10489
+ elixirJson: "none",
10490
+ elixirEmail: "none",
10491
+ elixirCaching: "none",
10492
+ elixirObservability: "none",
10493
+ elixirTesting: "none",
10494
+ elixirQuality: "none",
10495
+ elixirDeploy: "none",
9500
10496
  aiDocs: []
9501
10497
  },
9502
10498
  reproducibleCommand: "",
@@ -9541,7 +10537,7 @@ async function createProjectHandler(input, options = {}) {
9541
10537
  if (cliInput.yes) {
9542
10538
  const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
9543
10539
  config = {
9544
- ...getDefaultConfig(),
10540
+ ...getYesBaseConfig(flagConfig),
9545
10541
  ...flagConfig,
9546
10542
  projectName: finalBaseName,
9547
10543
  projectDir: finalResolvedPath,
@@ -9938,17 +10934,21 @@ async function history(options) {
9938
10934
  */
9939
10935
  async function createVirtual(options) {
9940
10936
  try {
10937
+ const ecosystem = options.ecosystem || "typescript";
10938
+ const isReactNative = ecosystem === "react-native";
10939
+ const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
10940
+ const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
9941
10941
  const result = await generateVirtualProject$1({
9942
10942
  config: {
10943
+ ecosystem,
9943
10944
  projectName: options.projectName || "my-project",
9944
10945
  projectDir: "/virtual",
9945
10946
  relativePath: "./virtual",
9946
- ecosystem: options.ecosystem || "typescript",
9947
10947
  database: options.database || "none",
9948
10948
  orm: options.orm || "none",
9949
- backend: options.backend || "hono",
9950
- runtime: options.runtime || "bun",
9951
- frontend: options.frontend || ["tanstack-router"],
10949
+ backend: options.backend || (isReactNative ? "none" : "hono"),
10950
+ runtime: options.runtime || (isReactNative ? "none" : "bun"),
10951
+ frontend,
9952
10952
  addons: options.addons || [],
9953
10953
  examples: options.examples || [],
9954
10954
  auth: options.auth || "none",
@@ -9961,11 +10961,11 @@ async function createVirtual(options) {
9961
10961
  versionChannel: options.versionChannel || "stable",
9962
10962
  install: false,
9963
10963
  dbSetup: options.dbSetup || "none",
9964
- api: options.api || "trpc",
10964
+ api: options.api || (isReactNative ? "none" : "trpc"),
9965
10965
  webDeploy: options.webDeploy || "none",
9966
10966
  serverDeploy: options.serverDeploy || "none",
9967
- cssFramework: options.cssFramework || "tailwind",
9968
- uiLibrary: options.uiLibrary || "shadcn-ui",
10967
+ cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
10968
+ uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
9969
10969
  shadcnBase: options.shadcnBase ?? "radix",
9970
10970
  shadcnStyle: options.shadcnStyle ?? "nova",
9971
10971
  shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
@@ -9975,8 +10975,8 @@ async function createVirtual(options) {
9975
10975
  shadcnRadius: options.shadcnRadius ?? "default",
9976
10976
  ai: options.ai || "none",
9977
10977
  stateManagement: options.stateManagement || "none",
9978
- forms: options.forms || "react-hook-form",
9979
- testing: options.testing || "vitest",
10978
+ forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
10979
+ testing: options.testing || (isReactNative ? "none" : "vitest"),
9980
10980
  validation: options.validation || "zod",
9981
10981
  realtime: options.realtime || "none",
9982
10982
  jobQueue: options.jobQueue || "none",
@@ -9985,6 +10985,13 @@ async function createVirtual(options) {
9985
10985
  observability: options.observability || "none",
9986
10986
  featureFlags: options.featureFlags || "none",
9987
10987
  analytics: options.analytics || "none",
10988
+ mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
10989
+ mobileUI: options.mobileUI || "none",
10990
+ mobileStorage: options.mobileStorage || "none",
10991
+ mobileTesting: options.mobileTesting || "none",
10992
+ mobilePush: options.mobilePush || "none",
10993
+ mobileOTA: options.mobileOTA || "none",
10994
+ mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
9988
10995
  cms: options.cms || "none",
9989
10996
  caching: options.caching || "none",
9990
10997
  i18n: options.i18n || "none",
@@ -10021,6 +11028,21 @@ async function createVirtual(options) {
10021
11028
  javaAuth: options.javaAuth || "none",
10022
11029
  javaLibraries: options.javaLibraries || [],
10023
11030
  javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
11031
+ elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
11032
+ elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
11033
+ elixirAuth: options.elixirAuth || "none",
11034
+ elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
11035
+ elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
11036
+ elixirJobs: options.elixirJobs || "none",
11037
+ elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
11038
+ elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
11039
+ elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
11040
+ elixirEmail: options.elixirEmail || "none",
11041
+ elixirCaching: options.elixirCaching || "none",
11042
+ elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
11043
+ elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
11044
+ elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
11045
+ elixirDeploy: options.elixirDeploy || "none",
10024
11046
  aiDocs: options.aiDocs || ["claude-md"]
10025
11047
  },
10026
11048
  templates: EMBEDDED_TEMPLATES$1