create-better-fullstack 1.7.1 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/addons-setup-CJwQAWFg.mjs +5 -0
- package/dist/{addons-setup-CGhYT2qC.mjs → addons-setup-CUmA_nra.mjs} +5 -5
- package/dist/{bts-config-bOXo9tbL.mjs → bts-config-YcroedMK.mjs} +45 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +258 -30
- package/dist/index.mjs +1084 -54
- package/dist/{mcp-BxEilSGM.mjs → mcp-DoPutOIG.mjs} +1 -1
- package/dist/mcp-entry.mjs +194 -41
- package/package.json +9 -9
- package/dist/addons-setup-BdKQJ1h6.mjs +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __reExport } from "./chunk-CCII7kTE.mjs";
|
|
3
|
-
import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-
|
|
4
|
-
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-
|
|
3
|
+
import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-YcroedMK.mjs";
|
|
4
|
+
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CUmA_nra.mjs";
|
|
5
5
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
6
6
|
import { createRouterClient, os } from "@orpc/server";
|
|
7
7
|
import pc from "picocolors";
|
|
@@ -363,7 +363,7 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
363
363
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
364
364
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
365
365
|
dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
|
|
366
|
-
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, go, or
|
|
366
|
+
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)"),
|
|
367
367
|
database: types_exports.DatabaseSchema.optional(),
|
|
368
368
|
orm: types_exports.ORMSchema.optional(),
|
|
369
369
|
auth: types_exports.AuthSchema.optional(),
|
|
@@ -388,6 +388,13 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
388
388
|
i18n: types_exports.I18nSchema.optional().describe("Internationalization (i18n) library"),
|
|
389
389
|
search: types_exports.SearchSchema.optional().describe("Search engine solution"),
|
|
390
390
|
fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
|
|
391
|
+
mobileNavigation: types_exports.MobileNavigationSchema.optional().describe("Mobile navigation (expo-router, react-navigation)"),
|
|
392
|
+
mobileUI: types_exports.MobileUISchema.optional().describe("Mobile UI (tamagui, gluestack-ui, uniwind, unistyles)"),
|
|
393
|
+
mobileStorage: types_exports.MobileStorageSchema.optional().describe("Mobile storage (mmkv)"),
|
|
394
|
+
mobileTesting: types_exports.MobileTestingSchema.optional().describe("Mobile testing (maestro, react-native-testing-library)"),
|
|
395
|
+
mobilePush: types_exports.MobilePushSchema.optional().describe("Mobile push notifications (expo-notifications)"),
|
|
396
|
+
mobileOTA: types_exports.MobileOTASchema.optional().describe("Mobile OTA updates (expo-updates)"),
|
|
397
|
+
mobileDeepLinking: types_exports.MobileDeepLinkingSchema.optional().describe("Mobile deep linking (expo-linking)"),
|
|
391
398
|
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
392
399
|
astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
|
|
393
400
|
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
@@ -446,6 +453,21 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
446
453
|
javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
|
|
447
454
|
javaLibraries: z.array(types_exports.JavaLibrariesSchema).optional().describe("Java application libraries"),
|
|
448
455
|
javaTestingLibraries: z.array(types_exports.JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
|
|
456
|
+
elixirWebFramework: types_exports.ElixirWebFrameworkSchema.optional().describe("Elixir web framework (phoenix, phoenix-live-view, none)"),
|
|
457
|
+
elixirOrm: types_exports.ElixirOrmSchema.optional().describe("Elixir ORM/database (ecto, ecto-sql, none)"),
|
|
458
|
+
elixirAuth: types_exports.ElixirAuthSchema.optional().describe("Elixir auth (phx-gen-auth, ueberauth, guardian, none)"),
|
|
459
|
+
elixirApi: types_exports.ElixirApiSchema.optional().describe("Elixir API layer (rest, absinthe, none)"),
|
|
460
|
+
elixirRealtime: types_exports.ElixirRealtimeSchema.optional().describe("Elixir realtime (channels, presence, pubsub, live-view-streams, none)"),
|
|
461
|
+
elixirJobs: types_exports.ElixirJobsSchema.optional().describe("Elixir jobs (oban, quantum, none)"),
|
|
462
|
+
elixirValidation: types_exports.ElixirValidationSchema.optional().describe("Elixir validation (ecto-changesets, nimble-options, none)"),
|
|
463
|
+
elixirHttp: types_exports.ElixirHttpSchema.optional().describe("Elixir HTTP client (req, finch, none)"),
|
|
464
|
+
elixirJson: types_exports.ElixirJsonSchema.optional().describe("Elixir JSON library (jason, none)"),
|
|
465
|
+
elixirEmail: types_exports.ElixirEmailSchema.optional().describe("Elixir email library (swoosh, none)"),
|
|
466
|
+
elixirCaching: types_exports.ElixirCachingSchema.optional().describe("Elixir caching (cachex, nebulex, none)"),
|
|
467
|
+
elixirObservability: types_exports.ElixirObservabilitySchema.optional().describe("Elixir observability (telemetry, opentelemetry, prom_ex, none)"),
|
|
468
|
+
elixirTesting: types_exports.ElixirTestingSchema.optional().describe("Elixir testing (ex_unit, mox, bypass, wallaby, none)"),
|
|
469
|
+
elixirQuality: types_exports.ElixirQualitySchema.optional().describe("Elixir code quality (credo, dialyxir, sobelow, none)"),
|
|
470
|
+
elixirDeploy: types_exports.ElixirDeploySchema.optional().describe("Elixir deploy target (docker, fly, gigalixir, mix-release, none)"),
|
|
449
471
|
aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
|
|
450
472
|
});
|
|
451
473
|
const CreateCommandInputSchema = z.tuple([types_exports.ProjectNameSchema.optional(), CreateCommandOptionsSchema]);
|
|
@@ -534,6 +556,7 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
534
556
|
}
|
|
535
557
|
const FULLSTACK_FRONTENDS$1 = [
|
|
536
558
|
"next",
|
|
559
|
+
"vinext",
|
|
537
560
|
"tanstack-start",
|
|
538
561
|
"astro",
|
|
539
562
|
"nuxt",
|
|
@@ -545,11 +568,11 @@ function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
|
545
568
|
const frontends = config.frontend || options.frontend || [];
|
|
546
569
|
if (backend === "self") {
|
|
547
570
|
const { web, native } = splitFrontends$1(frontends);
|
|
548
|
-
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
|
|
571
|
+
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
|
|
549
572
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
|
|
550
573
|
}
|
|
551
574
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
552
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
|
|
575
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
|
|
553
576
|
}
|
|
554
577
|
const WORKERS_COMPATIBLE_BACKENDS = [
|
|
555
578
|
"hono",
|
|
@@ -647,11 +670,11 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
|
|
|
647
670
|
};
|
|
648
671
|
if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
|
|
649
672
|
isCompatible: false,
|
|
650
|
-
reason: "Docker Compose currently supports Next.js, TanStack Router, React Router, React Vite, Solid, or Astro"
|
|
673
|
+
reason: "Docker Compose currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro"
|
|
651
674
|
};
|
|
652
|
-
if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next")) return {
|
|
675
|
+
if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
|
|
653
676
|
isCompatible: false,
|
|
654
|
-
reason: "Docker Compose self-backend support currently requires Next.js"
|
|
677
|
+
reason: "Docker Compose self-backend support currently requires Next.js or Vinext"
|
|
655
678
|
};
|
|
656
679
|
if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
|
|
657
680
|
isCompatible: false,
|
|
@@ -1093,6 +1116,10 @@ function getAddonDisplay(addon) {
|
|
|
1093
1116
|
label = "Storybook";
|
|
1094
1117
|
hint = "Component development and testing workshop";
|
|
1095
1118
|
break;
|
|
1119
|
+
case "swr":
|
|
1120
|
+
label = "SWR";
|
|
1121
|
+
hint = "React Hooks for data fetching and caching";
|
|
1122
|
+
break;
|
|
1096
1123
|
case "tanstack-query":
|
|
1097
1124
|
label = "TanStack Query";
|
|
1098
1125
|
hint = "Powerful async state management & data fetching";
|
|
@@ -1146,6 +1173,7 @@ const ADDON_GROUPS = {
|
|
|
1146
1173
|
],
|
|
1147
1174
|
Integrations: ["msw", "storybook"],
|
|
1148
1175
|
"AI Agents": ["mcp", "skills"],
|
|
1176
|
+
"Data Fetching": ["swr"],
|
|
1149
1177
|
TanStack: [
|
|
1150
1178
|
"tanstack-query",
|
|
1151
1179
|
"tanstack-table",
|
|
@@ -1154,6 +1182,12 @@ const ADDON_GROUPS = {
|
|
|
1154
1182
|
"tanstack-pacer"
|
|
1155
1183
|
]
|
|
1156
1184
|
};
|
|
1185
|
+
function createGroupedAddonOptions() {
|
|
1186
|
+
return Object.fromEntries(Object.keys(ADDON_GROUPS).map((group$1) => [group$1, []]));
|
|
1187
|
+
}
|
|
1188
|
+
function getAddonGroup(addon) {
|
|
1189
|
+
return Object.entries(ADDON_GROUPS).find(([, addons]) => addons.includes(addon))?.[0];
|
|
1190
|
+
}
|
|
1157
1191
|
function validateAddonCompatibilityForPrompt(addon, frontends, auth, backend, runtime) {
|
|
1158
1192
|
return validateAddonCompatibility$1(addon, frontends, auth, backend, runtime);
|
|
1159
1193
|
}
|
|
@@ -1163,14 +1197,7 @@ function getCompatibleAddonsForPrompt(allAddons, frontends, existingAddons = [],
|
|
|
1163
1197
|
async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
1164
1198
|
if (addons !== void 0) return addons;
|
|
1165
1199
|
const allAddons = types_exports.AddonsSchema.options.filter((addon) => addon !== "none");
|
|
1166
|
-
const groupedOptions =
|
|
1167
|
-
Tooling: [],
|
|
1168
|
-
Documentation: [],
|
|
1169
|
-
Extensions: [],
|
|
1170
|
-
Integrations: [],
|
|
1171
|
-
"AI Agents": [],
|
|
1172
|
-
TanStack: []
|
|
1173
|
-
};
|
|
1200
|
+
const groupedOptions = createGroupedAddonOptions();
|
|
1174
1201
|
const frontendsArray = frontends || [];
|
|
1175
1202
|
for (const addon of allAddons) {
|
|
1176
1203
|
const { isCompatible } = validateAddonCompatibilityForPrompt(addon, frontendsArray, auth, backend, runtime);
|
|
@@ -1181,12 +1208,8 @@ async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
|
1181
1208
|
label,
|
|
1182
1209
|
hint
|
|
1183
1210
|
};
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1187
|
-
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1188
|
-
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1189
|
-
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1211
|
+
const group$1 = getAddonGroup(addon);
|
|
1212
|
+
if (group$1) groupedOptions[group$1].push(option);
|
|
1190
1213
|
}
|
|
1191
1214
|
Object.keys(groupedOptions).forEach((group$1) => {
|
|
1192
1215
|
if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
|
|
@@ -1207,14 +1230,7 @@ async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
|
1207
1230
|
return response;
|
|
1208
1231
|
}
|
|
1209
1232
|
async function getAddonsToAdd(frontend, existingAddons = [], auth, backend, runtime) {
|
|
1210
|
-
const groupedOptions =
|
|
1211
|
-
Tooling: [],
|
|
1212
|
-
Documentation: [],
|
|
1213
|
-
Extensions: [],
|
|
1214
|
-
Integrations: [],
|
|
1215
|
-
"AI Agents": [],
|
|
1216
|
-
TanStack: []
|
|
1217
|
-
};
|
|
1233
|
+
const groupedOptions = createGroupedAddonOptions();
|
|
1218
1234
|
const frontendArray = frontend || [];
|
|
1219
1235
|
const compatibleAddons = getCompatibleAddonsForPrompt(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth, backend, runtime);
|
|
1220
1236
|
for (const addon of compatibleAddons) {
|
|
@@ -1224,12 +1240,8 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth, backend, runt
|
|
|
1224
1240
|
label,
|
|
1225
1241
|
hint
|
|
1226
1242
|
};
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1230
|
-
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1231
|
-
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1232
|
-
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1243
|
+
const group$1 = getAddonGroup(addon);
|
|
1244
|
+
if (group$1) groupedOptions[group$1].push(option);
|
|
1233
1245
|
}
|
|
1234
1246
|
Object.keys(groupedOptions).forEach((group$1) => {
|
|
1235
1247
|
if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
|
|
@@ -1513,6 +1525,24 @@ async function runGradleTests({ projectDir }) {
|
|
|
1513
1525
|
if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
|
|
1514
1526
|
}
|
|
1515
1527
|
}
|
|
1528
|
+
async function runMixCompile({ projectDir }) {
|
|
1529
|
+
const s = spinner();
|
|
1530
|
+
try {
|
|
1531
|
+
s.start("Running mix deps.get and mix compile...");
|
|
1532
|
+
await $({
|
|
1533
|
+
cwd: projectDir,
|
|
1534
|
+
stderr: "inherit"
|
|
1535
|
+
})`mix deps.get`;
|
|
1536
|
+
await $({
|
|
1537
|
+
cwd: projectDir,
|
|
1538
|
+
stderr: "inherit"
|
|
1539
|
+
})`mix compile`;
|
|
1540
|
+
s.stop("Elixir dependencies installed and project compiled");
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
s.stop(pc.red("mix compile failed"));
|
|
1543
|
+
if (error instanceof Error) consola.error(pc.red(`Mix error: ${error.message}`));
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1516
1546
|
|
|
1517
1547
|
//#endregion
|
|
1518
1548
|
//#region src/helpers/core/add-handler.ts
|
|
@@ -1817,6 +1847,7 @@ function resolveAnimationPrompt(context = {}) {
|
|
|
1817
1847
|
"react-vite",
|
|
1818
1848
|
"tanstack-start",
|
|
1819
1849
|
"next",
|
|
1850
|
+
"vinext",
|
|
1820
1851
|
"redwood"
|
|
1821
1852
|
].includes(f));
|
|
1822
1853
|
const isFresh = web.includes("fresh");
|
|
@@ -2054,6 +2085,7 @@ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript")
|
|
|
2054
2085
|
//#region src/prompts/backend.ts
|
|
2055
2086
|
const FULLSTACK_FRONTENDS = [
|
|
2056
2087
|
"next",
|
|
2088
|
+
"vinext",
|
|
2057
2089
|
"tanstack-start",
|
|
2058
2090
|
"astro",
|
|
2059
2091
|
"nuxt",
|
|
@@ -2179,6 +2211,12 @@ const CACHING_PROMPT_OPTIONS = [{
|
|
|
2179
2211
|
hint: "Skip caching layer setup"
|
|
2180
2212
|
}];
|
|
2181
2213
|
function resolveCachingPrompt(context = {}) {
|
|
2214
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
2215
|
+
shouldPrompt: false,
|
|
2216
|
+
mode: "single",
|
|
2217
|
+
options: [],
|
|
2218
|
+
autoValue: "none"
|
|
2219
|
+
};
|
|
2182
2220
|
if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
|
|
2183
2221
|
shouldPrompt: false,
|
|
2184
2222
|
mode: "single",
|
|
@@ -2247,6 +2285,11 @@ const CMS_PROMPT_OPTIONS = [
|
|
|
2247
2285
|
label: "TinaCMS",
|
|
2248
2286
|
hint: "Git-backed headless CMS with visual editing"
|
|
2249
2287
|
},
|
|
2288
|
+
{
|
|
2289
|
+
value: "directus",
|
|
2290
|
+
label: "Directus",
|
|
2291
|
+
hint: "Open data platform and headless CMS for SQL databases"
|
|
2292
|
+
},
|
|
2250
2293
|
{
|
|
2251
2294
|
value: "none",
|
|
2252
2295
|
label: "None",
|
|
@@ -2579,6 +2622,317 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
|
2579
2622
|
return response;
|
|
2580
2623
|
}
|
|
2581
2624
|
|
|
2625
|
+
//#endregion
|
|
2626
|
+
//#region src/prompts/elixir-ecosystem.ts
|
|
2627
|
+
function makeChoice(message, options, defaultValue, value) {
|
|
2628
|
+
const resolution = createStaticSinglePromptResolution(options, defaultValue, value);
|
|
2629
|
+
if (!resolution.shouldPrompt) return Promise.resolve(resolution.autoValue ?? defaultValue);
|
|
2630
|
+
return navigableSelect({
|
|
2631
|
+
message,
|
|
2632
|
+
options: resolution.options,
|
|
2633
|
+
initialValue: resolution.initialValue
|
|
2634
|
+
}).then((response) => isCancel$1(response) ? exitCancelled("Operation cancelled") : response);
|
|
2635
|
+
}
|
|
2636
|
+
const WEB_FRAMEWORK_OPTIONS = [
|
|
2637
|
+
{
|
|
2638
|
+
value: "phoenix",
|
|
2639
|
+
label: "Phoenix",
|
|
2640
|
+
hint: "Conventional Phoenix web application"
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
value: "phoenix-live-view",
|
|
2644
|
+
label: "Phoenix LiveView",
|
|
2645
|
+
hint: "Server-rendered realtime UI"
|
|
2646
|
+
},
|
|
2647
|
+
{
|
|
2648
|
+
value: "none",
|
|
2649
|
+
label: "None",
|
|
2650
|
+
hint: "No Elixir web framework"
|
|
2651
|
+
}
|
|
2652
|
+
];
|
|
2653
|
+
const ORM_OPTIONS = [
|
|
2654
|
+
{
|
|
2655
|
+
value: "ecto-sql",
|
|
2656
|
+
label: "Ecto SQL",
|
|
2657
|
+
hint: "Ecto plus SQL adapters and migrations"
|
|
2658
|
+
},
|
|
2659
|
+
{
|
|
2660
|
+
value: "ecto",
|
|
2661
|
+
label: "Ecto",
|
|
2662
|
+
hint: "Ecto schemas and changesets without SQL repo wiring"
|
|
2663
|
+
},
|
|
2664
|
+
{
|
|
2665
|
+
value: "none",
|
|
2666
|
+
label: "None",
|
|
2667
|
+
hint: "No database layer"
|
|
2668
|
+
}
|
|
2669
|
+
];
|
|
2670
|
+
const AUTH_OPTIONS = [
|
|
2671
|
+
{
|
|
2672
|
+
value: "phx-gen-auth",
|
|
2673
|
+
label: "phx.gen.auth",
|
|
2674
|
+
hint: "Phoenix account/session scaffold"
|
|
2675
|
+
},
|
|
2676
|
+
{
|
|
2677
|
+
value: "ueberauth",
|
|
2678
|
+
label: "Ueberauth",
|
|
2679
|
+
hint: "OAuth strategy foundation"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
value: "guardian",
|
|
2683
|
+
label: "Guardian",
|
|
2684
|
+
hint: "JWT authentication foundation"
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
value: "none",
|
|
2688
|
+
label: "None",
|
|
2689
|
+
hint: "No auth layer"
|
|
2690
|
+
}
|
|
2691
|
+
];
|
|
2692
|
+
const API_OPTIONS = [
|
|
2693
|
+
{
|
|
2694
|
+
value: "rest",
|
|
2695
|
+
label: "Phoenix REST",
|
|
2696
|
+
hint: "Controllers and JSON endpoints"
|
|
2697
|
+
},
|
|
2698
|
+
{
|
|
2699
|
+
value: "absinthe",
|
|
2700
|
+
label: "Absinthe GraphQL",
|
|
2701
|
+
hint: "GraphQL schema and resolvers"
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
value: "none",
|
|
2705
|
+
label: "None",
|
|
2706
|
+
hint: "No API layer"
|
|
2707
|
+
}
|
|
2708
|
+
];
|
|
2709
|
+
const REALTIME_OPTIONS = [
|
|
2710
|
+
{
|
|
2711
|
+
value: "channels",
|
|
2712
|
+
label: "Phoenix Channels",
|
|
2713
|
+
hint: "WebSocket channel endpoint"
|
|
2714
|
+
},
|
|
2715
|
+
{
|
|
2716
|
+
value: "presence",
|
|
2717
|
+
label: "Phoenix Presence",
|
|
2718
|
+
hint: "Presence tracking over PubSub"
|
|
2719
|
+
},
|
|
2720
|
+
{
|
|
2721
|
+
value: "pubsub",
|
|
2722
|
+
label: "Phoenix PubSub",
|
|
2723
|
+
hint: "PubSub foundation only"
|
|
2724
|
+
},
|
|
2725
|
+
{
|
|
2726
|
+
value: "live-view-streams",
|
|
2727
|
+
label: "LiveView Streams",
|
|
2728
|
+
hint: "Realtime LiveView stream demo"
|
|
2729
|
+
},
|
|
2730
|
+
{
|
|
2731
|
+
value: "none",
|
|
2732
|
+
label: "None",
|
|
2733
|
+
hint: "No realtime feature"
|
|
2734
|
+
}
|
|
2735
|
+
];
|
|
2736
|
+
const JOB_OPTIONS = [
|
|
2737
|
+
{
|
|
2738
|
+
value: "oban",
|
|
2739
|
+
label: "Oban",
|
|
2740
|
+
hint: "PostgreSQL-backed jobs and workers"
|
|
2741
|
+
},
|
|
2742
|
+
{
|
|
2743
|
+
value: "quantum",
|
|
2744
|
+
label: "Quantum",
|
|
2745
|
+
hint: "Cron-like scheduler"
|
|
2746
|
+
},
|
|
2747
|
+
{
|
|
2748
|
+
value: "none",
|
|
2749
|
+
label: "None",
|
|
2750
|
+
hint: "No jobs layer"
|
|
2751
|
+
}
|
|
2752
|
+
];
|
|
2753
|
+
const VALIDATION_OPTIONS = [
|
|
2754
|
+
{
|
|
2755
|
+
value: "ecto-changesets",
|
|
2756
|
+
label: "Ecto Changesets",
|
|
2757
|
+
hint: "Data validation with Ecto"
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
value: "nimble-options",
|
|
2761
|
+
label: "NimbleOptions",
|
|
2762
|
+
hint: "Declarative option validation"
|
|
2763
|
+
},
|
|
2764
|
+
{
|
|
2765
|
+
value: "none",
|
|
2766
|
+
label: "None",
|
|
2767
|
+
hint: "No extra validation helper"
|
|
2768
|
+
}
|
|
2769
|
+
];
|
|
2770
|
+
const HTTP_OPTIONS = [
|
|
2771
|
+
{
|
|
2772
|
+
value: "req",
|
|
2773
|
+
label: "Req",
|
|
2774
|
+
hint: "High-level HTTP client"
|
|
2775
|
+
},
|
|
2776
|
+
{
|
|
2777
|
+
value: "finch",
|
|
2778
|
+
label: "Finch",
|
|
2779
|
+
hint: "Pooled HTTP client"
|
|
2780
|
+
},
|
|
2781
|
+
{
|
|
2782
|
+
value: "none",
|
|
2783
|
+
label: "None",
|
|
2784
|
+
hint: "No HTTP client"
|
|
2785
|
+
}
|
|
2786
|
+
];
|
|
2787
|
+
const JSON_OPTIONS = [{
|
|
2788
|
+
value: "jason",
|
|
2789
|
+
label: "Jason",
|
|
2790
|
+
hint: "Phoenix default JSON library"
|
|
2791
|
+
}, {
|
|
2792
|
+
value: "none",
|
|
2793
|
+
label: "None",
|
|
2794
|
+
hint: "No JSON library"
|
|
2795
|
+
}];
|
|
2796
|
+
const EMAIL_OPTIONS = [{
|
|
2797
|
+
value: "swoosh",
|
|
2798
|
+
label: "Swoosh",
|
|
2799
|
+
hint: "Phoenix email library"
|
|
2800
|
+
}, {
|
|
2801
|
+
value: "none",
|
|
2802
|
+
label: "None",
|
|
2803
|
+
hint: "No email library"
|
|
2804
|
+
}];
|
|
2805
|
+
const CACHING_OPTIONS = [
|
|
2806
|
+
{
|
|
2807
|
+
value: "cachex",
|
|
2808
|
+
label: "Cachex",
|
|
2809
|
+
hint: "In-memory cache"
|
|
2810
|
+
},
|
|
2811
|
+
{
|
|
2812
|
+
value: "nebulex",
|
|
2813
|
+
label: "Nebulex",
|
|
2814
|
+
hint: "Cache abstraction"
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
value: "none",
|
|
2818
|
+
label: "None",
|
|
2819
|
+
hint: "No cache layer"
|
|
2820
|
+
}
|
|
2821
|
+
];
|
|
2822
|
+
const OBSERVABILITY_OPTIONS = [
|
|
2823
|
+
{
|
|
2824
|
+
value: "telemetry",
|
|
2825
|
+
label: "Telemetry",
|
|
2826
|
+
hint: "Phoenix telemetry metrics"
|
|
2827
|
+
},
|
|
2828
|
+
{
|
|
2829
|
+
value: "opentelemetry",
|
|
2830
|
+
label: "OpenTelemetry",
|
|
2831
|
+
hint: "Distributed tracing foundation"
|
|
2832
|
+
},
|
|
2833
|
+
{
|
|
2834
|
+
value: "prom_ex",
|
|
2835
|
+
label: "PromEx",
|
|
2836
|
+
hint: "Prometheus metrics for Phoenix"
|
|
2837
|
+
},
|
|
2838
|
+
{
|
|
2839
|
+
value: "none",
|
|
2840
|
+
label: "None",
|
|
2841
|
+
hint: "No observability add-on"
|
|
2842
|
+
}
|
|
2843
|
+
];
|
|
2844
|
+
const TESTING_OPTIONS = [
|
|
2845
|
+
{
|
|
2846
|
+
value: "ex_unit",
|
|
2847
|
+
label: "ExUnit",
|
|
2848
|
+
hint: "Standard Elixir tests"
|
|
2849
|
+
},
|
|
2850
|
+
{
|
|
2851
|
+
value: "mox",
|
|
2852
|
+
label: "Mox",
|
|
2853
|
+
hint: "Concurrent-safe mocks"
|
|
2854
|
+
},
|
|
2855
|
+
{
|
|
2856
|
+
value: "bypass",
|
|
2857
|
+
label: "Bypass",
|
|
2858
|
+
hint: "External HTTP service fakes"
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
value: "wallaby",
|
|
2862
|
+
label: "Wallaby",
|
|
2863
|
+
hint: "Browser acceptance testing"
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
value: "none",
|
|
2867
|
+
label: "None",
|
|
2868
|
+
hint: "No extra test library"
|
|
2869
|
+
}
|
|
2870
|
+
];
|
|
2871
|
+
const QUALITY_OPTIONS = [
|
|
2872
|
+
{
|
|
2873
|
+
value: "credo",
|
|
2874
|
+
label: "Credo",
|
|
2875
|
+
hint: "Static code analysis"
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
value: "dialyxir",
|
|
2879
|
+
label: "Dialyxir",
|
|
2880
|
+
hint: "Dialyzer integration"
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
value: "sobelow",
|
|
2884
|
+
label: "Sobelow",
|
|
2885
|
+
hint: "Phoenix security analysis"
|
|
2886
|
+
},
|
|
2887
|
+
{
|
|
2888
|
+
value: "none",
|
|
2889
|
+
label: "None",
|
|
2890
|
+
hint: "No code quality tool"
|
|
2891
|
+
}
|
|
2892
|
+
];
|
|
2893
|
+
const DEPLOY_OPTIONS = [
|
|
2894
|
+
{
|
|
2895
|
+
value: "docker",
|
|
2896
|
+
label: "Docker",
|
|
2897
|
+
hint: "Dockerfile for Phoenix releases"
|
|
2898
|
+
},
|
|
2899
|
+
{
|
|
2900
|
+
value: "fly",
|
|
2901
|
+
label: "Fly.io",
|
|
2902
|
+
hint: "Fly.io release config"
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
value: "gigalixir",
|
|
2906
|
+
label: "Gigalixir",
|
|
2907
|
+
hint: "Gigalixir Procfile and notes"
|
|
2908
|
+
},
|
|
2909
|
+
{
|
|
2910
|
+
value: "mix-release",
|
|
2911
|
+
label: "Mix Release",
|
|
2912
|
+
hint: "Release-ready runtime config"
|
|
2913
|
+
},
|
|
2914
|
+
{
|
|
2915
|
+
value: "none",
|
|
2916
|
+
label: "None",
|
|
2917
|
+
hint: "No deploy files"
|
|
2918
|
+
}
|
|
2919
|
+
];
|
|
2920
|
+
const getElixirWebFrameworkChoice = (value) => makeChoice("Select Elixir web framework", WEB_FRAMEWORK_OPTIONS, "phoenix", value);
|
|
2921
|
+
const getElixirOrmChoice = (value) => makeChoice("Select Elixir database layer", ORM_OPTIONS, "ecto-sql", value);
|
|
2922
|
+
const getElixirAuthChoice = (value) => makeChoice("Select Elixir auth", AUTH_OPTIONS, "none", value);
|
|
2923
|
+
const getElixirApiChoice = (value) => makeChoice("Select Elixir API layer", API_OPTIONS, "rest", value);
|
|
2924
|
+
const getElixirRealtimeChoice = (value) => makeChoice("Select Elixir realtime feature", REALTIME_OPTIONS, "channels", value);
|
|
2925
|
+
const getElixirJobsChoice = (value) => makeChoice("Select Elixir jobs layer", JOB_OPTIONS, "none", value);
|
|
2926
|
+
const getElixirValidationChoice = (value) => makeChoice("Select Elixir validation", VALIDATION_OPTIONS, "ecto-changesets", value);
|
|
2927
|
+
const getElixirHttpChoice = (value) => makeChoice("Select Elixir HTTP client", HTTP_OPTIONS, "req", value);
|
|
2928
|
+
const getElixirJsonChoice = (value) => makeChoice("Select Elixir JSON library", JSON_OPTIONS, "jason", value);
|
|
2929
|
+
const getElixirEmailChoice = (value) => makeChoice("Select Elixir email library", EMAIL_OPTIONS, "none", value);
|
|
2930
|
+
const getElixirCachingChoice = (value) => makeChoice("Select Elixir caching", CACHING_OPTIONS, "none", value);
|
|
2931
|
+
const getElixirObservabilityChoice = (value) => makeChoice("Select Elixir observability", OBSERVABILITY_OPTIONS, "telemetry", value);
|
|
2932
|
+
const getElixirTestingChoice = (value) => makeChoice("Select Elixir testing", TESTING_OPTIONS, "ex_unit", value);
|
|
2933
|
+
const getElixirQualityChoice = (value) => makeChoice("Select Elixir code quality", QUALITY_OPTIONS, "credo", value);
|
|
2934
|
+
const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target", DEPLOY_OPTIONS, "none", value);
|
|
2935
|
+
|
|
2582
2936
|
//#endregion
|
|
2583
2937
|
//#region src/prompts/ecosystem.ts
|
|
2584
2938
|
async function getEcosystemChoice(ecosystem) {
|
|
@@ -2589,7 +2943,12 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
2589
2943
|
{
|
|
2590
2944
|
value: "typescript",
|
|
2591
2945
|
label: "TypeScript",
|
|
2592
|
-
hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
|
|
2946
|
+
hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
|
|
2947
|
+
},
|
|
2948
|
+
{
|
|
2949
|
+
value: "react-native",
|
|
2950
|
+
label: "React Native",
|
|
2951
|
+
hint: "Expo and React Native mobile apps with native integrations"
|
|
2593
2952
|
},
|
|
2594
2953
|
{
|
|
2595
2954
|
value: "rust",
|
|
@@ -2605,6 +2964,11 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
2605
2964
|
value: "go",
|
|
2606
2965
|
label: "Go",
|
|
2607
2966
|
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2967
|
+
},
|
|
2968
|
+
{
|
|
2969
|
+
value: "java",
|
|
2970
|
+
label: "Java",
|
|
2971
|
+
hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
|
|
2608
2972
|
}
|
|
2609
2973
|
],
|
|
2610
2974
|
initialValue: "typescript"
|
|
@@ -2693,6 +3057,12 @@ const EMAIL_PROMPT_OPTIONS = [
|
|
|
2693
3057
|
];
|
|
2694
3058
|
const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
|
|
2695
3059
|
function resolveEmailPrompt(context = {}) {
|
|
3060
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
3061
|
+
shouldPrompt: false,
|
|
3062
|
+
mode: "single",
|
|
3063
|
+
options: [],
|
|
3064
|
+
autoValue: "none"
|
|
3065
|
+
};
|
|
2696
3066
|
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
|
|
2697
3067
|
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
2698
3068
|
shouldPrompt: false,
|
|
@@ -2774,6 +3144,11 @@ async function getFileStorageChoice(fileStorage, backend) {
|
|
|
2774
3144
|
label: "Cloudflare R2",
|
|
2775
3145
|
hint: "S3-compatible storage with zero egress fees"
|
|
2776
3146
|
},
|
|
3147
|
+
{
|
|
3148
|
+
value: "cloudinary",
|
|
3149
|
+
label: "Cloudinary",
|
|
3150
|
+
hint: "Image and media storage with transformations"
|
|
3151
|
+
},
|
|
2777
3152
|
{
|
|
2778
3153
|
value: "none",
|
|
2779
3154
|
label: "None",
|
|
@@ -2866,6 +3241,7 @@ function resolveFormsPrompt(context = {}) {
|
|
|
2866
3241
|
"react-vite",
|
|
2867
3242
|
"tanstack-start",
|
|
2868
3243
|
"next",
|
|
3244
|
+
"vinext",
|
|
2869
3245
|
"redwood"
|
|
2870
3246
|
].includes(f));
|
|
2871
3247
|
const isSolid = web.includes("solid");
|
|
@@ -2949,6 +3325,11 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
|
|
|
2949
3325
|
label: "Next.js",
|
|
2950
3326
|
hint: "The React Framework for the Web"
|
|
2951
3327
|
},
|
|
3328
|
+
{
|
|
3329
|
+
value: "vinext",
|
|
3330
|
+
label: "Vinext",
|
|
3331
|
+
hint: "The Vite Compiler for Next.js"
|
|
3332
|
+
},
|
|
2952
3333
|
{
|
|
2953
3334
|
value: "nuxt",
|
|
2954
3335
|
label: "Nuxt",
|
|
@@ -3003,8 +3384,8 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
|
|
|
3003
3384
|
const NATIVE_FRONTEND_PROMPT_OPTIONS = [
|
|
3004
3385
|
{
|
|
3005
3386
|
value: "native-bare",
|
|
3006
|
-
label: "
|
|
3007
|
-
hint: "
|
|
3387
|
+
label: "StyleSheet",
|
|
3388
|
+
hint: "Expo with StyleSheet (no styling library)"
|
|
3008
3389
|
},
|
|
3009
3390
|
{
|
|
3010
3391
|
value: "native-uniwind",
|
|
@@ -3101,6 +3482,17 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
3101
3482
|
return result;
|
|
3102
3483
|
}
|
|
3103
3484
|
}
|
|
3485
|
+
async function getNativeFrontendChoice(frontendOptions) {
|
|
3486
|
+
if (frontendOptions !== void 0) return frontendOptions.filter((frontend) => frontend.startsWith("native-"));
|
|
3487
|
+
const nativeFramework = await navigableSelect({
|
|
3488
|
+
message: "Choose React Native app type",
|
|
3489
|
+
options: NATIVE_FRONTEND_PROMPT_OPTIONS,
|
|
3490
|
+
initialValue: "native-bare"
|
|
3491
|
+
});
|
|
3492
|
+
if (isGoBack(nativeFramework)) return GO_BACK_SYMBOL;
|
|
3493
|
+
if (isCancel$1(nativeFramework)) return exitCancelled("Operation cancelled");
|
|
3494
|
+
return [nativeFramework];
|
|
3495
|
+
}
|
|
3104
3496
|
|
|
3105
3497
|
//#endregion
|
|
3106
3498
|
//#region src/prompts/git.ts
|
|
@@ -3421,6 +3813,18 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
|
|
|
3421
3813
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3422
3814
|
return response$1;
|
|
3423
3815
|
}
|
|
3816
|
+
if (ecosystem === "elixir") {
|
|
3817
|
+
if (!await commandExists("mix")) {
|
|
3818
|
+
log.warn("Mix is not installed. Please install Elixir from https://elixir-lang.org/install.html");
|
|
3819
|
+
return false;
|
|
3820
|
+
}
|
|
3821
|
+
const response$1 = await navigableConfirm({
|
|
3822
|
+
message: "Run mix deps.get and mix compile?",
|
|
3823
|
+
initialValue: DEFAULT_CONFIG.install
|
|
3824
|
+
});
|
|
3825
|
+
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3826
|
+
return response$1;
|
|
3827
|
+
}
|
|
3424
3828
|
const response = await navigableConfirm({
|
|
3425
3829
|
message: "Install dependencies?",
|
|
3426
3830
|
initialValue: DEFAULT_CONFIG.install
|
|
@@ -3787,6 +4191,11 @@ const LOGGING_PROMPT_OPTIONS = [
|
|
|
3787
4191
|
label: "Winston",
|
|
3788
4192
|
hint: "Flexible logging library with multiple transports"
|
|
3789
4193
|
},
|
|
4194
|
+
{
|
|
4195
|
+
value: "evlog",
|
|
4196
|
+
label: "evlog",
|
|
4197
|
+
hint: "Typed event-based logging for TypeScript"
|
|
4198
|
+
},
|
|
3790
4199
|
{
|
|
3791
4200
|
value: "none",
|
|
3792
4201
|
label: "None",
|
|
@@ -3827,6 +4236,143 @@ async function getLoggingChoice(logging, backend) {
|
|
|
3827
4236
|
return response;
|
|
3828
4237
|
}
|
|
3829
4238
|
|
|
4239
|
+
//#endregion
|
|
4240
|
+
//#region src/prompts/mobile.ts
|
|
4241
|
+
const MOBILE_NAVIGATION_OPTIONS = [
|
|
4242
|
+
{
|
|
4243
|
+
value: "expo-router",
|
|
4244
|
+
label: "Expo Router",
|
|
4245
|
+
hint: "File-based routing for Expo apps"
|
|
4246
|
+
},
|
|
4247
|
+
{
|
|
4248
|
+
value: "react-navigation",
|
|
4249
|
+
label: "React Navigation",
|
|
4250
|
+
hint: "Code-defined native stacks and tabs"
|
|
4251
|
+
},
|
|
4252
|
+
{
|
|
4253
|
+
value: "none",
|
|
4254
|
+
label: "None",
|
|
4255
|
+
hint: "Skip navigation setup"
|
|
4256
|
+
}
|
|
4257
|
+
];
|
|
4258
|
+
const MOBILE_UI_OPTIONS = [
|
|
4259
|
+
{
|
|
4260
|
+
value: "none",
|
|
4261
|
+
label: "None",
|
|
4262
|
+
hint: "Use React Native primitives"
|
|
4263
|
+
},
|
|
4264
|
+
{
|
|
4265
|
+
value: "tamagui",
|
|
4266
|
+
label: "Tamagui",
|
|
4267
|
+
hint: "Universal themed UI primitives"
|
|
4268
|
+
},
|
|
4269
|
+
{
|
|
4270
|
+
value: "gluestack-ui",
|
|
4271
|
+
label: "Gluestack UI",
|
|
4272
|
+
hint: "Accessible cross-platform components"
|
|
4273
|
+
},
|
|
4274
|
+
{
|
|
4275
|
+
value: "uniwind",
|
|
4276
|
+
label: "Uniwind",
|
|
4277
|
+
hint: "Tailwind-style React Native styling"
|
|
4278
|
+
},
|
|
4279
|
+
{
|
|
4280
|
+
value: "unistyles",
|
|
4281
|
+
label: "Unistyles",
|
|
4282
|
+
hint: "Type-safe React Native stylesheets"
|
|
4283
|
+
}
|
|
4284
|
+
];
|
|
4285
|
+
const MOBILE_STORAGE_OPTIONS = [{
|
|
4286
|
+
value: "none",
|
|
4287
|
+
label: "None",
|
|
4288
|
+
hint: "Skip device storage helpers"
|
|
4289
|
+
}, {
|
|
4290
|
+
value: "mmkv",
|
|
4291
|
+
label: "MMKV",
|
|
4292
|
+
hint: "Fast encrypted key-value storage"
|
|
4293
|
+
}];
|
|
4294
|
+
const MOBILE_TESTING_OPTIONS = [
|
|
4295
|
+
{
|
|
4296
|
+
value: "none",
|
|
4297
|
+
label: "None",
|
|
4298
|
+
hint: "Skip mobile testing setup"
|
|
4299
|
+
},
|
|
4300
|
+
{
|
|
4301
|
+
value: "maestro",
|
|
4302
|
+
label: "Maestro",
|
|
4303
|
+
hint: "Mobile E2E flow files"
|
|
4304
|
+
},
|
|
4305
|
+
{
|
|
4306
|
+
value: "react-native-testing-library",
|
|
4307
|
+
label: "React Native Testing Library",
|
|
4308
|
+
hint: "Unit tests for native components"
|
|
4309
|
+
},
|
|
4310
|
+
{
|
|
4311
|
+
value: "maestro-react-native-testing-library",
|
|
4312
|
+
label: "Maestro + RN Testing Library",
|
|
4313
|
+
hint: "Mobile E2E flows and unit tests"
|
|
4314
|
+
}
|
|
4315
|
+
];
|
|
4316
|
+
const MOBILE_PUSH_OPTIONS = [{
|
|
4317
|
+
value: "none",
|
|
4318
|
+
label: "None",
|
|
4319
|
+
hint: "Skip push notification setup"
|
|
4320
|
+
}, {
|
|
4321
|
+
value: "expo-notifications",
|
|
4322
|
+
label: "Expo Notifications",
|
|
4323
|
+
hint: "Expo push token helper"
|
|
4324
|
+
}];
|
|
4325
|
+
const MOBILE_OTA_OPTIONS = [{
|
|
4326
|
+
value: "none",
|
|
4327
|
+
label: "None",
|
|
4328
|
+
hint: "Skip OTA update setup"
|
|
4329
|
+
}, {
|
|
4330
|
+
value: "expo-updates",
|
|
4331
|
+
label: "Expo Updates",
|
|
4332
|
+
hint: "Runtime version and update helper"
|
|
4333
|
+
}];
|
|
4334
|
+
const MOBILE_DEEP_LINKING_OPTIONS = [{
|
|
4335
|
+
value: "expo-linking",
|
|
4336
|
+
label: "Expo Linking",
|
|
4337
|
+
hint: "Scheme config and redirect URI helpers"
|
|
4338
|
+
}, {
|
|
4339
|
+
value: "none",
|
|
4340
|
+
label: "None",
|
|
4341
|
+
hint: "Skip deep link helpers"
|
|
4342
|
+
}];
|
|
4343
|
+
async function promptMobileOption(options, defaultValue, selected, message) {
|
|
4344
|
+
const resolution = createStaticSinglePromptResolution(options, defaultValue, selected);
|
|
4345
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? defaultValue;
|
|
4346
|
+
const response = await navigableSelect({
|
|
4347
|
+
message,
|
|
4348
|
+
options: resolution.options,
|
|
4349
|
+
initialValue: resolution.initialValue
|
|
4350
|
+
});
|
|
4351
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4352
|
+
return response;
|
|
4353
|
+
}
|
|
4354
|
+
function getMobileNavigationChoice(mobileNavigation) {
|
|
4355
|
+
return promptMobileOption(MOBILE_NAVIGATION_OPTIONS, "expo-router", mobileNavigation, "Select mobile navigation");
|
|
4356
|
+
}
|
|
4357
|
+
function getMobileUIChoice(mobileUI) {
|
|
4358
|
+
return promptMobileOption(MOBILE_UI_OPTIONS, "none", mobileUI, "Select mobile UI");
|
|
4359
|
+
}
|
|
4360
|
+
function getMobileStorageChoice(mobileStorage) {
|
|
4361
|
+
return promptMobileOption(MOBILE_STORAGE_OPTIONS, "none", mobileStorage, "Select mobile storage");
|
|
4362
|
+
}
|
|
4363
|
+
function getMobileTestingChoice(mobileTesting) {
|
|
4364
|
+
return promptMobileOption(MOBILE_TESTING_OPTIONS, "none", mobileTesting, "Select mobile testing");
|
|
4365
|
+
}
|
|
4366
|
+
function getMobilePushChoice(mobilePush) {
|
|
4367
|
+
return promptMobileOption(MOBILE_PUSH_OPTIONS, "none", mobilePush, "Select mobile push");
|
|
4368
|
+
}
|
|
4369
|
+
function getMobileOTAChoice(mobileOTA) {
|
|
4370
|
+
return promptMobileOption(MOBILE_OTA_OPTIONS, "none", mobileOTA, "Select mobile OTA updates");
|
|
4371
|
+
}
|
|
4372
|
+
function getMobileDeepLinkingChoice(mobileDeepLinking) {
|
|
4373
|
+
return promptMobileOption(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking, "Select mobile deep linking");
|
|
4374
|
+
}
|
|
4375
|
+
|
|
3830
4376
|
//#endregion
|
|
3831
4377
|
//#region src/prompts/navigable-group.ts
|
|
3832
4378
|
/**
|
|
@@ -3910,6 +4456,12 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
|
3910
4456
|
];
|
|
3911
4457
|
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
3912
4458
|
function resolveObservabilityPrompt(context = {}) {
|
|
4459
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
4460
|
+
shouldPrompt: false,
|
|
4461
|
+
mode: "single",
|
|
4462
|
+
options: [],
|
|
4463
|
+
autoValue: "none"
|
|
4464
|
+
};
|
|
3913
4465
|
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
3914
4466
|
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
3915
4467
|
shouldPrompt: false,
|
|
@@ -5006,6 +5558,12 @@ const SEARCH_PROMPT_OPTIONS = [
|
|
|
5006
5558
|
];
|
|
5007
5559
|
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
5008
5560
|
function resolveSearchPrompt(context = {}) {
|
|
5561
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
5562
|
+
shouldPrompt: false,
|
|
5563
|
+
mode: "single",
|
|
5564
|
+
options: [],
|
|
5565
|
+
autoValue: "none"
|
|
5566
|
+
};
|
|
5009
5567
|
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
5010
5568
|
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
5011
5569
|
shouldPrompt: false,
|
|
@@ -5479,6 +6037,7 @@ function resolveStateManagementPrompt(context = {}) {
|
|
|
5479
6037
|
"react-vite",
|
|
5480
6038
|
"tanstack-start",
|
|
5481
6039
|
"next",
|
|
6040
|
+
"vinext",
|
|
5482
6041
|
"redwood"
|
|
5483
6042
|
].includes(f));
|
|
5484
6043
|
const isFresh = web.includes("fresh");
|
|
@@ -5604,6 +6163,10 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
5604
6163
|
label: "shadcn/ui",
|
|
5605
6164
|
hint: "Beautifully designed components built with Radix UI and Tailwind CSS"
|
|
5606
6165
|
},
|
|
6166
|
+
"shadcn-svelte": {
|
|
6167
|
+
label: "shadcn-svelte",
|
|
6168
|
+
hint: "Svelte component collection styled with Tailwind CSS"
|
|
6169
|
+
},
|
|
5607
6170
|
daisyui: {
|
|
5608
6171
|
label: "daisyUI",
|
|
5609
6172
|
hint: "Tailwind CSS component library with semantic class names"
|
|
@@ -5771,6 +6334,7 @@ const WEB_FRAMEWORKS = [
|
|
|
5771
6334
|
"react-vite",
|
|
5772
6335
|
"tanstack-start",
|
|
5773
6336
|
"next",
|
|
6337
|
+
"vinext",
|
|
5774
6338
|
"nuxt",
|
|
5775
6339
|
"svelte",
|
|
5776
6340
|
"solid",
|
|
@@ -5830,6 +6394,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5830
6394
|
const result = await navigableGroup({
|
|
5831
6395
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
5832
6396
|
frontend: ({ results }) => {
|
|
6397
|
+
if (results.ecosystem === "react-native") return getNativeFrontendChoice(flags.frontend);
|
|
5833
6398
|
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
5834
6399
|
return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
|
|
5835
6400
|
},
|
|
@@ -5882,6 +6447,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5882
6447
|
},
|
|
5883
6448
|
auth: ({ results }) => {
|
|
5884
6449
|
if (results.ecosystem === "typescript") return getAuthChoice(flags.auth, results.backend, results.frontend, "typescript");
|
|
6450
|
+
if (results.ecosystem === "react-native") return Promise.resolve(flags.auth ?? "none");
|
|
5885
6451
|
if (results.ecosystem === "go") return getAuthChoice(flags.auth, void 0, void 0, "go");
|
|
5886
6452
|
return Promise.resolve("none");
|
|
5887
6453
|
},
|
|
@@ -5890,6 +6456,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5890
6456
|
return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
|
|
5891
6457
|
},
|
|
5892
6458
|
email: ({ results }) => {
|
|
6459
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
5893
6460
|
return getEmailChoice(flags.email, results.backend, results.ecosystem);
|
|
5894
6461
|
},
|
|
5895
6462
|
effect: ({ results }) => {
|
|
@@ -5961,6 +6528,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5961
6528
|
return getLoggingChoice(flags.logging, results.backend);
|
|
5962
6529
|
},
|
|
5963
6530
|
observability: ({ results }) => {
|
|
6531
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
5964
6532
|
return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
|
|
5965
6533
|
},
|
|
5966
6534
|
featureFlags: ({ results }) => {
|
|
@@ -5976,6 +6544,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5976
6544
|
return getCMSChoice(flags.cms, results.backend);
|
|
5977
6545
|
},
|
|
5978
6546
|
caching: ({ results }) => {
|
|
6547
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
5979
6548
|
return getCachingChoice(flags.caching, results.backend, results.ecosystem);
|
|
5980
6549
|
},
|
|
5981
6550
|
i18n: ({ results }) => {
|
|
@@ -5983,12 +6552,50 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5983
6552
|
return getI18nChoice(flags.i18n, results.frontend);
|
|
5984
6553
|
},
|
|
5985
6554
|
search: ({ results }) => {
|
|
6555
|
+
if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
|
|
5986
6556
|
return getSearchChoice(flags.search, results.backend, results.ecosystem);
|
|
5987
6557
|
},
|
|
5988
6558
|
fileStorage: ({ results }) => {
|
|
5989
6559
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5990
6560
|
return getFileStorageChoice(flags.fileStorage, results.backend);
|
|
5991
6561
|
},
|
|
6562
|
+
mobileNavigation: ({ results }) => {
|
|
6563
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6564
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6565
|
+
return getMobileNavigationChoice(flags.mobileNavigation);
|
|
6566
|
+
},
|
|
6567
|
+
mobileUI: ({ results }) => {
|
|
6568
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6569
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6570
|
+
if (results.frontend.includes("native-uniwind")) return Promise.resolve("uniwind");
|
|
6571
|
+
if (results.frontend.includes("native-unistyles")) return Promise.resolve("unistyles");
|
|
6572
|
+
return getMobileUIChoice(flags.mobileUI);
|
|
6573
|
+
},
|
|
6574
|
+
mobileStorage: ({ results }) => {
|
|
6575
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6576
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6577
|
+
return getMobileStorageChoice(flags.mobileStorage);
|
|
6578
|
+
},
|
|
6579
|
+
mobileTesting: ({ results }) => {
|
|
6580
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6581
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6582
|
+
return getMobileTestingChoice(flags.mobileTesting);
|
|
6583
|
+
},
|
|
6584
|
+
mobilePush: ({ results }) => {
|
|
6585
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6586
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6587
|
+
return getMobilePushChoice(flags.mobilePush);
|
|
6588
|
+
},
|
|
6589
|
+
mobileOTA: ({ results }) => {
|
|
6590
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6591
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6592
|
+
return getMobileOTAChoice(flags.mobileOTA);
|
|
6593
|
+
},
|
|
6594
|
+
mobileDeepLinking: ({ results }) => {
|
|
6595
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6596
|
+
if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
|
|
6597
|
+
return getMobileDeepLinkingChoice(flags.mobileDeepLinking);
|
|
6598
|
+
},
|
|
5992
6599
|
rustWebFramework: ({ results }) => {
|
|
5993
6600
|
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
5994
6601
|
return getRustWebFrameworkChoice(flags.rustWebFramework);
|
|
@@ -6118,10 +6725,70 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6118
6725
|
if (results.javaBuildTool === "none") return Promise.resolve([]);
|
|
6119
6726
|
return getJavaTestingLibrariesChoice(flags.javaTestingLibraries);
|
|
6120
6727
|
},
|
|
6728
|
+
elixirWebFramework: ({ results }) => {
|
|
6729
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6730
|
+
return getElixirWebFrameworkChoice(flags.elixirWebFramework);
|
|
6731
|
+
},
|
|
6732
|
+
elixirOrm: ({ results }) => {
|
|
6733
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6734
|
+
return getElixirOrmChoice(flags.elixirOrm);
|
|
6735
|
+
},
|
|
6736
|
+
elixirAuth: ({ results }) => {
|
|
6737
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6738
|
+
return getElixirAuthChoice(flags.elixirAuth);
|
|
6739
|
+
},
|
|
6740
|
+
elixirApi: ({ results }) => {
|
|
6741
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6742
|
+
return getElixirApiChoice(flags.elixirApi);
|
|
6743
|
+
},
|
|
6744
|
+
elixirRealtime: ({ results }) => {
|
|
6745
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6746
|
+
return getElixirRealtimeChoice(flags.elixirRealtime);
|
|
6747
|
+
},
|
|
6748
|
+
elixirJobs: ({ results }) => {
|
|
6749
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6750
|
+
return getElixirJobsChoice(flags.elixirJobs);
|
|
6751
|
+
},
|
|
6752
|
+
elixirValidation: ({ results }) => {
|
|
6753
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6754
|
+
return getElixirValidationChoice(flags.elixirValidation);
|
|
6755
|
+
},
|
|
6756
|
+
elixirHttp: ({ results }) => {
|
|
6757
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6758
|
+
return getElixirHttpChoice(flags.elixirHttp);
|
|
6759
|
+
},
|
|
6760
|
+
elixirJson: ({ results }) => {
|
|
6761
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6762
|
+
return getElixirJsonChoice(flags.elixirJson);
|
|
6763
|
+
},
|
|
6764
|
+
elixirEmail: ({ results }) => {
|
|
6765
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6766
|
+
return getElixirEmailChoice(flags.elixirEmail);
|
|
6767
|
+
},
|
|
6768
|
+
elixirCaching: ({ results }) => {
|
|
6769
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6770
|
+
return getElixirCachingChoice(flags.elixirCaching);
|
|
6771
|
+
},
|
|
6772
|
+
elixirObservability: ({ results }) => {
|
|
6773
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6774
|
+
return getElixirObservabilityChoice(flags.elixirObservability);
|
|
6775
|
+
},
|
|
6776
|
+
elixirTesting: ({ results }) => {
|
|
6777
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6778
|
+
return getElixirTestingChoice(flags.elixirTesting);
|
|
6779
|
+
},
|
|
6780
|
+
elixirQuality: ({ results }) => {
|
|
6781
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6782
|
+
return getElixirQualityChoice(flags.elixirQuality);
|
|
6783
|
+
},
|
|
6784
|
+
elixirDeploy: ({ results }) => {
|
|
6785
|
+
if (results.ecosystem !== "elixir") return Promise.resolve("none");
|
|
6786
|
+
return getElixirDeployChoice(flags.elixirDeploy);
|
|
6787
|
+
},
|
|
6121
6788
|
aiDocs: () => getAiDocsChoice(flags.aiDocs),
|
|
6122
6789
|
git: () => getGitChoice(flags.git),
|
|
6123
6790
|
packageManager: ({ results }) => {
|
|
6124
|
-
if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
|
|
6791
|
+
if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java" || results.ecosystem === "elixir") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
|
|
6125
6792
|
return getPackageManagerChoice(flags.packageManager);
|
|
6126
6793
|
},
|
|
6127
6794
|
install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
|
|
@@ -6170,6 +6837,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6170
6837
|
i18n: result.i18n,
|
|
6171
6838
|
search: result.search,
|
|
6172
6839
|
fileStorage: result.fileStorage,
|
|
6840
|
+
mobileNavigation: result.mobileNavigation,
|
|
6841
|
+
mobileUI: result.mobileUI,
|
|
6842
|
+
mobileStorage: result.mobileStorage,
|
|
6843
|
+
mobileTesting: result.mobileTesting,
|
|
6844
|
+
mobilePush: result.mobilePush,
|
|
6845
|
+
mobileOTA: result.mobileOTA,
|
|
6846
|
+
mobileDeepLinking: result.mobileDeepLinking,
|
|
6173
6847
|
ecosystem: result.ecosystem,
|
|
6174
6848
|
rustWebFramework: result.rustWebFramework,
|
|
6175
6849
|
rustFrontend: result.rustFrontend,
|
|
@@ -6202,6 +6876,21 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6202
6876
|
javaAuth: result.javaAuth,
|
|
6203
6877
|
javaLibraries: result.javaLibraries,
|
|
6204
6878
|
javaTestingLibraries: result.javaTestingLibraries,
|
|
6879
|
+
elixirWebFramework: result.elixirWebFramework,
|
|
6880
|
+
elixirOrm: result.elixirOrm,
|
|
6881
|
+
elixirAuth: result.elixirAuth,
|
|
6882
|
+
elixirApi: result.elixirApi,
|
|
6883
|
+
elixirRealtime: result.elixirRealtime,
|
|
6884
|
+
elixirJobs: result.elixirJobs,
|
|
6885
|
+
elixirValidation: result.elixirValidation,
|
|
6886
|
+
elixirHttp: result.elixirHttp,
|
|
6887
|
+
elixirJson: result.elixirJson,
|
|
6888
|
+
elixirEmail: result.elixirEmail,
|
|
6889
|
+
elixirCaching: result.elixirCaching,
|
|
6890
|
+
elixirObservability: result.elixirObservability,
|
|
6891
|
+
elixirTesting: result.elixirTesting,
|
|
6892
|
+
elixirQuality: result.elixirQuality,
|
|
6893
|
+
elixirDeploy: result.elixirDeploy,
|
|
6205
6894
|
aiDocs: result.aiDocs
|
|
6206
6895
|
};
|
|
6207
6896
|
}
|
|
@@ -6358,7 +7047,17 @@ function displayConfig(config) {
|
|
|
6358
7047
|
if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
|
|
6359
7048
|
if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
|
|
6360
7049
|
if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
|
|
7050
|
+
if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
|
|
7051
|
+
if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
|
|
7052
|
+
if (config.mobileNavigation !== void 0) configDisplay.push(`${pc.blue("Mobile Navigation:")} ${String(config.mobileNavigation)}`);
|
|
7053
|
+
if (config.mobileUI !== void 0) configDisplay.push(`${pc.blue("Mobile UI:")} ${String(config.mobileUI)}`);
|
|
7054
|
+
if (config.mobileStorage !== void 0) configDisplay.push(`${pc.blue("Mobile Storage:")} ${String(config.mobileStorage)}`);
|
|
7055
|
+
if (config.mobileTesting !== void 0) configDisplay.push(`${pc.blue("Mobile Testing:")} ${String(config.mobileTesting)}`);
|
|
7056
|
+
if (config.mobilePush !== void 0) configDisplay.push(`${pc.blue("Mobile Push:")} ${String(config.mobilePush)}`);
|
|
7057
|
+
if (config.mobileOTA !== void 0) configDisplay.push(`${pc.blue("Mobile OTA:")} ${String(config.mobileOTA)}`);
|
|
7058
|
+
if (config.mobileDeepLinking !== void 0) configDisplay.push(`${pc.blue("Mobile Deep Linking:")} ${String(config.mobileDeepLinking)}`);
|
|
6361
7059
|
if (config.caching !== void 0) configDisplay.push(`${pc.blue("Caching:")} ${String(config.caching)}`);
|
|
7060
|
+
if (config.i18n !== void 0) configDisplay.push(`${pc.blue("i18n:")} ${String(config.i18n)}`);
|
|
6362
7061
|
if (config.cms !== void 0) configDisplay.push(`${pc.blue("CMS:")} ${String(config.cms)}`);
|
|
6363
7062
|
if (config.search !== void 0) configDisplay.push(`${pc.blue("Search:")} ${String(config.search)}`);
|
|
6364
7063
|
if (config.fileStorage !== void 0) configDisplay.push(`${pc.blue("File Storage:")} ${String(config.fileStorage)}`);
|
|
@@ -6372,6 +7071,11 @@ function displayConfig(config) {
|
|
|
6372
7071
|
const examplesText = examples.length > 0 && examples[0] !== void 0 ? examples.join(", ") : "none";
|
|
6373
7072
|
configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`);
|
|
6374
7073
|
}
|
|
7074
|
+
if (config.aiDocs !== void 0) {
|
|
7075
|
+
const aiDocs = Array.isArray(config.aiDocs) ? config.aiDocs : [config.aiDocs];
|
|
7076
|
+
const aiDocsText = aiDocs.length > 0 && aiDocs[0] !== void 0 ? aiDocs.join(", ") : "none";
|
|
7077
|
+
configDisplay.push(`${pc.blue("AI Docs:")} ${aiDocsText}`);
|
|
7078
|
+
}
|
|
6375
7079
|
if (config.git !== void 0) {
|
|
6376
7080
|
const gitText = typeof config.git === "boolean" ? config.git ? "Yes" : "No" : String(config.git);
|
|
6377
7081
|
configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
|
|
@@ -6465,6 +7169,13 @@ function getTypeScriptFlags(config) {
|
|
|
6465
7169
|
flags.push(`--cms ${config.cms}`);
|
|
6466
7170
|
flags.push(`--search ${config.search}`);
|
|
6467
7171
|
flags.push(`--file-storage ${config.fileStorage}`);
|
|
7172
|
+
flags.push(`--mobile-navigation ${config.mobileNavigation}`);
|
|
7173
|
+
flags.push(`--mobile-ui ${config.mobileUI}`);
|
|
7174
|
+
flags.push(`--mobile-storage ${config.mobileStorage}`);
|
|
7175
|
+
flags.push(`--mobile-testing ${config.mobileTesting}`);
|
|
7176
|
+
flags.push(`--mobile-push ${config.mobilePush}`);
|
|
7177
|
+
flags.push(`--mobile-ota ${config.mobileOTA}`);
|
|
7178
|
+
flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
|
|
6468
7179
|
if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
|
|
6469
7180
|
else flags.push("--addons none");
|
|
6470
7181
|
if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
|
|
@@ -6475,6 +7186,21 @@ function getTypeScriptFlags(config) {
|
|
|
6475
7186
|
appendCommonFlags(flags, config);
|
|
6476
7187
|
return flags;
|
|
6477
7188
|
}
|
|
7189
|
+
function getReactNativeFlags(config) {
|
|
7190
|
+
const flags = ["--ecosystem react-native"];
|
|
7191
|
+
if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
|
|
7192
|
+
else flags.push("--frontend native-bare");
|
|
7193
|
+
flags.push(`--auth ${config.auth}`);
|
|
7194
|
+
flags.push(`--mobile-navigation ${config.mobileNavigation}`);
|
|
7195
|
+
flags.push(`--mobile-ui ${config.mobileUI}`);
|
|
7196
|
+
flags.push(`--mobile-storage ${config.mobileStorage}`);
|
|
7197
|
+
flags.push(`--mobile-testing ${config.mobileTesting}`);
|
|
7198
|
+
flags.push(`--mobile-push ${config.mobilePush}`);
|
|
7199
|
+
flags.push(`--mobile-ota ${config.mobileOTA}`);
|
|
7200
|
+
flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
|
|
7201
|
+
appendCommonFlags(flags, config);
|
|
7202
|
+
return flags;
|
|
7203
|
+
}
|
|
6478
7204
|
function getRustFlags(config) {
|
|
6479
7205
|
const flags = ["--ecosystem rust"];
|
|
6480
7206
|
flags.push(`--rust-web-framework ${config.rustWebFramework}`);
|
|
@@ -6531,9 +7257,32 @@ function getJavaFlags(config) {
|
|
|
6531
7257
|
appendCommonFlags(flags, config);
|
|
6532
7258
|
return flags;
|
|
6533
7259
|
}
|
|
7260
|
+
function getElixirFlags(config) {
|
|
7261
|
+
const flags = ["--ecosystem elixir"];
|
|
7262
|
+
flags.push(`--elixir-web-framework ${config.elixirWebFramework}`);
|
|
7263
|
+
flags.push(`--elixir-orm ${config.elixirOrm}`);
|
|
7264
|
+
flags.push(`--elixir-auth ${config.elixirAuth}`);
|
|
7265
|
+
flags.push(`--elixir-api ${config.elixirApi}`);
|
|
7266
|
+
flags.push(`--elixir-realtime ${config.elixirRealtime}`);
|
|
7267
|
+
flags.push(`--elixir-jobs ${config.elixirJobs}`);
|
|
7268
|
+
flags.push(`--elixir-validation ${config.elixirValidation}`);
|
|
7269
|
+
flags.push(`--elixir-http ${config.elixirHttp}`);
|
|
7270
|
+
flags.push(`--elixir-json ${config.elixirJson}`);
|
|
7271
|
+
flags.push(`--elixir-email ${config.elixirEmail}`);
|
|
7272
|
+
flags.push(`--elixir-caching ${config.elixirCaching}`);
|
|
7273
|
+
flags.push(`--elixir-observability ${config.elixirObservability}`);
|
|
7274
|
+
flags.push(`--elixir-testing ${config.elixirTesting}`);
|
|
7275
|
+
flags.push(`--elixir-quality ${config.elixirQuality}`);
|
|
7276
|
+
flags.push(`--elixir-deploy ${config.elixirDeploy}`);
|
|
7277
|
+
appendCommonFlags(flags, config);
|
|
7278
|
+
return flags;
|
|
7279
|
+
}
|
|
6534
7280
|
function generateReproducibleCommand(config) {
|
|
6535
7281
|
let flags;
|
|
6536
7282
|
switch (config.ecosystem) {
|
|
7283
|
+
case "react-native":
|
|
7284
|
+
flags = getReactNativeFlags(config);
|
|
7285
|
+
break;
|
|
6537
7286
|
case "rust":
|
|
6538
7287
|
flags = getRustFlags(config);
|
|
6539
7288
|
break;
|
|
@@ -6546,6 +7295,9 @@ function generateReproducibleCommand(config) {
|
|
|
6546
7295
|
case "java":
|
|
6547
7296
|
flags = getJavaFlags(config);
|
|
6548
7297
|
break;
|
|
7298
|
+
case "elixir":
|
|
7299
|
+
flags = getElixirFlags(config);
|
|
7300
|
+
break;
|
|
6549
7301
|
case "typescript":
|
|
6550
7302
|
default:
|
|
6551
7303
|
flags = getTypeScriptFlags(config);
|
|
@@ -6768,7 +7520,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
|
|
|
6768
7520
|
}],
|
|
6769
7521
|
conflictsWithOptions: [{
|
|
6770
7522
|
optionKey: "frontend",
|
|
6771
|
-
values: ["next"]
|
|
7523
|
+
values: ["next", "vinext"]
|
|
6772
7524
|
}]
|
|
6773
7525
|
},
|
|
6774
7526
|
{
|
|
@@ -6832,7 +7584,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
|
|
|
6832
7584
|
}],
|
|
6833
7585
|
conflictsWithOptions: [{
|
|
6834
7586
|
optionKey: "frontend",
|
|
6835
|
-
values: ["next"]
|
|
7587
|
+
values: ["next", "vinext"]
|
|
6836
7588
|
}]
|
|
6837
7589
|
}
|
|
6838
7590
|
];
|
|
@@ -7173,6 +7925,18 @@ function validateBackendConstraints(config, providedFlags, options) {
|
|
|
7173
7925
|
function validateFrontendConstraints(config, providedFlags) {
|
|
7174
7926
|
const { frontend } = config;
|
|
7175
7927
|
if (frontend && frontend.length > 0) {
|
|
7928
|
+
if (config.ecosystem === "react-native" && frontend.some((item) => !item.startsWith("native-") && item !== "none")) incompatibilityError({
|
|
7929
|
+
message: "React Native ecosystem only supports native Expo frontends.",
|
|
7930
|
+
provided: {
|
|
7931
|
+
ecosystem: "react-native",
|
|
7932
|
+
frontend: frontend.join(" ")
|
|
7933
|
+
},
|
|
7934
|
+
suggestions: [
|
|
7935
|
+
"Use --frontend native-bare",
|
|
7936
|
+
"Use --frontend native-uniwind",
|
|
7937
|
+
"Use --frontend native-unistyles"
|
|
7938
|
+
]
|
|
7939
|
+
});
|
|
7176
7940
|
ensureSingleWebAndNative(frontend);
|
|
7177
7941
|
if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
|
|
7178
7942
|
}
|
|
@@ -7216,6 +7980,142 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
|
|
|
7216
7980
|
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle to enable JUnit/Mockito/Testcontainers", "Set --java-testing-libraries none for a source-only plain Java scaffold"]
|
|
7217
7981
|
});
|
|
7218
7982
|
}
|
|
7983
|
+
function validateElixirConstraints(config) {
|
|
7984
|
+
if (config.ecosystem !== "elixir") return;
|
|
7985
|
+
const hasPhoenix = config.elixirWebFramework !== "none";
|
|
7986
|
+
const hasEcto = config.elixirOrm !== "none";
|
|
7987
|
+
const unsupportedSelections = [
|
|
7988
|
+
{
|
|
7989
|
+
flag: "elixir-orm",
|
|
7990
|
+
value: config.elixirOrm,
|
|
7991
|
+
unsupported: ["ecto"],
|
|
7992
|
+
message: "Plain Ecto without SQL Repo wiring is not generated yet.",
|
|
7993
|
+
suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-orm none"]
|
|
7994
|
+
},
|
|
7995
|
+
{
|
|
7996
|
+
flag: "elixir-auth",
|
|
7997
|
+
value: config.elixirAuth,
|
|
7998
|
+
unsupported: ["ueberauth", "guardian"],
|
|
7999
|
+
message: "Only phx.gen.auth currently generates Phoenix auth files.",
|
|
8000
|
+
suggestions: ["Use --elixir-auth phx-gen-auth", "Use --elixir-auth none"]
|
|
8001
|
+
},
|
|
8002
|
+
{
|
|
8003
|
+
flag: "elixir-validation",
|
|
8004
|
+
value: config.elixirValidation,
|
|
8005
|
+
unsupported: ["nimble-options"],
|
|
8006
|
+
message: "NimbleOptions is not generated yet.",
|
|
8007
|
+
suggestions: ["Use --elixir-validation ecto-changesets", "Use --elixir-validation none"]
|
|
8008
|
+
},
|
|
8009
|
+
{
|
|
8010
|
+
flag: "elixir-caching",
|
|
8011
|
+
value: config.elixirCaching,
|
|
8012
|
+
unsupported: ["nebulex"],
|
|
8013
|
+
message: "Nebulex cache modules are not generated yet.",
|
|
8014
|
+
suggestions: ["Use --elixir-caching cachex", "Use --elixir-caching none"]
|
|
8015
|
+
},
|
|
8016
|
+
{
|
|
8017
|
+
flag: "elixir-observability",
|
|
8018
|
+
value: config.elixirObservability,
|
|
8019
|
+
unsupported: ["opentelemetry", "prom_ex"],
|
|
8020
|
+
message: "OpenTelemetry and PromEx setup are not generated yet.",
|
|
8021
|
+
suggestions: ["Use --elixir-observability telemetry", "Use --elixir-observability none"]
|
|
8022
|
+
},
|
|
8023
|
+
{
|
|
8024
|
+
flag: "elixir-testing",
|
|
8025
|
+
value: config.elixirTesting,
|
|
8026
|
+
unsupported: [
|
|
8027
|
+
"mox",
|
|
8028
|
+
"bypass",
|
|
8029
|
+
"wallaby"
|
|
8030
|
+
],
|
|
8031
|
+
message: "Generated Phoenix projects currently include ExUnit tests only.",
|
|
8032
|
+
suggestions: ["Use --elixir-testing ex_unit"]
|
|
8033
|
+
},
|
|
8034
|
+
{
|
|
8035
|
+
flag: "elixir-deploy",
|
|
8036
|
+
value: config.elixirDeploy,
|
|
8037
|
+
unsupported: ["fly", "gigalixir"],
|
|
8038
|
+
message: "Fly.io and Gigalixir config files are not generated yet.",
|
|
8039
|
+
suggestions: ["Use --elixir-deploy docker", "Use --elixir-deploy mix-release"]
|
|
8040
|
+
}
|
|
8041
|
+
];
|
|
8042
|
+
for (const selection of unsupportedSelections) if (selection.value && selection.unsupported.includes(selection.value)) incompatibilityError({
|
|
8043
|
+
message: selection.message,
|
|
8044
|
+
provided: { [selection.flag]: selection.value },
|
|
8045
|
+
suggestions: selection.suggestions
|
|
8046
|
+
});
|
|
8047
|
+
if (!hasPhoenix) {
|
|
8048
|
+
const phoenixOnlySelections = [
|
|
8049
|
+
{
|
|
8050
|
+
flag: "elixir-auth",
|
|
8051
|
+
value: config.elixirAuth,
|
|
8052
|
+
message: "Elixir auth scaffolds require Phoenix."
|
|
8053
|
+
},
|
|
8054
|
+
{
|
|
8055
|
+
flag: "elixir-api",
|
|
8056
|
+
value: config.elixirApi,
|
|
8057
|
+
message: "Elixir API scaffolds require Phoenix."
|
|
8058
|
+
},
|
|
8059
|
+
{
|
|
8060
|
+
flag: "elixir-realtime",
|
|
8061
|
+
value: config.elixirRealtime,
|
|
8062
|
+
message: "Elixir realtime scaffolds require Phoenix."
|
|
8063
|
+
}
|
|
8064
|
+
];
|
|
8065
|
+
for (const selection of phoenixOnlySelections) {
|
|
8066
|
+
if (!selection.value || selection.value === "none") continue;
|
|
8067
|
+
incompatibilityError({
|
|
8068
|
+
message: selection.message,
|
|
8069
|
+
provided: {
|
|
8070
|
+
"elixir-web-framework": config.elixirWebFramework ?? "none",
|
|
8071
|
+
[selection.flag]: selection.value
|
|
8072
|
+
},
|
|
8073
|
+
suggestions: [
|
|
8074
|
+
"Use --elixir-web-framework phoenix",
|
|
8075
|
+
"Use --elixir-web-framework phoenix-live-view",
|
|
8076
|
+
`Use --${selection.flag} none`
|
|
8077
|
+
]
|
|
8078
|
+
});
|
|
8079
|
+
}
|
|
8080
|
+
}
|
|
8081
|
+
if (hasPhoenix && config.elixirJson === "none") incompatibilityError({
|
|
8082
|
+
message: "Phoenix JSON scaffolds require Jason.",
|
|
8083
|
+
provided: { "elixir-json": "none" },
|
|
8084
|
+
suggestions: ["Use --elixir-json jason"]
|
|
8085
|
+
});
|
|
8086
|
+
if (config.elixirAuth === "phx-gen-auth" && !hasEcto) incompatibilityError({
|
|
8087
|
+
message: "phx.gen.auth requires Ecto in the generated Phoenix scaffold.",
|
|
8088
|
+
provided: {
|
|
8089
|
+
"elixir-auth": "phx-gen-auth",
|
|
8090
|
+
"elixir-orm": config.elixirOrm ?? "none"
|
|
8091
|
+
},
|
|
8092
|
+
suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-auth none"]
|
|
8093
|
+
});
|
|
8094
|
+
if (config.elixirJobs === "oban" && config.elixirOrm !== "ecto-sql") incompatibilityError({
|
|
8095
|
+
message: "Oban requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
|
|
8096
|
+
provided: {
|
|
8097
|
+
"elixir-jobs": "oban",
|
|
8098
|
+
"elixir-orm": config.elixirOrm ?? "none"
|
|
8099
|
+
},
|
|
8100
|
+
suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-jobs none"]
|
|
8101
|
+
});
|
|
8102
|
+
if (config.elixirRealtime === "live-view-streams" && config.elixirWebFramework !== "phoenix-live-view") incompatibilityError({
|
|
8103
|
+
message: "LiveView Streams require Phoenix LiveView.",
|
|
8104
|
+
provided: {
|
|
8105
|
+
"elixir-realtime": "live-view-streams",
|
|
8106
|
+
"elixir-web-framework": config.elixirWebFramework ?? "none"
|
|
8107
|
+
},
|
|
8108
|
+
suggestions: ["Use --elixir-web-framework phoenix-live-view", "Use --elixir-realtime channels"]
|
|
8109
|
+
});
|
|
8110
|
+
if (config.elixirApi === "absinthe" && !hasEcto) incompatibilityError({
|
|
8111
|
+
message: "Absinthe GraphQL requires Ecto in the current generated Phoenix scaffold.",
|
|
8112
|
+
provided: {
|
|
8113
|
+
"elixir-api": "absinthe",
|
|
8114
|
+
"elixir-orm": config.elixirOrm ?? "none"
|
|
8115
|
+
},
|
|
8116
|
+
suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-api rest"]
|
|
8117
|
+
});
|
|
8118
|
+
}
|
|
7219
8119
|
function validateEmailConstraints(config) {
|
|
7220
8120
|
if (!config.email || config.email === "none") return;
|
|
7221
8121
|
if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
|
|
@@ -7340,6 +8240,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
7340
8240
|
validateCachingConstraints(config);
|
|
7341
8241
|
validateSearchConstraints(config);
|
|
7342
8242
|
validateJavaConstraints(config, providedFlags);
|
|
8243
|
+
validateElixirConstraints(config);
|
|
7343
8244
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
7344
8245
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
7345
8246
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
@@ -7381,6 +8282,7 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
7381
8282
|
validateCachingConstraints(config);
|
|
7382
8283
|
validateSearchConstraints(config);
|
|
7383
8284
|
validateJavaConstraints(config);
|
|
8285
|
+
validateElixirConstraints(config);
|
|
7384
8286
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
7385
8287
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database);
|
|
7386
8288
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
@@ -8753,6 +9655,10 @@ async function displayPostInstallInstructions(config) {
|
|
|
8753
9655
|
displayPythonInstructions(config);
|
|
8754
9656
|
return;
|
|
8755
9657
|
}
|
|
9658
|
+
if (ecosystem === "elixir") {
|
|
9659
|
+
displayElixirInstructions(config);
|
|
9660
|
+
return;
|
|
9661
|
+
}
|
|
8756
9662
|
const isConvex = backend === "convex";
|
|
8757
9663
|
const isBackendSelf = backend === "self";
|
|
8758
9664
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : packageManager === "yarn" ? "yarn" : "bun run";
|
|
@@ -8916,7 +9822,7 @@ function getBunWebNativeWarning() {
|
|
|
8916
9822
|
}
|
|
8917
9823
|
function getClerkInstructions(backend, frontend) {
|
|
8918
9824
|
if (backend === "convex") return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
8919
|
-
if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
|
|
9825
|
+
if (backend === "self" && (frontend.includes("next") || frontend.includes("vinext") || frontend.includes("tanstack-start"))) {
|
|
8920
9826
|
const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
|
|
8921
9827
|
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Create an application in ${pc.underline("https://dashboard.clerk.com/")}\n${pc.cyan("•")} Set ${publishableKeyVar} in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Set CLERK_SECRET_KEY in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Clerk middleware and a protected dashboard route are already generated`;
|
|
8922
9828
|
}
|
|
@@ -9303,6 +10209,36 @@ function displayPythonInstructions(config) {
|
|
|
9303
10209
|
output += pc.dim("Your star helps other developers discover the project.");
|
|
9304
10210
|
consola$1.box(output);
|
|
9305
10211
|
}
|
|
10212
|
+
function displayElixirInstructions(config) {
|
|
10213
|
+
const { relativePath, depsInstalled, elixirWebFramework, elixirOrm, elixirApi, elixirRealtime, elixirJobs, elixirAuth, elixirDeploy } = config;
|
|
10214
|
+
let output = `${pc.bold("Project created successfully!")}\n\n`;
|
|
10215
|
+
output += `${pc.bold("Next steps:")}\n`;
|
|
10216
|
+
output += `${pc.cyan("1.")} cd ${relativePath}\n`;
|
|
10217
|
+
if (!depsInstalled) {
|
|
10218
|
+
output += `${pc.cyan("2.")} mix deps.get\n`;
|
|
10219
|
+
output += `${pc.cyan("3.")} mix ecto.setup\n`;
|
|
10220
|
+
output += `${pc.cyan("4.")} mix phx.server\n`;
|
|
10221
|
+
} else {
|
|
10222
|
+
output += `${pc.cyan("2.")} mix ecto.setup\n`;
|
|
10223
|
+
output += `${pc.cyan("3.")} mix phx.server\n`;
|
|
10224
|
+
}
|
|
10225
|
+
output += `\n${pc.bold("Selected Elixir stack:")}\n`;
|
|
10226
|
+
output += `${pc.cyan("•")} Web: ${elixirWebFramework}\n`;
|
|
10227
|
+
output += `${pc.cyan("•")} Database: ${elixirOrm}\n`;
|
|
10228
|
+
output += `${pc.cyan("•")} API: ${elixirApi}\n`;
|
|
10229
|
+
output += `${pc.cyan("•")} Realtime: ${elixirRealtime}\n`;
|
|
10230
|
+
output += `${pc.cyan("•")} Jobs: ${elixirJobs}\n`;
|
|
10231
|
+
output += `${pc.cyan("•")} Auth: ${elixirAuth}\n`;
|
|
10232
|
+
output += `${pc.cyan("•")} Deploy: ${elixirDeploy}\n`;
|
|
10233
|
+
output += `\n${pc.bold("Common Mix commands:")}\n`;
|
|
10234
|
+
output += `${pc.cyan("•")} Run: mix phx.server\n`;
|
|
10235
|
+
output += `${pc.cyan("•")} Test: mix test\n`;
|
|
10236
|
+
output += `${pc.cyan("•")} Format: mix format\n`;
|
|
10237
|
+
output += `${pc.cyan("•")} Compile: mix compile\n`;
|
|
10238
|
+
output += `\n${pc.bold("Your Phoenix app will be available at:")}\n`;
|
|
10239
|
+
output += `${pc.cyan("•")} Web: http://localhost:4000\n`;
|
|
10240
|
+
consola$1.box(output);
|
|
10241
|
+
}
|
|
9306
10242
|
|
|
9307
10243
|
//#endregion
|
|
9308
10244
|
//#region src/helpers/core/create-project.ts
|
|
@@ -9325,7 +10261,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
9325
10261
|
await writeBtsConfig(options);
|
|
9326
10262
|
await formatProject(projectDir);
|
|
9327
10263
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
9328
|
-
if (options.install && options.ecosystem === "typescript") await installDependencies({
|
|
10264
|
+
if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) await installDependencies({
|
|
9329
10265
|
projectDir,
|
|
9330
10266
|
packageManager: options.packageManager
|
|
9331
10267
|
});
|
|
@@ -9334,6 +10270,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
9334
10270
|
if (options.install && options.ecosystem === "go") await runGoModTidy({ projectDir });
|
|
9335
10271
|
if (options.install && options.ecosystem === "java" && options.javaBuildTool !== "none") if (options.javaBuildTool === "gradle") await runGradleTests({ projectDir });
|
|
9336
10272
|
else await runMavenTests({ projectDir });
|
|
10273
|
+
if (options.install && options.ecosystem === "elixir") await runMixCompile({ projectDir });
|
|
9337
10274
|
await initializeGit(projectDir, options.git);
|
|
9338
10275
|
if (!isSilent()) await displayPostInstallInstructions({
|
|
9339
10276
|
...options,
|
|
@@ -9374,6 +10311,51 @@ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
|
|
|
9374
10311
|
|
|
9375
10312
|
//#endregion
|
|
9376
10313
|
//#region src/helpers/core/command-handlers.ts
|
|
10314
|
+
function getYesBaseConfig(flagConfig) {
|
|
10315
|
+
const baseConfig = getDefaultConfig();
|
|
10316
|
+
if (flagConfig.ecosystem !== "react-native") return baseConfig;
|
|
10317
|
+
return {
|
|
10318
|
+
...baseConfig,
|
|
10319
|
+
backend: "none",
|
|
10320
|
+
runtime: "none",
|
|
10321
|
+
frontend: ["native-bare"],
|
|
10322
|
+
addons: [],
|
|
10323
|
+
examples: [],
|
|
10324
|
+
auth: "none",
|
|
10325
|
+
payments: "none",
|
|
10326
|
+
email: "none",
|
|
10327
|
+
fileUpload: "none",
|
|
10328
|
+
effect: "none",
|
|
10329
|
+
dbSetup: "none",
|
|
10330
|
+
api: "none",
|
|
10331
|
+
webDeploy: "none",
|
|
10332
|
+
serverDeploy: "none",
|
|
10333
|
+
cssFramework: "none",
|
|
10334
|
+
uiLibrary: "none",
|
|
10335
|
+
stateManagement: "none",
|
|
10336
|
+
forms: "none",
|
|
10337
|
+
testing: "none",
|
|
10338
|
+
realtime: "none",
|
|
10339
|
+
jobQueue: "none",
|
|
10340
|
+
animation: "none",
|
|
10341
|
+
logging: "none",
|
|
10342
|
+
observability: "none",
|
|
10343
|
+
featureFlags: "none",
|
|
10344
|
+
analytics: "none",
|
|
10345
|
+
cms: "none",
|
|
10346
|
+
caching: "none",
|
|
10347
|
+
i18n: "none",
|
|
10348
|
+
search: "none",
|
|
10349
|
+
fileStorage: "none",
|
|
10350
|
+
mobileNavigation: "expo-router",
|
|
10351
|
+
mobileUI: "none",
|
|
10352
|
+
mobileStorage: "none",
|
|
10353
|
+
mobileTesting: "none",
|
|
10354
|
+
mobilePush: "none",
|
|
10355
|
+
mobileOTA: "none",
|
|
10356
|
+
mobileDeepLinking: "none"
|
|
10357
|
+
};
|
|
10358
|
+
}
|
|
9377
10359
|
function shouldPromptForVersionChannel(input) {
|
|
9378
10360
|
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
9379
10361
|
return canPromptInteractively();
|
|
@@ -9476,6 +10458,13 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9476
10458
|
featureFlags: "none",
|
|
9477
10459
|
analytics: "none",
|
|
9478
10460
|
fileStorage: "none",
|
|
10461
|
+
mobileNavigation: "none",
|
|
10462
|
+
mobileUI: "none",
|
|
10463
|
+
mobileStorage: "none",
|
|
10464
|
+
mobileTesting: "none",
|
|
10465
|
+
mobilePush: "none",
|
|
10466
|
+
mobileOTA: "none",
|
|
10467
|
+
mobileDeepLinking: "none",
|
|
9479
10468
|
pythonWebFramework: "none",
|
|
9480
10469
|
pythonOrm: "none",
|
|
9481
10470
|
pythonValidation: "none",
|
|
@@ -9497,6 +10486,21 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9497
10486
|
javaAuth: "none",
|
|
9498
10487
|
javaLibraries: [],
|
|
9499
10488
|
javaTestingLibraries: [],
|
|
10489
|
+
elixirWebFramework: "none",
|
|
10490
|
+
elixirOrm: "none",
|
|
10491
|
+
elixirAuth: "none",
|
|
10492
|
+
elixirApi: "none",
|
|
10493
|
+
elixirRealtime: "none",
|
|
10494
|
+
elixirJobs: "none",
|
|
10495
|
+
elixirValidation: "none",
|
|
10496
|
+
elixirHttp: "none",
|
|
10497
|
+
elixirJson: "none",
|
|
10498
|
+
elixirEmail: "none",
|
|
10499
|
+
elixirCaching: "none",
|
|
10500
|
+
elixirObservability: "none",
|
|
10501
|
+
elixirTesting: "none",
|
|
10502
|
+
elixirQuality: "none",
|
|
10503
|
+
elixirDeploy: "none",
|
|
9500
10504
|
aiDocs: []
|
|
9501
10505
|
},
|
|
9502
10506
|
reproducibleCommand: "",
|
|
@@ -9541,7 +10545,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9541
10545
|
if (cliInput.yes) {
|
|
9542
10546
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
9543
10547
|
config = {
|
|
9544
|
-
...
|
|
10548
|
+
...getYesBaseConfig(flagConfig),
|
|
9545
10549
|
...flagConfig,
|
|
9546
10550
|
projectName: finalBaseName,
|
|
9547
10551
|
projectDir: finalResolvedPath,
|
|
@@ -9938,17 +10942,21 @@ async function history(options) {
|
|
|
9938
10942
|
*/
|
|
9939
10943
|
async function createVirtual(options) {
|
|
9940
10944
|
try {
|
|
10945
|
+
const ecosystem = options.ecosystem || "typescript";
|
|
10946
|
+
const isReactNative = ecosystem === "react-native";
|
|
10947
|
+
const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
|
|
10948
|
+
const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
|
|
9941
10949
|
const result = await generateVirtualProject$1({
|
|
9942
10950
|
config: {
|
|
10951
|
+
ecosystem,
|
|
9943
10952
|
projectName: options.projectName || "my-project",
|
|
9944
10953
|
projectDir: "/virtual",
|
|
9945
10954
|
relativePath: "./virtual",
|
|
9946
|
-
ecosystem: options.ecosystem || "typescript",
|
|
9947
10955
|
database: options.database || "none",
|
|
9948
10956
|
orm: options.orm || "none",
|
|
9949
|
-
backend: options.backend || "hono",
|
|
9950
|
-
runtime: options.runtime || "bun",
|
|
9951
|
-
frontend
|
|
10957
|
+
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
10958
|
+
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
10959
|
+
frontend,
|
|
9952
10960
|
addons: options.addons || [],
|
|
9953
10961
|
examples: options.examples || [],
|
|
9954
10962
|
auth: options.auth || "none",
|
|
@@ -9961,11 +10969,11 @@ async function createVirtual(options) {
|
|
|
9961
10969
|
versionChannel: options.versionChannel || "stable",
|
|
9962
10970
|
install: false,
|
|
9963
10971
|
dbSetup: options.dbSetup || "none",
|
|
9964
|
-
api: options.api || "trpc",
|
|
10972
|
+
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
9965
10973
|
webDeploy: options.webDeploy || "none",
|
|
9966
10974
|
serverDeploy: options.serverDeploy || "none",
|
|
9967
|
-
cssFramework: options.cssFramework || "tailwind",
|
|
9968
|
-
uiLibrary: options.uiLibrary || "shadcn-ui",
|
|
10975
|
+
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
10976
|
+
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
9969
10977
|
shadcnBase: options.shadcnBase ?? "radix",
|
|
9970
10978
|
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
9971
10979
|
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
@@ -9975,8 +10983,8 @@ async function createVirtual(options) {
|
|
|
9975
10983
|
shadcnRadius: options.shadcnRadius ?? "default",
|
|
9976
10984
|
ai: options.ai || "none",
|
|
9977
10985
|
stateManagement: options.stateManagement || "none",
|
|
9978
|
-
forms: options.forms || "react-hook-form",
|
|
9979
|
-
testing: options.testing || "vitest",
|
|
10986
|
+
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
10987
|
+
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
9980
10988
|
validation: options.validation || "zod",
|
|
9981
10989
|
realtime: options.realtime || "none",
|
|
9982
10990
|
jobQueue: options.jobQueue || "none",
|
|
@@ -9985,6 +10993,13 @@ async function createVirtual(options) {
|
|
|
9985
10993
|
observability: options.observability || "none",
|
|
9986
10994
|
featureFlags: options.featureFlags || "none",
|
|
9987
10995
|
analytics: options.analytics || "none",
|
|
10996
|
+
mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
|
|
10997
|
+
mobileUI: options.mobileUI || "none",
|
|
10998
|
+
mobileStorage: options.mobileStorage || "none",
|
|
10999
|
+
mobileTesting: options.mobileTesting || "none",
|
|
11000
|
+
mobilePush: options.mobilePush || "none",
|
|
11001
|
+
mobileOTA: options.mobileOTA || "none",
|
|
11002
|
+
mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
|
|
9988
11003
|
cms: options.cms || "none",
|
|
9989
11004
|
caching: options.caching || "none",
|
|
9990
11005
|
i18n: options.i18n || "none",
|
|
@@ -10021,6 +11036,21 @@ async function createVirtual(options) {
|
|
|
10021
11036
|
javaAuth: options.javaAuth || "none",
|
|
10022
11037
|
javaLibraries: options.javaLibraries || [],
|
|
10023
11038
|
javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
|
|
11039
|
+
elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
|
|
11040
|
+
elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
|
|
11041
|
+
elixirAuth: options.elixirAuth || "none",
|
|
11042
|
+
elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
|
|
11043
|
+
elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
|
|
11044
|
+
elixirJobs: options.elixirJobs || "none",
|
|
11045
|
+
elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
|
|
11046
|
+
elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
|
|
11047
|
+
elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
|
|
11048
|
+
elixirEmail: options.elixirEmail || "none",
|
|
11049
|
+
elixirCaching: options.elixirCaching || "none",
|
|
11050
|
+
elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
|
|
11051
|
+
elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
|
|
11052
|
+
elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
|
|
11053
|
+
elixirDeploy: options.elixirDeploy || "none",
|
|
10024
11054
|
aiDocs: options.aiDocs || ["claude-md"]
|
|
10025
11055
|
},
|
|
10026
11056
|
templates: EMBEDDED_TEMPLATES$1
|