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/README.md +2 -2
- package/dist/addons-setup-CJwQAWFg.mjs +5 -0
- package/dist/{addons-setup-CGhYT2qC.mjs → addons-setup-CUmA_nra.mjs} +5 -5
- package/dist/{bts-config-bOXo9tbL.mjs → bts-config-YcroedMK.mjs} +45 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +239 -17
- package/dist/index.mjs +1046 -24
- package/dist/{mcp-BxEilSGM.mjs → mcp-DoPutOIG.mjs} +1 -1
- package/dist/mcp-entry.mjs +194 -41
- package/package.json +9 -9
- package/dist/addons-setup-BdKQJ1h6.mjs +0 -5
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-
|
|
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-
|
|
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
|
|
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
|
-
...
|
|
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
|
|
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
|