create-better-fullstack 1.8.0 → 2.0.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 +14 -0
- package/dist/addons-setup-Ca069cmy.mjs +5 -0
- package/dist/{addons-setup-CUmA_nra.mjs → addons-setup-DHoByttt.mjs} +1 -1
- package/dist/{bts-config-YcroedMK.mjs → bts-config-BMniWIbd.mjs} +180 -4
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +76 -24
- package/dist/index.mjs +1689 -1052
- package/dist/{mcp-DoPutOIG.mjs → mcp-YvDMaVXB.mjs} +1 -1
- package/dist/mcp-entry.mjs +12 -2
- package/package.json +7 -7
- package/dist/addons-setup-CJwQAWFg.mjs +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
3
|
-
import { a as
|
|
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";
|
|
2
|
+
import { a as getGraphBackendUrl, c as getPrimaryGraphPart, d as getLatestCLIVersion, f as DEFAULT_CONFIG, g as getUserPkgManager, h as getDefaultConfig, i as getGraphBackendDeployInstructions, l as hasGraphPart, n as updateBtsConfig, o as getGraphPart, p as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, s as getGraphSummary, t as readBtsConfig, u as types_exports } from "./bts-config-BMniWIbd.mjs";
|
|
3
|
+
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-DHoByttt.mjs";
|
|
5
4
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
6
5
|
import { createRouterClient, os } from "@orpc/server";
|
|
7
6
|
import pc from "picocolors";
|
|
@@ -10,7 +9,7 @@ import z from "zod";
|
|
|
10
9
|
import envPaths from "env-paths";
|
|
11
10
|
import fs from "fs-extra";
|
|
12
11
|
import path from "node:path";
|
|
13
|
-
import { allowedApisForFrontends, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
12
|
+
import { allowedApisForFrontends, formatStackPartSpec, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
14
13
|
import { ECOSYSTEM_GROUPS, EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, checkAllVersions, generateCliReport, generateVirtualProject, generateVirtualProject as generateVirtualProject$1, listEcosystems, processAddonTemplates, processAddonsDeps, validatePreflightConfig } from "@better-fullstack/template-generator";
|
|
15
14
|
import gradient from "gradient-string";
|
|
16
15
|
import path$1 from "path";
|
|
@@ -349,20 +348,16 @@ function showEcosystems() {
|
|
|
349
348
|
console.log(`\nUsage: update-deps --ecosystem <name>`);
|
|
350
349
|
}
|
|
351
350
|
|
|
352
|
-
//#endregion
|
|
353
|
-
//#region src/types.ts
|
|
354
|
-
var types_exports = {};
|
|
355
|
-
import * as import__better_fullstack_types from "@better-fullstack/types";
|
|
356
|
-
__reExport(types_exports, import__better_fullstack_types);
|
|
357
|
-
|
|
358
351
|
//#endregion
|
|
359
352
|
//#region src/create-command-input.ts
|
|
360
353
|
const CreateCommandOptionsSchema = z.object({
|
|
361
354
|
template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
|
|
362
355
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
363
356
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
357
|
+
part: z.array(z.string()).optional().describe("Stack graph part binding, e.g. frontend:typescript:next or backend.orm:go:gorm"),
|
|
364
358
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
365
359
|
dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
|
|
360
|
+
verify: z.boolean().optional().default(false).describe("Run generated project checks after scaffolding without starting dev servers"),
|
|
366
361
|
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)"),
|
|
367
362
|
database: types_exports.DatabaseSchema.optional(),
|
|
368
363
|
orm: types_exports.ORMSchema.optional(),
|
|
@@ -653,8 +648,8 @@ function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
|
|
|
653
648
|
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
654
649
|
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
655
650
|
}
|
|
656
|
-
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
657
|
-
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
651
|
+
function validateServerDeployRequiresBackend(serverDeploy, backend, hasGraphBackend = false) {
|
|
652
|
+
if (serverDeploy && serverDeploy !== "none" && !hasGraphBackend && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
658
653
|
}
|
|
659
654
|
function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
660
655
|
const baseCompatibility = validateAddonCompatibility(addon, frontend, _auth);
|
|
@@ -1116,6 +1111,10 @@ function getAddonDisplay(addon) {
|
|
|
1116
1111
|
label = "Storybook";
|
|
1117
1112
|
hint = "Component development and testing workshop";
|
|
1118
1113
|
break;
|
|
1114
|
+
case "swr":
|
|
1115
|
+
label = "SWR";
|
|
1116
|
+
hint = "React Hooks for data fetching and caching";
|
|
1117
|
+
break;
|
|
1119
1118
|
case "tanstack-query":
|
|
1120
1119
|
label = "TanStack Query";
|
|
1121
1120
|
hint = "Powerful async state management & data fetching";
|
|
@@ -1169,6 +1168,7 @@ const ADDON_GROUPS = {
|
|
|
1169
1168
|
],
|
|
1170
1169
|
Integrations: ["msw", "storybook"],
|
|
1171
1170
|
"AI Agents": ["mcp", "skills"],
|
|
1171
|
+
"Data Fetching": ["swr"],
|
|
1172
1172
|
TanStack: [
|
|
1173
1173
|
"tanstack-query",
|
|
1174
1174
|
"tanstack-table",
|
|
@@ -1177,6 +1177,12 @@ const ADDON_GROUPS = {
|
|
|
1177
1177
|
"tanstack-pacer"
|
|
1178
1178
|
]
|
|
1179
1179
|
};
|
|
1180
|
+
function createGroupedAddonOptions() {
|
|
1181
|
+
return Object.fromEntries(Object.keys(ADDON_GROUPS).map((group$1) => [group$1, []]));
|
|
1182
|
+
}
|
|
1183
|
+
function getAddonGroup(addon) {
|
|
1184
|
+
return Object.entries(ADDON_GROUPS).find(([, addons]) => addons.includes(addon))?.[0];
|
|
1185
|
+
}
|
|
1180
1186
|
function validateAddonCompatibilityForPrompt(addon, frontends, auth, backend, runtime) {
|
|
1181
1187
|
return validateAddonCompatibility$1(addon, frontends, auth, backend, runtime);
|
|
1182
1188
|
}
|
|
@@ -1186,14 +1192,7 @@ function getCompatibleAddonsForPrompt(allAddons, frontends, existingAddons = [],
|
|
|
1186
1192
|
async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
1187
1193
|
if (addons !== void 0) return addons;
|
|
1188
1194
|
const allAddons = types_exports.AddonsSchema.options.filter((addon) => addon !== "none");
|
|
1189
|
-
const groupedOptions =
|
|
1190
|
-
Tooling: [],
|
|
1191
|
-
Documentation: [],
|
|
1192
|
-
Extensions: [],
|
|
1193
|
-
Integrations: [],
|
|
1194
|
-
"AI Agents": [],
|
|
1195
|
-
TanStack: []
|
|
1196
|
-
};
|
|
1195
|
+
const groupedOptions = createGroupedAddonOptions();
|
|
1197
1196
|
const frontendsArray = frontends || [];
|
|
1198
1197
|
for (const addon of allAddons) {
|
|
1199
1198
|
const { isCompatible } = validateAddonCompatibilityForPrompt(addon, frontendsArray, auth, backend, runtime);
|
|
@@ -1204,12 +1203,8 @@ async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
|
1204
1203
|
label,
|
|
1205
1204
|
hint
|
|
1206
1205
|
};
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1210
|
-
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1211
|
-
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1212
|
-
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1206
|
+
const group$1 = getAddonGroup(addon);
|
|
1207
|
+
if (group$1) groupedOptions[group$1].push(option);
|
|
1213
1208
|
}
|
|
1214
1209
|
Object.keys(groupedOptions).forEach((group$1) => {
|
|
1215
1210
|
if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
|
|
@@ -1230,14 +1225,7 @@ async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
|
1230
1225
|
return response;
|
|
1231
1226
|
}
|
|
1232
1227
|
async function getAddonsToAdd(frontend, existingAddons = [], auth, backend, runtime) {
|
|
1233
|
-
const groupedOptions =
|
|
1234
|
-
Tooling: [],
|
|
1235
|
-
Documentation: [],
|
|
1236
|
-
Extensions: [],
|
|
1237
|
-
Integrations: [],
|
|
1238
|
-
"AI Agents": [],
|
|
1239
|
-
TanStack: []
|
|
1240
|
-
};
|
|
1228
|
+
const groupedOptions = createGroupedAddonOptions();
|
|
1241
1229
|
const frontendArray = frontend || [];
|
|
1242
1230
|
const compatibleAddons = getCompatibleAddonsForPrompt(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth, backend, runtime);
|
|
1243
1231
|
for (const addon of compatibleAddons) {
|
|
@@ -1247,12 +1235,8 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth, backend, runt
|
|
|
1247
1235
|
label,
|
|
1248
1236
|
hint
|
|
1249
1237
|
};
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
|
|
1253
|
-
else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
|
|
1254
|
-
else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
|
|
1255
|
-
else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
|
|
1238
|
+
const group$1 = getAddonGroup(addon);
|
|
1239
|
+
if (group$1) groupedOptions[group$1].push(option);
|
|
1256
1240
|
}
|
|
1257
1241
|
Object.keys(groupedOptions).forEach((group$1) => {
|
|
1258
1242
|
if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
|
|
@@ -2296,6 +2280,11 @@ const CMS_PROMPT_OPTIONS = [
|
|
|
2296
2280
|
label: "TinaCMS",
|
|
2297
2281
|
hint: "Git-backed headless CMS with visual editing"
|
|
2298
2282
|
},
|
|
2283
|
+
{
|
|
2284
|
+
value: "directus",
|
|
2285
|
+
label: "Directus",
|
|
2286
|
+
hint: "Open data platform and headless CMS for SQL databases"
|
|
2287
|
+
},
|
|
2299
2288
|
{
|
|
2300
2289
|
value: "none",
|
|
2301
2290
|
label: "None",
|
|
@@ -2941,42 +2930,48 @@ const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target
|
|
|
2941
2930
|
|
|
2942
2931
|
//#endregion
|
|
2943
2932
|
//#region src/prompts/ecosystem.ts
|
|
2933
|
+
const ECOSYSTEM_PROMPT_OPTIONS = [
|
|
2934
|
+
{
|
|
2935
|
+
value: "typescript",
|
|
2936
|
+
label: "TypeScript",
|
|
2937
|
+
hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
|
|
2938
|
+
},
|
|
2939
|
+
{
|
|
2940
|
+
value: "react-native",
|
|
2941
|
+
label: "React Native",
|
|
2942
|
+
hint: "Expo and React Native mobile apps with native integrations"
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
value: "rust",
|
|
2946
|
+
label: "Rust",
|
|
2947
|
+
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
value: "python",
|
|
2951
|
+
label: "Python",
|
|
2952
|
+
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
value: "go",
|
|
2956
|
+
label: "Go",
|
|
2957
|
+
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2958
|
+
},
|
|
2959
|
+
{
|
|
2960
|
+
value: "java",
|
|
2961
|
+
label: "Java",
|
|
2962
|
+
hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
|
|
2963
|
+
},
|
|
2964
|
+
{
|
|
2965
|
+
value: "elixir",
|
|
2966
|
+
label: "Elixir",
|
|
2967
|
+
hint: "Elixir ecosystem with Phoenix, LiveView, Ecto, and more"
|
|
2968
|
+
}
|
|
2969
|
+
];
|
|
2944
2970
|
async function getEcosystemChoice(ecosystem) {
|
|
2945
2971
|
if (ecosystem !== void 0) return ecosystem;
|
|
2946
2972
|
const response = await navigableSelect({
|
|
2947
2973
|
message: "Select ecosystem",
|
|
2948
|
-
options:
|
|
2949
|
-
{
|
|
2950
|
-
value: "typescript",
|
|
2951
|
-
label: "TypeScript",
|
|
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"
|
|
2958
|
-
},
|
|
2959
|
-
{
|
|
2960
|
-
value: "rust",
|
|
2961
|
-
label: "Rust",
|
|
2962
|
-
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2963
|
-
},
|
|
2964
|
-
{
|
|
2965
|
-
value: "python",
|
|
2966
|
-
label: "Python",
|
|
2967
|
-
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2968
|
-
},
|
|
2969
|
-
{
|
|
2970
|
-
value: "go",
|
|
2971
|
-
label: "Go",
|
|
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"
|
|
2978
|
-
}
|
|
2979
|
-
],
|
|
2974
|
+
options: ECOSYSTEM_PROMPT_OPTIONS,
|
|
2980
2975
|
initialValue: "typescript"
|
|
2981
2976
|
});
|
|
2982
2977
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
@@ -3150,6 +3145,11 @@ async function getFileStorageChoice(fileStorage, backend) {
|
|
|
3150
3145
|
label: "Cloudflare R2",
|
|
3151
3146
|
hint: "S3-compatible storage with zero egress fees"
|
|
3152
3147
|
},
|
|
3148
|
+
{
|
|
3149
|
+
value: "cloudinary",
|
|
3150
|
+
label: "Cloudinary",
|
|
3151
|
+
hint: "Image and media storage with transformations"
|
|
3152
|
+
},
|
|
3153
3153
|
{
|
|
3154
3154
|
value: "none",
|
|
3155
3155
|
label: "None",
|
|
@@ -3385,8 +3385,8 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
|
|
|
3385
3385
|
const NATIVE_FRONTEND_PROMPT_OPTIONS = [
|
|
3386
3386
|
{
|
|
3387
3387
|
value: "native-bare",
|
|
3388
|
-
label: "
|
|
3389
|
-
hint: "
|
|
3388
|
+
label: "StyleSheet",
|
|
3389
|
+
hint: "Expo with StyleSheet (no styling library)"
|
|
3390
3390
|
},
|
|
3391
3391
|
{
|
|
3392
3392
|
value: "native-uniwind",
|
|
@@ -4192,6 +4192,11 @@ const LOGGING_PROMPT_OPTIONS = [
|
|
|
4192
4192
|
label: "Winston",
|
|
4193
4193
|
hint: "Flexible logging library with multiple transports"
|
|
4194
4194
|
},
|
|
4195
|
+
{
|
|
4196
|
+
value: "evlog",
|
|
4197
|
+
label: "evlog",
|
|
4198
|
+
hint: "Typed event-based logging for TypeScript"
|
|
4199
|
+
},
|
|
4195
4200
|
{
|
|
4196
4201
|
value: "none",
|
|
4197
4202
|
label: "None",
|
|
@@ -4370,369 +4375,88 @@ function getMobileDeepLinkingChoice(mobileDeepLinking) {
|
|
|
4370
4375
|
}
|
|
4371
4376
|
|
|
4372
4377
|
//#endregion
|
|
4373
|
-
//#region src/prompts/
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
delete results[prevName];
|
|
4399
|
-
currentIndex--;
|
|
4400
|
-
continue;
|
|
4401
|
-
}
|
|
4402
|
-
goingBack = false;
|
|
4403
|
-
continue;
|
|
4404
|
-
}
|
|
4405
|
-
if (isCancel$1(result)) {
|
|
4406
|
-
if (typeof opts?.onCancel === "function") {
|
|
4407
|
-
results[name] = "canceled";
|
|
4408
|
-
opts.onCancel({ results });
|
|
4409
|
-
}
|
|
4410
|
-
setIsFirstPrompt$1(false);
|
|
4411
|
-
return results;
|
|
4412
|
-
}
|
|
4413
|
-
if (goingBack && !didLastPromptShowUI()) {
|
|
4414
|
-
if (currentIndex > 0) {
|
|
4415
|
-
const prevName = promptNames[currentIndex - 1];
|
|
4416
|
-
delete results[prevName];
|
|
4417
|
-
currentIndex--;
|
|
4418
|
-
continue;
|
|
4378
|
+
//#region src/prompts/package-manager.ts
|
|
4379
|
+
async function getPackageManagerChoice(packageManager) {
|
|
4380
|
+
if (packageManager !== void 0) return packageManager;
|
|
4381
|
+
const response = await navigableSelect({
|
|
4382
|
+
message: "Choose package manager",
|
|
4383
|
+
options: [
|
|
4384
|
+
{
|
|
4385
|
+
value: "npm",
|
|
4386
|
+
label: "npm",
|
|
4387
|
+
hint: "not recommended"
|
|
4388
|
+
},
|
|
4389
|
+
{
|
|
4390
|
+
value: "pnpm",
|
|
4391
|
+
label: "pnpm",
|
|
4392
|
+
hint: "Fast, disk space efficient package manager"
|
|
4393
|
+
},
|
|
4394
|
+
{
|
|
4395
|
+
value: "bun",
|
|
4396
|
+
label: "bun",
|
|
4397
|
+
hint: "All-in-one JavaScript runtime & toolkit"
|
|
4398
|
+
},
|
|
4399
|
+
{
|
|
4400
|
+
value: "yarn",
|
|
4401
|
+
label: "yarn",
|
|
4402
|
+
hint: "Yarn Berry (v4) with PnP or node_modules"
|
|
4419
4403
|
}
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
setIsFirstPrompt$1(false);
|
|
4426
|
-
return results;
|
|
4404
|
+
],
|
|
4405
|
+
initialValue: getUserPkgManager()
|
|
4406
|
+
});
|
|
4407
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4408
|
+
return response;
|
|
4427
4409
|
}
|
|
4428
4410
|
|
|
4429
4411
|
//#endregion
|
|
4430
|
-
//#region src/prompts/
|
|
4431
|
-
const
|
|
4412
|
+
//#region src/prompts/python-ecosystem.ts
|
|
4413
|
+
const PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
4432
4414
|
{
|
|
4433
|
-
value: "
|
|
4434
|
-
label: "
|
|
4435
|
-
hint: "
|
|
4415
|
+
value: "fastapi",
|
|
4416
|
+
label: "FastAPI",
|
|
4417
|
+
hint: "Modern, fast (high-performance) web framework for building APIs"
|
|
4436
4418
|
},
|
|
4437
4419
|
{
|
|
4438
|
-
value: "
|
|
4439
|
-
label: "
|
|
4440
|
-
hint: "
|
|
4420
|
+
value: "django",
|
|
4421
|
+
label: "Django",
|
|
4422
|
+
hint: "High-level Python web framework with batteries included"
|
|
4441
4423
|
},
|
|
4442
4424
|
{
|
|
4443
|
-
value: "
|
|
4444
|
-
label: "
|
|
4445
|
-
hint: "
|
|
4425
|
+
value: "flask",
|
|
4426
|
+
label: "Flask",
|
|
4427
|
+
hint: "Lightweight WSGI web framework with minimal boilerplate"
|
|
4428
|
+
},
|
|
4429
|
+
{
|
|
4430
|
+
value: "litestar",
|
|
4431
|
+
label: "Litestar",
|
|
4432
|
+
hint: "High-performance ASGI framework with class-based controllers"
|
|
4446
4433
|
},
|
|
4447
4434
|
{
|
|
4448
4435
|
value: "none",
|
|
4449
4436
|
label: "None",
|
|
4450
|
-
hint: "
|
|
4437
|
+
hint: "No web framework"
|
|
4451
4438
|
}
|
|
4452
4439
|
];
|
|
4453
|
-
const
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
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 {
|
|
4463
|
-
shouldPrompt: false,
|
|
4464
|
-
mode: "single",
|
|
4465
|
-
options: [],
|
|
4466
|
-
autoValue: "none"
|
|
4467
|
-
};
|
|
4468
|
-
return context.observability !== void 0 ? {
|
|
4469
|
-
shouldPrompt: false,
|
|
4470
|
-
mode: "single",
|
|
4471
|
-
options,
|
|
4472
|
-
autoValue: context.observability
|
|
4473
|
-
} : {
|
|
4474
|
-
shouldPrompt: true,
|
|
4475
|
-
mode: "single",
|
|
4476
|
-
options,
|
|
4477
|
-
initialValue: "none"
|
|
4478
|
-
};
|
|
4479
|
-
}
|
|
4480
|
-
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
4481
|
-
const resolution = resolveObservabilityPrompt({
|
|
4482
|
-
observability,
|
|
4483
|
-
backend,
|
|
4484
|
-
ecosystem
|
|
4485
|
-
});
|
|
4486
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4487
|
-
const response = await navigableSelect({
|
|
4488
|
-
message: "Select observability solution",
|
|
4489
|
-
options: resolution.options,
|
|
4490
|
-
initialValue: resolution.initialValue
|
|
4491
|
-
});
|
|
4492
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4493
|
-
return response;
|
|
4494
|
-
}
|
|
4495
|
-
|
|
4496
|
-
//#endregion
|
|
4497
|
-
//#region src/prompts/orm.ts
|
|
4498
|
-
const ormOptions = {
|
|
4499
|
-
prisma: {
|
|
4500
|
-
value: "prisma",
|
|
4501
|
-
label: "Prisma",
|
|
4502
|
-
hint: "Powerful, feature-rich ORM"
|
|
4503
|
-
},
|
|
4504
|
-
mongoose: {
|
|
4505
|
-
value: "mongoose",
|
|
4506
|
-
label: "Mongoose",
|
|
4507
|
-
hint: "Elegant object modeling tool"
|
|
4508
|
-
},
|
|
4509
|
-
drizzle: {
|
|
4510
|
-
value: "drizzle",
|
|
4511
|
-
label: "Drizzle",
|
|
4512
|
-
hint: "Lightweight and performant TypeScript ORM"
|
|
4513
|
-
},
|
|
4514
|
-
typeorm: {
|
|
4515
|
-
value: "typeorm",
|
|
4516
|
-
label: "TypeORM",
|
|
4517
|
-
hint: "Traditional ORM with Active Record/Data Mapper"
|
|
4440
|
+
const PYTHON_ORM_PROMPT_OPTIONS = [
|
|
4441
|
+
{
|
|
4442
|
+
value: "sqlalchemy",
|
|
4443
|
+
label: "SQLAlchemy",
|
|
4444
|
+
hint: "The SQL toolkit and ORM for Python"
|
|
4518
4445
|
},
|
|
4519
|
-
|
|
4520
|
-
value: "
|
|
4521
|
-
label: "
|
|
4522
|
-
hint: "
|
|
4446
|
+
{
|
|
4447
|
+
value: "sqlmodel",
|
|
4448
|
+
label: "SQLModel",
|
|
4449
|
+
hint: "SQL databases in Python with Pydantic and SQLAlchemy"
|
|
4523
4450
|
},
|
|
4524
|
-
|
|
4525
|
-
value: "
|
|
4526
|
-
label: "
|
|
4527
|
-
hint: "
|
|
4451
|
+
{
|
|
4452
|
+
value: "tortoise-orm",
|
|
4453
|
+
label: "Tortoise ORM",
|
|
4454
|
+
hint: "Async-first ORM with Django-like API"
|
|
4528
4455
|
},
|
|
4529
|
-
|
|
4530
|
-
value: "
|
|
4531
|
-
label: "
|
|
4532
|
-
hint: "
|
|
4533
|
-
}
|
|
4534
|
-
};
|
|
4535
|
-
function resolveORMPrompt(context) {
|
|
4536
|
-
if (context.backend === "convex" || !context.hasDatabase) return {
|
|
4537
|
-
shouldPrompt: false,
|
|
4538
|
-
mode: "single",
|
|
4539
|
-
options: [],
|
|
4540
|
-
autoValue: "none"
|
|
4541
|
-
};
|
|
4542
|
-
if (context.database === "edgedb" || context.database === "redis") return {
|
|
4543
|
-
shouldPrompt: false,
|
|
4544
|
-
mode: "single",
|
|
4545
|
-
options: [],
|
|
4546
|
-
autoValue: "none"
|
|
4547
|
-
};
|
|
4548
|
-
if (context.orm !== void 0) return {
|
|
4549
|
-
shouldPrompt: false,
|
|
4550
|
-
mode: "single",
|
|
4551
|
-
options: [],
|
|
4552
|
-
autoValue: context.orm
|
|
4553
|
-
};
|
|
4554
|
-
return {
|
|
4555
|
-
shouldPrompt: true,
|
|
4556
|
-
mode: "single",
|
|
4557
|
-
options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
|
|
4558
|
-
ormOptions.drizzle,
|
|
4559
|
-
ormOptions.prisma,
|
|
4560
|
-
ormOptions.typeorm,
|
|
4561
|
-
ormOptions.kysely,
|
|
4562
|
-
ormOptions.mikroorm,
|
|
4563
|
-
ormOptions.sequelize
|
|
4564
|
-
],
|
|
4565
|
-
initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
4566
|
-
};
|
|
4567
|
-
}
|
|
4568
|
-
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
4569
|
-
const resolution = resolveORMPrompt({
|
|
4570
|
-
orm,
|
|
4571
|
-
hasDatabase,
|
|
4572
|
-
database,
|
|
4573
|
-
backend,
|
|
4574
|
-
runtime
|
|
4575
|
-
});
|
|
4576
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4577
|
-
const response = await navigableSelect({
|
|
4578
|
-
message: "Select ORM",
|
|
4579
|
-
options: resolution.options,
|
|
4580
|
-
initialValue: resolution.initialValue
|
|
4581
|
-
});
|
|
4582
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4583
|
-
return response;
|
|
4584
|
-
}
|
|
4585
|
-
|
|
4586
|
-
//#endregion
|
|
4587
|
-
//#region src/prompts/package-manager.ts
|
|
4588
|
-
async function getPackageManagerChoice(packageManager) {
|
|
4589
|
-
if (packageManager !== void 0) return packageManager;
|
|
4590
|
-
const response = await navigableSelect({
|
|
4591
|
-
message: "Choose package manager",
|
|
4592
|
-
options: [
|
|
4593
|
-
{
|
|
4594
|
-
value: "npm",
|
|
4595
|
-
label: "npm",
|
|
4596
|
-
hint: "not recommended"
|
|
4597
|
-
},
|
|
4598
|
-
{
|
|
4599
|
-
value: "pnpm",
|
|
4600
|
-
label: "pnpm",
|
|
4601
|
-
hint: "Fast, disk space efficient package manager"
|
|
4602
|
-
},
|
|
4603
|
-
{
|
|
4604
|
-
value: "bun",
|
|
4605
|
-
label: "bun",
|
|
4606
|
-
hint: "All-in-one JavaScript runtime & toolkit"
|
|
4607
|
-
},
|
|
4608
|
-
{
|
|
4609
|
-
value: "yarn",
|
|
4610
|
-
label: "yarn",
|
|
4611
|
-
hint: "Yarn Berry (v4) with PnP or node_modules"
|
|
4612
|
-
}
|
|
4613
|
-
],
|
|
4614
|
-
initialValue: getUserPkgManager()
|
|
4615
|
-
});
|
|
4616
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4617
|
-
return response;
|
|
4618
|
-
}
|
|
4619
|
-
|
|
4620
|
-
//#endregion
|
|
4621
|
-
//#region src/prompts/payments.ts
|
|
4622
|
-
function resolvePaymentsPrompt(context = {}) {
|
|
4623
|
-
if (context.payments !== void 0) return {
|
|
4624
|
-
shouldPrompt: false,
|
|
4625
|
-
mode: "single",
|
|
4626
|
-
options: [],
|
|
4627
|
-
autoValue: context.payments
|
|
4628
|
-
};
|
|
4629
|
-
if (context.backend === "none") return {
|
|
4630
|
-
shouldPrompt: false,
|
|
4631
|
-
mode: "single",
|
|
4632
|
-
options: [],
|
|
4633
|
-
autoValue: "none"
|
|
4634
|
-
};
|
|
4635
|
-
const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
4636
|
-
const options = [];
|
|
4637
|
-
if (isPolarCompatible) options.push({
|
|
4638
|
-
value: "polar",
|
|
4639
|
-
label: "Polar",
|
|
4640
|
-
hint: "Turn your software into a business. 6 lines of code."
|
|
4641
|
-
});
|
|
4642
|
-
options.push({
|
|
4643
|
-
value: "stripe",
|
|
4644
|
-
label: "Stripe",
|
|
4645
|
-
hint: "Payment processing platform for internet businesses."
|
|
4646
|
-
}, {
|
|
4647
|
-
value: "lemon-squeezy",
|
|
4648
|
-
label: "Lemon Squeezy",
|
|
4649
|
-
hint: "All-in-one platform for SaaS, digital products, and subscriptions."
|
|
4650
|
-
}, {
|
|
4651
|
-
value: "paddle",
|
|
4652
|
-
label: "Paddle",
|
|
4653
|
-
hint: "Complete payments infrastructure for SaaS."
|
|
4654
|
-
}, {
|
|
4655
|
-
value: "dodo",
|
|
4656
|
-
label: "Dodo Payments",
|
|
4657
|
-
hint: "Simple payment infrastructure for developers."
|
|
4658
|
-
}, {
|
|
4659
|
-
value: "none",
|
|
4660
|
-
label: "None",
|
|
4661
|
-
hint: "No payments integration"
|
|
4662
|
-
});
|
|
4663
|
-
return {
|
|
4664
|
-
shouldPrompt: true,
|
|
4665
|
-
mode: "single",
|
|
4666
|
-
options,
|
|
4667
|
-
initialValue: DEFAULT_CONFIG.payments
|
|
4668
|
-
};
|
|
4669
|
-
}
|
|
4670
|
-
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
4671
|
-
const resolution = resolvePaymentsPrompt({
|
|
4672
|
-
payments,
|
|
4673
|
-
auth,
|
|
4674
|
-
backend,
|
|
4675
|
-
frontends
|
|
4676
|
-
});
|
|
4677
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4678
|
-
const response = await navigableSelect({
|
|
4679
|
-
message: "Select payments provider",
|
|
4680
|
-
options: resolution.options,
|
|
4681
|
-
initialValue: resolution.initialValue
|
|
4682
|
-
});
|
|
4683
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4684
|
-
return response;
|
|
4685
|
-
}
|
|
4686
|
-
|
|
4687
|
-
//#endregion
|
|
4688
|
-
//#region src/prompts/python-ecosystem.ts
|
|
4689
|
-
const PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
4690
|
-
{
|
|
4691
|
-
value: "fastapi",
|
|
4692
|
-
label: "FastAPI",
|
|
4693
|
-
hint: "Modern, fast (high-performance) web framework for building APIs"
|
|
4694
|
-
},
|
|
4695
|
-
{
|
|
4696
|
-
value: "django",
|
|
4697
|
-
label: "Django",
|
|
4698
|
-
hint: "High-level Python web framework with batteries included"
|
|
4699
|
-
},
|
|
4700
|
-
{
|
|
4701
|
-
value: "flask",
|
|
4702
|
-
label: "Flask",
|
|
4703
|
-
hint: "Lightweight WSGI web framework with minimal boilerplate"
|
|
4704
|
-
},
|
|
4705
|
-
{
|
|
4706
|
-
value: "litestar",
|
|
4707
|
-
label: "Litestar",
|
|
4708
|
-
hint: "High-performance ASGI framework with class-based controllers"
|
|
4709
|
-
},
|
|
4710
|
-
{
|
|
4711
|
-
value: "none",
|
|
4712
|
-
label: "None",
|
|
4713
|
-
hint: "No web framework"
|
|
4714
|
-
}
|
|
4715
|
-
];
|
|
4716
|
-
const PYTHON_ORM_PROMPT_OPTIONS = [
|
|
4717
|
-
{
|
|
4718
|
-
value: "sqlalchemy",
|
|
4719
|
-
label: "SQLAlchemy",
|
|
4720
|
-
hint: "The SQL toolkit and ORM for Python"
|
|
4721
|
-
},
|
|
4722
|
-
{
|
|
4723
|
-
value: "sqlmodel",
|
|
4724
|
-
label: "SQLModel",
|
|
4725
|
-
hint: "SQL databases in Python with Pydantic and SQLAlchemy"
|
|
4726
|
-
},
|
|
4727
|
-
{
|
|
4728
|
-
value: "tortoise-orm",
|
|
4729
|
-
label: "Tortoise ORM",
|
|
4730
|
-
hint: "Async-first ORM with Django-like API"
|
|
4731
|
-
},
|
|
4732
|
-
{
|
|
4733
|
-
value: "none",
|
|
4734
|
-
label: "None",
|
|
4735
|
-
hint: "No ORM/database layer"
|
|
4456
|
+
{
|
|
4457
|
+
value: "none",
|
|
4458
|
+
label: "None",
|
|
4459
|
+
hint: "No ORM/database layer"
|
|
4736
4460
|
}
|
|
4737
4461
|
];
|
|
4738
4462
|
const PYTHON_VALIDATION_PROMPT_OPTIONS = [{
|
|
@@ -5016,171 +4740,44 @@ async function getPythonQualityChoice(pythonQuality) {
|
|
|
5016
4740
|
}
|
|
5017
4741
|
|
|
5018
4742
|
//#endregion
|
|
5019
|
-
//#region src/prompts/
|
|
5020
|
-
const
|
|
4743
|
+
//#region src/prompts/rust-ecosystem.ts
|
|
4744
|
+
const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
5021
4745
|
{
|
|
5022
|
-
value: "
|
|
5023
|
-
label: "
|
|
5024
|
-
hint: "
|
|
4746
|
+
value: "axum",
|
|
4747
|
+
label: "Axum",
|
|
4748
|
+
hint: "Ergonomic and modular web framework from Tokio"
|
|
5025
4749
|
},
|
|
5026
4750
|
{
|
|
5027
|
-
value: "
|
|
5028
|
-
label: "
|
|
5029
|
-
hint: "
|
|
4751
|
+
value: "actix-web",
|
|
4752
|
+
label: "Actix Web",
|
|
4753
|
+
hint: "Powerful, pragmatic, and extremely fast web framework"
|
|
5030
4754
|
},
|
|
5031
4755
|
{
|
|
5032
|
-
value: "
|
|
5033
|
-
label: "
|
|
5034
|
-
hint: "
|
|
4756
|
+
value: "rocket",
|
|
4757
|
+
label: "Rocket",
|
|
4758
|
+
hint: "Convention-over-configuration web framework, 25k+ stars"
|
|
5035
4759
|
},
|
|
5036
4760
|
{
|
|
5037
|
-
value: "
|
|
5038
|
-
label: "
|
|
5039
|
-
hint: "
|
|
5040
|
-
}
|
|
4761
|
+
value: "none",
|
|
4762
|
+
label: "None",
|
|
4763
|
+
hint: "No web framework"
|
|
4764
|
+
}
|
|
4765
|
+
];
|
|
4766
|
+
const RUST_FRONTEND_PROMPT_OPTIONS = [
|
|
5041
4767
|
{
|
|
5042
|
-
value: "
|
|
5043
|
-
label: "
|
|
5044
|
-
hint: "
|
|
4768
|
+
value: "leptos",
|
|
4769
|
+
label: "Leptos",
|
|
4770
|
+
hint: "Build fast web applications with Rust"
|
|
5045
4771
|
},
|
|
5046
4772
|
{
|
|
5047
|
-
value: "
|
|
5048
|
-
label: "
|
|
5049
|
-
hint: "
|
|
4773
|
+
value: "dioxus",
|
|
4774
|
+
label: "Dioxus",
|
|
4775
|
+
hint: "Fullstack, cross-platform UI library for Rust"
|
|
5050
4776
|
},
|
|
5051
4777
|
{
|
|
5052
4778
|
value: "none",
|
|
5053
4779
|
label: "None",
|
|
5054
|
-
hint: "
|
|
5055
|
-
}
|
|
5056
|
-
];
|
|
5057
|
-
function resolveRealtimePrompt(context = {}) {
|
|
5058
|
-
if (context.backend === "none" || context.backend === "convex") return {
|
|
5059
|
-
shouldPrompt: false,
|
|
5060
|
-
mode: "single",
|
|
5061
|
-
options: [],
|
|
5062
|
-
autoValue: "none"
|
|
5063
|
-
};
|
|
5064
|
-
return context.realtime !== void 0 ? {
|
|
5065
|
-
shouldPrompt: false,
|
|
5066
|
-
mode: "single",
|
|
5067
|
-
options: REALTIME_PROMPT_OPTIONS,
|
|
5068
|
-
autoValue: context.realtime
|
|
5069
|
-
} : {
|
|
5070
|
-
shouldPrompt: true,
|
|
5071
|
-
mode: "single",
|
|
5072
|
-
options: REALTIME_PROMPT_OPTIONS,
|
|
5073
|
-
initialValue: "none"
|
|
5074
|
-
};
|
|
5075
|
-
}
|
|
5076
|
-
async function getRealtimeChoice(realtime, backend) {
|
|
5077
|
-
const resolution = resolveRealtimePrompt({
|
|
5078
|
-
realtime,
|
|
5079
|
-
backend
|
|
5080
|
-
});
|
|
5081
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5082
|
-
const response = await navigableSelect({
|
|
5083
|
-
message: "Select real-time solution",
|
|
5084
|
-
options: resolution.options,
|
|
5085
|
-
initialValue: resolution.initialValue
|
|
5086
|
-
});
|
|
5087
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5088
|
-
return response;
|
|
5089
|
-
}
|
|
5090
|
-
|
|
5091
|
-
//#endregion
|
|
5092
|
-
//#region src/prompts/runtime.ts
|
|
5093
|
-
const RUNTIME_PROMPT_OPTIONS = [
|
|
5094
|
-
{
|
|
5095
|
-
value: "bun",
|
|
5096
|
-
label: "Bun",
|
|
5097
|
-
hint: "Fast all-in-one JavaScript runtime"
|
|
5098
|
-
},
|
|
5099
|
-
{
|
|
5100
|
-
value: "node",
|
|
5101
|
-
label: "Node.js",
|
|
5102
|
-
hint: "Traditional Node.js runtime"
|
|
5103
|
-
},
|
|
5104
|
-
{
|
|
5105
|
-
value: "workers",
|
|
5106
|
-
label: "Cloudflare Workers",
|
|
5107
|
-
hint: "Edge runtime on Cloudflare's global network"
|
|
5108
|
-
}
|
|
5109
|
-
];
|
|
5110
|
-
function resolveRuntimePrompt(context = {}) {
|
|
5111
|
-
if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
|
|
5112
|
-
shouldPrompt: false,
|
|
5113
|
-
mode: "single",
|
|
5114
|
-
options: [],
|
|
5115
|
-
autoValue: "none"
|
|
5116
|
-
};
|
|
5117
|
-
const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
|
|
5118
|
-
return context.runtime !== void 0 ? {
|
|
5119
|
-
shouldPrompt: false,
|
|
5120
|
-
mode: "single",
|
|
5121
|
-
options,
|
|
5122
|
-
autoValue: context.runtime
|
|
5123
|
-
} : {
|
|
5124
|
-
shouldPrompt: true,
|
|
5125
|
-
mode: "single",
|
|
5126
|
-
options,
|
|
5127
|
-
initialValue: DEFAULT_CONFIG.runtime
|
|
5128
|
-
};
|
|
5129
|
-
}
|
|
5130
|
-
async function getRuntimeChoice(runtime, backend) {
|
|
5131
|
-
const resolution = resolveRuntimePrompt({
|
|
5132
|
-
runtime,
|
|
5133
|
-
backend
|
|
5134
|
-
});
|
|
5135
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5136
|
-
const response = await navigableSelect({
|
|
5137
|
-
message: "Select runtime",
|
|
5138
|
-
options: resolution.options,
|
|
5139
|
-
initialValue: resolution.initialValue
|
|
5140
|
-
});
|
|
5141
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5142
|
-
return response;
|
|
5143
|
-
}
|
|
5144
|
-
|
|
5145
|
-
//#endregion
|
|
5146
|
-
//#region src/prompts/rust-ecosystem.ts
|
|
5147
|
-
const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
5148
|
-
{
|
|
5149
|
-
value: "axum",
|
|
5150
|
-
label: "Axum",
|
|
5151
|
-
hint: "Ergonomic and modular web framework from Tokio"
|
|
5152
|
-
},
|
|
5153
|
-
{
|
|
5154
|
-
value: "actix-web",
|
|
5155
|
-
label: "Actix Web",
|
|
5156
|
-
hint: "Powerful, pragmatic, and extremely fast web framework"
|
|
5157
|
-
},
|
|
5158
|
-
{
|
|
5159
|
-
value: "rocket",
|
|
5160
|
-
label: "Rocket",
|
|
5161
|
-
hint: "Convention-over-configuration web framework, 25k+ stars"
|
|
5162
|
-
},
|
|
5163
|
-
{
|
|
5164
|
-
value: "none",
|
|
5165
|
-
label: "None",
|
|
5166
|
-
hint: "No web framework"
|
|
5167
|
-
}
|
|
5168
|
-
];
|
|
5169
|
-
const RUST_FRONTEND_PROMPT_OPTIONS = [
|
|
5170
|
-
{
|
|
5171
|
-
value: "leptos",
|
|
5172
|
-
label: "Leptos",
|
|
5173
|
-
hint: "Build fast web applications with Rust"
|
|
5174
|
-
},
|
|
5175
|
-
{
|
|
5176
|
-
value: "dioxus",
|
|
5177
|
-
label: "Dioxus",
|
|
5178
|
-
hint: "Fullstack, cross-platform UI library for Rust"
|
|
5179
|
-
},
|
|
5180
|
-
{
|
|
5181
|
-
value: "none",
|
|
5182
|
-
label: "None",
|
|
5183
|
-
hint: "No Rust frontend (API only)"
|
|
4780
|
+
hint: "No Rust frontend (API only)"
|
|
5184
4781
|
}
|
|
5185
4782
|
];
|
|
5186
4783
|
const RUST_ORM_PROMPT_OPTIONS = [
|
|
@@ -5523,88 +5120,6 @@ async function getRustAuthChoice(rustAuth) {
|
|
|
5523
5120
|
return response;
|
|
5524
5121
|
}
|
|
5525
5122
|
|
|
5526
|
-
//#endregion
|
|
5527
|
-
//#region src/prompts/search.ts
|
|
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";
|
|
5589
|
-
const response = await navigableSelect({
|
|
5590
|
-
message: "Select search engine",
|
|
5591
|
-
options: resolution.options,
|
|
5592
|
-
initialValue: resolution.initialValue
|
|
5593
|
-
});
|
|
5594
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5595
|
-
return response;
|
|
5596
|
-
}
|
|
5597
|
-
|
|
5598
|
-
//#endregion
|
|
5599
|
-
//#region src/prompts/server-deploy.ts
|
|
5600
|
-
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
5601
|
-
if (deployment !== void 0) return deployment;
|
|
5602
|
-
if (backend === "none" || backend === "convex") return "none";
|
|
5603
|
-
if (backend !== "hono") return "none";
|
|
5604
|
-
if (runtime === "workers") return "cloudflare";
|
|
5605
|
-
return "none";
|
|
5606
|
-
}
|
|
5607
|
-
|
|
5608
5123
|
//#endregion
|
|
5609
5124
|
//#region src/prompts/shadcn-options.ts
|
|
5610
5125
|
const BASE_OPTIONS = [{
|
|
@@ -6012,90 +5527,910 @@ async function promptShadcnRadius() {
|
|
|
6012
5527
|
}
|
|
6013
5528
|
|
|
6014
5529
|
//#endregion
|
|
6015
|
-
//#region src/prompts/
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
"
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
"
|
|
6035
|
-
"
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
5530
|
+
//#region src/prompts/ui-library.ts
|
|
5531
|
+
const UI_LIBRARY_OPTIONS = {
|
|
5532
|
+
"shadcn-ui": {
|
|
5533
|
+
label: "shadcn/ui",
|
|
5534
|
+
hint: "Beautifully designed components built with Radix UI and Tailwind CSS"
|
|
5535
|
+
},
|
|
5536
|
+
"shadcn-svelte": {
|
|
5537
|
+
label: "shadcn-svelte",
|
|
5538
|
+
hint: "Svelte component collection styled with Tailwind CSS"
|
|
5539
|
+
},
|
|
5540
|
+
daisyui: {
|
|
5541
|
+
label: "daisyUI",
|
|
5542
|
+
hint: "Tailwind CSS component library with semantic class names"
|
|
5543
|
+
},
|
|
5544
|
+
"radix-ui": {
|
|
5545
|
+
label: "Radix UI",
|
|
5546
|
+
hint: "Unstyled, accessible UI primitives for React"
|
|
5547
|
+
},
|
|
5548
|
+
"headless-ui": {
|
|
5549
|
+
label: "Headless UI",
|
|
5550
|
+
hint: "Unstyled, accessible UI components from Tailwind Labs"
|
|
5551
|
+
},
|
|
5552
|
+
"park-ui": {
|
|
5553
|
+
label: "Park UI",
|
|
5554
|
+
hint: "Beautifully designed components built on Ark UI"
|
|
5555
|
+
},
|
|
5556
|
+
"chakra-ui": {
|
|
5557
|
+
label: "Chakra UI",
|
|
5558
|
+
hint: "Simple, modular and accessible component library"
|
|
5559
|
+
},
|
|
5560
|
+
nextui: {
|
|
5561
|
+
label: "NextUI",
|
|
5562
|
+
hint: "Beautiful, fast and modern React UI library"
|
|
5563
|
+
},
|
|
5564
|
+
mantine: {
|
|
5565
|
+
label: "Mantine",
|
|
5566
|
+
hint: "Full-featured React component library with 120+ components"
|
|
5567
|
+
},
|
|
5568
|
+
mui: {
|
|
5569
|
+
label: "MUI",
|
|
5570
|
+
hint: "Popular React component library implementing Material Design"
|
|
5571
|
+
},
|
|
5572
|
+
antd: {
|
|
5573
|
+
label: "Ant Design",
|
|
5574
|
+
hint: "Enterprise-class React UI component library"
|
|
5575
|
+
},
|
|
5576
|
+
"base-ui": {
|
|
5577
|
+
label: "Base UI",
|
|
5578
|
+
hint: "Unstyled, accessible components from MUI team (Radix successor)"
|
|
5579
|
+
},
|
|
5580
|
+
"ark-ui": {
|
|
5581
|
+
label: "Ark UI",
|
|
5582
|
+
hint: "Headless, accessible UI components for React, Vue, Solid, and Svelte"
|
|
5583
|
+
},
|
|
5584
|
+
"react-aria": {
|
|
5585
|
+
label: "React Aria",
|
|
5586
|
+
hint: "Adobe's accessible, unstyled UI components for React"
|
|
5587
|
+
},
|
|
5588
|
+
none: {
|
|
5589
|
+
label: "None",
|
|
5590
|
+
hint: "No UI component library"
|
|
5591
|
+
}
|
|
5592
|
+
};
|
|
5593
|
+
function resolveUILibraryPrompt(context = {}) {
|
|
5594
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
5595
|
+
if (web.length === 0) return {
|
|
5596
|
+
shouldPrompt: false,
|
|
5597
|
+
mode: "single",
|
|
5598
|
+
options: [],
|
|
5599
|
+
autoValue: "none"
|
|
5600
|
+
};
|
|
5601
|
+
const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
|
|
5602
|
+
if (context.uiLibrary !== void 0) return {
|
|
5603
|
+
shouldPrompt: false,
|
|
5604
|
+
mode: "single",
|
|
5605
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5606
|
+
value: lib,
|
|
5607
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5608
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5609
|
+
})),
|
|
5610
|
+
autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
|
|
5611
|
+
};
|
|
5612
|
+
const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
|
|
5613
|
+
return {
|
|
5614
|
+
shouldPrompt: true,
|
|
5615
|
+
mode: "single",
|
|
5616
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5617
|
+
value: lib,
|
|
5618
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5619
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5620
|
+
})),
|
|
5621
|
+
initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
|
|
5625
|
+
const resolution = resolveUILibraryPrompt({
|
|
5626
|
+
uiLibrary,
|
|
5627
|
+
frontends,
|
|
5628
|
+
astroIntegration
|
|
5629
|
+
});
|
|
5630
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5631
|
+
const selected = await navigableSelect({
|
|
5632
|
+
message: "Select UI component library",
|
|
5633
|
+
options: resolution.options,
|
|
5634
|
+
initialValue: resolution.initialValue
|
|
5635
|
+
});
|
|
5636
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5637
|
+
return selected;
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
//#endregion
|
|
5641
|
+
//#region src/utils/compatibility.ts
|
|
5642
|
+
const WEB_FRAMEWORKS = [
|
|
5643
|
+
"tanstack-router",
|
|
5644
|
+
"react-router",
|
|
5645
|
+
"react-vite",
|
|
5646
|
+
"tanstack-start",
|
|
5647
|
+
"next",
|
|
5648
|
+
"vinext",
|
|
5649
|
+
"nuxt",
|
|
5650
|
+
"svelte",
|
|
5651
|
+
"solid",
|
|
5652
|
+
"solid-start",
|
|
5653
|
+
"astro",
|
|
5654
|
+
"qwik",
|
|
5655
|
+
"angular",
|
|
5656
|
+
"redwood",
|
|
5657
|
+
"fresh"
|
|
5658
|
+
];
|
|
5659
|
+
|
|
5660
|
+
//#endregion
|
|
5661
|
+
//#region src/prompts/web-deploy.ts
|
|
5662
|
+
function hasWebFrontend(frontends) {
|
|
5663
|
+
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
5664
|
+
}
|
|
5665
|
+
function getDeploymentDisplay(deployment) {
|
|
5666
|
+
if (deployment === "cloudflare") return {
|
|
5667
|
+
label: "Cloudflare",
|
|
5668
|
+
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
5669
|
+
};
|
|
5670
|
+
if (deployment === "vercel") return {
|
|
5671
|
+
label: "Vercel",
|
|
5672
|
+
hint: "Deploy to Vercel's edge network"
|
|
5673
|
+
};
|
|
5674
|
+
return {
|
|
5675
|
+
label: deployment,
|
|
5676
|
+
hint: `Add ${deployment} deployment`
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
5680
|
+
if (deployment !== void 0) return deployment;
|
|
5681
|
+
if (!hasWebFrontend(frontend)) return "none";
|
|
5682
|
+
const response = await navigableSelect({
|
|
5683
|
+
message: "Select web deployment",
|
|
5684
|
+
options: [
|
|
5685
|
+
"cloudflare",
|
|
5686
|
+
"vercel",
|
|
5687
|
+
"none"
|
|
5688
|
+
].map((deploy) => {
|
|
5689
|
+
const { label, hint } = getDeploymentDisplay(deploy);
|
|
5690
|
+
return {
|
|
5691
|
+
value: deploy,
|
|
5692
|
+
label,
|
|
5693
|
+
hint
|
|
5694
|
+
};
|
|
5695
|
+
}),
|
|
5696
|
+
initialValue: DEFAULT_CONFIG.webDeploy
|
|
5697
|
+
});
|
|
5698
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5699
|
+
return response;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
//#endregion
|
|
5703
|
+
//#region src/prompts/multi-ecosystem-composer.ts
|
|
5704
|
+
async function getCompositionModeChoice() {
|
|
5705
|
+
const response = await navigableSelect({
|
|
5706
|
+
message: "Select project composition",
|
|
5707
|
+
options: [{
|
|
5708
|
+
value: "single",
|
|
5709
|
+
label: "Single ecosystem",
|
|
5710
|
+
hint: "Use the classic guided flow"
|
|
5711
|
+
}, {
|
|
5712
|
+
value: "multi",
|
|
5713
|
+
label: "Multi ecosystem",
|
|
5714
|
+
hint: "Compose a TypeScript frontend with another backend ecosystem"
|
|
5715
|
+
}],
|
|
5716
|
+
initialValue: "single"
|
|
5717
|
+
});
|
|
5718
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5719
|
+
return response;
|
|
5720
|
+
}
|
|
5721
|
+
async function selectBackendEcosystem() {
|
|
5722
|
+
const response = await navigableSelect({
|
|
5723
|
+
message: "Select backend ecosystem",
|
|
5724
|
+
options: [
|
|
5725
|
+
{
|
|
5726
|
+
value: "go",
|
|
5727
|
+
label: "Go",
|
|
5728
|
+
hint: "Gin, Echo, Fiber, Chi"
|
|
5729
|
+
},
|
|
5730
|
+
{
|
|
5731
|
+
value: "rust",
|
|
5732
|
+
label: "Rust",
|
|
5733
|
+
hint: "Axum, Actix Web, Rocket"
|
|
5734
|
+
},
|
|
5735
|
+
{
|
|
5736
|
+
value: "python",
|
|
5737
|
+
label: "Python",
|
|
5738
|
+
hint: "FastAPI, Django, Flask"
|
|
5739
|
+
},
|
|
5740
|
+
{
|
|
5741
|
+
value: "java",
|
|
5742
|
+
label: "Java",
|
|
5743
|
+
hint: "Spring Boot, Quarkus"
|
|
5744
|
+
},
|
|
5745
|
+
{
|
|
5746
|
+
value: "elixir",
|
|
5747
|
+
label: "Elixir",
|
|
5748
|
+
hint: "Phoenix, LiveView"
|
|
5749
|
+
}
|
|
5750
|
+
],
|
|
5751
|
+
initialValue: "go"
|
|
5752
|
+
});
|
|
5753
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5754
|
+
return response;
|
|
5755
|
+
}
|
|
5756
|
+
async function selectServerDeployment(deployment) {
|
|
5757
|
+
if (deployment !== void 0) return deployment;
|
|
5758
|
+
const response = await navigableSelect({
|
|
5759
|
+
message: "Select server deployment",
|
|
5760
|
+
options: [
|
|
5761
|
+
{
|
|
5762
|
+
value: "none",
|
|
5763
|
+
label: "None",
|
|
5764
|
+
hint: "Skip server deployment setup"
|
|
5765
|
+
},
|
|
5766
|
+
{
|
|
5767
|
+
value: "railway",
|
|
5768
|
+
label: "Railway",
|
|
5769
|
+
hint: "Deploy a standalone backend service"
|
|
5770
|
+
},
|
|
5771
|
+
{
|
|
5772
|
+
value: "docker",
|
|
5773
|
+
label: "Docker",
|
|
5774
|
+
hint: "Containerize the backend service"
|
|
5775
|
+
},
|
|
5776
|
+
{
|
|
5777
|
+
value: "fly",
|
|
5778
|
+
label: "Fly",
|
|
5779
|
+
hint: "Deploy close to users"
|
|
5780
|
+
},
|
|
5781
|
+
{
|
|
5782
|
+
value: "vercel",
|
|
5783
|
+
label: "Vercel",
|
|
5784
|
+
hint: "Deploy from the backend workspace"
|
|
5785
|
+
}
|
|
5786
|
+
],
|
|
5787
|
+
initialValue: "none"
|
|
5788
|
+
});
|
|
5789
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5790
|
+
return response;
|
|
5791
|
+
}
|
|
5792
|
+
function promptValue(value) {
|
|
5793
|
+
if (isCancel$1(value) || isGoBack(value)) return exitCancelled("Operation cancelled");
|
|
5794
|
+
return value;
|
|
5795
|
+
}
|
|
5796
|
+
async function selectDatabaseConfig(flags) {
|
|
5797
|
+
const database = promptValue(await getDatabaseChoice(flags.database, "hono", "bun"));
|
|
5798
|
+
return {
|
|
5799
|
+
database,
|
|
5800
|
+
dbSetup: promptValue(await getDBSetupChoice(database, flags.dbSetup, "none", "none", "none"))
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath) {
|
|
5804
|
+
const baseConfig = getDefaultConfig();
|
|
5805
|
+
const frontend = promptValue(await navigableSelect({
|
|
5806
|
+
message: "Select TypeScript web frontend",
|
|
5807
|
+
options: WEB_FRONTEND_PROMPT_OPTIONS,
|
|
5808
|
+
initialValue: flags.frontend?.[0] ?? "next"
|
|
5809
|
+
}));
|
|
5810
|
+
const frontendList = [frontend];
|
|
5811
|
+
const astroIntegration = frontend === "astro" ? promptValue(await getAstroIntegrationChoice(flags.astroIntegration)) : void 0;
|
|
5812
|
+
const uiLibrary = hasWebStyling$1(frontendList) ? promptValue(await getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
|
|
5813
|
+
const shadcnOptions = uiLibrary === "shadcn-ui" ? await getShadcnOptions({
|
|
5814
|
+
shadcnBase: flags.shadcnBase,
|
|
5815
|
+
shadcnStyle: flags.shadcnStyle,
|
|
5816
|
+
shadcnIconLibrary: flags.shadcnIconLibrary,
|
|
5817
|
+
shadcnColorTheme: flags.shadcnColorTheme,
|
|
5818
|
+
shadcnBaseColor: flags.shadcnBaseColor,
|
|
5819
|
+
shadcnFont: flags.shadcnFont,
|
|
5820
|
+
shadcnRadius: flags.shadcnRadius
|
|
5821
|
+
}) : void 0;
|
|
5822
|
+
const cssFramework = hasWebStyling$1(frontendList) ? promptValue(await getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
|
|
5823
|
+
const backendEcosystem = await selectBackendEcosystem();
|
|
5824
|
+
const stackPartSpecs = [`frontend:typescript:${frontend}`];
|
|
5825
|
+
const backendChoices = {};
|
|
5826
|
+
let database = "none";
|
|
5827
|
+
let dbSetup = "none";
|
|
5828
|
+
if (backendEcosystem === "go") {
|
|
5829
|
+
const goWebFramework = promptValue(await getGoWebFrameworkChoice(flags.goWebFramework));
|
|
5830
|
+
if (goWebFramework !== "none") {
|
|
5831
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5832
|
+
database = databaseConfig.database;
|
|
5833
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5834
|
+
}
|
|
5835
|
+
const goOrm = database === "none" || goWebFramework === "none" ? "none" : promptValue(await getGoOrmChoice(flags.goOrm));
|
|
5836
|
+
const goApi = goWebFramework === "none" ? "none" : promptValue(await getGoApiChoice(flags.goApi));
|
|
5837
|
+
const goAuth = goWebFramework === "none" ? "none" : promptValue(await getGoAuthChoice(flags.goAuth));
|
|
5838
|
+
const goCli = goWebFramework === "none" ? "none" : promptValue(await getGoCliChoice(flags.goCli));
|
|
5839
|
+
const goLogging = goWebFramework === "none" ? "none" : promptValue(await getGoLoggingChoice(flags.goLogging));
|
|
5840
|
+
Object.assign(backendChoices, {
|
|
5841
|
+
goWebFramework,
|
|
5842
|
+
goOrm,
|
|
5843
|
+
goApi,
|
|
5844
|
+
goAuth,
|
|
5845
|
+
goCli,
|
|
5846
|
+
goLogging
|
|
5847
|
+
});
|
|
5848
|
+
if (goWebFramework !== "none") stackPartSpecs.push(`backend:go:${goWebFramework}`);
|
|
5849
|
+
if (goOrm !== "none") stackPartSpecs.push(`backend.orm:go:${goOrm}`);
|
|
5850
|
+
if (goApi !== "none") stackPartSpecs.push(`backend.api:go:${goApi}`);
|
|
5851
|
+
if (goAuth !== "none") stackPartSpecs.push(`backend.auth:go:${goAuth}`);
|
|
5852
|
+
}
|
|
5853
|
+
if (backendEcosystem === "rust") {
|
|
5854
|
+
const rustWebFramework = promptValue(await getRustWebFrameworkChoice(flags.rustWebFramework));
|
|
5855
|
+
if (rustWebFramework !== "none") {
|
|
5856
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5857
|
+
database = databaseConfig.database;
|
|
5858
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5859
|
+
}
|
|
5860
|
+
const rustOrm = database === "none" || rustWebFramework === "none" ? "none" : promptValue(await getRustOrmChoice(flags.rustOrm));
|
|
5861
|
+
const rustApi = rustWebFramework === "none" ? "none" : promptValue(await getRustApiChoice(flags.rustApi));
|
|
5862
|
+
const rustAuth = rustWebFramework === "none" ? "none" : promptValue(await getRustAuthChoice(flags.rustAuth));
|
|
5863
|
+
const rustFrontend = "none";
|
|
5864
|
+
const rustCli = rustWebFramework === "none" ? "none" : promptValue(await getRustCliChoice(flags.rustCli));
|
|
5865
|
+
const rustLibraries = rustWebFramework === "none" ? [] : promptValue(await getRustLibrariesChoice(flags.rustLibraries));
|
|
5866
|
+
const rustLogging = rustWebFramework === "none" ? "none" : promptValue(await getRustLoggingChoice(flags.rustLogging));
|
|
5867
|
+
const rustErrorHandling = promptValue(await getRustErrorHandlingChoice(flags.rustErrorHandling));
|
|
5868
|
+
const rustCaching = rustWebFramework === "none" ? "none" : promptValue(await getRustCachingChoice(flags.rustCaching));
|
|
5869
|
+
Object.assign(backendChoices, {
|
|
5870
|
+
rustWebFramework,
|
|
5871
|
+
rustOrm,
|
|
5872
|
+
rustApi,
|
|
5873
|
+
rustAuth,
|
|
5874
|
+
rustFrontend,
|
|
5875
|
+
rustCli,
|
|
5876
|
+
rustLibraries,
|
|
5877
|
+
rustLogging,
|
|
5878
|
+
rustErrorHandling,
|
|
5879
|
+
rustCaching
|
|
5880
|
+
});
|
|
5881
|
+
if (rustWebFramework !== "none") stackPartSpecs.push(`backend:rust:${rustWebFramework}`);
|
|
5882
|
+
if (rustOrm !== "none") stackPartSpecs.push(`backend.orm:rust:${rustOrm}`);
|
|
5883
|
+
if (rustApi !== "none") stackPartSpecs.push(`backend.api:rust:${rustApi}`);
|
|
5884
|
+
if (rustAuth !== "none") stackPartSpecs.push(`backend.auth:rust:${rustAuth}`);
|
|
5885
|
+
}
|
|
5886
|
+
if (backendEcosystem === "python") {
|
|
5887
|
+
const pythonWebFramework = promptValue(await getPythonWebFrameworkChoice(flags.pythonWebFramework));
|
|
5888
|
+
if (pythonWebFramework !== "none") {
|
|
5889
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5890
|
+
database = databaseConfig.database;
|
|
5891
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5892
|
+
}
|
|
5893
|
+
const pythonOrm = database === "none" || pythonWebFramework === "none" ? "none" : promptValue(await getPythonOrmChoice(flags.pythonOrm));
|
|
5894
|
+
const pythonValidation = pythonWebFramework === "none" ? "none" : promptValue(await getPythonValidationChoice(flags.pythonValidation));
|
|
5895
|
+
const pythonAi = pythonWebFramework === "none" ? [] : promptValue(await getPythonAiChoice(flags.pythonAi));
|
|
5896
|
+
const pythonAuth = pythonWebFramework === "none" ? "none" : promptValue(await getPythonAuthChoice(flags.pythonAuth));
|
|
5897
|
+
const pythonTaskQueue = pythonWebFramework === "none" ? "none" : promptValue(await getPythonTaskQueueChoice(flags.pythonTaskQueue));
|
|
5898
|
+
const pythonGraphql = pythonWebFramework === "none" ? "none" : promptValue(await getPythonGraphqlChoice(flags.pythonGraphql));
|
|
5899
|
+
const pythonQuality = pythonWebFramework === "none" ? "none" : promptValue(await getPythonQualityChoice(flags.pythonQuality));
|
|
5900
|
+
Object.assign(backendChoices, {
|
|
5901
|
+
pythonWebFramework,
|
|
5902
|
+
pythonOrm,
|
|
5903
|
+
pythonValidation,
|
|
5904
|
+
pythonAi,
|
|
5905
|
+
pythonAuth,
|
|
5906
|
+
pythonTaskQueue,
|
|
5907
|
+
pythonGraphql,
|
|
5908
|
+
pythonQuality
|
|
5909
|
+
});
|
|
5910
|
+
if (pythonWebFramework !== "none") stackPartSpecs.push(`backend:python:${pythonWebFramework}`);
|
|
5911
|
+
if (pythonOrm !== "none") stackPartSpecs.push(`backend.orm:python:${pythonOrm}`);
|
|
5912
|
+
if (pythonAuth !== "none") stackPartSpecs.push(`backend.auth:python:${pythonAuth}`);
|
|
5913
|
+
if (pythonTaskQueue !== "none") stackPartSpecs.push(`backend.jobQueue:python:${pythonTaskQueue}`);
|
|
5914
|
+
if (pythonGraphql !== "none") stackPartSpecs.push(`backend.api:python:${pythonGraphql}`);
|
|
5915
|
+
}
|
|
5916
|
+
if (backendEcosystem === "java") {
|
|
5917
|
+
const javaWebFramework = promptValue(await getJavaWebFrameworkChoice(flags.javaWebFramework));
|
|
5918
|
+
const javaBuildTool = promptValue(await getJavaBuildToolChoice(flags.javaBuildTool));
|
|
5919
|
+
if (javaWebFramework !== "none" && javaBuildTool !== "none") {
|
|
5920
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5921
|
+
database = databaseConfig.database;
|
|
5922
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5923
|
+
}
|
|
5924
|
+
const javaOrm = database === "none" || javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaOrmChoice(flags.javaOrm));
|
|
5925
|
+
const javaAuth = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaAuthChoice(flags.javaAuth));
|
|
5926
|
+
const javaLibraries = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? [] : promptValue(await getJavaLibrariesChoice(flags.javaLibraries));
|
|
5927
|
+
const javaTestingLibraries = promptValue(await getJavaTestingLibrariesChoice(flags.javaTestingLibraries));
|
|
5928
|
+
Object.assign(backendChoices, {
|
|
5929
|
+
javaWebFramework,
|
|
5930
|
+
javaBuildTool,
|
|
5931
|
+
javaOrm,
|
|
5932
|
+
javaAuth,
|
|
5933
|
+
javaLibraries,
|
|
5934
|
+
javaTestingLibraries
|
|
5935
|
+
});
|
|
5936
|
+
if (javaWebFramework !== "none") stackPartSpecs.push(`backend:java:${javaWebFramework}`);
|
|
5937
|
+
if (javaOrm !== "none") stackPartSpecs.push(`backend.orm:java:${javaOrm}`);
|
|
5938
|
+
if (javaAuth !== "none") stackPartSpecs.push(`backend.auth:java:${javaAuth}`);
|
|
5939
|
+
}
|
|
5940
|
+
if (backendEcosystem === "elixir") {
|
|
5941
|
+
const elixirWebFramework = promptValue(await getElixirWebFrameworkChoice(flags.elixirWebFramework));
|
|
5942
|
+
if (elixirWebFramework !== "none") {
|
|
5943
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5944
|
+
database = databaseConfig.database;
|
|
5945
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5946
|
+
}
|
|
5947
|
+
const elixirOrm = database === "none" || elixirWebFramework === "none" ? "none" : promptValue(await getElixirOrmChoice(flags.elixirOrm));
|
|
5948
|
+
const elixirAuth = elixirWebFramework === "none" ? "none" : promptValue(await getElixirAuthChoice(flags.elixirAuth));
|
|
5949
|
+
const elixirApi = elixirWebFramework === "none" ? "none" : promptValue(await getElixirApiChoice(flags.elixirApi));
|
|
5950
|
+
const elixirRealtime = elixirWebFramework === "none" ? "none" : promptValue(await getElixirRealtimeChoice(flags.elixirRealtime));
|
|
5951
|
+
const elixirJobs = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJobsChoice(flags.elixirJobs));
|
|
5952
|
+
const elixirValidation = elixirWebFramework === "none" ? "none" : promptValue(await getElixirValidationChoice(flags.elixirValidation));
|
|
5953
|
+
const elixirHttp = elixirWebFramework === "none" ? "none" : promptValue(await getElixirHttpChoice(flags.elixirHttp));
|
|
5954
|
+
const elixirJson = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJsonChoice(flags.elixirJson));
|
|
5955
|
+
const elixirEmail = elixirWebFramework === "none" ? "none" : promptValue(await getElixirEmailChoice(flags.elixirEmail));
|
|
5956
|
+
const elixirCaching = elixirWebFramework === "none" ? "none" : promptValue(await getElixirCachingChoice(flags.elixirCaching));
|
|
5957
|
+
const elixirObservability = elixirWebFramework === "none" ? "none" : promptValue(await getElixirObservabilityChoice(flags.elixirObservability));
|
|
5958
|
+
const elixirTesting = elixirWebFramework === "none" ? "none" : promptValue(await getElixirTestingChoice(flags.elixirTesting));
|
|
5959
|
+
const elixirQuality = elixirWebFramework === "none" ? "none" : promptValue(await getElixirQualityChoice(flags.elixirQuality));
|
|
5960
|
+
const elixirDeploy = elixirWebFramework === "none" ? "none" : promptValue(await getElixirDeployChoice(flags.elixirDeploy));
|
|
5961
|
+
Object.assign(backendChoices, {
|
|
5962
|
+
elixirWebFramework,
|
|
5963
|
+
elixirOrm,
|
|
5964
|
+
elixirAuth,
|
|
5965
|
+
elixirApi,
|
|
5966
|
+
elixirRealtime,
|
|
5967
|
+
elixirJobs,
|
|
5968
|
+
elixirValidation,
|
|
5969
|
+
elixirHttp,
|
|
5970
|
+
elixirJson,
|
|
5971
|
+
elixirEmail,
|
|
5972
|
+
elixirCaching,
|
|
5973
|
+
elixirObservability,
|
|
5974
|
+
elixirTesting,
|
|
5975
|
+
elixirQuality,
|
|
5976
|
+
elixirDeploy
|
|
5977
|
+
});
|
|
5978
|
+
if (elixirWebFramework !== "none") stackPartSpecs.push(`backend:elixir:${elixirWebFramework}`);
|
|
5979
|
+
if (elixirOrm !== "none") stackPartSpecs.push(`backend.orm:elixir:${elixirOrm}`);
|
|
5980
|
+
if (elixirAuth !== "none") stackPartSpecs.push(`backend.auth:elixir:${elixirAuth}`);
|
|
5981
|
+
if (elixirApi !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirApi}`);
|
|
5982
|
+
if (elixirRealtime !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirRealtime}`);
|
|
5983
|
+
if (elixirJobs !== "none") stackPartSpecs.push(`backend.jobQueue:elixir:${elixirJobs}`);
|
|
5984
|
+
if (elixirEmail !== "none") stackPartSpecs.push(`backend.email:elixir:${elixirEmail}`);
|
|
5985
|
+
if (elixirCaching !== "none") stackPartSpecs.push(`backend.caching:elixir:${elixirCaching}`);
|
|
5986
|
+
if (elixirObservability !== "none") stackPartSpecs.push(`backend.observability:elixir:${elixirObservability}`);
|
|
5987
|
+
if (elixirTesting !== "none") stackPartSpecs.push(`backend.testing:elixir:${elixirTesting}`);
|
|
5988
|
+
if (elixirDeploy !== "none") stackPartSpecs.push(`backend.deploy:elixir:${elixirDeploy}`);
|
|
5989
|
+
}
|
|
5990
|
+
if (database !== "none") stackPartSpecs.push(`database:universal:${database}`);
|
|
5991
|
+
const stackParts = (0, types_exports.parseStackPartSpecs)(stackPartSpecs, "selected");
|
|
5992
|
+
const graphPartial = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
|
|
5993
|
+
const addons = promptValue(await getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
|
|
5994
|
+
const webDeploy = promptValue(await getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
|
|
5995
|
+
const serverDeploy = await selectServerDeployment(flags.serverDeploy);
|
|
5996
|
+
const aiDocs = promptValue(await getAiDocsChoice(flags.aiDocs));
|
|
5997
|
+
const git = promptValue(await getGitChoice(flags.git));
|
|
5998
|
+
const packageManager = promptValue(await getPackageManagerChoice(flags.packageManager));
|
|
5999
|
+
const install = promptValue(await getinstallChoice(flags.install, "typescript", "none"));
|
|
6000
|
+
return {
|
|
6001
|
+
...baseConfig,
|
|
6002
|
+
...flags,
|
|
6003
|
+
...graphPartial,
|
|
6004
|
+
...backendChoices,
|
|
6005
|
+
projectName,
|
|
6006
|
+
projectDir,
|
|
6007
|
+
relativePath,
|
|
6008
|
+
ecosystem: "typescript",
|
|
6009
|
+
frontend: frontendList,
|
|
6010
|
+
backend: "none",
|
|
6011
|
+
runtime: "none",
|
|
6012
|
+
database,
|
|
6013
|
+
orm: "none",
|
|
6014
|
+
api: "none",
|
|
6015
|
+
auth: "none",
|
|
6016
|
+
astroIntegration,
|
|
6017
|
+
uiLibrary,
|
|
6018
|
+
...shadcnOptions,
|
|
6019
|
+
cssFramework,
|
|
6020
|
+
addons,
|
|
6021
|
+
examples: [],
|
|
6022
|
+
dbSetup,
|
|
6023
|
+
webDeploy,
|
|
6024
|
+
serverDeploy,
|
|
6025
|
+
aiDocs,
|
|
6026
|
+
git,
|
|
6027
|
+
packageManager,
|
|
6028
|
+
install,
|
|
6029
|
+
stackParts
|
|
6030
|
+
};
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
//#endregion
|
|
6034
|
+
//#region src/prompts/navigable-group.ts
|
|
6035
|
+
/**
|
|
6036
|
+
* Navigable group - a group of prompts that allows going back
|
|
6037
|
+
*/
|
|
6038
|
+
/**
|
|
6039
|
+
* Define a group of prompts that supports going back to previous prompts.
|
|
6040
|
+
* Returns a result object with all the values, or handles cancel/go-back navigation.
|
|
6041
|
+
*/
|
|
6042
|
+
async function navigableGroup(prompts, opts) {
|
|
6043
|
+
const results = {};
|
|
6044
|
+
const promptNames = Object.keys(prompts);
|
|
6045
|
+
let currentIndex = 0;
|
|
6046
|
+
let goingBack = false;
|
|
6047
|
+
while (currentIndex < promptNames.length) {
|
|
6048
|
+
const name = promptNames[currentIndex];
|
|
6049
|
+
const prompt = prompts[name];
|
|
6050
|
+
setIsFirstPrompt$1(currentIndex === 0);
|
|
6051
|
+
setLastPromptShownUI(false);
|
|
6052
|
+
const result = await prompt({ results })?.catch((e) => {
|
|
6053
|
+
throw e;
|
|
6054
|
+
});
|
|
6055
|
+
if (isGoBack(result)) {
|
|
6056
|
+
goingBack = true;
|
|
6057
|
+
if (currentIndex > 0) {
|
|
6058
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6059
|
+
delete results[prevName];
|
|
6060
|
+
currentIndex--;
|
|
6061
|
+
continue;
|
|
6062
|
+
}
|
|
6063
|
+
goingBack = false;
|
|
6064
|
+
continue;
|
|
6065
|
+
}
|
|
6066
|
+
if (isCancel$1(result)) {
|
|
6067
|
+
if (typeof opts?.onCancel === "function") {
|
|
6068
|
+
results[name] = "canceled";
|
|
6069
|
+
opts.onCancel({ results });
|
|
6070
|
+
}
|
|
6071
|
+
setIsFirstPrompt$1(false);
|
|
6072
|
+
return results;
|
|
6073
|
+
}
|
|
6074
|
+
if (goingBack && !didLastPromptShowUI()) {
|
|
6075
|
+
if (currentIndex > 0) {
|
|
6076
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6077
|
+
delete results[prevName];
|
|
6078
|
+
currentIndex--;
|
|
6079
|
+
continue;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
goingBack = false;
|
|
6083
|
+
results[name] = result;
|
|
6084
|
+
currentIndex++;
|
|
6085
|
+
}
|
|
6086
|
+
setIsFirstPrompt$1(false);
|
|
6087
|
+
return results;
|
|
6088
|
+
}
|
|
6089
|
+
|
|
6090
|
+
//#endregion
|
|
6091
|
+
//#region src/prompts/observability.ts
|
|
6092
|
+
const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
6093
|
+
{
|
|
6094
|
+
value: "opentelemetry",
|
|
6095
|
+
label: "OpenTelemetry",
|
|
6096
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
6097
|
+
},
|
|
6098
|
+
{
|
|
6099
|
+
value: "sentry",
|
|
6100
|
+
label: "Sentry",
|
|
6101
|
+
hint: "Error tracking and performance monitoring"
|
|
6102
|
+
},
|
|
6103
|
+
{
|
|
6104
|
+
value: "grafana",
|
|
6105
|
+
label: "Grafana",
|
|
6106
|
+
hint: "Prometheus metrics for Grafana dashboards and alerting"
|
|
6107
|
+
},
|
|
6108
|
+
{
|
|
6109
|
+
value: "none",
|
|
6110
|
+
label: "None",
|
|
6111
|
+
hint: "Skip observability/tracing setup"
|
|
6112
|
+
}
|
|
6113
|
+
];
|
|
6114
|
+
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
6115
|
+
function resolveObservabilityPrompt(context = {}) {
|
|
6116
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6117
|
+
shouldPrompt: false,
|
|
6118
|
+
mode: "single",
|
|
6119
|
+
options: [],
|
|
6120
|
+
autoValue: "none"
|
|
6121
|
+
};
|
|
6122
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
6123
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6124
|
+
shouldPrompt: false,
|
|
6125
|
+
mode: "single",
|
|
6126
|
+
options: [],
|
|
6127
|
+
autoValue: "none"
|
|
6128
|
+
};
|
|
6129
|
+
return context.observability !== void 0 ? {
|
|
6130
|
+
shouldPrompt: false,
|
|
6131
|
+
mode: "single",
|
|
6132
|
+
options,
|
|
6133
|
+
autoValue: context.observability
|
|
6134
|
+
} : {
|
|
6135
|
+
shouldPrompt: true,
|
|
6136
|
+
mode: "single",
|
|
6137
|
+
options,
|
|
6138
|
+
initialValue: "none"
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
6142
|
+
const resolution = resolveObservabilityPrompt({
|
|
6143
|
+
observability,
|
|
6144
|
+
backend,
|
|
6145
|
+
ecosystem
|
|
6146
|
+
});
|
|
6147
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6148
|
+
const response = await navigableSelect({
|
|
6149
|
+
message: "Select observability solution",
|
|
6150
|
+
options: resolution.options,
|
|
6151
|
+
initialValue: resolution.initialValue
|
|
6152
|
+
});
|
|
6153
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6154
|
+
return response;
|
|
6155
|
+
}
|
|
6156
|
+
|
|
6157
|
+
//#endregion
|
|
6158
|
+
//#region src/prompts/orm.ts
|
|
6159
|
+
const ormOptions = {
|
|
6160
|
+
prisma: {
|
|
6161
|
+
value: "prisma",
|
|
6162
|
+
label: "Prisma",
|
|
6163
|
+
hint: "Powerful, feature-rich ORM"
|
|
6164
|
+
},
|
|
6165
|
+
mongoose: {
|
|
6166
|
+
value: "mongoose",
|
|
6167
|
+
label: "Mongoose",
|
|
6168
|
+
hint: "Elegant object modeling tool"
|
|
6169
|
+
},
|
|
6170
|
+
drizzle: {
|
|
6171
|
+
value: "drizzle",
|
|
6172
|
+
label: "Drizzle",
|
|
6173
|
+
hint: "Lightweight and performant TypeScript ORM"
|
|
6174
|
+
},
|
|
6175
|
+
typeorm: {
|
|
6176
|
+
value: "typeorm",
|
|
6177
|
+
label: "TypeORM",
|
|
6178
|
+
hint: "Traditional ORM with Active Record/Data Mapper"
|
|
6179
|
+
},
|
|
6180
|
+
kysely: {
|
|
6181
|
+
value: "kysely",
|
|
6182
|
+
label: "Kysely",
|
|
6183
|
+
hint: "Type-safe SQL query builder"
|
|
6184
|
+
},
|
|
6185
|
+
mikroorm: {
|
|
6186
|
+
value: "mikroorm",
|
|
6187
|
+
label: "MikroORM",
|
|
6188
|
+
hint: "Data Mapper ORM for DDD"
|
|
6189
|
+
},
|
|
6190
|
+
sequelize: {
|
|
6191
|
+
value: "sequelize",
|
|
6192
|
+
label: "Sequelize",
|
|
6193
|
+
hint: "Mature ORM with wide adoption"
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6196
|
+
function resolveORMPrompt(context) {
|
|
6197
|
+
if (context.backend === "convex" || !context.hasDatabase) return {
|
|
6198
|
+
shouldPrompt: false,
|
|
6199
|
+
mode: "single",
|
|
6200
|
+
options: [],
|
|
6201
|
+
autoValue: "none"
|
|
6202
|
+
};
|
|
6203
|
+
if (context.database === "edgedb" || context.database === "redis") return {
|
|
6204
|
+
shouldPrompt: false,
|
|
6205
|
+
mode: "single",
|
|
6206
|
+
options: [],
|
|
6207
|
+
autoValue: "none"
|
|
6208
|
+
};
|
|
6209
|
+
if (context.orm !== void 0) return {
|
|
6210
|
+
shouldPrompt: false,
|
|
6211
|
+
mode: "single",
|
|
6212
|
+
options: [],
|
|
6213
|
+
autoValue: context.orm
|
|
6214
|
+
};
|
|
6215
|
+
return {
|
|
6216
|
+
shouldPrompt: true,
|
|
6217
|
+
mode: "single",
|
|
6218
|
+
options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
|
|
6219
|
+
ormOptions.drizzle,
|
|
6220
|
+
ormOptions.prisma,
|
|
6221
|
+
ormOptions.typeorm,
|
|
6222
|
+
ormOptions.kysely,
|
|
6223
|
+
ormOptions.mikroorm,
|
|
6224
|
+
ormOptions.sequelize
|
|
6225
|
+
],
|
|
6226
|
+
initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
6227
|
+
};
|
|
6228
|
+
}
|
|
6229
|
+
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
6230
|
+
const resolution = resolveORMPrompt({
|
|
6231
|
+
orm,
|
|
6232
|
+
hasDatabase,
|
|
6233
|
+
database,
|
|
6234
|
+
backend,
|
|
6235
|
+
runtime
|
|
6236
|
+
});
|
|
6237
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6238
|
+
const response = await navigableSelect({
|
|
6239
|
+
message: "Select ORM",
|
|
6240
|
+
options: resolution.options,
|
|
6241
|
+
initialValue: resolution.initialValue
|
|
6242
|
+
});
|
|
6243
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6244
|
+
return response;
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
//#endregion
|
|
6248
|
+
//#region src/prompts/payments.ts
|
|
6249
|
+
function resolvePaymentsPrompt(context = {}) {
|
|
6250
|
+
if (context.payments !== void 0) return {
|
|
6251
|
+
shouldPrompt: false,
|
|
6252
|
+
mode: "single",
|
|
6253
|
+
options: [],
|
|
6254
|
+
autoValue: context.payments
|
|
6255
|
+
};
|
|
6256
|
+
if (context.backend === "none") return {
|
|
6257
|
+
shouldPrompt: false,
|
|
6258
|
+
mode: "single",
|
|
6259
|
+
options: [],
|
|
6260
|
+
autoValue: "none"
|
|
6261
|
+
};
|
|
6262
|
+
const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
6263
|
+
const options = [];
|
|
6264
|
+
if (isPolarCompatible) options.push({
|
|
6265
|
+
value: "polar",
|
|
6266
|
+
label: "Polar",
|
|
6267
|
+
hint: "Turn your software into a business. 6 lines of code."
|
|
6268
|
+
});
|
|
6269
|
+
options.push({
|
|
6270
|
+
value: "stripe",
|
|
6271
|
+
label: "Stripe",
|
|
6272
|
+
hint: "Payment processing platform for internet businesses."
|
|
6070
6273
|
}, {
|
|
6071
|
-
value: "
|
|
6072
|
-
label: "
|
|
6073
|
-
hint: "
|
|
6274
|
+
value: "lemon-squeezy",
|
|
6275
|
+
label: "Lemon Squeezy",
|
|
6276
|
+
hint: "All-in-one platform for SaaS, digital products, and subscriptions."
|
|
6074
6277
|
}, {
|
|
6075
|
-
value: "
|
|
6076
|
-
label: "
|
|
6077
|
-
hint: "
|
|
6278
|
+
value: "paddle",
|
|
6279
|
+
label: "Paddle",
|
|
6280
|
+
hint: "Complete payments infrastructure for SaaS."
|
|
6281
|
+
}, {
|
|
6282
|
+
value: "dodo",
|
|
6283
|
+
label: "Dodo Payments",
|
|
6284
|
+
hint: "Simple payment infrastructure for developers."
|
|
6285
|
+
}, {
|
|
6286
|
+
value: "none",
|
|
6287
|
+
label: "None",
|
|
6288
|
+
hint: "No payments integration"
|
|
6078
6289
|
});
|
|
6079
|
-
|
|
6290
|
+
return {
|
|
6291
|
+
shouldPrompt: true,
|
|
6292
|
+
mode: "single",
|
|
6293
|
+
options,
|
|
6294
|
+
initialValue: DEFAULT_CONFIG.payments
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6297
|
+
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
6298
|
+
const resolution = resolvePaymentsPrompt({
|
|
6299
|
+
payments,
|
|
6300
|
+
auth,
|
|
6301
|
+
backend,
|
|
6302
|
+
frontends
|
|
6303
|
+
});
|
|
6304
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6305
|
+
const response = await navigableSelect({
|
|
6306
|
+
message: "Select payments provider",
|
|
6307
|
+
options: resolution.options,
|
|
6308
|
+
initialValue: resolution.initialValue
|
|
6309
|
+
});
|
|
6310
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6311
|
+
return response;
|
|
6312
|
+
}
|
|
6313
|
+
|
|
6314
|
+
//#endregion
|
|
6315
|
+
//#region src/prompts/realtime.ts
|
|
6316
|
+
const REALTIME_PROMPT_OPTIONS = [
|
|
6317
|
+
{
|
|
6318
|
+
value: "socket-io",
|
|
6319
|
+
label: "Socket.IO",
|
|
6320
|
+
hint: "Real-time bidirectional communication with fallbacks"
|
|
6321
|
+
},
|
|
6322
|
+
{
|
|
6323
|
+
value: "partykit",
|
|
6324
|
+
label: "PartyKit",
|
|
6325
|
+
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
6326
|
+
},
|
|
6327
|
+
{
|
|
6328
|
+
value: "ably",
|
|
6329
|
+
label: "Ably",
|
|
6330
|
+
hint: "Real-time messaging platform with pub/sub and presence"
|
|
6331
|
+
},
|
|
6332
|
+
{
|
|
6333
|
+
value: "pusher",
|
|
6334
|
+
label: "Pusher",
|
|
6335
|
+
hint: "Real-time communication APIs with channels and events"
|
|
6336
|
+
},
|
|
6337
|
+
{
|
|
6338
|
+
value: "liveblocks",
|
|
6339
|
+
label: "Liveblocks",
|
|
6340
|
+
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
6341
|
+
},
|
|
6342
|
+
{
|
|
6343
|
+
value: "yjs",
|
|
6344
|
+
label: "Y.js",
|
|
6345
|
+
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
6346
|
+
},
|
|
6347
|
+
{
|
|
6080
6348
|
value: "none",
|
|
6081
6349
|
label: "None",
|
|
6082
|
-
hint: "Skip
|
|
6350
|
+
hint: "Skip real-time/WebSocket integration"
|
|
6351
|
+
}
|
|
6352
|
+
];
|
|
6353
|
+
function resolveRealtimePrompt(context = {}) {
|
|
6354
|
+
if (context.backend === "none" || context.backend === "convex") return {
|
|
6355
|
+
shouldPrompt: false,
|
|
6356
|
+
mode: "single",
|
|
6357
|
+
options: [],
|
|
6358
|
+
autoValue: "none"
|
|
6359
|
+
};
|
|
6360
|
+
return context.realtime !== void 0 ? {
|
|
6361
|
+
shouldPrompt: false,
|
|
6362
|
+
mode: "single",
|
|
6363
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6364
|
+
autoValue: context.realtime
|
|
6365
|
+
} : {
|
|
6366
|
+
shouldPrompt: true,
|
|
6367
|
+
mode: "single",
|
|
6368
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6369
|
+
initialValue: "none"
|
|
6370
|
+
};
|
|
6371
|
+
}
|
|
6372
|
+
async function getRealtimeChoice(realtime, backend) {
|
|
6373
|
+
const resolution = resolveRealtimePrompt({
|
|
6374
|
+
realtime,
|
|
6375
|
+
backend
|
|
6376
|
+
});
|
|
6377
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6378
|
+
const response = await navigableSelect({
|
|
6379
|
+
message: "Select real-time solution",
|
|
6380
|
+
options: resolution.options,
|
|
6381
|
+
initialValue: resolution.initialValue
|
|
6083
6382
|
});
|
|
6084
|
-
return
|
|
6383
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6384
|
+
return response;
|
|
6385
|
+
}
|
|
6386
|
+
|
|
6387
|
+
//#endregion
|
|
6388
|
+
//#region src/prompts/runtime.ts
|
|
6389
|
+
const RUNTIME_PROMPT_OPTIONS = [
|
|
6390
|
+
{
|
|
6391
|
+
value: "bun",
|
|
6392
|
+
label: "Bun",
|
|
6393
|
+
hint: "Fast all-in-one JavaScript runtime"
|
|
6394
|
+
},
|
|
6395
|
+
{
|
|
6396
|
+
value: "node",
|
|
6397
|
+
label: "Node.js",
|
|
6398
|
+
hint: "Traditional Node.js runtime"
|
|
6399
|
+
},
|
|
6400
|
+
{
|
|
6401
|
+
value: "workers",
|
|
6402
|
+
label: "Cloudflare Workers",
|
|
6403
|
+
hint: "Edge runtime on Cloudflare's global network"
|
|
6404
|
+
}
|
|
6405
|
+
];
|
|
6406
|
+
function resolveRuntimePrompt(context = {}) {
|
|
6407
|
+
if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
|
|
6408
|
+
shouldPrompt: false,
|
|
6409
|
+
mode: "single",
|
|
6410
|
+
options: [],
|
|
6411
|
+
autoValue: "none"
|
|
6412
|
+
};
|
|
6413
|
+
const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
|
|
6414
|
+
return context.runtime !== void 0 ? {
|
|
6415
|
+
shouldPrompt: false,
|
|
6416
|
+
mode: "single",
|
|
6417
|
+
options,
|
|
6418
|
+
autoValue: context.runtime
|
|
6419
|
+
} : {
|
|
6085
6420
|
shouldPrompt: true,
|
|
6086
6421
|
mode: "single",
|
|
6087
6422
|
options,
|
|
6088
|
-
initialValue:
|
|
6423
|
+
initialValue: DEFAULT_CONFIG.runtime
|
|
6089
6424
|
};
|
|
6090
6425
|
}
|
|
6091
|
-
async function
|
|
6092
|
-
const resolution =
|
|
6093
|
-
|
|
6094
|
-
|
|
6426
|
+
async function getRuntimeChoice(runtime, backend) {
|
|
6427
|
+
const resolution = resolveRuntimePrompt({
|
|
6428
|
+
runtime,
|
|
6429
|
+
backend
|
|
6095
6430
|
});
|
|
6096
6431
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6097
6432
|
const response = await navigableSelect({
|
|
6098
|
-
message: "Select
|
|
6433
|
+
message: "Select runtime",
|
|
6099
6434
|
options: resolution.options,
|
|
6100
6435
|
initialValue: resolution.initialValue
|
|
6101
6436
|
});
|
|
@@ -6104,47 +6439,70 @@ async function getStateManagementChoice(stateManagement, frontends) {
|
|
|
6104
6439
|
}
|
|
6105
6440
|
|
|
6106
6441
|
//#endregion
|
|
6107
|
-
//#region src/prompts/
|
|
6108
|
-
const
|
|
6109
|
-
{
|
|
6110
|
-
value: "vitest",
|
|
6111
|
-
label: "Vitest",
|
|
6112
|
-
hint: "Blazing fast Vite-native unit test framework"
|
|
6113
|
-
},
|
|
6442
|
+
//#region src/prompts/search.ts
|
|
6443
|
+
const SEARCH_PROMPT_OPTIONS = [
|
|
6114
6444
|
{
|
|
6115
|
-
value: "
|
|
6116
|
-
label: "
|
|
6117
|
-
hint: "
|
|
6445
|
+
value: "meilisearch",
|
|
6446
|
+
label: "Meilisearch",
|
|
6447
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
6118
6448
|
},
|
|
6119
6449
|
{
|
|
6120
|
-
value: "
|
|
6121
|
-
label: "
|
|
6122
|
-
hint: "
|
|
6450
|
+
value: "typesense",
|
|
6451
|
+
label: "Typesense",
|
|
6452
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
6123
6453
|
},
|
|
6124
6454
|
{
|
|
6125
|
-
value: "
|
|
6126
|
-
label: "
|
|
6127
|
-
hint: "
|
|
6455
|
+
value: "elasticsearch",
|
|
6456
|
+
label: "Elasticsearch",
|
|
6457
|
+
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
6128
6458
|
},
|
|
6129
6459
|
{
|
|
6130
|
-
value: "
|
|
6131
|
-
label: "
|
|
6132
|
-
hint: "
|
|
6460
|
+
value: "algolia",
|
|
6461
|
+
label: "Algolia",
|
|
6462
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
6133
6463
|
},
|
|
6134
6464
|
{
|
|
6135
6465
|
value: "none",
|
|
6136
6466
|
label: "None",
|
|
6137
|
-
hint: "Skip
|
|
6467
|
+
hint: "Skip search engine setup"
|
|
6138
6468
|
}
|
|
6139
6469
|
];
|
|
6140
|
-
|
|
6141
|
-
|
|
6470
|
+
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
6471
|
+
function resolveSearchPrompt(context = {}) {
|
|
6472
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6473
|
+
shouldPrompt: false,
|
|
6474
|
+
mode: "single",
|
|
6475
|
+
options: [],
|
|
6476
|
+
autoValue: "none"
|
|
6477
|
+
};
|
|
6478
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
6479
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6480
|
+
shouldPrompt: false,
|
|
6481
|
+
mode: "single",
|
|
6482
|
+
options: [],
|
|
6483
|
+
autoValue: "none"
|
|
6484
|
+
};
|
|
6485
|
+
return context.search !== void 0 ? {
|
|
6486
|
+
shouldPrompt: false,
|
|
6487
|
+
mode: "single",
|
|
6488
|
+
options,
|
|
6489
|
+
autoValue: context.search
|
|
6490
|
+
} : {
|
|
6491
|
+
shouldPrompt: true,
|
|
6492
|
+
mode: "single",
|
|
6493
|
+
options,
|
|
6494
|
+
initialValue: "none"
|
|
6495
|
+
};
|
|
6142
6496
|
}
|
|
6143
|
-
async function
|
|
6144
|
-
const resolution =
|
|
6497
|
+
async function getSearchChoice(search, backend, ecosystem) {
|
|
6498
|
+
const resolution = resolveSearchPrompt({
|
|
6499
|
+
search,
|
|
6500
|
+
backend,
|
|
6501
|
+
ecosystem
|
|
6502
|
+
});
|
|
6145
6503
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6146
6504
|
const response = await navigableSelect({
|
|
6147
|
-
message: "Select
|
|
6505
|
+
message: "Select search engine",
|
|
6148
6506
|
options: resolution.options,
|
|
6149
6507
|
initialValue: resolution.initialValue
|
|
6150
6508
|
});
|
|
@@ -6153,110 +6511,154 @@ async function getTestingChoice(testing) {
|
|
|
6153
6511
|
}
|
|
6154
6512
|
|
|
6155
6513
|
//#endregion
|
|
6156
|
-
//#region src/prompts/
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
}
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6514
|
+
//#region src/prompts/server-deploy.ts
|
|
6515
|
+
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
6516
|
+
if (deployment !== void 0) return deployment;
|
|
6517
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
6518
|
+
if (backend !== "hono") return "none";
|
|
6519
|
+
if (runtime === "workers") return "cloudflare";
|
|
6520
|
+
return "none";
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
//#endregion
|
|
6524
|
+
//#region src/prompts/state-management.ts
|
|
6525
|
+
function resolveStateManagementPrompt(context = {}) {
|
|
6526
|
+
if (context.stateManagement !== void 0) return {
|
|
6527
|
+
shouldPrompt: false,
|
|
6528
|
+
mode: "single",
|
|
6529
|
+
options: [],
|
|
6530
|
+
autoValue: context.stateManagement
|
|
6531
|
+
};
|
|
6532
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
6533
|
+
if (web.length === 0) return {
|
|
6534
|
+
shouldPrompt: false,
|
|
6535
|
+
mode: "single",
|
|
6536
|
+
options: [],
|
|
6537
|
+
autoValue: "none"
|
|
6538
|
+
};
|
|
6539
|
+
const isReact = web.some((f) => [
|
|
6540
|
+
"tanstack-router",
|
|
6541
|
+
"react-router",
|
|
6542
|
+
"react-vite",
|
|
6543
|
+
"tanstack-start",
|
|
6544
|
+
"next",
|
|
6545
|
+
"vinext",
|
|
6546
|
+
"redwood"
|
|
6547
|
+
].includes(f));
|
|
6548
|
+
const isFresh = web.includes("fresh");
|
|
6549
|
+
const options = [];
|
|
6550
|
+
if (isReact) options.push({
|
|
6551
|
+
value: "zustand",
|
|
6552
|
+
label: "Zustand",
|
|
6553
|
+
hint: "Lightweight state management with simple API"
|
|
6554
|
+
}, {
|
|
6555
|
+
value: "jotai",
|
|
6556
|
+
label: "Jotai",
|
|
6557
|
+
hint: "Primitive and flexible atomic state"
|
|
6558
|
+
}, {
|
|
6559
|
+
value: "redux-toolkit",
|
|
6560
|
+
label: "Redux Toolkit",
|
|
6561
|
+
hint: "Enterprise-standard state with excellent TS support"
|
|
6562
|
+
}, {
|
|
6563
|
+
value: "valtio",
|
|
6564
|
+
label: "Valtio",
|
|
6565
|
+
hint: "Proxy-based state management"
|
|
6566
|
+
}, {
|
|
6567
|
+
value: "legend-state",
|
|
6568
|
+
label: "Legend State",
|
|
6569
|
+
hint: "High-performance observable state for React"
|
|
6570
|
+
}, {
|
|
6571
|
+
value: "mobx",
|
|
6572
|
+
label: "MobX",
|
|
6573
|
+
hint: "Observable-based reactive state management"
|
|
6574
|
+
});
|
|
6575
|
+
if (!isFresh) options.push({
|
|
6576
|
+
value: "nanostores",
|
|
6577
|
+
label: "Nanostores",
|
|
6578
|
+
hint: "Tiny state manager (1KB) for all frameworks"
|
|
6579
|
+
}, {
|
|
6580
|
+
value: "xstate",
|
|
6581
|
+
label: "XState",
|
|
6582
|
+
hint: "State machines and statecharts for complex logic"
|
|
6583
|
+
}, {
|
|
6584
|
+
value: "tanstack-store",
|
|
6585
|
+
label: "TanStack Store",
|
|
6586
|
+
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
6587
|
+
});
|
|
6588
|
+
options.push({
|
|
6589
|
+
value: "none",
|
|
6590
|
+
label: "None",
|
|
6591
|
+
hint: "Skip state management setup"
|
|
6592
|
+
});
|
|
6593
|
+
return {
|
|
6594
|
+
shouldPrompt: true,
|
|
6595
|
+
mode: "single",
|
|
6596
|
+
options,
|
|
6597
|
+
initialValue: "none"
|
|
6598
|
+
};
|
|
6599
|
+
}
|
|
6600
|
+
async function getStateManagementChoice(stateManagement, frontends) {
|
|
6601
|
+
const resolution = resolveStateManagementPrompt({
|
|
6602
|
+
stateManagement,
|
|
6603
|
+
frontends
|
|
6604
|
+
});
|
|
6605
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6606
|
+
const response = await navigableSelect({
|
|
6607
|
+
message: "Select state management",
|
|
6608
|
+
options: resolution.options,
|
|
6609
|
+
initialValue: resolution.initialValue
|
|
6610
|
+
});
|
|
6611
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6612
|
+
return response;
|
|
6613
|
+
}
|
|
6614
|
+
|
|
6615
|
+
//#endregion
|
|
6616
|
+
//#region src/prompts/testing.ts
|
|
6617
|
+
const TESTING_PROMPT_OPTIONS = [
|
|
6618
|
+
{
|
|
6619
|
+
value: "vitest",
|
|
6620
|
+
label: "Vitest",
|
|
6621
|
+
hint: "Blazing fast Vite-native unit test framework"
|
|
6193
6622
|
},
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6623
|
+
{
|
|
6624
|
+
value: "vitest-playwright",
|
|
6625
|
+
label: "Vitest + Playwright",
|
|
6626
|
+
hint: "Both unit and E2E testing for complete coverage"
|
|
6197
6627
|
},
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6628
|
+
{
|
|
6629
|
+
value: "playwright",
|
|
6630
|
+
label: "Playwright",
|
|
6631
|
+
hint: "End-to-end testing framework by Microsoft"
|
|
6201
6632
|
},
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6633
|
+
{
|
|
6634
|
+
value: "jest",
|
|
6635
|
+
label: "Jest",
|
|
6636
|
+
hint: "Classic testing framework with wide ecosystem"
|
|
6205
6637
|
},
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6638
|
+
{
|
|
6639
|
+
value: "cypress",
|
|
6640
|
+
label: "Cypress",
|
|
6641
|
+
hint: "E2E testing with time travel debugging"
|
|
6209
6642
|
},
|
|
6210
|
-
|
|
6643
|
+
{
|
|
6644
|
+
value: "none",
|
|
6211
6645
|
label: "None",
|
|
6212
|
-
hint: "
|
|
6646
|
+
hint: "Skip testing framework setup"
|
|
6213
6647
|
}
|
|
6214
|
-
|
|
6215
|
-
function
|
|
6216
|
-
|
|
6217
|
-
if (web.length === 0) return {
|
|
6218
|
-
shouldPrompt: false,
|
|
6219
|
-
mode: "single",
|
|
6220
|
-
options: [],
|
|
6221
|
-
autoValue: "none"
|
|
6222
|
-
};
|
|
6223
|
-
const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
|
|
6224
|
-
if (context.uiLibrary !== void 0) return {
|
|
6225
|
-
shouldPrompt: false,
|
|
6226
|
-
mode: "single",
|
|
6227
|
-
options: compatibleLibraries.map((lib) => ({
|
|
6228
|
-
value: lib,
|
|
6229
|
-
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
6230
|
-
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
6231
|
-
})),
|
|
6232
|
-
autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
|
|
6233
|
-
};
|
|
6234
|
-
const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
|
|
6235
|
-
return {
|
|
6236
|
-
shouldPrompt: true,
|
|
6237
|
-
mode: "single",
|
|
6238
|
-
options: compatibleLibraries.map((lib) => ({
|
|
6239
|
-
value: lib,
|
|
6240
|
-
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
6241
|
-
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
6242
|
-
})),
|
|
6243
|
-
initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
|
|
6244
|
-
};
|
|
6648
|
+
];
|
|
6649
|
+
function resolveTestingPrompt(testing) {
|
|
6650
|
+
return createStaticSinglePromptResolution(TESTING_PROMPT_OPTIONS, "vitest", testing);
|
|
6245
6651
|
}
|
|
6246
|
-
async function
|
|
6247
|
-
const resolution =
|
|
6248
|
-
uiLibrary,
|
|
6249
|
-
frontends,
|
|
6250
|
-
astroIntegration
|
|
6251
|
-
});
|
|
6652
|
+
async function getTestingChoice(testing) {
|
|
6653
|
+
const resolution = resolveTestingPrompt(testing);
|
|
6252
6654
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6253
|
-
const
|
|
6254
|
-
message: "Select
|
|
6655
|
+
const response = await navigableSelect({
|
|
6656
|
+
message: "Select testing framework",
|
|
6255
6657
|
options: resolution.options,
|
|
6256
6658
|
initialValue: resolution.initialValue
|
|
6257
6659
|
});
|
|
6258
|
-
if (isCancel$1(
|
|
6259
|
-
return
|
|
6660
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6661
|
+
return response;
|
|
6260
6662
|
}
|
|
6261
6663
|
|
|
6262
6664
|
//#endregion
|
|
@@ -6318,71 +6720,12 @@ async function getValidationChoice(validation) {
|
|
|
6318
6720
|
return response;
|
|
6319
6721
|
}
|
|
6320
6722
|
|
|
6321
|
-
//#endregion
|
|
6322
|
-
//#region src/utils/compatibility.ts
|
|
6323
|
-
const WEB_FRAMEWORKS = [
|
|
6324
|
-
"tanstack-router",
|
|
6325
|
-
"react-router",
|
|
6326
|
-
"react-vite",
|
|
6327
|
-
"tanstack-start",
|
|
6328
|
-
"next",
|
|
6329
|
-
"vinext",
|
|
6330
|
-
"nuxt",
|
|
6331
|
-
"svelte",
|
|
6332
|
-
"solid",
|
|
6333
|
-
"solid-start",
|
|
6334
|
-
"astro",
|
|
6335
|
-
"qwik",
|
|
6336
|
-
"angular",
|
|
6337
|
-
"redwood",
|
|
6338
|
-
"fresh"
|
|
6339
|
-
];
|
|
6340
|
-
|
|
6341
|
-
//#endregion
|
|
6342
|
-
//#region src/prompts/web-deploy.ts
|
|
6343
|
-
function hasWebFrontend(frontends) {
|
|
6344
|
-
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
6345
|
-
}
|
|
6346
|
-
function getDeploymentDisplay(deployment) {
|
|
6347
|
-
if (deployment === "cloudflare") return {
|
|
6348
|
-
label: "Cloudflare",
|
|
6349
|
-
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
6350
|
-
};
|
|
6351
|
-
if (deployment === "vercel") return {
|
|
6352
|
-
label: "Vercel",
|
|
6353
|
-
hint: "Deploy to Vercel's edge network"
|
|
6354
|
-
};
|
|
6355
|
-
return {
|
|
6356
|
-
label: deployment,
|
|
6357
|
-
hint: `Add ${deployment} deployment`
|
|
6358
|
-
};
|
|
6359
|
-
}
|
|
6360
|
-
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
6361
|
-
if (deployment !== void 0) return deployment;
|
|
6362
|
-
if (!hasWebFrontend(frontend)) return "none";
|
|
6363
|
-
const response = await navigableSelect({
|
|
6364
|
-
message: "Select web deployment",
|
|
6365
|
-
options: [
|
|
6366
|
-
"cloudflare",
|
|
6367
|
-
"vercel",
|
|
6368
|
-
"none"
|
|
6369
|
-
].map((deploy) => {
|
|
6370
|
-
const { label, hint } = getDeploymentDisplay(deploy);
|
|
6371
|
-
return {
|
|
6372
|
-
value: deploy,
|
|
6373
|
-
label,
|
|
6374
|
-
hint
|
|
6375
|
-
};
|
|
6376
|
-
}),
|
|
6377
|
-
initialValue: DEFAULT_CONFIG.webDeploy
|
|
6378
|
-
});
|
|
6379
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6380
|
-
return response;
|
|
6381
|
-
}
|
|
6382
|
-
|
|
6383
6723
|
//#endregion
|
|
6384
6724
|
//#region src/prompts/config-prompts.ts
|
|
6385
6725
|
async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
6726
|
+
if (flags.ecosystem === void 0 && flags.stackParts === void 0) {
|
|
6727
|
+
if (await getCompositionModeChoice() === "multi") return gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath);
|
|
6728
|
+
}
|
|
6386
6729
|
const result = await navigableGroup({
|
|
6387
6730
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
6388
6731
|
frontend: ({ results }) => {
|
|
@@ -7009,6 +7352,15 @@ async function trackProjectCreation(config, disableAnalytics = false) {
|
|
|
7009
7352
|
|
|
7010
7353
|
//#endregion
|
|
7011
7354
|
//#region src/utils/display-config.ts
|
|
7355
|
+
function getSelectedGraphPart(config, role, ownerPartId) {
|
|
7356
|
+
return config.stackParts?.find((part) => part.role === role && part.ownerPartId === ownerPartId && part.source !== "provided");
|
|
7357
|
+
}
|
|
7358
|
+
function getGraphDisplayValue(config, role) {
|
|
7359
|
+
const primaryBackend = getSelectedGraphPart(config, "backend");
|
|
7360
|
+
const part = role === "backend" ? primaryBackend : getSelectedGraphPart(config, role, primaryBackend?.id) ?? getSelectedGraphPart(config, role);
|
|
7361
|
+
if (!part) return null;
|
|
7362
|
+
return `${part.ecosystem}:${part.toolId}`;
|
|
7363
|
+
}
|
|
7012
7364
|
function displayConfig(config) {
|
|
7013
7365
|
const configDisplay = [];
|
|
7014
7366
|
if (config.projectName) configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`);
|
|
@@ -7019,14 +7371,26 @@ function displayConfig(config) {
|
|
|
7019
7371
|
}
|
|
7020
7372
|
if (config.uiLibrary !== void 0) configDisplay.push(`${pc.blue("UI Library:")} ${String(config.uiLibrary)}`);
|
|
7021
7373
|
if (config.cssFramework !== void 0) configDisplay.push(`${pc.blue("CSS Framework:")} ${String(config.cssFramework)}`);
|
|
7022
|
-
if (config.backend !== void 0)
|
|
7374
|
+
if (config.backend !== void 0) {
|
|
7375
|
+
const graphBackend = config.backend === "none" ? getGraphDisplayValue(config, "backend") : null;
|
|
7376
|
+
configDisplay.push(`${pc.blue("Backend:")} ${graphBackend ?? String(config.backend)}`);
|
|
7377
|
+
}
|
|
7023
7378
|
if (config.runtime !== void 0) configDisplay.push(`${pc.blue("Runtime:")} ${String(config.runtime)}`);
|
|
7024
|
-
if (config.api !== void 0)
|
|
7379
|
+
if (config.api !== void 0) {
|
|
7380
|
+
const graphApi = config.api === "none" ? getGraphDisplayValue(config, "api") : null;
|
|
7381
|
+
configDisplay.push(`${pc.blue("API:")} ${graphApi ?? String(config.api)}`);
|
|
7382
|
+
}
|
|
7025
7383
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
7026
|
-
if (config.orm !== void 0)
|
|
7384
|
+
if (config.orm !== void 0) {
|
|
7385
|
+
const graphOrm = config.orm === "none" ? getGraphDisplayValue(config, "orm") : null;
|
|
7386
|
+
configDisplay.push(`${pc.blue("ORM:")} ${graphOrm ?? String(config.orm)}`);
|
|
7387
|
+
}
|
|
7027
7388
|
if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
|
|
7028
7389
|
if (config.payments !== void 0) configDisplay.push(`${pc.blue("Payments:")} ${String(config.payments)}`);
|
|
7029
|
-
if (config.email !== void 0)
|
|
7390
|
+
if (config.email !== void 0) {
|
|
7391
|
+
const graphEmail = config.email === "none" ? getGraphDisplayValue(config, "email") : null;
|
|
7392
|
+
configDisplay.push(`${pc.blue("Email:")} ${graphEmail ?? String(config.email)}`);
|
|
7393
|
+
}
|
|
7030
7394
|
if (config.fileUpload !== void 0) configDisplay.push(`${pc.blue("File Upload:")} ${String(config.fileUpload)}`);
|
|
7031
7395
|
if (config.effect !== void 0) configDisplay.push(`${pc.blue("Effect:")} ${String(config.effect)}`);
|
|
7032
7396
|
if (config.ai !== void 0) configDisplay.push(`${pc.blue("AI:")} ${String(config.ai)}`);
|
|
@@ -7038,6 +7402,42 @@ function displayConfig(config) {
|
|
|
7038
7402
|
if (config.realtime !== void 0) configDisplay.push(`${pc.blue("Realtime:")} ${String(config.realtime)}`);
|
|
7039
7403
|
if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
|
|
7040
7404
|
if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
|
|
7405
|
+
const graphBackendPart = getSelectedGraphPart(config, "backend");
|
|
7406
|
+
if (graphBackendPart?.ecosystem === "go") {
|
|
7407
|
+
if (config.goCli && config.goCli !== "none") configDisplay.push(`${pc.blue("Go CLI:")} ${String(config.goCli)}`);
|
|
7408
|
+
if (config.goLogging && config.goLogging !== "none") configDisplay.push(`${pc.blue("Go Logging:")} ${String(config.goLogging)}`);
|
|
7409
|
+
}
|
|
7410
|
+
if (graphBackendPart?.ecosystem === "rust") {
|
|
7411
|
+
if (config.rustCli && config.rustCli !== "none") configDisplay.push(`${pc.blue("Rust CLI:")} ${String(config.rustCli)}`);
|
|
7412
|
+
if (config.rustLibraries && config.rustLibraries.length > 0) configDisplay.push(`${pc.blue("Rust Libraries:")} ${config.rustLibraries.join(", ")}`);
|
|
7413
|
+
if (config.rustLogging && config.rustLogging !== "none") configDisplay.push(`${pc.blue("Rust Logging:")} ${String(config.rustLogging)}`);
|
|
7414
|
+
if (config.rustErrorHandling && config.rustErrorHandling !== "none") configDisplay.push(`${pc.blue("Rust Error Handling:")} ${String(config.rustErrorHandling)}`);
|
|
7415
|
+
if (config.rustCaching && config.rustCaching !== "none") configDisplay.push(`${pc.blue("Rust Caching:")} ${String(config.rustCaching)}`);
|
|
7416
|
+
}
|
|
7417
|
+
if (graphBackendPart?.ecosystem === "python") {
|
|
7418
|
+
if (config.pythonValidation && config.pythonValidation !== "none") configDisplay.push(`${pc.blue("Python Validation:")} ${String(config.pythonValidation)}`);
|
|
7419
|
+
if (config.pythonAi && config.pythonAi.length > 0) configDisplay.push(`${pc.blue("Python AI:")} ${config.pythonAi.join(", ")}`);
|
|
7420
|
+
if (config.pythonTaskQueue && config.pythonTaskQueue !== "none") configDisplay.push(`${pc.blue("Python Task Queue:")} ${String(config.pythonTaskQueue)}`);
|
|
7421
|
+
if (config.pythonGraphql && config.pythonGraphql !== "none") configDisplay.push(`${pc.blue("Python GraphQL:")} ${String(config.pythonGraphql)}`);
|
|
7422
|
+
if (config.pythonQuality && config.pythonQuality !== "none") configDisplay.push(`${pc.blue("Python Quality:")} ${String(config.pythonQuality)}`);
|
|
7423
|
+
}
|
|
7424
|
+
if (graphBackendPart?.ecosystem === "java") {
|
|
7425
|
+
if (config.javaBuildTool && config.javaBuildTool !== "none") configDisplay.push(`${pc.blue("Java Build Tool:")} ${String(config.javaBuildTool)}`);
|
|
7426
|
+
if (config.javaLibraries && config.javaLibraries.length > 0) configDisplay.push(`${pc.blue("Java Libraries:")} ${config.javaLibraries.join(", ")}`);
|
|
7427
|
+
if (config.javaTestingLibraries && config.javaTestingLibraries.length > 0) configDisplay.push(`${pc.blue("Java Testing Libraries:")} ${config.javaTestingLibraries.join(", ")}`);
|
|
7428
|
+
}
|
|
7429
|
+
if (graphBackendPart?.ecosystem === "elixir") {
|
|
7430
|
+
if (config.elixirRealtime && config.elixirRealtime !== "none") configDisplay.push(`${pc.blue("Elixir Realtime:")} ${String(config.elixirRealtime)}`);
|
|
7431
|
+
if (config.elixirJobs && config.elixirJobs !== "none") configDisplay.push(`${pc.blue("Elixir Jobs:")} ${String(config.elixirJobs)}`);
|
|
7432
|
+
if (config.elixirValidation && config.elixirValidation !== "none") configDisplay.push(`${pc.blue("Elixir Validation:")} ${String(config.elixirValidation)}`);
|
|
7433
|
+
if (config.elixirHttp && config.elixirHttp !== "none") configDisplay.push(`${pc.blue("Elixir HTTP:")} ${String(config.elixirHttp)}`);
|
|
7434
|
+
if (config.elixirJson && config.elixirJson !== "none") configDisplay.push(`${pc.blue("Elixir JSON:")} ${String(config.elixirJson)}`);
|
|
7435
|
+
if (config.elixirCaching && config.elixirCaching !== "none") configDisplay.push(`${pc.blue("Elixir Caching:")} ${String(config.elixirCaching)}`);
|
|
7436
|
+
if (config.elixirObservability && config.elixirObservability !== "none") configDisplay.push(`${pc.blue("Elixir Observability:")} ${String(config.elixirObservability)}`);
|
|
7437
|
+
if (config.elixirTesting && config.elixirTesting !== "none") configDisplay.push(`${pc.blue("Elixir Testing:")} ${String(config.elixirTesting)}`);
|
|
7438
|
+
if (config.elixirQuality && config.elixirQuality !== "none") configDisplay.push(`${pc.blue("Elixir Quality:")} ${String(config.elixirQuality)}`);
|
|
7439
|
+
if (config.elixirDeploy && config.elixirDeploy !== "none") configDisplay.push(`${pc.blue("Elixir Deploy:")} ${String(config.elixirDeploy)}`);
|
|
7440
|
+
}
|
|
7041
7441
|
if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
|
|
7042
7442
|
if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
|
|
7043
7443
|
if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
|
|
@@ -7081,6 +7481,10 @@ function displayConfig(config) {
|
|
|
7081
7481
|
if (config.dbSetup !== void 0) configDisplay.push(`${pc.blue("Database Setup:")} ${String(config.dbSetup)}`);
|
|
7082
7482
|
if (config.webDeploy !== void 0) configDisplay.push(`${pc.blue("Web Deployment:")} ${String(config.webDeploy)}`);
|
|
7083
7483
|
if (config.serverDeploy !== void 0) configDisplay.push(`${pc.blue("Server Deployment:")} ${String(config.serverDeploy)}`);
|
|
7484
|
+
if (config.stackParts?.length) {
|
|
7485
|
+
const stackParts = config.stackParts.filter((part) => part.source !== "provided").map((part) => formatStackPartSpec(part, config.stackParts ?? []));
|
|
7486
|
+
if (stackParts.length > 0) configDisplay.push(`${pc.blue("Stack Parts:")} ${stackParts.join(", ")}`);
|
|
7487
|
+
}
|
|
7084
7488
|
if (configDisplay.length === 0) return pc.yellow("No configuration selected.");
|
|
7085
7489
|
return configDisplay.join("\n");
|
|
7086
7490
|
}
|
|
@@ -7109,6 +7513,103 @@ function appendCommonFlags(flags, config) {
|
|
|
7109
7513
|
if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
|
|
7110
7514
|
flags.push(config.install ? "--install" : "--no-install");
|
|
7111
7515
|
}
|
|
7516
|
+
function hasGraphPrimaryPart(config, role, ecosystem) {
|
|
7517
|
+
return config.stackParts?.some((part) => part.source !== "provided" && part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
|
|
7518
|
+
}
|
|
7519
|
+
function appendChangedStringFlag(flags, flag, value, defaultValue) {
|
|
7520
|
+
if (value !== defaultValue) flags.push(`--${flag} ${value}`);
|
|
7521
|
+
}
|
|
7522
|
+
function appendChangedArrayFlag(flags, flag, values, defaultValues) {
|
|
7523
|
+
if (values.length !== defaultValues.length || values.some((value, index) => value !== defaultValues[index])) flags.push(formatArrayFlag(flag, values));
|
|
7524
|
+
}
|
|
7525
|
+
function appendGraphExtraFlags(flags, config) {
|
|
7526
|
+
appendChangedArrayFlag(flags, "addons", config.addons, ["turborepo"]);
|
|
7527
|
+
appendChangedArrayFlag(flags, "examples", config.examples, []);
|
|
7528
|
+
appendChangedStringFlag(flags, "db-setup", config.dbSetup, "none");
|
|
7529
|
+
appendChangedStringFlag(flags, "web-deploy", config.webDeploy, "none");
|
|
7530
|
+
appendChangedStringFlag(flags, "server-deploy", config.serverDeploy, "none");
|
|
7531
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript")) {
|
|
7532
|
+
appendChangedStringFlag(flags, "css-framework", config.cssFramework, "tailwind");
|
|
7533
|
+
appendChangedStringFlag(flags, "ui-library", config.uiLibrary, "shadcn-ui");
|
|
7534
|
+
if (config.uiLibrary === "shadcn-ui") {
|
|
7535
|
+
appendChangedStringFlag(flags, "shadcn-base", config.shadcnBase ?? "radix", "radix");
|
|
7536
|
+
appendChangedStringFlag(flags, "shadcn-style", config.shadcnStyle ?? "nova", "nova");
|
|
7537
|
+
appendChangedStringFlag(flags, "shadcn-icon-library", config.shadcnIconLibrary ?? "lucide", "lucide");
|
|
7538
|
+
appendChangedStringFlag(flags, "shadcn-color-theme", config.shadcnColorTheme ?? "neutral", "neutral");
|
|
7539
|
+
appendChangedStringFlag(flags, "shadcn-base-color", config.shadcnBaseColor ?? "neutral", "neutral");
|
|
7540
|
+
appendChangedStringFlag(flags, "shadcn-font", config.shadcnFont ?? "inter", "inter");
|
|
7541
|
+
appendChangedStringFlag(flags, "shadcn-radius", config.shadcnRadius ?? "default", "default");
|
|
7542
|
+
}
|
|
7543
|
+
appendChangedStringFlag(flags, "state-management", config.stateManagement, "none");
|
|
7544
|
+
appendChangedStringFlag(flags, "forms", config.forms, "react-hook-form");
|
|
7545
|
+
appendChangedStringFlag(flags, "validation", config.validation, "zod");
|
|
7546
|
+
appendChangedStringFlag(flags, "testing", config.testing, "vitest");
|
|
7547
|
+
appendChangedStringFlag(flags, "animation", config.animation, "none");
|
|
7548
|
+
}
|
|
7549
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript") || hasGraphPrimaryPart(config, "backend", "typescript")) {
|
|
7550
|
+
appendChangedStringFlag(flags, "payments", config.payments, "none");
|
|
7551
|
+
appendChangedStringFlag(flags, "email", config.email, "none");
|
|
7552
|
+
appendChangedStringFlag(flags, "file-upload", config.fileUpload, "none");
|
|
7553
|
+
appendChangedStringFlag(flags, "effect", config.effect, "none");
|
|
7554
|
+
appendChangedStringFlag(flags, "ai", config.ai, "none");
|
|
7555
|
+
appendChangedStringFlag(flags, "realtime", config.realtime, "none");
|
|
7556
|
+
appendChangedStringFlag(flags, "job-queue", config.jobQueue, "none");
|
|
7557
|
+
appendChangedStringFlag(flags, "logging", config.logging, "none");
|
|
7558
|
+
appendChangedStringFlag(flags, "observability", config.observability, "none");
|
|
7559
|
+
appendChangedStringFlag(flags, "feature-flags", config.featureFlags, "none");
|
|
7560
|
+
appendChangedStringFlag(flags, "caching", config.caching, "none");
|
|
7561
|
+
appendChangedStringFlag(flags, "i18n", config.i18n, "none");
|
|
7562
|
+
appendChangedStringFlag(flags, "cms", config.cms, "none");
|
|
7563
|
+
appendChangedStringFlag(flags, "search", config.search, "none");
|
|
7564
|
+
appendChangedStringFlag(flags, "file-storage", config.fileStorage, "none");
|
|
7565
|
+
}
|
|
7566
|
+
if (hasGraphPrimaryPart(config, "mobile")) {
|
|
7567
|
+
appendChangedStringFlag(flags, "mobile-navigation", config.mobileNavigation, "expo-router");
|
|
7568
|
+
appendChangedStringFlag(flags, "mobile-ui", config.mobileUI, "none");
|
|
7569
|
+
appendChangedStringFlag(flags, "mobile-storage", config.mobileStorage, "none");
|
|
7570
|
+
appendChangedStringFlag(flags, "mobile-testing", config.mobileTesting, "none");
|
|
7571
|
+
appendChangedStringFlag(flags, "mobile-push", config.mobilePush, "none");
|
|
7572
|
+
appendChangedStringFlag(flags, "mobile-ota", config.mobileOTA, "none");
|
|
7573
|
+
appendChangedStringFlag(flags, "mobile-deep-linking", config.mobileDeepLinking, "none");
|
|
7574
|
+
}
|
|
7575
|
+
if (hasGraphPrimaryPart(config, "frontend", "rust")) appendChangedStringFlag(flags, "rust-frontend", config.rustFrontend, "none");
|
|
7576
|
+
if (hasGraphPrimaryPart(config, "backend", "rust")) {
|
|
7577
|
+
appendChangedStringFlag(flags, "rust-cli", config.rustCli, "none");
|
|
7578
|
+
appendChangedArrayFlag(flags, "rust-libraries", config.rustLibraries, []);
|
|
7579
|
+
appendChangedStringFlag(flags, "rust-logging", config.rustLogging, "tracing");
|
|
7580
|
+
appendChangedStringFlag(flags, "rust-error-handling", config.rustErrorHandling, "anyhow-thiserror");
|
|
7581
|
+
appendChangedStringFlag(flags, "rust-caching", config.rustCaching, "none");
|
|
7582
|
+
}
|
|
7583
|
+
if (hasGraphPrimaryPart(config, "backend", "python")) {
|
|
7584
|
+
appendChangedStringFlag(flags, "python-validation", config.pythonValidation, "none");
|
|
7585
|
+
appendChangedArrayFlag(flags, "python-ai", config.pythonAi, []);
|
|
7586
|
+
appendChangedStringFlag(flags, "python-task-queue", config.pythonTaskQueue, "none");
|
|
7587
|
+
appendChangedStringFlag(flags, "python-graphql", config.pythonGraphql, "none");
|
|
7588
|
+
appendChangedStringFlag(flags, "python-quality", config.pythonQuality, "none");
|
|
7589
|
+
}
|
|
7590
|
+
if (hasGraphPrimaryPart(config, "backend", "go")) {
|
|
7591
|
+
appendChangedStringFlag(flags, "go-cli", config.goCli, "none");
|
|
7592
|
+
appendChangedStringFlag(flags, "go-logging", config.goLogging, "none");
|
|
7593
|
+
}
|
|
7594
|
+
if (hasGraphPrimaryPart(config, "backend", "java")) {
|
|
7595
|
+
appendChangedStringFlag(flags, "java-build-tool", config.javaBuildTool, "maven");
|
|
7596
|
+
appendChangedArrayFlag(flags, "java-libraries", config.javaLibraries, []);
|
|
7597
|
+
appendChangedArrayFlag(flags, "java-testing-libraries", config.javaTestingLibraries, ["junit5"]);
|
|
7598
|
+
}
|
|
7599
|
+
if (hasGraphPrimaryPart(config, "backend", "elixir")) {
|
|
7600
|
+
appendChangedStringFlag(flags, "elixir-realtime", config.elixirRealtime, "channels");
|
|
7601
|
+
appendChangedStringFlag(flags, "elixir-jobs", config.elixirJobs, "none");
|
|
7602
|
+
appendChangedStringFlag(flags, "elixir-validation", config.elixirValidation, "ecto-changesets");
|
|
7603
|
+
appendChangedStringFlag(flags, "elixir-http", config.elixirHttp, "req");
|
|
7604
|
+
appendChangedStringFlag(flags, "elixir-json", config.elixirJson, "jason");
|
|
7605
|
+
if (!hasGraphPart(config, "email", "elixir")) appendChangedStringFlag(flags, "elixir-email", config.elixirEmail, "none");
|
|
7606
|
+
appendChangedStringFlag(flags, "elixir-caching", config.elixirCaching, "none");
|
|
7607
|
+
appendChangedStringFlag(flags, "elixir-observability", config.elixirObservability, "telemetry");
|
|
7608
|
+
appendChangedStringFlag(flags, "elixir-testing", config.elixirTesting, "ex_unit");
|
|
7609
|
+
appendChangedStringFlag(flags, "elixir-quality", config.elixirQuality, "credo");
|
|
7610
|
+
appendChangedStringFlag(flags, "elixir-deploy", config.elixirDeploy, "none");
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7112
7613
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
7113
7614
|
flags.push(`--email ${config.email}`);
|
|
7114
7615
|
flags.push(`--observability ${config.observability}`);
|
|
@@ -7271,6 +7772,12 @@ function getElixirFlags(config) {
|
|
|
7271
7772
|
}
|
|
7272
7773
|
function generateReproducibleCommand(config) {
|
|
7273
7774
|
let flags;
|
|
7775
|
+
if (config.stackParts && config.stackParts.length > 0) {
|
|
7776
|
+
flags = config.stackParts.filter((part) => part.source !== "provided").map((part) => `--part ${(0, types_exports.formatStackPartSpec)(part, config.stackParts ?? [])}`);
|
|
7777
|
+
appendGraphExtraFlags(flags, config);
|
|
7778
|
+
appendCommonFlags(flags, config);
|
|
7779
|
+
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7780
|
+
}
|
|
7274
7781
|
switch (config.ecosystem) {
|
|
7275
7782
|
case "react-native":
|
|
7276
7783
|
flags = getReactNativeFlags(config);
|
|
@@ -7298,6 +7805,98 @@ function generateReproducibleCommand(config) {
|
|
|
7298
7805
|
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7299
7806
|
}
|
|
7300
7807
|
|
|
7808
|
+
//#endregion
|
|
7809
|
+
//#region src/utils/generated-checks.ts
|
|
7810
|
+
function getGraphTarget(config) {
|
|
7811
|
+
const backend = getPrimaryGraphPart(config, "backend");
|
|
7812
|
+
if (!backend || backend.ecosystem === "typescript" || backend.ecosystem === "react-native" || backend.ecosystem === "universal") return null;
|
|
7813
|
+
return {
|
|
7814
|
+
ecosystem: backend.ecosystem,
|
|
7815
|
+
projectDir: path.join(config.projectDir, backend.targetPath ?? "apps/server")
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
function getSingleEcosystemTarget(config) {
|
|
7819
|
+
if (config.ecosystem === "typescript" || config.ecosystem === "react-native" || config.ecosystem === "java") return null;
|
|
7820
|
+
return {
|
|
7821
|
+
ecosystem: config.ecosystem,
|
|
7822
|
+
projectDir: config.projectDir
|
|
7823
|
+
};
|
|
7824
|
+
}
|
|
7825
|
+
async function runCommand(cwd, command, args) {
|
|
7826
|
+
await $({
|
|
7827
|
+
cwd,
|
|
7828
|
+
stdout: "inherit",
|
|
7829
|
+
stderr: "inherit"
|
|
7830
|
+
})`${command} ${args}`;
|
|
7831
|
+
}
|
|
7832
|
+
async function verifyTarget(target) {
|
|
7833
|
+
const s = spinner();
|
|
7834
|
+
const cwd = target.projectDir;
|
|
7835
|
+
switch (target.ecosystem) {
|
|
7836
|
+
case "go":
|
|
7837
|
+
s.start("Verifying generated Go server...");
|
|
7838
|
+
await runCommand(cwd, "go", ["mod", "tidy"]);
|
|
7839
|
+
await runCommand(cwd, "go", ["test", "./..."]);
|
|
7840
|
+
s.stop("Generated Go server checks passed");
|
|
7841
|
+
return;
|
|
7842
|
+
case "rust":
|
|
7843
|
+
s.start("Verifying generated Rust server...");
|
|
7844
|
+
await runCommand(cwd, "cargo", ["check"]);
|
|
7845
|
+
s.stop("Generated Rust server checks passed");
|
|
7846
|
+
return;
|
|
7847
|
+
case "python":
|
|
7848
|
+
s.start("Verifying generated Python server...");
|
|
7849
|
+
await runCommand(cwd, "uv", ["sync"]);
|
|
7850
|
+
await runCommand(cwd, "uv", [
|
|
7851
|
+
"run",
|
|
7852
|
+
"ruff",
|
|
7853
|
+
"check",
|
|
7854
|
+
"."
|
|
7855
|
+
]);
|
|
7856
|
+
s.stop("Generated Python server checks passed");
|
|
7857
|
+
return;
|
|
7858
|
+
case "elixir":
|
|
7859
|
+
if (!await commandExists("mix")) {
|
|
7860
|
+
log.warn(pc.yellow("Skipping Elixir verification because mix is not on PATH"));
|
|
7861
|
+
return;
|
|
7862
|
+
}
|
|
7863
|
+
s.start("Verifying generated Elixir server...");
|
|
7864
|
+
await runCommand(cwd, "mix", ["deps.get"]);
|
|
7865
|
+
await runCommand(cwd, "mix", ["compile"]);
|
|
7866
|
+
s.stop("Generated Elixir server checks passed");
|
|
7867
|
+
return;
|
|
7868
|
+
default: log.warn(pc.yellow(`No generated checks are configured for ${target.ecosystem}`));
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
async function runGeneratedChecks(config) {
|
|
7872
|
+
const target = getGraphTarget(config) ?? getSingleEcosystemTarget(config);
|
|
7873
|
+
if (!target) {
|
|
7874
|
+
log.warn(pc.yellow("No generated checks are configured for this stack"));
|
|
7875
|
+
return;
|
|
7876
|
+
}
|
|
7877
|
+
await verifyTarget(target);
|
|
7878
|
+
}
|
|
7879
|
+
|
|
7880
|
+
//#endregion
|
|
7881
|
+
//#region src/utils/preflight-display.ts
|
|
7882
|
+
function displayPreflightWarnings({ warnings }) {
|
|
7883
|
+
if (warnings.length === 0) return;
|
|
7884
|
+
const count = warnings.length;
|
|
7885
|
+
const lines = [pc.bold(pc.yellow(`${count} feature${count > 1 ? "s" : ""} will not generate templates:`)), ""];
|
|
7886
|
+
warnings.forEach((w, i) => {
|
|
7887
|
+
const selected = Array.isArray(w.selectedValue) ? w.selectedValue.join(", ") : w.selectedValue;
|
|
7888
|
+
lines.push(` ${pc.yellow(`${i + 1}.`)} ${pc.bold(w.featureDisplayName)} ${pc.dim(`(${selected})`)}`);
|
|
7889
|
+
lines.push(` ${w.reason}`);
|
|
7890
|
+
w.suggestions.forEach((s) => lines.push(` ${pc.green("•")} ${s}`));
|
|
7891
|
+
if (i < count - 1) lines.push("");
|
|
7892
|
+
});
|
|
7893
|
+
consola.box({
|
|
7894
|
+
title: pc.yellow("Pre-flight Check"),
|
|
7895
|
+
message: lines.join("\n"),
|
|
7896
|
+
style: { borderColor: "yellow" }
|
|
7897
|
+
});
|
|
7898
|
+
}
|
|
7899
|
+
|
|
7301
7900
|
//#endregion
|
|
7302
7901
|
//#region src/utils/project-directory.ts
|
|
7303
7902
|
async function handleDirectoryConflict(currentPathInput) {
|
|
@@ -7648,6 +8247,10 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7648
8247
|
const db = cfg.database;
|
|
7649
8248
|
const orm = cfg.orm;
|
|
7650
8249
|
const has = (k) => flags ? flags.has(k) : true;
|
|
8250
|
+
const hasGraphOrm = cfg.stackParts?.some((part) => part.role === "orm" && part.source !== "provided");
|
|
8251
|
+
const ecosystemOrm = getEcosystemOrm(cfg);
|
|
8252
|
+
const hasEcosystemOrm = ecosystemOrm !== void 0 && ecosystemOrm !== "none";
|
|
8253
|
+
const isNonTypeScriptSqliteDefault = cfg.ecosystem !== void 0 && cfg.ecosystem !== "typescript" && cfg.ecosystem !== "react-native" && db === "sqlite" && !hasEcosystemOrm;
|
|
7651
8254
|
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") incompatibilityError({
|
|
7652
8255
|
message: "Mongoose ORM requires MongoDB database.",
|
|
7653
8256
|
provided: {
|
|
@@ -7704,7 +8307,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7704
8307
|
},
|
|
7705
8308
|
suggestions: ["Use --orm mongoose", "Use --orm prisma"]
|
|
7706
8309
|
});
|
|
7707
|
-
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none") missingRequirementError({
|
|
8310
|
+
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none" && !hasGraphOrm && !hasEcosystemOrm && !isNonTypeScriptSqliteDefault) missingRequirementError({
|
|
7708
8311
|
message: "Database selection requires an ORM.",
|
|
7709
8312
|
provided: {
|
|
7710
8313
|
database: db,
|
|
@@ -7746,6 +8349,26 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7746
8349
|
]
|
|
7747
8350
|
});
|
|
7748
8351
|
}
|
|
8352
|
+
function getEcosystemOrm(cfg) {
|
|
8353
|
+
switch (cfg.ecosystem) {
|
|
8354
|
+
case "rust": return cfg.rustOrm;
|
|
8355
|
+
case "python": return cfg.pythonOrm;
|
|
8356
|
+
case "go": return cfg.goOrm;
|
|
8357
|
+
case "java": return cfg.javaOrm;
|
|
8358
|
+
case "elixir": return cfg.elixirOrm;
|
|
8359
|
+
default: return;
|
|
8360
|
+
}
|
|
8361
|
+
}
|
|
8362
|
+
function getEcosystemBackend(cfg) {
|
|
8363
|
+
switch (cfg.ecosystem) {
|
|
8364
|
+
case "rust": return cfg.rustWebFramework && cfg.rustWebFramework !== "none" ? cfg.rustWebFramework : void 0;
|
|
8365
|
+
case "python": return cfg.pythonWebFramework && cfg.pythonWebFramework !== "none" ? cfg.pythonWebFramework : void 0;
|
|
8366
|
+
case "go": return cfg.goWebFramework && cfg.goWebFramework !== "none" ? cfg.goWebFramework : void 0;
|
|
8367
|
+
case "java": return cfg.javaWebFramework && cfg.javaWebFramework !== "none" ? cfg.javaWebFramework : void 0;
|
|
8368
|
+
case "elixir": return cfg.elixirWebFramework && cfg.elixirWebFramework !== "none" ? cfg.elixirWebFramework : void 0;
|
|
8369
|
+
default: return;
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
7749
8372
|
function validateDatabaseSetup(config, providedFlags) {
|
|
7750
8373
|
const { dbSetup, database, runtime } = config;
|
|
7751
8374
|
if (providedFlags.has("dbSetup") && providedFlags.has("database") && dbSetup && dbSetup !== "none" && database === "none") exitWithError("Database setup requires a database. Please choose a database or set '--db-setup none'.");
|
|
@@ -7871,7 +8494,9 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
7871
8494
|
}
|
|
7872
8495
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
7873
8496
|
const { backend } = config;
|
|
7874
|
-
|
|
8497
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8498
|
+
const hasEcosystemBackend = getEcosystemBackend(config) !== void 0;
|
|
8499
|
+
if (backend !== "none" || hasGraphBackend || hasEcosystemBackend) return;
|
|
7875
8500
|
const has = (k) => providedFlags.has(k);
|
|
7876
8501
|
if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
|
|
7877
8502
|
if (has("database") && config.database !== "none") exitWithError("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
|
|
@@ -8215,6 +8840,10 @@ function validatePythonApiConstraints(config) {
|
|
|
8215
8840
|
});
|
|
8216
8841
|
}
|
|
8217
8842
|
function validateFullConfig(config, providedFlags, options) {
|
|
8843
|
+
if (config.stackParts && !options.yolo) {
|
|
8844
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8845
|
+
if (graphValidation.issues.length > 0) exitWithError(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8846
|
+
}
|
|
8218
8847
|
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
8219
8848
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
8220
8849
|
validateDatabaseSetup(config, providedFlags);
|
|
@@ -8233,7 +8862,8 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8233
8862
|
validateSearchConstraints(config);
|
|
8234
8863
|
validateJavaConstraints(config, providedFlags);
|
|
8235
8864
|
validateElixirConstraints(config);
|
|
8236
|
-
|
|
8865
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8866
|
+
if (!(providedFlags.has("serverDeploy") && !options.yes && !options.part?.length && options.ecosystem === void 0 && options.backend === void 0 && config.stackParts === void 0)) validateServerDeployRequiresBackend(config.serverDeploy, config.backend, Boolean(hasGraphBackend));
|
|
8237
8867
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
8238
8868
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
8239
8869
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
|
|
@@ -8264,6 +8894,10 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8264
8894
|
}
|
|
8265
8895
|
function validateConfigForProgrammaticUse(config) {
|
|
8266
8896
|
try {
|
|
8897
|
+
if (config.stackParts) {
|
|
8898
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8899
|
+
if (graphValidation.issues.length > 0) throw new Error(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8900
|
+
}
|
|
8267
8901
|
validateEcosystemAuthCompatibility(config);
|
|
8268
8902
|
validateDatabaseOrmAuth(config);
|
|
8269
8903
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
@@ -8355,7 +8989,14 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
8355
8989
|
return config;
|
|
8356
8990
|
}
|
|
8357
8991
|
function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
8358
|
-
if (!options.yolo)
|
|
8992
|
+
if (!options.yolo) {
|
|
8993
|
+
validateYesFlagCombination(options, getProvidedFlags(options));
|
|
8994
|
+
try {
|
|
8995
|
+
validateArrayOptions(options);
|
|
8996
|
+
} catch (error) {
|
|
8997
|
+
exitWithError(error instanceof Error ? error.message : String(error));
|
|
8998
|
+
}
|
|
8999
|
+
}
|
|
8359
9000
|
const config = processFlags(options, projectName);
|
|
8360
9001
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
8361
9002
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
@@ -8367,26 +9008,6 @@ function validateConfigCompatibility(config, providedFlags, options) {
|
|
|
8367
9008
|
else validateConfigForProgrammaticUse(config);
|
|
8368
9009
|
}
|
|
8369
9010
|
|
|
8370
|
-
//#endregion
|
|
8371
|
-
//#region src/utils/preflight-display.ts
|
|
8372
|
-
function displayPreflightWarnings({ warnings }) {
|
|
8373
|
-
if (warnings.length === 0) return;
|
|
8374
|
-
const count = warnings.length;
|
|
8375
|
-
const lines = [pc.bold(pc.yellow(`${count} feature${count > 1 ? "s" : ""} will not generate templates:`)), ""];
|
|
8376
|
-
warnings.forEach((w, i) => {
|
|
8377
|
-
const selected = Array.isArray(w.selectedValue) ? w.selectedValue.join(", ") : w.selectedValue;
|
|
8378
|
-
lines.push(` ${pc.yellow(`${i + 1}.`)} ${pc.bold(w.featureDisplayName)} ${pc.dim(`(${selected})`)}`);
|
|
8379
|
-
lines.push(` ${w.reason}`);
|
|
8380
|
-
w.suggestions.forEach((s) => lines.push(` ${pc.green("•")} ${s}`));
|
|
8381
|
-
if (i < count - 1) lines.push("");
|
|
8382
|
-
});
|
|
8383
|
-
consola.box({
|
|
8384
|
-
title: pc.yellow("Pre-flight Check"),
|
|
8385
|
-
message: lines.join("\n"),
|
|
8386
|
-
style: { borderColor: "yellow" }
|
|
8387
|
-
});
|
|
8388
|
-
}
|
|
8389
|
-
|
|
8390
9011
|
//#endregion
|
|
8391
9012
|
//#region src/utils/file-formatter.ts
|
|
8392
9013
|
const formatOptions = {
|
|
@@ -9658,7 +10279,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
9658
10279
|
const hasHusky = addons?.includes("husky");
|
|
9659
10280
|
const hasLefthook = addons?.includes("lefthook");
|
|
9660
10281
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
|
|
9661
|
-
const
|
|
10282
|
+
const graphOrmPart = getGraphPart(config, "orm");
|
|
10283
|
+
const databaseInstructions = !isConvex && database !== "none" && !graphOrmPart ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
10284
|
+
const graphDatabaseInstructions = !isConvex && database !== "none" && graphOrmPart ? getGraphDatabaseInstructions(database, graphOrmPart.ecosystem, graphOrmPart.toolId) : "";
|
|
9662
10285
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
9663
10286
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
9664
10287
|
const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
|
|
@@ -9672,14 +10295,17 @@ async function displayPostInstallInstructions(config) {
|
|
|
9672
10295
|
const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
|
|
9673
10296
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
9674
10297
|
const vercelDeployInstructions = getVercelDeployInstructions(webDeploy, serverDeploy, backend);
|
|
10298
|
+
const graphBackendDeployInstructions = getGraphBackendDeployInstructions(config);
|
|
9675
10299
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
9676
10300
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
9677
10301
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
9678
|
-
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
10302
|
+
const noOrmWarning = !isConvex && database !== "none" && orm === "none" && !hasGraphPart(config, "orm") ? getNoOrmWarning() : "";
|
|
9679
10303
|
const hasFresh = frontend?.includes("fresh");
|
|
9680
10304
|
const webPort = String(getLocalWebDevPort(frontend ?? []));
|
|
9681
10305
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
9682
|
-
|
|
10306
|
+
const graphSummary = getGraphSummary(config);
|
|
10307
|
+
let output = graphSummary ? `${pc.bold("Generated:")} ${graphSummary}\n\n` : "";
|
|
10308
|
+
output += `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
9683
10309
|
let stepCounter = 2;
|
|
9684
10310
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
9685
10311
|
if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH.\n Install: ${pc.underline("https://docs.deno.com/runtime/getting_started/installation/")}\n`;
|
|
@@ -9696,13 +10322,14 @@ async function displayPostInstallInstructions(config) {
|
|
|
9696
10322
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
9697
10323
|
}
|
|
9698
10324
|
}
|
|
9699
|
-
const
|
|
10325
|
+
const graphBackendUrl = getGraphBackendUrl(config);
|
|
10326
|
+
const hasStandaloneBackend = backend !== "none" || Boolean(graphBackendUrl);
|
|
9700
10327
|
if (hasWeb || hasStandaloneBackend || addons?.includes("starlight") || addons?.includes("fumadocs")) {
|
|
9701
10328
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
9702
10329
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
9703
10330
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
9704
10331
|
if (!isConvex && !isBackendSelf && hasStandaloneBackend) {
|
|
9705
|
-
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
10332
|
+
output += `${pc.cyan("•")} Backend API: ${graphBackendUrl ?? "http://localhost:3000"}\n`;
|
|
9706
10333
|
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api-reference\n`;
|
|
9707
10334
|
}
|
|
9708
10335
|
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
@@ -9711,6 +10338,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9711
10338
|
}
|
|
9712
10339
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
9713
10340
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
10341
|
+
if (graphDatabaseInstructions) output += `\n${graphDatabaseInstructions.trim()}\n`;
|
|
9714
10342
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
9715
10343
|
if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
|
|
9716
10344
|
if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
|
|
@@ -9718,6 +10346,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9718
10346
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
9719
10347
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
9720
10348
|
if (vercelDeployInstructions) output += `\n${vercelDeployInstructions.trim()}\n`;
|
|
10349
|
+
if (graphBackendDeployInstructions) output += `\n${graphBackendDeployInstructions.trim()}\n`;
|
|
9721
10350
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
9722
10351
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
9723
10352
|
if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
|
|
@@ -9809,6 +10438,9 @@ function getStarlightInstructions(runCmd) {
|
|
|
9809
10438
|
function getNoOrmWarning() {
|
|
9810
10439
|
return `\n${pc.yellow("WARNING:")} Database selected without an ORM. Features requiring\n database access (e.g., examples, auth) need manual setup.`;
|
|
9811
10440
|
}
|
|
10441
|
+
function getGraphDatabaseInstructions(database, ecosystem, ormTool) {
|
|
10442
|
+
return `${pc.bold("Database setup:")}\n${pc.cyan("•")} Database: ${database}\n${pc.cyan("•")} ORM: ${ecosystem}:${ormTool}\n${pc.cyan("•")} Configure ${pc.white("DATABASE_URL")} in ${pc.white("apps/server/.env")}`;
|
|
10443
|
+
}
|
|
9812
10444
|
function getBunWebNativeWarning() {
|
|
9813
10445
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
9814
10446
|
}
|
|
@@ -10067,14 +10699,14 @@ function displayJavaInstructions(config) {
|
|
|
10067
10699
|
}
|
|
10068
10700
|
const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
|
|
10069
10701
|
const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
|
|
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;
|
|
10702
|
+
const runCommand$1 = 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;
|
|
10071
10703
|
const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
|
|
10072
10704
|
const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
|
|
10073
10705
|
const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
|
|
10074
10706
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10075
10707
|
let stepCounter = 2;
|
|
10076
10708
|
if (!depsInstalled && buildToolCommand && effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan(`${stepCounter++}.`)} ${buildToolCommand} test\n`;
|
|
10077
|
-
if (runCommand) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10709
|
+
if (runCommand$1) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10078
10710
|
else if (sourceCompileCommand && sourceRunCommand) {
|
|
10079
10711
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceCompileCommand}\n`;
|
|
10080
10712
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
|
|
@@ -10122,9 +10754,9 @@ function displayJavaInstructions(config) {
|
|
|
10122
10754
|
output += `${pc.cyan("•")} Testing: ${testingList}\n`;
|
|
10123
10755
|
}
|
|
10124
10756
|
output += `\n${pc.bold("Common Java commands:")}\n`;
|
|
10125
|
-
if (buildToolCommand && runCommand && packageCommand) {
|
|
10757
|
+
if (buildToolCommand && runCommand$1 && packageCommand) {
|
|
10126
10758
|
if (effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan("•")} Test: ${buildToolCommand} test\n`;
|
|
10127
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10759
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10128
10760
|
output += `${pc.cyan("•")} Package: ${packageCommand}\n`;
|
|
10129
10761
|
} else if (sourceCompileCommand && sourceRunCommand) {
|
|
10130
10762
|
output += `${pc.cyan("•")} Compile: ${sourceCompileCommand}\n`;
|
|
@@ -10142,14 +10774,14 @@ function displayJavaInstructions(config) {
|
|
|
10142
10774
|
function displayPythonInstructions(config) {
|
|
10143
10775
|
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
|
|
10144
10776
|
const cdCmd = `cd ${relativePath}`;
|
|
10145
|
-
let runCommand = "uv run uvicorn app.main:app --reload";
|
|
10146
|
-
if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
|
|
10147
|
-
else if (pythonWebFramework === "flask") runCommand = "uv run flask --app app.main run --reload";
|
|
10148
|
-
else if (pythonWebFramework === "litestar") runCommand = "litestar --app src.app.main:app run --reload --port 3001";
|
|
10777
|
+
let runCommand$1 = "uv run uvicorn app.main:app --reload";
|
|
10778
|
+
if (pythonWebFramework === "django") runCommand$1 = "uv run python manage.py runserver";
|
|
10779
|
+
else if (pythonWebFramework === "flask") runCommand$1 = "uv run flask --app app.main run --reload";
|
|
10780
|
+
else if (pythonWebFramework === "litestar") runCommand$1 = "litestar --app src.app.main:app run --reload --port 3001";
|
|
10149
10781
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10150
10782
|
let stepCounter = 2;
|
|
10151
10783
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} uv sync\n`;
|
|
10152
|
-
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10784
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10153
10785
|
output += `\n${pc.bold("Your Python project includes:")}\n`;
|
|
10154
10786
|
if (pythonWebFramework && pythonWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
|
|
10155
10787
|
fastapi: "FastAPI",
|
|
@@ -10187,7 +10819,7 @@ function displayPythonInstructions(config) {
|
|
|
10187
10819
|
}[pythonQuality] || pythonQuality}\n`;
|
|
10188
10820
|
output += `\n${pc.bold("Common Python commands:")}\n`;
|
|
10189
10821
|
output += `${pc.cyan("•")} Install: uv sync\n`;
|
|
10190
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10822
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10191
10823
|
output += `${pc.cyan("•")} Test: uv run pytest\n`;
|
|
10192
10824
|
if (pythonQuality === "ruff") {
|
|
10193
10825
|
output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
|
|
@@ -10349,7 +10981,7 @@ function getYesBaseConfig(flagConfig) {
|
|
|
10349
10981
|
};
|
|
10350
10982
|
}
|
|
10351
10983
|
function shouldPromptForVersionChannel(input) {
|
|
10352
|
-
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
10984
|
+
if (input.yes || input.part?.length || input.versionChannel !== void 0 || isSilent()) return false;
|
|
10353
10985
|
return canPromptInteractively();
|
|
10354
10986
|
}
|
|
10355
10987
|
async function createProjectHandler(input, options = {}) {
|
|
@@ -10534,7 +11166,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10534
11166
|
}
|
|
10535
11167
|
}
|
|
10536
11168
|
let config;
|
|
10537
|
-
if (cliInput.yes) {
|
|
11169
|
+
if (cliInput.yes || cliInput.part?.length) {
|
|
10538
11170
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
10539
11171
|
config = {
|
|
10540
11172
|
...getYesBaseConfig(flagConfig),
|
|
@@ -10563,6 +11195,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10563
11195
|
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, currentPathInput),
|
|
10564
11196
|
versionChannel
|
|
10565
11197
|
};
|
|
11198
|
+
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
10566
11199
|
}
|
|
10567
11200
|
const preflight = validatePreflightConfig(config);
|
|
10568
11201
|
if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
|
|
@@ -10609,6 +11242,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10609
11242
|
};
|
|
10610
11243
|
}
|
|
10611
11244
|
await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
|
|
11245
|
+
if (cliInput.verify ?? input.verify) await runGeneratedChecks(config);
|
|
10612
11246
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
10613
11247
|
if (!isSilent()) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
10614
11248
|
await trackProjectCreation(config, input.disableAnalytics);
|
|
@@ -10938,113 +11572,116 @@ async function createVirtual(options) {
|
|
|
10938
11572
|
const isReactNative = ecosystem === "react-native";
|
|
10939
11573
|
const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
|
|
10940
11574
|
const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
|
|
11575
|
+
const config = {
|
|
11576
|
+
ecosystem,
|
|
11577
|
+
projectName: options.projectName || "my-project",
|
|
11578
|
+
projectDir: "/virtual",
|
|
11579
|
+
relativePath: "./virtual",
|
|
11580
|
+
database: options.database || "none",
|
|
11581
|
+
orm: options.orm || "none",
|
|
11582
|
+
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
11583
|
+
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
11584
|
+
frontend,
|
|
11585
|
+
addons: options.addons || [],
|
|
11586
|
+
examples: options.examples || [],
|
|
11587
|
+
auth: options.auth || "none",
|
|
11588
|
+
payments: options.payments || "none",
|
|
11589
|
+
email: options.email || "none",
|
|
11590
|
+
fileUpload: options.fileUpload || "none",
|
|
11591
|
+
effect: options.effect || "none",
|
|
11592
|
+
git: options.git ?? false,
|
|
11593
|
+
packageManager: options.packageManager || "bun",
|
|
11594
|
+
versionChannel: options.versionChannel || "stable",
|
|
11595
|
+
install: false,
|
|
11596
|
+
dbSetup: options.dbSetup || "none",
|
|
11597
|
+
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
11598
|
+
webDeploy: options.webDeploy || "none",
|
|
11599
|
+
serverDeploy: options.serverDeploy || "none",
|
|
11600
|
+
astroIntegration: options.astroIntegration || "none",
|
|
11601
|
+
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
11602
|
+
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
11603
|
+
shadcnBase: options.shadcnBase ?? "radix",
|
|
11604
|
+
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
11605
|
+
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
11606
|
+
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
11607
|
+
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
11608
|
+
shadcnFont: options.shadcnFont ?? "inter",
|
|
11609
|
+
shadcnRadius: options.shadcnRadius ?? "default",
|
|
11610
|
+
ai: options.ai || "none",
|
|
11611
|
+
stateManagement: options.stateManagement || "none",
|
|
11612
|
+
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
11613
|
+
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
11614
|
+
validation: options.validation || "zod",
|
|
11615
|
+
realtime: options.realtime || "none",
|
|
11616
|
+
jobQueue: options.jobQueue || "none",
|
|
11617
|
+
animation: options.animation || "none",
|
|
11618
|
+
logging: options.logging || "none",
|
|
11619
|
+
observability: options.observability || "none",
|
|
11620
|
+
featureFlags: options.featureFlags || "none",
|
|
11621
|
+
analytics: options.analytics || "none",
|
|
11622
|
+
mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
|
|
11623
|
+
mobileUI: options.mobileUI || "none",
|
|
11624
|
+
mobileStorage: options.mobileStorage || "none",
|
|
11625
|
+
mobileTesting: options.mobileTesting || "none",
|
|
11626
|
+
mobilePush: options.mobilePush || "none",
|
|
11627
|
+
mobileOTA: options.mobileOTA || "none",
|
|
11628
|
+
mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
|
|
11629
|
+
cms: options.cms || "none",
|
|
11630
|
+
caching: options.caching || "none",
|
|
11631
|
+
i18n: options.i18n || "none",
|
|
11632
|
+
search: options.search || "none",
|
|
11633
|
+
fileStorage: options.fileStorage || "none",
|
|
11634
|
+
rustWebFramework: options.rustWebFramework || "none",
|
|
11635
|
+
rustFrontend: options.rustFrontend || "none",
|
|
11636
|
+
rustOrm: options.rustOrm || "none",
|
|
11637
|
+
rustApi: options.rustApi || "none",
|
|
11638
|
+
rustCli: options.rustCli || "none",
|
|
11639
|
+
rustLibraries: options.rustLibraries || [],
|
|
11640
|
+
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
11641
|
+
rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
|
|
11642
|
+
rustCaching: options.rustCaching || "none",
|
|
11643
|
+
rustAuth: options.rustAuth || "none",
|
|
11644
|
+
pythonWebFramework: options.pythonWebFramework || "none",
|
|
11645
|
+
pythonOrm: options.pythonOrm || "none",
|
|
11646
|
+
pythonValidation: options.pythonValidation || "none",
|
|
11647
|
+
pythonAi: options.pythonAi || [],
|
|
11648
|
+
pythonAuth: options.pythonAuth || "none",
|
|
11649
|
+
pythonApi: options.pythonApi || "none",
|
|
11650
|
+
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
11651
|
+
pythonGraphql: options.pythonGraphql || "none",
|
|
11652
|
+
pythonQuality: options.pythonQuality || "none",
|
|
11653
|
+
goWebFramework: options.goWebFramework || "none",
|
|
11654
|
+
goOrm: options.goOrm || "none",
|
|
11655
|
+
goApi: options.goApi || "none",
|
|
11656
|
+
goCli: options.goCli || "none",
|
|
11657
|
+
goLogging: options.goLogging || "none",
|
|
11658
|
+
goAuth: options.goAuth || "none",
|
|
11659
|
+
javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
|
|
11660
|
+
javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
|
|
11661
|
+
javaOrm: options.javaOrm || "none",
|
|
11662
|
+
javaAuth: options.javaAuth || "none",
|
|
11663
|
+
javaLibraries: options.javaLibraries || [],
|
|
11664
|
+
javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
|
|
11665
|
+
elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
|
|
11666
|
+
elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
|
|
11667
|
+
elixirAuth: options.elixirAuth || "none",
|
|
11668
|
+
elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
|
|
11669
|
+
elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
|
|
11670
|
+
elixirJobs: options.elixirJobs || "none",
|
|
11671
|
+
elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
|
|
11672
|
+
elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
|
|
11673
|
+
elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
|
|
11674
|
+
elixirEmail: options.elixirEmail || "none",
|
|
11675
|
+
elixirCaching: options.elixirCaching || "none",
|
|
11676
|
+
elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
|
|
11677
|
+
elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
|
|
11678
|
+
elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
|
|
11679
|
+
elixirDeploy: options.elixirDeploy || "none",
|
|
11680
|
+
aiDocs: options.aiDocs || ["claude-md"]
|
|
11681
|
+
};
|
|
11682
|
+
if (options.stackParts) config.stackParts = options.stackParts;
|
|
10941
11683
|
const result = await generateVirtualProject$1({
|
|
10942
|
-
config
|
|
10943
|
-
ecosystem,
|
|
10944
|
-
projectName: options.projectName || "my-project",
|
|
10945
|
-
projectDir: "/virtual",
|
|
10946
|
-
relativePath: "./virtual",
|
|
10947
|
-
database: options.database || "none",
|
|
10948
|
-
orm: options.orm || "none",
|
|
10949
|
-
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
10950
|
-
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
10951
|
-
frontend,
|
|
10952
|
-
addons: options.addons || [],
|
|
10953
|
-
examples: options.examples || [],
|
|
10954
|
-
auth: options.auth || "none",
|
|
10955
|
-
payments: options.payments || "none",
|
|
10956
|
-
email: options.email || "none",
|
|
10957
|
-
fileUpload: options.fileUpload || "none",
|
|
10958
|
-
effect: options.effect || "none",
|
|
10959
|
-
git: options.git ?? false,
|
|
10960
|
-
packageManager: options.packageManager || "bun",
|
|
10961
|
-
versionChannel: options.versionChannel || "stable",
|
|
10962
|
-
install: false,
|
|
10963
|
-
dbSetup: options.dbSetup || "none",
|
|
10964
|
-
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
10965
|
-
webDeploy: options.webDeploy || "none",
|
|
10966
|
-
serverDeploy: options.serverDeploy || "none",
|
|
10967
|
-
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
10968
|
-
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
10969
|
-
shadcnBase: options.shadcnBase ?? "radix",
|
|
10970
|
-
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
10971
|
-
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
10972
|
-
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
10973
|
-
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
10974
|
-
shadcnFont: options.shadcnFont ?? "inter",
|
|
10975
|
-
shadcnRadius: options.shadcnRadius ?? "default",
|
|
10976
|
-
ai: options.ai || "none",
|
|
10977
|
-
stateManagement: options.stateManagement || "none",
|
|
10978
|
-
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
10979
|
-
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
10980
|
-
validation: options.validation || "zod",
|
|
10981
|
-
realtime: options.realtime || "none",
|
|
10982
|
-
jobQueue: options.jobQueue || "none",
|
|
10983
|
-
animation: options.animation || "none",
|
|
10984
|
-
logging: options.logging || "none",
|
|
10985
|
-
observability: options.observability || "none",
|
|
10986
|
-
featureFlags: options.featureFlags || "none",
|
|
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"),
|
|
10995
|
-
cms: options.cms || "none",
|
|
10996
|
-
caching: options.caching || "none",
|
|
10997
|
-
i18n: options.i18n || "none",
|
|
10998
|
-
search: options.search || "none",
|
|
10999
|
-
fileStorage: options.fileStorage || "none",
|
|
11000
|
-
rustWebFramework: options.rustWebFramework || "none",
|
|
11001
|
-
rustFrontend: options.rustFrontend || "none",
|
|
11002
|
-
rustOrm: options.rustOrm || "none",
|
|
11003
|
-
rustApi: options.rustApi || "none",
|
|
11004
|
-
rustCli: options.rustCli || "none",
|
|
11005
|
-
rustLibraries: options.rustLibraries || [],
|
|
11006
|
-
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
11007
|
-
rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
|
|
11008
|
-
rustCaching: options.rustCaching || "none",
|
|
11009
|
-
rustAuth: options.rustAuth || "none",
|
|
11010
|
-
pythonWebFramework: options.pythonWebFramework || "none",
|
|
11011
|
-
pythonOrm: options.pythonOrm || "none",
|
|
11012
|
-
pythonValidation: options.pythonValidation || "none",
|
|
11013
|
-
pythonAi: options.pythonAi || [],
|
|
11014
|
-
pythonAuth: options.pythonAuth || "none",
|
|
11015
|
-
pythonApi: options.pythonApi || "none",
|
|
11016
|
-
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
11017
|
-
pythonGraphql: options.pythonGraphql || "none",
|
|
11018
|
-
pythonQuality: options.pythonQuality || "none",
|
|
11019
|
-
goWebFramework: options.goWebFramework || "none",
|
|
11020
|
-
goOrm: options.goOrm || "none",
|
|
11021
|
-
goApi: options.goApi || "none",
|
|
11022
|
-
goCli: options.goCli || "none",
|
|
11023
|
-
goLogging: options.goLogging || "none",
|
|
11024
|
-
goAuth: options.goAuth || "none",
|
|
11025
|
-
javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
|
|
11026
|
-
javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
|
|
11027
|
-
javaOrm: options.javaOrm || "none",
|
|
11028
|
-
javaAuth: options.javaAuth || "none",
|
|
11029
|
-
javaLibraries: options.javaLibraries || [],
|
|
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",
|
|
11046
|
-
aiDocs: options.aiDocs || ["claude-md"]
|
|
11047
|
-
},
|
|
11684
|
+
config,
|
|
11048
11685
|
templates: EMBEDDED_TEMPLATES$1
|
|
11049
11686
|
});
|
|
11050
11687
|
if (result.success && result.tree) return {
|