create-better-fullstack 1.7.0 → 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-DQa6TRrx.mjs → addons-setup-CUmA_nra.mjs} +5 -5
- package/dist/{bts-config-B_rZ4_sj.mjs → bts-config-YcroedMK.mjs} +48 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +333 -64
- package/dist/index.mjs +1539 -133
- package/dist/{mcp-CuEEG8e5.mjs → mcp-DoPutOIG.mjs} +1 -1
- package/dist/mcp-entry.mjs +307 -53
- package/package.json +11 -11
- package/dist/addons-setup-CBK1Htlc.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(),
|
|
@@ -430,21 +437,37 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
430
437
|
pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
|
|
431
438
|
pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
|
|
432
439
|
pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
|
|
440
|
+
pythonApi: types_exports.PythonApiSchema.optional().describe("Python API framework (django-rest-framework, django-ninja)"),
|
|
433
441
|
pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
|
|
434
442
|
pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
|
|
435
|
-
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
|
|
443
|
+
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff, mypy, pyright)"),
|
|
436
444
|
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
|
|
437
445
|
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
|
|
438
446
|
goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
|
|
439
|
-
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
|
|
447
|
+
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
|
|
440
448
|
goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
|
|
441
449
|
goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt)"),
|
|
442
|
-
javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, none)"),
|
|
450
|
+
javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
|
|
443
451
|
javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
|
|
444
452
|
javaOrm: types_exports.JavaOrmSchema.optional().describe("Java ORM/database (spring-data-jpa)"),
|
|
445
453
|
javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
|
|
446
454
|
javaLibraries: z.array(types_exports.JavaLibrariesSchema).optional().describe("Java application libraries"),
|
|
447
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)"),
|
|
448
471
|
aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
|
|
449
472
|
});
|
|
450
473
|
const CreateCommandInputSchema = z.tuple([types_exports.ProjectNameSchema.optional(), CreateCommandOptionsSchema]);
|
|
@@ -533,6 +556,7 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
533
556
|
}
|
|
534
557
|
const FULLSTACK_FRONTENDS$1 = [
|
|
535
558
|
"next",
|
|
559
|
+
"vinext",
|
|
536
560
|
"tanstack-start",
|
|
537
561
|
"astro",
|
|
538
562
|
"nuxt",
|
|
@@ -544,11 +568,11 @@ function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
|
544
568
|
const frontends = config.frontend || options.frontend || [];
|
|
545
569
|
if (backend === "self") {
|
|
546
570
|
const { web, native } = splitFrontends$1(frontends);
|
|
547
|
-
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.");
|
|
548
572
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
|
|
549
573
|
}
|
|
550
574
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
551
|
-
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.");
|
|
552
576
|
}
|
|
553
577
|
const WORKERS_COMPATIBLE_BACKENDS = [
|
|
554
578
|
"hono",
|
|
@@ -646,11 +670,11 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
|
|
|
646
670
|
};
|
|
647
671
|
if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
|
|
648
672
|
isCompatible: false,
|
|
649
|
-
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"
|
|
650
674
|
};
|
|
651
|
-
if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next")) return {
|
|
675
|
+
if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
|
|
652
676
|
isCompatible: false,
|
|
653
|
-
reason: "Docker Compose self-backend support currently requires Next.js"
|
|
677
|
+
reason: "Docker Compose self-backend support currently requires Next.js or Vinext"
|
|
654
678
|
};
|
|
655
679
|
if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
|
|
656
680
|
isCompatible: false,
|
|
@@ -1512,6 +1536,24 @@ async function runGradleTests({ projectDir }) {
|
|
|
1512
1536
|
if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
|
|
1513
1537
|
}
|
|
1514
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
|
+
}
|
|
1515
1557
|
|
|
1516
1558
|
//#endregion
|
|
1517
1559
|
//#region src/helpers/core/add-handler.ts
|
|
@@ -1816,6 +1858,7 @@ function resolveAnimationPrompt(context = {}) {
|
|
|
1816
1858
|
"react-vite",
|
|
1817
1859
|
"tanstack-start",
|
|
1818
1860
|
"next",
|
|
1861
|
+
"vinext",
|
|
1819
1862
|
"redwood"
|
|
1820
1863
|
].includes(f));
|
|
1821
1864
|
const isFresh = web.includes("fresh");
|
|
@@ -2053,6 +2096,7 @@ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript")
|
|
|
2053
2096
|
//#region src/prompts/backend.ts
|
|
2054
2097
|
const FULLSTACK_FRONTENDS = [
|
|
2055
2098
|
"next",
|
|
2099
|
+
"vinext",
|
|
2056
2100
|
"tanstack-start",
|
|
2057
2101
|
"astro",
|
|
2058
2102
|
"nuxt",
|
|
@@ -2178,6 +2222,23 @@ const CACHING_PROMPT_OPTIONS = [{
|
|
|
2178
2222
|
hint: "Skip caching layer setup"
|
|
2179
2223
|
}];
|
|
2180
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
|
+
};
|
|
2231
|
+
if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
|
|
2232
|
+
shouldPrompt: false,
|
|
2233
|
+
mode: "single",
|
|
2234
|
+
options: CACHING_PROMPT_OPTIONS,
|
|
2235
|
+
autoValue: context.caching
|
|
2236
|
+
} : {
|
|
2237
|
+
shouldPrompt: true,
|
|
2238
|
+
mode: "single",
|
|
2239
|
+
options: CACHING_PROMPT_OPTIONS,
|
|
2240
|
+
initialValue: "none"
|
|
2241
|
+
};
|
|
2181
2242
|
if (context.backend === "none" || context.backend === "convex") return {
|
|
2182
2243
|
shouldPrompt: false,
|
|
2183
2244
|
mode: "single",
|
|
@@ -2196,10 +2257,11 @@ function resolveCachingPrompt(context = {}) {
|
|
|
2196
2257
|
initialValue: "none"
|
|
2197
2258
|
};
|
|
2198
2259
|
}
|
|
2199
|
-
async function getCachingChoice(caching, backend) {
|
|
2260
|
+
async function getCachingChoice(caching, backend, ecosystem) {
|
|
2200
2261
|
const resolution = resolveCachingPrompt({
|
|
2201
2262
|
caching,
|
|
2202
|
-
backend
|
|
2263
|
+
backend,
|
|
2264
|
+
ecosystem
|
|
2203
2265
|
});
|
|
2204
2266
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
2205
2267
|
const response = await navigableSelect({
|
|
@@ -2566,6 +2628,317 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
|
2566
2628
|
return response;
|
|
2567
2629
|
}
|
|
2568
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
|
+
|
|
2569
2942
|
//#endregion
|
|
2570
2943
|
//#region src/prompts/ecosystem.ts
|
|
2571
2944
|
async function getEcosystemChoice(ecosystem) {
|
|
@@ -2576,7 +2949,12 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
2576
2949
|
{
|
|
2577
2950
|
value: "typescript",
|
|
2578
2951
|
label: "TypeScript",
|
|
2579
|
-
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"
|
|
2580
2958
|
},
|
|
2581
2959
|
{
|
|
2582
2960
|
value: "rust",
|
|
@@ -2592,6 +2970,11 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
2592
2970
|
value: "go",
|
|
2593
2971
|
label: "Go",
|
|
2594
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"
|
|
2595
2978
|
}
|
|
2596
2979
|
],
|
|
2597
2980
|
initialValue: "typescript"
|
|
@@ -2678,8 +3061,16 @@ const EMAIL_PROMPT_OPTIONS = [
|
|
|
2678
3061
|
hint: "No email integration"
|
|
2679
3062
|
}
|
|
2680
3063
|
];
|
|
3064
|
+
const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
|
|
2681
3065
|
function resolveEmailPrompt(context = {}) {
|
|
2682
|
-
if (context.
|
|
3066
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
3067
|
+
shouldPrompt: false,
|
|
3068
|
+
mode: "single",
|
|
3069
|
+
options: [],
|
|
3070
|
+
autoValue: "none"
|
|
3071
|
+
};
|
|
3072
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
|
|
3073
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
2683
3074
|
shouldPrompt: false,
|
|
2684
3075
|
mode: "single",
|
|
2685
3076
|
options: [],
|
|
@@ -2688,19 +3079,20 @@ function resolveEmailPrompt(context = {}) {
|
|
|
2688
3079
|
return context.email !== void 0 ? {
|
|
2689
3080
|
shouldPrompt: false,
|
|
2690
3081
|
mode: "single",
|
|
2691
|
-
options
|
|
3082
|
+
options,
|
|
2692
3083
|
autoValue: context.email
|
|
2693
3084
|
} : {
|
|
2694
3085
|
shouldPrompt: true,
|
|
2695
3086
|
mode: "single",
|
|
2696
|
-
options
|
|
3087
|
+
options,
|
|
2697
3088
|
initialValue: DEFAULT_CONFIG.email ?? "none"
|
|
2698
3089
|
};
|
|
2699
3090
|
}
|
|
2700
|
-
async function getEmailChoice(email, backend) {
|
|
3091
|
+
async function getEmailChoice(email, backend, ecosystem) {
|
|
2701
3092
|
const resolution = resolveEmailPrompt({
|
|
2702
3093
|
email,
|
|
2703
|
-
backend
|
|
3094
|
+
backend,
|
|
3095
|
+
ecosystem
|
|
2704
3096
|
});
|
|
2705
3097
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
2706
3098
|
const response = await navigableSelect({
|
|
@@ -2850,6 +3242,7 @@ function resolveFormsPrompt(context = {}) {
|
|
|
2850
3242
|
"react-vite",
|
|
2851
3243
|
"tanstack-start",
|
|
2852
3244
|
"next",
|
|
3245
|
+
"vinext",
|
|
2853
3246
|
"redwood"
|
|
2854
3247
|
].includes(f));
|
|
2855
3248
|
const isSolid = web.includes("solid");
|
|
@@ -2933,6 +3326,11 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
|
|
|
2933
3326
|
label: "Next.js",
|
|
2934
3327
|
hint: "The React Framework for the Web"
|
|
2935
3328
|
},
|
|
3329
|
+
{
|
|
3330
|
+
value: "vinext",
|
|
3331
|
+
label: "Vinext",
|
|
3332
|
+
hint: "The Vite Compiler for Next.js"
|
|
3333
|
+
},
|
|
2936
3334
|
{
|
|
2937
3335
|
value: "nuxt",
|
|
2938
3336
|
label: "Nuxt",
|
|
@@ -3085,6 +3483,17 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
3085
3483
|
return result;
|
|
3086
3484
|
}
|
|
3087
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
|
+
}
|
|
3088
3497
|
|
|
3089
3498
|
//#endregion
|
|
3090
3499
|
//#region src/prompts/git.ts
|
|
@@ -3169,6 +3578,11 @@ const GO_CLI_PROMPT_OPTIONS = [
|
|
|
3169
3578
|
label: "Bubble Tea",
|
|
3170
3579
|
hint: "Powerful TUI framework based on The Elm Architecture"
|
|
3171
3580
|
},
|
|
3581
|
+
{
|
|
3582
|
+
value: "urfave-cli",
|
|
3583
|
+
label: "urfave/cli",
|
|
3584
|
+
hint: "Declarative CLI framework with commands, flags, and shell completion"
|
|
3585
|
+
},
|
|
3172
3586
|
{
|
|
3173
3587
|
value: "none",
|
|
3174
3588
|
label: "None",
|
|
@@ -3191,6 +3605,11 @@ const GO_LOGGING_PROMPT_OPTIONS = [
|
|
|
3191
3605
|
label: "slog",
|
|
3192
3606
|
hint: "Go 1.21+ stdlib structured logging (no external dependency)"
|
|
3193
3607
|
},
|
|
3608
|
+
{
|
|
3609
|
+
value: "logrus",
|
|
3610
|
+
label: "Logrus",
|
|
3611
|
+
hint: "Classic structured logger with hooks and formatter ecosystem"
|
|
3612
|
+
},
|
|
3194
3613
|
{
|
|
3195
3614
|
value: "none",
|
|
3196
3615
|
label: "None",
|
|
@@ -3395,6 +3814,18 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
|
|
|
3395
3814
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3396
3815
|
return response$1;
|
|
3397
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
|
+
}
|
|
3398
3829
|
const response = await navigableConfirm({
|
|
3399
3830
|
message: "Install dependencies?",
|
|
3400
3831
|
initialValue: DEFAULT_CONFIG.install
|
|
@@ -3405,15 +3836,23 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
|
|
|
3405
3836
|
|
|
3406
3837
|
//#endregion
|
|
3407
3838
|
//#region src/prompts/java-ecosystem.ts
|
|
3408
|
-
const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3839
|
+
const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
3840
|
+
{
|
|
3841
|
+
value: "spring-boot",
|
|
3842
|
+
label: "Spring Boot",
|
|
3843
|
+
hint: "Production-grade Java framework with embedded server and auto-configuration"
|
|
3844
|
+
},
|
|
3845
|
+
{
|
|
3846
|
+
value: "quarkus",
|
|
3847
|
+
label: "Quarkus",
|
|
3848
|
+
hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
|
|
3849
|
+
},
|
|
3850
|
+
{
|
|
3851
|
+
value: "none",
|
|
3852
|
+
label: "None",
|
|
3853
|
+
hint: "No Java web framework"
|
|
3854
|
+
}
|
|
3855
|
+
];
|
|
3417
3856
|
const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
|
|
3418
3857
|
{
|
|
3419
3858
|
value: "maven",
|
|
@@ -3542,6 +3981,46 @@ const JAVA_LIBRARY_PROMPT_OPTIONS = [
|
|
|
3542
3981
|
label: "Caffeine",
|
|
3543
3982
|
hint: "High-performance in-memory caching through Spring Cache"
|
|
3544
3983
|
},
|
|
3984
|
+
{
|
|
3985
|
+
value: "resilience4j",
|
|
3986
|
+
label: "Resilience4j",
|
|
3987
|
+
hint: "Fault tolerance patterns for retries, circuit breakers, and rate limiting"
|
|
3988
|
+
},
|
|
3989
|
+
{
|
|
3990
|
+
value: "spring-webflux",
|
|
3991
|
+
label: "Spring WebFlux",
|
|
3992
|
+
hint: "Reactive HTTP stack for Spring applications"
|
|
3993
|
+
},
|
|
3994
|
+
{
|
|
3995
|
+
value: "spring-batch",
|
|
3996
|
+
label: "Spring Batch",
|
|
3997
|
+
hint: "Batch processing framework for ETL and scheduled jobs"
|
|
3998
|
+
},
|
|
3999
|
+
{
|
|
4000
|
+
value: "spring-kafka",
|
|
4001
|
+
label: "Spring for Apache Kafka",
|
|
4002
|
+
hint: "Kafka producer, consumer, and listener integration"
|
|
4003
|
+
},
|
|
4004
|
+
{
|
|
4005
|
+
value: "spring-mail",
|
|
4006
|
+
label: "Spring Mail",
|
|
4007
|
+
hint: "Email support through Jakarta Mail and Spring abstractions"
|
|
4008
|
+
},
|
|
4009
|
+
{
|
|
4010
|
+
value: "spring-devtools",
|
|
4011
|
+
label: "Spring Boot DevTools",
|
|
4012
|
+
hint: "Developer-time restart and local productivity support"
|
|
4013
|
+
},
|
|
4014
|
+
{
|
|
4015
|
+
value: "micrometer-prometheus",
|
|
4016
|
+
label: "Micrometer Prometheus",
|
|
4017
|
+
hint: "Prometheus metrics registry for Micrometer and Actuator"
|
|
4018
|
+
},
|
|
4019
|
+
{
|
|
4020
|
+
value: "thymeleaf",
|
|
4021
|
+
label: "Thymeleaf",
|
|
4022
|
+
hint: "Server-rendered HTML templates for Spring MVC apps"
|
|
4023
|
+
},
|
|
3545
4024
|
{
|
|
3546
4025
|
value: "none",
|
|
3547
4026
|
label: "None",
|
|
@@ -3753,6 +4232,143 @@ async function getLoggingChoice(logging, backend) {
|
|
|
3753
4232
|
return response;
|
|
3754
4233
|
}
|
|
3755
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
|
+
|
|
3756
4372
|
//#endregion
|
|
3757
4373
|
//#region src/prompts/navigable-group.ts
|
|
3758
4374
|
/**
|
|
@@ -3834,8 +4450,16 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
|
3834
4450
|
hint: "Skip observability/tracing setup"
|
|
3835
4451
|
}
|
|
3836
4452
|
];
|
|
4453
|
+
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
3837
4454
|
function resolveObservabilityPrompt(context = {}) {
|
|
3838
|
-
if (context.
|
|
4455
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
4456
|
+
shouldPrompt: false,
|
|
4457
|
+
mode: "single",
|
|
4458
|
+
options: [],
|
|
4459
|
+
autoValue: "none"
|
|
4460
|
+
};
|
|
4461
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
4462
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
3839
4463
|
shouldPrompt: false,
|
|
3840
4464
|
mode: "single",
|
|
3841
4465
|
options: [],
|
|
@@ -3844,19 +4468,20 @@ function resolveObservabilityPrompt(context = {}) {
|
|
|
3844
4468
|
return context.observability !== void 0 ? {
|
|
3845
4469
|
shouldPrompt: false,
|
|
3846
4470
|
mode: "single",
|
|
3847
|
-
options
|
|
4471
|
+
options,
|
|
3848
4472
|
autoValue: context.observability
|
|
3849
4473
|
} : {
|
|
3850
4474
|
shouldPrompt: true,
|
|
3851
4475
|
mode: "single",
|
|
3852
|
-
options
|
|
4476
|
+
options,
|
|
3853
4477
|
initialValue: "none"
|
|
3854
4478
|
};
|
|
3855
4479
|
}
|
|
3856
|
-
async function getObservabilityChoice(observability, backend) {
|
|
4480
|
+
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
3857
4481
|
const resolution = resolveObservabilityPrompt({
|
|
3858
4482
|
observability,
|
|
3859
|
-
backend
|
|
4483
|
+
backend,
|
|
4484
|
+
ecosystem
|
|
3860
4485
|
});
|
|
3861
4486
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
3862
4487
|
const response = await navigableSelect({
|
|
@@ -4154,52 +4779,113 @@ const PYTHON_AI_PROMPT_OPTIONS = [
|
|
|
4154
4779
|
value: "crewai",
|
|
4155
4780
|
label: "CrewAI",
|
|
4156
4781
|
hint: "Multi-agent orchestration framework"
|
|
4782
|
+
},
|
|
4783
|
+
{
|
|
4784
|
+
value: "haystack",
|
|
4785
|
+
label: "Haystack",
|
|
4786
|
+
hint: "Composable LLM pipelines, RAG, and search applications"
|
|
4157
4787
|
}
|
|
4158
4788
|
];
|
|
4159
4789
|
const PYTHON_AUTH_PROMPT_OPTIONS = [
|
|
4160
4790
|
{
|
|
4161
|
-
value: "authlib",
|
|
4162
|
-
label: "Authlib",
|
|
4163
|
-
hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
|
|
4791
|
+
value: "authlib",
|
|
4792
|
+
label: "Authlib",
|
|
4793
|
+
hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
|
|
4794
|
+
},
|
|
4795
|
+
{
|
|
4796
|
+
value: "jwt",
|
|
4797
|
+
label: "JWT (python-jose)",
|
|
4798
|
+
hint: "Simple JWT token creation and verification"
|
|
4799
|
+
},
|
|
4800
|
+
{
|
|
4801
|
+
value: "none",
|
|
4802
|
+
label: "None",
|
|
4803
|
+
hint: "No authentication library"
|
|
4804
|
+
}
|
|
4805
|
+
];
|
|
4806
|
+
const PYTHON_API_PROMPT_OPTIONS = [
|
|
4807
|
+
{
|
|
4808
|
+
value: "django-rest-framework",
|
|
4809
|
+
label: "Django REST Framework",
|
|
4810
|
+
hint: "Mature, widely used toolkit for building Django REST APIs"
|
|
4811
|
+
},
|
|
4812
|
+
{
|
|
4813
|
+
value: "django-ninja",
|
|
4814
|
+
label: "Django Ninja",
|
|
4815
|
+
hint: "FastAPI-style Django APIs with type hints and automatic OpenAPI docs"
|
|
4816
|
+
},
|
|
4817
|
+
{
|
|
4818
|
+
value: "none",
|
|
4819
|
+
label: "None",
|
|
4820
|
+
hint: "No additional Python API framework"
|
|
4821
|
+
}
|
|
4822
|
+
];
|
|
4823
|
+
const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [
|
|
4824
|
+
{
|
|
4825
|
+
value: "celery",
|
|
4826
|
+
label: "Celery",
|
|
4827
|
+
hint: "Distributed task queue for Python"
|
|
4828
|
+
},
|
|
4829
|
+
{
|
|
4830
|
+
value: "rq",
|
|
4831
|
+
label: "RQ",
|
|
4832
|
+
hint: "Simple Redis-backed job queue for Python"
|
|
4833
|
+
},
|
|
4834
|
+
{
|
|
4835
|
+
value: "dramatiq",
|
|
4836
|
+
label: "Dramatiq",
|
|
4837
|
+
hint: "Distributed task processing with Redis or RabbitMQ brokers"
|
|
4838
|
+
},
|
|
4839
|
+
{
|
|
4840
|
+
value: "huey",
|
|
4841
|
+
label: "Huey",
|
|
4842
|
+
hint: "Lightweight task queue with Redis-backed scheduling"
|
|
4843
|
+
},
|
|
4844
|
+
{
|
|
4845
|
+
value: "none",
|
|
4846
|
+
label: "None",
|
|
4847
|
+
hint: "No task queue"
|
|
4848
|
+
}
|
|
4849
|
+
];
|
|
4850
|
+
const PYTHON_GRAPHQL_PROMPT_OPTIONS = [
|
|
4851
|
+
{
|
|
4852
|
+
value: "strawberry",
|
|
4853
|
+
label: "Strawberry",
|
|
4854
|
+
hint: "Python GraphQL library using dataclasses and type hints"
|
|
4855
|
+
},
|
|
4856
|
+
{
|
|
4857
|
+
value: "ariadne",
|
|
4858
|
+
label: "Ariadne",
|
|
4859
|
+
hint: "Schema-first GraphQL server library for Python"
|
|
4860
|
+
},
|
|
4861
|
+
{
|
|
4862
|
+
value: "none",
|
|
4863
|
+
label: "None",
|
|
4864
|
+
hint: "No GraphQL framework"
|
|
4865
|
+
}
|
|
4866
|
+
];
|
|
4867
|
+
const PYTHON_QUALITY_PROMPT_OPTIONS = [
|
|
4868
|
+
{
|
|
4869
|
+
value: "ruff",
|
|
4870
|
+
label: "Ruff",
|
|
4871
|
+
hint: "An extremely fast Python linter and formatter"
|
|
4872
|
+
},
|
|
4873
|
+
{
|
|
4874
|
+
value: "mypy",
|
|
4875
|
+
label: "mypy",
|
|
4876
|
+
hint: "Static type checker for Python"
|
|
4164
4877
|
},
|
|
4165
4878
|
{
|
|
4166
|
-
value: "
|
|
4167
|
-
label: "
|
|
4168
|
-
hint: "
|
|
4879
|
+
value: "pyright",
|
|
4880
|
+
label: "Pyright",
|
|
4881
|
+
hint: "Fast Python type checker from Microsoft"
|
|
4169
4882
|
},
|
|
4170
4883
|
{
|
|
4171
4884
|
value: "none",
|
|
4172
4885
|
label: "None",
|
|
4173
|
-
hint: "No
|
|
4886
|
+
hint: "No code quality tools"
|
|
4174
4887
|
}
|
|
4175
4888
|
];
|
|
4176
|
-
const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [{
|
|
4177
|
-
value: "celery",
|
|
4178
|
-
label: "Celery",
|
|
4179
|
-
hint: "Distributed task queue for Python"
|
|
4180
|
-
}, {
|
|
4181
|
-
value: "none",
|
|
4182
|
-
label: "None",
|
|
4183
|
-
hint: "No task queue"
|
|
4184
|
-
}];
|
|
4185
|
-
const PYTHON_GRAPHQL_PROMPT_OPTIONS = [{
|
|
4186
|
-
value: "strawberry",
|
|
4187
|
-
label: "Strawberry",
|
|
4188
|
-
hint: "Python GraphQL library using dataclasses and type hints"
|
|
4189
|
-
}, {
|
|
4190
|
-
value: "none",
|
|
4191
|
-
label: "None",
|
|
4192
|
-
hint: "No GraphQL framework"
|
|
4193
|
-
}];
|
|
4194
|
-
const PYTHON_QUALITY_PROMPT_OPTIONS = [{
|
|
4195
|
-
value: "ruff",
|
|
4196
|
-
label: "Ruff",
|
|
4197
|
-
hint: "An extremely fast Python linter and formatter"
|
|
4198
|
-
}, {
|
|
4199
|
-
value: "none",
|
|
4200
|
-
label: "None",
|
|
4201
|
-
hint: "No code quality tools"
|
|
4202
|
-
}];
|
|
4203
4889
|
function resolvePythonWebFrameworkPrompt(pythonWebFramework) {
|
|
4204
4890
|
return createStaticSinglePromptResolution(PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS, "fastapi", pythonWebFramework);
|
|
4205
4891
|
}
|
|
@@ -4272,6 +4958,20 @@ async function getPythonAuthChoice(pythonAuth) {
|
|
|
4272
4958
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4273
4959
|
return response;
|
|
4274
4960
|
}
|
|
4961
|
+
function resolvePythonApiPrompt(pythonApi) {
|
|
4962
|
+
return createStaticSinglePromptResolution(PYTHON_API_PROMPT_OPTIONS, "none", pythonApi);
|
|
4963
|
+
}
|
|
4964
|
+
async function getPythonApiChoice(pythonApi) {
|
|
4965
|
+
const resolution = resolvePythonApiPrompt(pythonApi);
|
|
4966
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4967
|
+
const response = await navigableSelect({
|
|
4968
|
+
message: "Select Python API framework",
|
|
4969
|
+
options: resolution.options,
|
|
4970
|
+
initialValue: resolution.initialValue
|
|
4971
|
+
});
|
|
4972
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4973
|
+
return response;
|
|
4974
|
+
}
|
|
4275
4975
|
function resolvePythonTaskQueuePrompt(pythonTaskQueue) {
|
|
4276
4976
|
return createStaticSinglePromptResolution(PYTHON_TASK_QUEUE_PROMPT_OPTIONS, "none", pythonTaskQueue);
|
|
4277
4977
|
}
|
|
@@ -4545,6 +5245,51 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
|
|
|
4545
5245
|
label: "Serde",
|
|
4546
5246
|
hint: "Serialization framework for Rust"
|
|
4547
5247
|
},
|
|
5248
|
+
{
|
|
5249
|
+
value: "uuid",
|
|
5250
|
+
label: "uuid",
|
|
5251
|
+
hint: "UUID generation and parsing with Serde support"
|
|
5252
|
+
},
|
|
5253
|
+
{
|
|
5254
|
+
value: "chrono",
|
|
5255
|
+
label: "Chrono",
|
|
5256
|
+
hint: "Date and time library with Serde support"
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
value: "reqwest",
|
|
5260
|
+
label: "Reqwest",
|
|
5261
|
+
hint: "Ergonomic HTTP client with JSON and Rustls TLS support"
|
|
5262
|
+
},
|
|
5263
|
+
{
|
|
5264
|
+
value: "config",
|
|
5265
|
+
label: "config",
|
|
5266
|
+
hint: "Layered configuration from files, environment, and defaults"
|
|
5267
|
+
},
|
|
5268
|
+
{
|
|
5269
|
+
value: "dashmap",
|
|
5270
|
+
label: "DashMap",
|
|
5271
|
+
hint: "Concurrent hash map for shared mutable state"
|
|
5272
|
+
},
|
|
5273
|
+
{
|
|
5274
|
+
value: "parking-lot",
|
|
5275
|
+
label: "parking_lot",
|
|
5276
|
+
hint: "Compact, fast synchronization primitives"
|
|
5277
|
+
},
|
|
5278
|
+
{
|
|
5279
|
+
value: "secrecy",
|
|
5280
|
+
label: "Secrecy",
|
|
5281
|
+
hint: "Secret value wrapper that avoids accidental exposure"
|
|
5282
|
+
},
|
|
5283
|
+
{
|
|
5284
|
+
value: "tokio-util",
|
|
5285
|
+
label: "Tokio Util",
|
|
5286
|
+
hint: "Tokio utilities for codecs, cancellation tokens, and IO helpers"
|
|
5287
|
+
},
|
|
5288
|
+
{
|
|
5289
|
+
value: "utoipa",
|
|
5290
|
+
label: "utoipa",
|
|
5291
|
+
hint: "OpenAPI documentation generation from Rust types"
|
|
5292
|
+
},
|
|
4548
5293
|
{
|
|
4549
5294
|
value: "validator",
|
|
4550
5295
|
label: "Validator",
|
|
@@ -4569,6 +5314,16 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
|
|
|
4569
5314
|
value: "mockall",
|
|
4570
5315
|
label: "Mockall",
|
|
4571
5316
|
hint: "Powerful mocking library for Rust"
|
|
5317
|
+
},
|
|
5318
|
+
{
|
|
5319
|
+
value: "proptest",
|
|
5320
|
+
label: "Proptest",
|
|
5321
|
+
hint: "Property-based testing for Rust"
|
|
5322
|
+
},
|
|
5323
|
+
{
|
|
5324
|
+
value: "insta",
|
|
5325
|
+
label: "Insta",
|
|
5326
|
+
hint: "Snapshot testing for Rust"
|
|
4572
5327
|
}
|
|
4573
5328
|
];
|
|
4574
5329
|
const RUST_LOGGING_PROMPT_OPTIONS = [
|
|
@@ -4770,39 +5525,71 @@ async function getRustAuthChoice(rustAuth) {
|
|
|
4770
5525
|
|
|
4771
5526
|
//#endregion
|
|
4772
5527
|
//#region src/prompts/search.ts
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
5528
|
+
const SEARCH_PROMPT_OPTIONS = [
|
|
5529
|
+
{
|
|
5530
|
+
value: "meilisearch",
|
|
5531
|
+
label: "Meilisearch",
|
|
5532
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
5533
|
+
},
|
|
5534
|
+
{
|
|
5535
|
+
value: "typesense",
|
|
5536
|
+
label: "Typesense",
|
|
5537
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
5538
|
+
},
|
|
5539
|
+
{
|
|
5540
|
+
value: "elasticsearch",
|
|
5541
|
+
label: "Elasticsearch",
|
|
5542
|
+
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
5543
|
+
},
|
|
5544
|
+
{
|
|
5545
|
+
value: "algolia",
|
|
5546
|
+
label: "Algolia",
|
|
5547
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
5548
|
+
},
|
|
5549
|
+
{
|
|
5550
|
+
value: "none",
|
|
5551
|
+
label: "None",
|
|
5552
|
+
hint: "Skip search engine setup"
|
|
5553
|
+
}
|
|
5554
|
+
];
|
|
5555
|
+
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
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
|
+
};
|
|
5563
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
5564
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
5565
|
+
shouldPrompt: false,
|
|
5566
|
+
mode: "single",
|
|
5567
|
+
options: [],
|
|
5568
|
+
autoValue: "none"
|
|
5569
|
+
};
|
|
5570
|
+
return context.search !== void 0 ? {
|
|
5571
|
+
shouldPrompt: false,
|
|
5572
|
+
mode: "single",
|
|
5573
|
+
options,
|
|
5574
|
+
autoValue: context.search
|
|
5575
|
+
} : {
|
|
5576
|
+
shouldPrompt: true,
|
|
5577
|
+
mode: "single",
|
|
5578
|
+
options,
|
|
5579
|
+
initialValue: "none"
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
async function getSearchChoice(search, backend, ecosystem) {
|
|
5583
|
+
const resolution = resolveSearchPrompt({
|
|
5584
|
+
search,
|
|
5585
|
+
backend,
|
|
5586
|
+
ecosystem
|
|
5587
|
+
});
|
|
5588
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4776
5589
|
const response = await navigableSelect({
|
|
4777
5590
|
message: "Select search engine",
|
|
4778
|
-
options:
|
|
4779
|
-
|
|
4780
|
-
value: "meilisearch",
|
|
4781
|
-
label: "Meilisearch",
|
|
4782
|
-
hint: "Lightning-fast search engine with typo tolerance"
|
|
4783
|
-
},
|
|
4784
|
-
{
|
|
4785
|
-
value: "typesense",
|
|
4786
|
-
label: "Typesense",
|
|
4787
|
-
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
4788
|
-
},
|
|
4789
|
-
{
|
|
4790
|
-
value: "elasticsearch",
|
|
4791
|
-
label: "Elasticsearch",
|
|
4792
|
-
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
4793
|
-
},
|
|
4794
|
-
{
|
|
4795
|
-
value: "algolia",
|
|
4796
|
-
label: "Algolia",
|
|
4797
|
-
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
4798
|
-
},
|
|
4799
|
-
{
|
|
4800
|
-
value: "none",
|
|
4801
|
-
label: "None",
|
|
4802
|
-
hint: "Skip search engine setup"
|
|
4803
|
-
}
|
|
4804
|
-
],
|
|
4805
|
-
initialValue: "none"
|
|
5591
|
+
options: resolution.options,
|
|
5592
|
+
initialValue: resolution.initialValue
|
|
4806
5593
|
});
|
|
4807
5594
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4808
5595
|
return response;
|
|
@@ -4854,6 +5641,16 @@ const STYLE_OPTIONS = [
|
|
|
4854
5641
|
value: "mira",
|
|
4855
5642
|
label: "Mira",
|
|
4856
5643
|
hint: "Dense, made for data-heavy interfaces"
|
|
5644
|
+
},
|
|
5645
|
+
{
|
|
5646
|
+
value: "luma",
|
|
5647
|
+
label: "Luma",
|
|
5648
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5649
|
+
},
|
|
5650
|
+
{
|
|
5651
|
+
value: "sera",
|
|
5652
|
+
label: "Sera",
|
|
5653
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
4857
5654
|
}
|
|
4858
5655
|
];
|
|
4859
5656
|
const ICON_LIBRARY_OPTIONS = [
|
|
@@ -4881,6 +5678,16 @@ const ICON_LIBRARY_OPTIONS = [
|
|
|
4881
5678
|
value: "remixicon",
|
|
4882
5679
|
label: "Remix Icon",
|
|
4883
5680
|
hint: "Open-source neutral style icons"
|
|
5681
|
+
},
|
|
5682
|
+
{
|
|
5683
|
+
value: "heroicons",
|
|
5684
|
+
label: "Heroicons",
|
|
5685
|
+
hint: "Tailwind Labs SVG icon set"
|
|
5686
|
+
},
|
|
5687
|
+
{
|
|
5688
|
+
value: "react-icons",
|
|
5689
|
+
label: "React Icons",
|
|
5690
|
+
hint: "Popular wrapper for many icon packs"
|
|
4884
5691
|
}
|
|
4885
5692
|
];
|
|
4886
5693
|
const COLOR_THEME_OPTIONS = [
|
|
@@ -5226,6 +6033,7 @@ function resolveStateManagementPrompt(context = {}) {
|
|
|
5226
6033
|
"react-vite",
|
|
5227
6034
|
"tanstack-start",
|
|
5228
6035
|
"next",
|
|
6036
|
+
"vinext",
|
|
5229
6037
|
"redwood"
|
|
5230
6038
|
].includes(f));
|
|
5231
6039
|
const isFresh = web.includes("fresh");
|
|
@@ -5379,6 +6187,14 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
5379
6187
|
label: "Mantine",
|
|
5380
6188
|
hint: "Full-featured React component library with 120+ components"
|
|
5381
6189
|
},
|
|
6190
|
+
mui: {
|
|
6191
|
+
label: "MUI",
|
|
6192
|
+
hint: "Popular React component library implementing Material Design"
|
|
6193
|
+
},
|
|
6194
|
+
antd: {
|
|
6195
|
+
label: "Ant Design",
|
|
6196
|
+
hint: "Enterprise-class React UI component library"
|
|
6197
|
+
},
|
|
5382
6198
|
"base-ui": {
|
|
5383
6199
|
label: "Base UI",
|
|
5384
6200
|
hint: "Unstyled, accessible components from MUI team (Radix successor)"
|
|
@@ -5510,6 +6326,7 @@ const WEB_FRAMEWORKS = [
|
|
|
5510
6326
|
"react-vite",
|
|
5511
6327
|
"tanstack-start",
|
|
5512
6328
|
"next",
|
|
6329
|
+
"vinext",
|
|
5513
6330
|
"nuxt",
|
|
5514
6331
|
"svelte",
|
|
5515
6332
|
"solid",
|
|
@@ -5569,6 +6386,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5569
6386
|
const result = await navigableGroup({
|
|
5570
6387
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
5571
6388
|
frontend: ({ results }) => {
|
|
6389
|
+
if (results.ecosystem === "react-native") return getNativeFrontendChoice(flags.frontend);
|
|
5572
6390
|
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
5573
6391
|
return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
|
|
5574
6392
|
},
|
|
@@ -5621,6 +6439,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5621
6439
|
},
|
|
5622
6440
|
auth: ({ results }) => {
|
|
5623
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");
|
|
5624
6443
|
if (results.ecosystem === "go") return getAuthChoice(flags.auth, void 0, void 0, "go");
|
|
5625
6444
|
return Promise.resolve("none");
|
|
5626
6445
|
},
|
|
@@ -5629,8 +6448,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5629
6448
|
return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
|
|
5630
6449
|
},
|
|
5631
6450
|
email: ({ results }) => {
|
|
5632
|
-
if (results.ecosystem
|
|
5633
|
-
return getEmailChoice(flags.email, results.backend);
|
|
6451
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
6452
|
+
return getEmailChoice(flags.email, results.backend, results.ecosystem);
|
|
5634
6453
|
},
|
|
5635
6454
|
effect: ({ results }) => {
|
|
5636
6455
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5701,8 +6520,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5701
6520
|
return getLoggingChoice(flags.logging, results.backend);
|
|
5702
6521
|
},
|
|
5703
6522
|
observability: ({ results }) => {
|
|
5704
|
-
if (results.ecosystem
|
|
5705
|
-
return getObservabilityChoice(flags.observability, results.backend);
|
|
6523
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
6524
|
+
return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
|
|
5706
6525
|
},
|
|
5707
6526
|
featureFlags: ({ results }) => {
|
|
5708
6527
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5717,21 +6536,58 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5717
6536
|
return getCMSChoice(flags.cms, results.backend);
|
|
5718
6537
|
},
|
|
5719
6538
|
caching: ({ results }) => {
|
|
5720
|
-
if (results.ecosystem
|
|
5721
|
-
return getCachingChoice(flags.caching, results.backend);
|
|
6539
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
6540
|
+
return getCachingChoice(flags.caching, results.backend, results.ecosystem);
|
|
5722
6541
|
},
|
|
5723
6542
|
i18n: ({ results }) => {
|
|
5724
6543
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5725
6544
|
return getI18nChoice(flags.i18n, results.frontend);
|
|
5726
6545
|
},
|
|
5727
6546
|
search: ({ results }) => {
|
|
5728
|
-
if (results.ecosystem
|
|
5729
|
-
return getSearchChoice(flags.search, results.backend);
|
|
6547
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
6548
|
+
return getSearchChoice(flags.search, results.backend, results.ecosystem);
|
|
5730
6549
|
},
|
|
5731
6550
|
fileStorage: ({ results }) => {
|
|
5732
6551
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5733
6552
|
return getFileStorageChoice(flags.fileStorage, results.backend);
|
|
5734
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
|
+
},
|
|
5735
6591
|
rustWebFramework: ({ results }) => {
|
|
5736
6592
|
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
5737
6593
|
return getRustWebFrameworkChoice(flags.rustWebFramework);
|
|
@@ -5792,6 +6648,11 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5792
6648
|
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
5793
6649
|
return getPythonAuthChoice(flags.pythonAuth);
|
|
5794
6650
|
},
|
|
6651
|
+
pythonApi: ({ results }) => {
|
|
6652
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
6653
|
+
if (results.pythonWebFramework !== "django") return Promise.resolve("none");
|
|
6654
|
+
return getPythonApiChoice(flags.pythonApi);
|
|
6655
|
+
},
|
|
5795
6656
|
pythonTaskQueue: ({ results }) => {
|
|
5796
6657
|
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
5797
6658
|
return getPythonTaskQueueChoice(flags.pythonTaskQueue);
|
|
@@ -5856,10 +6717,70 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5856
6717
|
if (results.javaBuildTool === "none") return Promise.resolve([]);
|
|
5857
6718
|
return getJavaTestingLibrariesChoice(flags.javaTestingLibraries);
|
|
5858
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
|
+
},
|
|
5859
6780
|
aiDocs: () => getAiDocsChoice(flags.aiDocs),
|
|
5860
6781
|
git: () => getGitChoice(flags.git),
|
|
5861
6782
|
packageManager: ({ results }) => {
|
|
5862
|
-
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());
|
|
5863
6784
|
return getPackageManagerChoice(flags.packageManager);
|
|
5864
6785
|
},
|
|
5865
6786
|
install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
|
|
@@ -5908,6 +6829,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5908
6829
|
i18n: result.i18n,
|
|
5909
6830
|
search: result.search,
|
|
5910
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,
|
|
5911
6839
|
ecosystem: result.ecosystem,
|
|
5912
6840
|
rustWebFramework: result.rustWebFramework,
|
|
5913
6841
|
rustFrontend: result.rustFrontend,
|
|
@@ -5924,6 +6852,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5924
6852
|
pythonValidation: result.pythonValidation,
|
|
5925
6853
|
pythonAi: result.pythonAi,
|
|
5926
6854
|
pythonAuth: result.pythonAuth,
|
|
6855
|
+
pythonApi: result.pythonApi,
|
|
5927
6856
|
pythonTaskQueue: result.pythonTaskQueue,
|
|
5928
6857
|
pythonGraphql: result.pythonGraphql,
|
|
5929
6858
|
pythonQuality: result.pythonQuality,
|
|
@@ -5939,6 +6868,21 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5939
6868
|
javaAuth: result.javaAuth,
|
|
5940
6869
|
javaLibraries: result.javaLibraries,
|
|
5941
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,
|
|
5942
6886
|
aiDocs: result.aiDocs
|
|
5943
6887
|
};
|
|
5944
6888
|
}
|
|
@@ -6095,7 +7039,17 @@ function displayConfig(config) {
|
|
|
6095
7039
|
if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
|
|
6096
7040
|
if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
|
|
6097
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)}`);
|
|
6098
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)}`);
|
|
6099
7053
|
if (config.cms !== void 0) configDisplay.push(`${pc.blue("CMS:")} ${String(config.cms)}`);
|
|
6100
7054
|
if (config.search !== void 0) configDisplay.push(`${pc.blue("Search:")} ${String(config.search)}`);
|
|
6101
7055
|
if (config.fileStorage !== void 0) configDisplay.push(`${pc.blue("File Storage:")} ${String(config.fileStorage)}`);
|
|
@@ -6109,6 +7063,11 @@ function displayConfig(config) {
|
|
|
6109
7063
|
const examplesText = examples.length > 0 && examples[0] !== void 0 ? examples.join(", ") : "none";
|
|
6110
7064
|
configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`);
|
|
6111
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
|
+
}
|
|
6112
7071
|
if (config.git !== void 0) {
|
|
6113
7072
|
const gitText = typeof config.git === "boolean" ? config.git ? "Yes" : "No" : String(config.git);
|
|
6114
7073
|
configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
|
|
@@ -6151,6 +7110,10 @@ function appendCommonFlags(flags, config) {
|
|
|
6151
7110
|
flags.push(config.install ? "--install" : "--no-install");
|
|
6152
7111
|
}
|
|
6153
7112
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
7113
|
+
flags.push(`--email ${config.email}`);
|
|
7114
|
+
flags.push(`--observability ${config.observability}`);
|
|
7115
|
+
flags.push(`--caching ${config.caching}`);
|
|
7116
|
+
flags.push(`--search ${config.search}`);
|
|
6154
7117
|
flags.push(formatArrayFlag("addons", config.addons));
|
|
6155
7118
|
flags.push(formatArrayFlag("examples", config.examples));
|
|
6156
7119
|
flags.push(`--db-setup ${config.dbSetup}`);
|
|
@@ -6192,11 +7155,19 @@ function getTypeScriptFlags(config) {
|
|
|
6192
7155
|
flags.push(`--job-queue ${config.jobQueue}`);
|
|
6193
7156
|
flags.push(`--logging ${config.logging}`);
|
|
6194
7157
|
flags.push(`--observability ${config.observability}`);
|
|
7158
|
+
flags.push(`--feature-flags ${config.featureFlags}`);
|
|
6195
7159
|
flags.push(`--caching ${config.caching}`);
|
|
6196
7160
|
flags.push(`--i18n ${config.i18n}`);
|
|
6197
7161
|
flags.push(`--cms ${config.cms}`);
|
|
6198
7162
|
flags.push(`--search ${config.search}`);
|
|
6199
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}`);
|
|
6200
7171
|
if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
|
|
6201
7172
|
else flags.push("--addons none");
|
|
6202
7173
|
if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
|
|
@@ -6207,6 +7178,21 @@ function getTypeScriptFlags(config) {
|
|
|
6207
7178
|
appendCommonFlags(flags, config);
|
|
6208
7179
|
return flags;
|
|
6209
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
|
+
}
|
|
6210
7196
|
function getRustFlags(config) {
|
|
6211
7197
|
const flags = ["--ecosystem rust"];
|
|
6212
7198
|
flags.push(`--rust-web-framework ${config.rustWebFramework}`);
|
|
@@ -6230,6 +7216,7 @@ function getPythonFlags(config) {
|
|
|
6230
7216
|
flags.push(`--python-validation ${config.pythonValidation}`);
|
|
6231
7217
|
flags.push(formatArrayFlag("python-ai", config.pythonAi));
|
|
6232
7218
|
flags.push(`--python-auth ${config.pythonAuth}`);
|
|
7219
|
+
flags.push(`--python-api ${config.pythonApi}`);
|
|
6233
7220
|
flags.push(`--python-task-queue ${config.pythonTaskQueue}`);
|
|
6234
7221
|
flags.push(`--python-graphql ${config.pythonGraphql}`);
|
|
6235
7222
|
flags.push(`--python-quality ${config.pythonQuality}`);
|
|
@@ -6262,9 +7249,32 @@ function getJavaFlags(config) {
|
|
|
6262
7249
|
appendCommonFlags(flags, config);
|
|
6263
7250
|
return flags;
|
|
6264
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
|
+
}
|
|
6265
7272
|
function generateReproducibleCommand(config) {
|
|
6266
7273
|
let flags;
|
|
6267
7274
|
switch (config.ecosystem) {
|
|
7275
|
+
case "react-native":
|
|
7276
|
+
flags = getReactNativeFlags(config);
|
|
7277
|
+
break;
|
|
6268
7278
|
case "rust":
|
|
6269
7279
|
flags = getRustFlags(config);
|
|
6270
7280
|
break;
|
|
@@ -6277,6 +7287,9 @@ function generateReproducibleCommand(config) {
|
|
|
6277
7287
|
case "java":
|
|
6278
7288
|
flags = getJavaFlags(config);
|
|
6279
7289
|
break;
|
|
7290
|
+
case "elixir":
|
|
7291
|
+
flags = getElixirFlags(config);
|
|
7292
|
+
break;
|
|
6280
7293
|
case "typescript":
|
|
6281
7294
|
default:
|
|
6282
7295
|
flags = getTypeScriptFlags(config);
|
|
@@ -6499,7 +7512,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
|
|
|
6499
7512
|
}],
|
|
6500
7513
|
conflictsWithOptions: [{
|
|
6501
7514
|
optionKey: "frontend",
|
|
6502
|
-
values: ["next"]
|
|
7515
|
+
values: ["next", "vinext"]
|
|
6503
7516
|
}]
|
|
6504
7517
|
},
|
|
6505
7518
|
{
|
|
@@ -6563,7 +7576,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
|
|
|
6563
7576
|
}],
|
|
6564
7577
|
conflictsWithOptions: [{
|
|
6565
7578
|
optionKey: "frontend",
|
|
6566
|
-
values: ["next"]
|
|
7579
|
+
values: ["next", "vinext"]
|
|
6567
7580
|
}]
|
|
6568
7581
|
}
|
|
6569
7582
|
];
|
|
@@ -6904,6 +7917,18 @@ function validateBackendConstraints(config, providedFlags, options) {
|
|
|
6904
7917
|
function validateFrontendConstraints(config, providedFlags) {
|
|
6905
7918
|
const { frontend } = config;
|
|
6906
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
|
+
});
|
|
6907
7932
|
ensureSingleWebAndNative(frontend);
|
|
6908
7933
|
if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
|
|
6909
7934
|
}
|
|
@@ -6914,19 +7939,20 @@ function validateApiConstraints(_config, _options) {}
|
|
|
6914
7939
|
function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
|
|
6915
7940
|
if (config.ecosystem !== "java") return;
|
|
6916
7941
|
const hasSpringBoot = config.javaWebFramework === "spring-boot";
|
|
7942
|
+
const hasJavaWebFramework = config.javaWebFramework !== "none";
|
|
6917
7943
|
const hasNoBuildTool = config.javaBuildTool === "none";
|
|
6918
7944
|
const hasJavaLibraries = (config.javaLibraries ?? []).some((library) => library !== "none");
|
|
6919
7945
|
const hasJavaTestingLibraries = (config.javaTestingLibraries ?? []).some((library) => library !== "none");
|
|
6920
7946
|
const hasSpringOnlyFeatures = config.javaOrm !== "none" || config.javaAuth !== "none" || hasJavaLibraries;
|
|
6921
|
-
if (hasNoBuildTool &&
|
|
6922
|
-
message: "
|
|
7947
|
+
if (hasNoBuildTool && hasJavaWebFramework) incompatibilityError({
|
|
7948
|
+
message: "Java web frameworks require Maven or Gradle in the Java scaffold.",
|
|
6923
7949
|
provided: {
|
|
6924
7950
|
"java-web-framework": config.javaWebFramework ?? "none",
|
|
6925
7951
|
"java-build-tool": config.javaBuildTool ?? "none"
|
|
6926
7952
|
},
|
|
6927
|
-
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with
|
|
7953
|
+
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Java web frameworks", "Use --java-web-framework none for a plain Java source-only scaffold"]
|
|
6928
7954
|
});
|
|
6929
|
-
if ((
|
|
7955
|
+
if ((!hasSpringBoot || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
|
|
6930
7956
|
message: "Spring-only Java features require the Spring Boot scaffold with Maven or Gradle.",
|
|
6931
7957
|
provided: {
|
|
6932
7958
|
"java-web-framework": config.javaWebFramework ?? "none",
|
|
@@ -6935,7 +7961,7 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
|
|
|
6935
7961
|
"java-auth": config.javaAuth ?? "none",
|
|
6936
7962
|
"java-libraries": (config.javaLibraries ?? []).join(" ") || "none"
|
|
6937
7963
|
},
|
|
6938
|
-
suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java"]
|
|
7964
|
+
suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java or Quarkus"]
|
|
6939
7965
|
});
|
|
6940
7966
|
if (hasNoBuildTool && hasJavaTestingLibraries) incompatibilityError({
|
|
6941
7967
|
message: "Java testing libraries require Maven or Gradle to manage test dependencies.",
|
|
@@ -6946,6 +7972,218 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
|
|
|
6946
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"]
|
|
6947
7973
|
});
|
|
6948
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
|
+
}
|
|
8111
|
+
function validateEmailConstraints(config) {
|
|
8112
|
+
if (!config.email || config.email === "none") return;
|
|
8113
|
+
if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
|
|
8114
|
+
message: "Only Resend email is available for non-TypeScript ecosystems.",
|
|
8115
|
+
provided: {
|
|
8116
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
8117
|
+
email: config.email
|
|
8118
|
+
},
|
|
8119
|
+
suggestions: ["Use --email resend", "Use --email none"]
|
|
8120
|
+
});
|
|
8121
|
+
if (config.ecosystem === "java" && config.email === "resend" && config.javaBuildTool === "none") incompatibilityError({
|
|
8122
|
+
message: "Resend email for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
8123
|
+
provided: {
|
|
8124
|
+
"java-build-tool": "none",
|
|
8125
|
+
email: "resend"
|
|
8126
|
+
},
|
|
8127
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
8128
|
+
});
|
|
8129
|
+
}
|
|
8130
|
+
function validateObservabilityConstraints(config) {
|
|
8131
|
+
if (!config.observability || config.observability === "none") return;
|
|
8132
|
+
if (config.ecosystem !== "typescript" && config.observability !== "sentry") incompatibilityError({
|
|
8133
|
+
message: "Only Sentry observability is available for non-TypeScript ecosystems.",
|
|
8134
|
+
provided: {
|
|
8135
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
8136
|
+
observability: config.observability
|
|
8137
|
+
},
|
|
8138
|
+
suggestions: ["Use --observability sentry", "Use --observability none"]
|
|
8139
|
+
});
|
|
8140
|
+
if (config.ecosystem === "java" && config.observability === "sentry" && config.javaBuildTool === "none") incompatibilityError({
|
|
8141
|
+
message: "Sentry observability for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
8142
|
+
provided: {
|
|
8143
|
+
"java-build-tool": "none",
|
|
8144
|
+
observability: "sentry"
|
|
8145
|
+
},
|
|
8146
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
8147
|
+
});
|
|
8148
|
+
}
|
|
8149
|
+
function validateCachingConstraints(config) {
|
|
8150
|
+
if (!config.caching || config.caching === "none") return;
|
|
8151
|
+
if (config.ecosystem !== "typescript" && config.caching !== "upstash-redis") incompatibilityError({
|
|
8152
|
+
message: "Only Upstash Redis caching is available for non-TypeScript ecosystems.",
|
|
8153
|
+
provided: {
|
|
8154
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
8155
|
+
caching: config.caching
|
|
8156
|
+
},
|
|
8157
|
+
suggestions: ["Use --caching upstash-redis", "Use --caching none"]
|
|
8158
|
+
});
|
|
8159
|
+
if (config.ecosystem === "java" && config.caching === "upstash-redis" && config.javaBuildTool === "none") incompatibilityError({
|
|
8160
|
+
message: "Upstash Redis caching for Java requires Maven or Gradle to manage the Redis client dependency.",
|
|
8161
|
+
provided: {
|
|
8162
|
+
"java-build-tool": "none",
|
|
8163
|
+
caching: "upstash-redis"
|
|
8164
|
+
},
|
|
8165
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
8166
|
+
});
|
|
8167
|
+
}
|
|
8168
|
+
function validateSearchConstraints(config) {
|
|
8169
|
+
if (!config.search || config.search === "none") return;
|
|
8170
|
+
if (config.ecosystem !== "typescript" && config.search !== "meilisearch") incompatibilityError({
|
|
8171
|
+
message: "Only Meilisearch search is available for non-TypeScript ecosystems.",
|
|
8172
|
+
provided: {
|
|
8173
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
8174
|
+
search: config.search
|
|
8175
|
+
},
|
|
8176
|
+
suggestions: ["Use --search meilisearch", "Use --search none"]
|
|
8177
|
+
});
|
|
8178
|
+
if (config.ecosystem === "java" && config.search === "meilisearch" && config.javaBuildTool === "none") incompatibilityError({
|
|
8179
|
+
message: "Meilisearch search for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
8180
|
+
provided: {
|
|
8181
|
+
"java-build-tool": "none",
|
|
8182
|
+
search: "meilisearch"
|
|
8183
|
+
},
|
|
8184
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
8185
|
+
});
|
|
8186
|
+
}
|
|
6949
8187
|
function validateShadcnConstraints(config, providedFlags) {
|
|
6950
8188
|
const shadcnFlagMap = {
|
|
6951
8189
|
shadcnBase: "--shadcn-base",
|
|
@@ -6966,6 +8204,16 @@ function validateShadcnConstraints(config, providedFlags) {
|
|
|
6966
8204
|
suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
|
|
6967
8205
|
});
|
|
6968
8206
|
}
|
|
8207
|
+
function validatePythonApiConstraints(config) {
|
|
8208
|
+
if (config.ecosystem === "python" && config.pythonApi && config.pythonApi !== "none" && config.pythonWebFramework !== "django") incompatibilityError({
|
|
8209
|
+
message: "Python API frameworks require --python-web-framework django.",
|
|
8210
|
+
provided: {
|
|
8211
|
+
"python-web-framework": config.pythonWebFramework || "none",
|
|
8212
|
+
"python-api": config.pythonApi
|
|
8213
|
+
},
|
|
8214
|
+
suggestions: ["Use --python-web-framework django with --python-api django-rest-framework or django-ninja", "Set --python-api none for FastAPI, Flask, Litestar, or no Python web framework"]
|
|
8215
|
+
});
|
|
8216
|
+
}
|
|
6969
8217
|
function validateFullConfig(config, providedFlags, options) {
|
|
6970
8218
|
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
6971
8219
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
@@ -6978,7 +8226,13 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
6978
8226
|
validateBackendConstraints(config, providedFlags, options);
|
|
6979
8227
|
validateFrontendConstraints(config, providedFlags);
|
|
6980
8228
|
/* @__PURE__ */ validateApiConstraints(config, options);
|
|
8229
|
+
validatePythonApiConstraints(config);
|
|
8230
|
+
validateEmailConstraints(config);
|
|
8231
|
+
validateObservabilityConstraints(config);
|
|
8232
|
+
validateCachingConstraints(config);
|
|
8233
|
+
validateSearchConstraints(config);
|
|
6981
8234
|
validateJavaConstraints(config, providedFlags);
|
|
8235
|
+
validateElixirConstraints(config);
|
|
6982
8236
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
6983
8237
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
6984
8238
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
@@ -7014,7 +8268,13 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
7014
8268
|
validateDatabaseOrmAuth(config);
|
|
7015
8269
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
7016
8270
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
8271
|
+
validatePythonApiConstraints(config);
|
|
8272
|
+
validateEmailConstraints(config);
|
|
8273
|
+
validateObservabilityConstraints(config);
|
|
8274
|
+
validateCachingConstraints(config);
|
|
8275
|
+
validateSearchConstraints(config);
|
|
7017
8276
|
validateJavaConstraints(config);
|
|
8277
|
+
validateElixirConstraints(config);
|
|
7018
8278
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
7019
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);
|
|
7020
8280
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
@@ -8387,6 +9647,10 @@ async function displayPostInstallInstructions(config) {
|
|
|
8387
9647
|
displayPythonInstructions(config);
|
|
8388
9648
|
return;
|
|
8389
9649
|
}
|
|
9650
|
+
if (ecosystem === "elixir") {
|
|
9651
|
+
displayElixirInstructions(config);
|
|
9652
|
+
return;
|
|
9653
|
+
}
|
|
8390
9654
|
const isConvex = backend === "convex";
|
|
8391
9655
|
const isBackendSelf = backend === "self";
|
|
8392
9656
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : packageManager === "yarn" ? "yarn" : "bun run";
|
|
@@ -8550,7 +9814,7 @@ function getBunWebNativeWarning() {
|
|
|
8550
9814
|
}
|
|
8551
9815
|
function getClerkInstructions(backend, frontend) {
|
|
8552
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`;
|
|
8553
|
-
if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
|
|
9817
|
+
if (backend === "self" && (frontend.includes("next") || frontend.includes("vinext") || frontend.includes("tanstack-start"))) {
|
|
8554
9818
|
const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
|
|
8555
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`;
|
|
8556
9820
|
}
|
|
@@ -8688,7 +9952,8 @@ function displayGoInstructions(config) {
|
|
|
8688
9952
|
if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{ "grpc-go": "gRPC-Go" }[goApi] || goApi}\n`;
|
|
8689
9953
|
if (goCli && goCli !== "none") output += `${pc.cyan("•")} CLI: ${{
|
|
8690
9954
|
cobra: "Cobra",
|
|
8691
|
-
bubbletea: "Bubble Tea"
|
|
9955
|
+
bubbletea: "Bubble Tea",
|
|
9956
|
+
"urfave-cli": "urfave/cli"
|
|
8692
9957
|
}[goCli] || goCli}\n`;
|
|
8693
9958
|
if (goLogging && goLogging !== "none") output += `${pc.cyan("•")} Logging: ${{
|
|
8694
9959
|
zap: "Zap",
|
|
@@ -8790,6 +10055,7 @@ function displayJavaInstructions(config) {
|
|
|
8790
10055
|
const { projectName, relativePath, depsInstalled, javaWebFramework, javaBuildTool, javaOrm, javaAuth, javaLibraries, javaTestingLibraries } = config;
|
|
8791
10056
|
const cdCmd = `cd ${relativePath}`;
|
|
8792
10057
|
const isSpringBoot = javaWebFramework === "spring-boot" && javaBuildTool !== "none";
|
|
10058
|
+
const isQuarkus = javaWebFramework === "quarkus" && javaBuildTool !== "none";
|
|
8793
10059
|
const rawJavaLibraries = isSpringBoot ? javaLibraries.filter((library) => library !== "none") : [];
|
|
8794
10060
|
const rawJavaLibrarySet = new Set(rawJavaLibraries);
|
|
8795
10061
|
const jpaRequiredJavaLibraries = new Set(["flyway", "liquibase"]);
|
|
@@ -8801,7 +10067,7 @@ function displayJavaInstructions(config) {
|
|
|
8801
10067
|
}
|
|
8802
10068
|
const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
|
|
8803
10069
|
const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
|
|
8804
|
-
const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
|
|
10070
|
+
const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : isQuarkus ? javaBuildTool === "gradle" ? `${buildToolCommand} quarkusDev` : `${buildToolCommand} quarkus:dev` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
|
|
8805
10071
|
const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
|
|
8806
10072
|
const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
|
|
8807
10073
|
const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
|
|
@@ -8814,7 +10080,10 @@ function displayJavaInstructions(config) {
|
|
|
8814
10080
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
|
|
8815
10081
|
} else output += `${pc.cyan(`${stepCounter++}.`)} Add Maven or Gradle, then run the app\n`;
|
|
8816
10082
|
output += `\n${pc.bold("Your Java project includes:")}\n`;
|
|
8817
|
-
if (isSpringBoot) output += `${pc.cyan("•")} Web Framework: ${{
|
|
10083
|
+
if (isSpringBoot || isQuarkus) output += `${pc.cyan("•")} Web Framework: ${{
|
|
10084
|
+
"spring-boot": "Spring Boot",
|
|
10085
|
+
quarkus: "Quarkus"
|
|
10086
|
+
}[javaWebFramework] || javaWebFramework}\n`;
|
|
8818
10087
|
else output += `${pc.cyan("•")} Scaffold: Plain Java\n`;
|
|
8819
10088
|
if (javaBuildTool && javaBuildTool !== "none") output += `${pc.cyan("•")} Build Tool: ${{
|
|
8820
10089
|
maven: "Maven Wrapper",
|
|
@@ -8871,7 +10140,7 @@ function displayJavaInstructions(config) {
|
|
|
8871
10140
|
consola$1.box(output);
|
|
8872
10141
|
}
|
|
8873
10142
|
function displayPythonInstructions(config) {
|
|
8874
|
-
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonTaskQueue, pythonQuality } = config;
|
|
10143
|
+
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
|
|
8875
10144
|
const cdCmd = `cd ${relativePath}`;
|
|
8876
10145
|
let runCommand = "uv run uvicorn app.main:app --reload";
|
|
8877
10146
|
if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
|
|
@@ -8906,14 +10175,25 @@ function displayPythonInstructions(config) {
|
|
|
8906
10175
|
const aiList = pythonAi.filter((ai) => ai !== "none").map((ai) => aiNames[ai] || ai).join(", ");
|
|
8907
10176
|
output += `${pc.cyan("•")} AI: ${aiList}\n`;
|
|
8908
10177
|
}
|
|
10178
|
+
if (pythonApi && pythonApi !== "none") output += `${pc.cyan("•")} API Framework: ${{
|
|
10179
|
+
"django-rest-framework": "Django REST Framework",
|
|
10180
|
+
"django-ninja": "Django Ninja"
|
|
10181
|
+
}[pythonApi] || pythonApi}\n`;
|
|
8909
10182
|
if (pythonTaskQueue && pythonTaskQueue !== "none") output += `${pc.cyan("•")} Task Queue: ${{ celery: "Celery" }[pythonTaskQueue] || pythonTaskQueue}\n`;
|
|
8910
|
-
if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
|
|
10183
|
+
if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
|
|
10184
|
+
ruff: "Ruff",
|
|
10185
|
+
mypy: "mypy",
|
|
10186
|
+
pyright: "Pyright"
|
|
10187
|
+
}[pythonQuality] || pythonQuality}\n`;
|
|
8911
10188
|
output += `\n${pc.bold("Common Python commands:")}\n`;
|
|
8912
10189
|
output += `${pc.cyan("•")} Install: uv sync\n`;
|
|
8913
10190
|
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
8914
10191
|
output += `${pc.cyan("•")} Test: uv run pytest\n`;
|
|
8915
|
-
|
|
8916
|
-
|
|
10192
|
+
if (pythonQuality === "ruff") {
|
|
10193
|
+
output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
|
|
10194
|
+
output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
|
|
10195
|
+
} else if (pythonQuality === "mypy") output += `${pc.cyan("•")} Type check: uv run mypy src/app tests\n`;
|
|
10196
|
+
else if (pythonQuality === "pyright") output += `${pc.cyan("•")} Type check: uv run pyright\n`;
|
|
8917
10197
|
output += `\n${pc.bold("Your project will be available at:")}\n`;
|
|
8918
10198
|
output += `${pc.cyan("•")} API: http://localhost:8000\n`;
|
|
8919
10199
|
output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
|
|
@@ -8921,6 +10201,36 @@ function displayPythonInstructions(config) {
|
|
|
8921
10201
|
output += pc.dim("Your star helps other developers discover the project.");
|
|
8922
10202
|
consola$1.box(output);
|
|
8923
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
|
+
}
|
|
8924
10234
|
|
|
8925
10235
|
//#endregion
|
|
8926
10236
|
//#region src/helpers/core/create-project.ts
|
|
@@ -8943,7 +10253,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
8943
10253
|
await writeBtsConfig(options);
|
|
8944
10254
|
await formatProject(projectDir);
|
|
8945
10255
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
8946
|
-
if (options.install && options.ecosystem === "typescript") await installDependencies({
|
|
10256
|
+
if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) await installDependencies({
|
|
8947
10257
|
projectDir,
|
|
8948
10258
|
packageManager: options.packageManager
|
|
8949
10259
|
});
|
|
@@ -8952,6 +10262,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
8952
10262
|
if (options.install && options.ecosystem === "go") await runGoModTidy({ projectDir });
|
|
8953
10263
|
if (options.install && options.ecosystem === "java" && options.javaBuildTool !== "none") if (options.javaBuildTool === "gradle") await runGradleTests({ projectDir });
|
|
8954
10264
|
else await runMavenTests({ projectDir });
|
|
10265
|
+
if (options.install && options.ecosystem === "elixir") await runMixCompile({ projectDir });
|
|
8955
10266
|
await initializeGit(projectDir, options.git);
|
|
8956
10267
|
if (!isSilent()) await displayPostInstallInstructions({
|
|
8957
10268
|
...options,
|
|
@@ -8992,6 +10303,51 @@ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
|
|
|
8992
10303
|
|
|
8993
10304
|
//#endregion
|
|
8994
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
|
+
}
|
|
8995
10351
|
function shouldPromptForVersionChannel(input) {
|
|
8996
10352
|
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
8997
10353
|
return canPromptInteractively();
|
|
@@ -9094,11 +10450,19 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9094
10450
|
featureFlags: "none",
|
|
9095
10451
|
analytics: "none",
|
|
9096
10452
|
fileStorage: "none",
|
|
10453
|
+
mobileNavigation: "none",
|
|
10454
|
+
mobileUI: "none",
|
|
10455
|
+
mobileStorage: "none",
|
|
10456
|
+
mobileTesting: "none",
|
|
10457
|
+
mobilePush: "none",
|
|
10458
|
+
mobileOTA: "none",
|
|
10459
|
+
mobileDeepLinking: "none",
|
|
9097
10460
|
pythonWebFramework: "none",
|
|
9098
10461
|
pythonOrm: "none",
|
|
9099
10462
|
pythonValidation: "none",
|
|
9100
10463
|
pythonAi: [],
|
|
9101
10464
|
pythonAuth: "none",
|
|
10465
|
+
pythonApi: "none",
|
|
9102
10466
|
pythonTaskQueue: "none",
|
|
9103
10467
|
pythonGraphql: "none",
|
|
9104
10468
|
pythonQuality: "none",
|
|
@@ -9114,6 +10478,21 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9114
10478
|
javaAuth: "none",
|
|
9115
10479
|
javaLibraries: [],
|
|
9116
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",
|
|
9117
10496
|
aiDocs: []
|
|
9118
10497
|
},
|
|
9119
10498
|
reproducibleCommand: "",
|
|
@@ -9158,7 +10537,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9158
10537
|
if (cliInput.yes) {
|
|
9159
10538
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
9160
10539
|
config = {
|
|
9161
|
-
...
|
|
10540
|
+
...getYesBaseConfig(flagConfig),
|
|
9162
10541
|
...flagConfig,
|
|
9163
10542
|
projectName: finalBaseName,
|
|
9164
10543
|
projectDir: finalResolvedPath,
|
|
@@ -9555,17 +10934,21 @@ async function history(options) {
|
|
|
9555
10934
|
*/
|
|
9556
10935
|
async function createVirtual(options) {
|
|
9557
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");
|
|
9558
10941
|
const result = await generateVirtualProject$1({
|
|
9559
10942
|
config: {
|
|
10943
|
+
ecosystem,
|
|
9560
10944
|
projectName: options.projectName || "my-project",
|
|
9561
10945
|
projectDir: "/virtual",
|
|
9562
10946
|
relativePath: "./virtual",
|
|
9563
|
-
ecosystem: options.ecosystem || "typescript",
|
|
9564
10947
|
database: options.database || "none",
|
|
9565
10948
|
orm: options.orm || "none",
|
|
9566
|
-
backend: options.backend || "hono",
|
|
9567
|
-
runtime: options.runtime || "bun",
|
|
9568
|
-
frontend
|
|
10949
|
+
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
10950
|
+
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
10951
|
+
frontend,
|
|
9569
10952
|
addons: options.addons || [],
|
|
9570
10953
|
examples: options.examples || [],
|
|
9571
10954
|
auth: options.auth || "none",
|
|
@@ -9578,11 +10961,11 @@ async function createVirtual(options) {
|
|
|
9578
10961
|
versionChannel: options.versionChannel || "stable",
|
|
9579
10962
|
install: false,
|
|
9580
10963
|
dbSetup: options.dbSetup || "none",
|
|
9581
|
-
api: options.api || "trpc",
|
|
10964
|
+
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
9582
10965
|
webDeploy: options.webDeploy || "none",
|
|
9583
10966
|
serverDeploy: options.serverDeploy || "none",
|
|
9584
|
-
cssFramework: options.cssFramework || "tailwind",
|
|
9585
|
-
uiLibrary: options.uiLibrary || "shadcn-ui",
|
|
10967
|
+
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
10968
|
+
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
9586
10969
|
shadcnBase: options.shadcnBase ?? "radix",
|
|
9587
10970
|
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
9588
10971
|
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
@@ -9592,8 +10975,8 @@ async function createVirtual(options) {
|
|
|
9592
10975
|
shadcnRadius: options.shadcnRadius ?? "default",
|
|
9593
10976
|
ai: options.ai || "none",
|
|
9594
10977
|
stateManagement: options.stateManagement || "none",
|
|
9595
|
-
forms: options.forms || "react-hook-form",
|
|
9596
|
-
testing: options.testing || "vitest",
|
|
10978
|
+
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
10979
|
+
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
9597
10980
|
validation: options.validation || "zod",
|
|
9598
10981
|
realtime: options.realtime || "none",
|
|
9599
10982
|
jobQueue: options.jobQueue || "none",
|
|
@@ -9602,6 +10985,13 @@ async function createVirtual(options) {
|
|
|
9602
10985
|
observability: options.observability || "none",
|
|
9603
10986
|
featureFlags: options.featureFlags || "none",
|
|
9604
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"),
|
|
9605
10995
|
cms: options.cms || "none",
|
|
9606
10996
|
caching: options.caching || "none",
|
|
9607
10997
|
i18n: options.i18n || "none",
|
|
@@ -9622,6 +11012,7 @@ async function createVirtual(options) {
|
|
|
9622
11012
|
pythonValidation: options.pythonValidation || "none",
|
|
9623
11013
|
pythonAi: options.pythonAi || [],
|
|
9624
11014
|
pythonAuth: options.pythonAuth || "none",
|
|
11015
|
+
pythonApi: options.pythonApi || "none",
|
|
9625
11016
|
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
9626
11017
|
pythonGraphql: options.pythonGraphql || "none",
|
|
9627
11018
|
pythonQuality: options.pythonQuality || "none",
|
|
@@ -9637,6 +11028,21 @@ async function createVirtual(options) {
|
|
|
9637
11028
|
javaAuth: options.javaAuth || "none",
|
|
9638
11029
|
javaLibraries: options.javaLibraries || [],
|
|
9639
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",
|
|
9640
11046
|
aiDocs: options.aiDocs || ["claude-md"]
|
|
9641
11047
|
},
|
|
9642
11048
|
templates: EMBEDDED_TEMPLATES$1
|