create-better-fullstack 1.8.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/addons-setup-CExVq7Mg.mjs +5 -0
- package/dist/{addons-setup-CUmA_nra.mjs → addons-setup-DqVFXnDv.mjs} +1 -1
- package/dist/bts-config-CSvxsFML.mjs +565 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +55 -9
- package/dist/index.mjs +1665 -989
- package/dist/{mcp-DoPutOIG.mjs → mcp-DfnYbMju.mjs} +1 -1
- package/dist/mcp-entry.mjs +12 -2
- package/package.json +6 -6
- package/dist/addons-setup-CJwQAWFg.mjs +0 -5
- package/dist/bts-config-YcroedMK.mjs +0 -321
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-CSvxsFML.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-DqVFXnDv.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);
|
|
@@ -755,9 +750,10 @@ function validateAIFrontendCompatibility(ai, frontends = []) {
|
|
|
755
750
|
*/
|
|
756
751
|
function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astroIntegration) {
|
|
757
752
|
if (!uiLibrary || uiLibrary === "none") return;
|
|
753
|
+
const { web } = splitFrontends$1(frontends);
|
|
754
|
+
if (web.length === 0) return;
|
|
758
755
|
const compatible = getCompatibleUILibraries(frontends, astroIntegration);
|
|
759
756
|
if (!compatible.includes(uiLibrary)) {
|
|
760
|
-
const { web } = splitFrontends$1(frontends);
|
|
761
757
|
const isAstroNonReact = web.includes("astro") && astroIntegration !== "react";
|
|
762
758
|
const supportsAstroReact = getCompatibleUILibraries(["astro"], "react").includes(uiLibrary);
|
|
763
759
|
if (isAstroNonReact && supportsAstroReact) {
|
|
@@ -2935,42 +2931,48 @@ const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target
|
|
|
2935
2931
|
|
|
2936
2932
|
//#endregion
|
|
2937
2933
|
//#region src/prompts/ecosystem.ts
|
|
2934
|
+
const ECOSYSTEM_PROMPT_OPTIONS = [
|
|
2935
|
+
{
|
|
2936
|
+
value: "typescript",
|
|
2937
|
+
label: "TypeScript",
|
|
2938
|
+
hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
|
|
2939
|
+
},
|
|
2940
|
+
{
|
|
2941
|
+
value: "react-native",
|
|
2942
|
+
label: "React Native",
|
|
2943
|
+
hint: "Expo and React Native mobile apps with native integrations"
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
value: "rust",
|
|
2947
|
+
label: "Rust",
|
|
2948
|
+
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2949
|
+
},
|
|
2950
|
+
{
|
|
2951
|
+
value: "python",
|
|
2952
|
+
label: "Python",
|
|
2953
|
+
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2954
|
+
},
|
|
2955
|
+
{
|
|
2956
|
+
value: "go",
|
|
2957
|
+
label: "Go",
|
|
2958
|
+
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2959
|
+
},
|
|
2960
|
+
{
|
|
2961
|
+
value: "java",
|
|
2962
|
+
label: "Java",
|
|
2963
|
+
hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
|
|
2964
|
+
},
|
|
2965
|
+
{
|
|
2966
|
+
value: "elixir",
|
|
2967
|
+
label: "Elixir",
|
|
2968
|
+
hint: "Elixir ecosystem with Phoenix, LiveView, Ecto, and more"
|
|
2969
|
+
}
|
|
2970
|
+
];
|
|
2938
2971
|
async function getEcosystemChoice(ecosystem) {
|
|
2939
2972
|
if (ecosystem !== void 0) return ecosystem;
|
|
2940
2973
|
const response = await navigableSelect({
|
|
2941
2974
|
message: "Select ecosystem",
|
|
2942
|
-
options:
|
|
2943
|
-
{
|
|
2944
|
-
value: "typescript",
|
|
2945
|
-
label: "TypeScript",
|
|
2946
|
-
hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
|
|
2947
|
-
},
|
|
2948
|
-
{
|
|
2949
|
-
value: "react-native",
|
|
2950
|
-
label: "React Native",
|
|
2951
|
-
hint: "Expo and React Native mobile apps with native integrations"
|
|
2952
|
-
},
|
|
2953
|
-
{
|
|
2954
|
-
value: "rust",
|
|
2955
|
-
label: "Rust",
|
|
2956
|
-
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2957
|
-
},
|
|
2958
|
-
{
|
|
2959
|
-
value: "python",
|
|
2960
|
-
label: "Python",
|
|
2961
|
-
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2962
|
-
},
|
|
2963
|
-
{
|
|
2964
|
-
value: "go",
|
|
2965
|
-
label: "Go",
|
|
2966
|
-
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2967
|
-
},
|
|
2968
|
-
{
|
|
2969
|
-
value: "java",
|
|
2970
|
-
label: "Java",
|
|
2971
|
-
hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
|
|
2972
|
-
}
|
|
2973
|
-
],
|
|
2975
|
+
options: ECOSYSTEM_PROMPT_OPTIONS,
|
|
2974
2976
|
initialValue: "typescript"
|
|
2975
2977
|
});
|
|
2976
2978
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
@@ -4343,6 +4345,7 @@ const MOBILE_DEEP_LINKING_OPTIONS = [{
|
|
|
4343
4345
|
async function promptMobileOption(options, defaultValue, selected, message) {
|
|
4344
4346
|
const resolution = createStaticSinglePromptResolution(options, defaultValue, selected);
|
|
4345
4347
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? defaultValue;
|
|
4348
|
+
if (!canPromptInteractively()) return defaultValue;
|
|
4346
4349
|
const response = await navigableSelect({
|
|
4347
4350
|
message,
|
|
4348
4351
|
options: resolution.options,
|
|
@@ -4373,224 +4376,12 @@ function getMobileDeepLinkingChoice(mobileDeepLinking) {
|
|
|
4373
4376
|
return promptMobileOption(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking, "Select mobile deep linking");
|
|
4374
4377
|
}
|
|
4375
4378
|
|
|
4376
|
-
//#endregion
|
|
4377
|
-
//#region src/prompts/navigable-group.ts
|
|
4378
|
-
/**
|
|
4379
|
-
* Navigable group - a group of prompts that allows going back
|
|
4380
|
-
*/
|
|
4381
|
-
/**
|
|
4382
|
-
* Define a group of prompts that supports going back to previous prompts.
|
|
4383
|
-
* Returns a result object with all the values, or handles cancel/go-back navigation.
|
|
4384
|
-
*/
|
|
4385
|
-
async function navigableGroup(prompts, opts) {
|
|
4386
|
-
const results = {};
|
|
4387
|
-
const promptNames = Object.keys(prompts);
|
|
4388
|
-
let currentIndex = 0;
|
|
4389
|
-
let goingBack = false;
|
|
4390
|
-
while (currentIndex < promptNames.length) {
|
|
4391
|
-
const name = promptNames[currentIndex];
|
|
4392
|
-
const prompt = prompts[name];
|
|
4393
|
-
setIsFirstPrompt$1(currentIndex === 0);
|
|
4394
|
-
setLastPromptShownUI(false);
|
|
4395
|
-
const result = await prompt({ results })?.catch((e) => {
|
|
4396
|
-
throw e;
|
|
4397
|
-
});
|
|
4398
|
-
if (isGoBack(result)) {
|
|
4399
|
-
goingBack = true;
|
|
4400
|
-
if (currentIndex > 0) {
|
|
4401
|
-
const prevName = promptNames[currentIndex - 1];
|
|
4402
|
-
delete results[prevName];
|
|
4403
|
-
currentIndex--;
|
|
4404
|
-
continue;
|
|
4405
|
-
}
|
|
4406
|
-
goingBack = false;
|
|
4407
|
-
continue;
|
|
4408
|
-
}
|
|
4409
|
-
if (isCancel$1(result)) {
|
|
4410
|
-
if (typeof opts?.onCancel === "function") {
|
|
4411
|
-
results[name] = "canceled";
|
|
4412
|
-
opts.onCancel({ results });
|
|
4413
|
-
}
|
|
4414
|
-
setIsFirstPrompt$1(false);
|
|
4415
|
-
return results;
|
|
4416
|
-
}
|
|
4417
|
-
if (goingBack && !didLastPromptShowUI()) {
|
|
4418
|
-
if (currentIndex > 0) {
|
|
4419
|
-
const prevName = promptNames[currentIndex - 1];
|
|
4420
|
-
delete results[prevName];
|
|
4421
|
-
currentIndex--;
|
|
4422
|
-
continue;
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
goingBack = false;
|
|
4426
|
-
results[name] = result;
|
|
4427
|
-
currentIndex++;
|
|
4428
|
-
}
|
|
4429
|
-
setIsFirstPrompt$1(false);
|
|
4430
|
-
return results;
|
|
4431
|
-
}
|
|
4432
|
-
|
|
4433
|
-
//#endregion
|
|
4434
|
-
//#region src/prompts/observability.ts
|
|
4435
|
-
const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
4436
|
-
{
|
|
4437
|
-
value: "opentelemetry",
|
|
4438
|
-
label: "OpenTelemetry",
|
|
4439
|
-
hint: "Observability framework for traces, metrics, and logs"
|
|
4440
|
-
},
|
|
4441
|
-
{
|
|
4442
|
-
value: "sentry",
|
|
4443
|
-
label: "Sentry",
|
|
4444
|
-
hint: "Error tracking and performance monitoring"
|
|
4445
|
-
},
|
|
4446
|
-
{
|
|
4447
|
-
value: "grafana",
|
|
4448
|
-
label: "Grafana",
|
|
4449
|
-
hint: "Prometheus metrics for Grafana dashboards and alerting"
|
|
4450
|
-
},
|
|
4451
|
-
{
|
|
4452
|
-
value: "none",
|
|
4453
|
-
label: "None",
|
|
4454
|
-
hint: "Skip observability/tracing setup"
|
|
4455
|
-
}
|
|
4456
|
-
];
|
|
4457
|
-
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
4458
|
-
function resolveObservabilityPrompt(context = {}) {
|
|
4459
|
-
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
4460
|
-
shouldPrompt: false,
|
|
4461
|
-
mode: "single",
|
|
4462
|
-
options: [],
|
|
4463
|
-
autoValue: "none"
|
|
4464
|
-
};
|
|
4465
|
-
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
4466
|
-
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
4467
|
-
shouldPrompt: false,
|
|
4468
|
-
mode: "single",
|
|
4469
|
-
options: [],
|
|
4470
|
-
autoValue: "none"
|
|
4471
|
-
};
|
|
4472
|
-
return context.observability !== void 0 ? {
|
|
4473
|
-
shouldPrompt: false,
|
|
4474
|
-
mode: "single",
|
|
4475
|
-
options,
|
|
4476
|
-
autoValue: context.observability
|
|
4477
|
-
} : {
|
|
4478
|
-
shouldPrompt: true,
|
|
4479
|
-
mode: "single",
|
|
4480
|
-
options,
|
|
4481
|
-
initialValue: "none"
|
|
4482
|
-
};
|
|
4483
|
-
}
|
|
4484
|
-
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
4485
|
-
const resolution = resolveObservabilityPrompt({
|
|
4486
|
-
observability,
|
|
4487
|
-
backend,
|
|
4488
|
-
ecosystem
|
|
4489
|
-
});
|
|
4490
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4491
|
-
const response = await navigableSelect({
|
|
4492
|
-
message: "Select observability solution",
|
|
4493
|
-
options: resolution.options,
|
|
4494
|
-
initialValue: resolution.initialValue
|
|
4495
|
-
});
|
|
4496
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4497
|
-
return response;
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
//#endregion
|
|
4501
|
-
//#region src/prompts/orm.ts
|
|
4502
|
-
const ormOptions = {
|
|
4503
|
-
prisma: {
|
|
4504
|
-
value: "prisma",
|
|
4505
|
-
label: "Prisma",
|
|
4506
|
-
hint: "Powerful, feature-rich ORM"
|
|
4507
|
-
},
|
|
4508
|
-
mongoose: {
|
|
4509
|
-
value: "mongoose",
|
|
4510
|
-
label: "Mongoose",
|
|
4511
|
-
hint: "Elegant object modeling tool"
|
|
4512
|
-
},
|
|
4513
|
-
drizzle: {
|
|
4514
|
-
value: "drizzle",
|
|
4515
|
-
label: "Drizzle",
|
|
4516
|
-
hint: "Lightweight and performant TypeScript ORM"
|
|
4517
|
-
},
|
|
4518
|
-
typeorm: {
|
|
4519
|
-
value: "typeorm",
|
|
4520
|
-
label: "TypeORM",
|
|
4521
|
-
hint: "Traditional ORM with Active Record/Data Mapper"
|
|
4522
|
-
},
|
|
4523
|
-
kysely: {
|
|
4524
|
-
value: "kysely",
|
|
4525
|
-
label: "Kysely",
|
|
4526
|
-
hint: "Type-safe SQL query builder"
|
|
4527
|
-
},
|
|
4528
|
-
mikroorm: {
|
|
4529
|
-
value: "mikroorm",
|
|
4530
|
-
label: "MikroORM",
|
|
4531
|
-
hint: "Data Mapper ORM for DDD"
|
|
4532
|
-
},
|
|
4533
|
-
sequelize: {
|
|
4534
|
-
value: "sequelize",
|
|
4535
|
-
label: "Sequelize",
|
|
4536
|
-
hint: "Mature ORM with wide adoption"
|
|
4537
|
-
}
|
|
4538
|
-
};
|
|
4539
|
-
function resolveORMPrompt(context) {
|
|
4540
|
-
if (context.backend === "convex" || !context.hasDatabase) return {
|
|
4541
|
-
shouldPrompt: false,
|
|
4542
|
-
mode: "single",
|
|
4543
|
-
options: [],
|
|
4544
|
-
autoValue: "none"
|
|
4545
|
-
};
|
|
4546
|
-
if (context.database === "edgedb" || context.database === "redis") return {
|
|
4547
|
-
shouldPrompt: false,
|
|
4548
|
-
mode: "single",
|
|
4549
|
-
options: [],
|
|
4550
|
-
autoValue: "none"
|
|
4551
|
-
};
|
|
4552
|
-
if (context.orm !== void 0) return {
|
|
4553
|
-
shouldPrompt: false,
|
|
4554
|
-
mode: "single",
|
|
4555
|
-
options: [],
|
|
4556
|
-
autoValue: context.orm
|
|
4557
|
-
};
|
|
4558
|
-
return {
|
|
4559
|
-
shouldPrompt: true,
|
|
4560
|
-
mode: "single",
|
|
4561
|
-
options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
|
|
4562
|
-
ormOptions.drizzle,
|
|
4563
|
-
ormOptions.prisma,
|
|
4564
|
-
ormOptions.typeorm,
|
|
4565
|
-
ormOptions.kysely,
|
|
4566
|
-
ormOptions.mikroorm,
|
|
4567
|
-
ormOptions.sequelize
|
|
4568
|
-
],
|
|
4569
|
-
initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
4570
|
-
};
|
|
4571
|
-
}
|
|
4572
|
-
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
4573
|
-
const resolution = resolveORMPrompt({
|
|
4574
|
-
orm,
|
|
4575
|
-
hasDatabase,
|
|
4576
|
-
database,
|
|
4577
|
-
backend,
|
|
4578
|
-
runtime
|
|
4579
|
-
});
|
|
4580
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4581
|
-
const response = await navigableSelect({
|
|
4582
|
-
message: "Select ORM",
|
|
4583
|
-
options: resolution.options,
|
|
4584
|
-
initialValue: resolution.initialValue
|
|
4585
|
-
});
|
|
4586
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4587
|
-
return response;
|
|
4588
|
-
}
|
|
4589
|
-
|
|
4590
4379
|
//#endregion
|
|
4591
4380
|
//#region src/prompts/package-manager.ts
|
|
4592
4381
|
async function getPackageManagerChoice(packageManager) {
|
|
4593
4382
|
if (packageManager !== void 0) return packageManager;
|
|
4383
|
+
const detectedPackageManager = getUserPkgManager();
|
|
4384
|
+
if (!canPromptInteractively()) return detectedPackageManager;
|
|
4594
4385
|
const response = await navigableSelect({
|
|
4595
4386
|
message: "Choose package manager",
|
|
4596
4387
|
options: [
|
|
@@ -4615,74 +4406,7 @@ async function getPackageManagerChoice(packageManager) {
|
|
|
4615
4406
|
hint: "Yarn Berry (v4) with PnP or node_modules"
|
|
4616
4407
|
}
|
|
4617
4408
|
],
|
|
4618
|
-
initialValue:
|
|
4619
|
-
});
|
|
4620
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4621
|
-
return response;
|
|
4622
|
-
}
|
|
4623
|
-
|
|
4624
|
-
//#endregion
|
|
4625
|
-
//#region src/prompts/payments.ts
|
|
4626
|
-
function resolvePaymentsPrompt(context = {}) {
|
|
4627
|
-
if (context.payments !== void 0) return {
|
|
4628
|
-
shouldPrompt: false,
|
|
4629
|
-
mode: "single",
|
|
4630
|
-
options: [],
|
|
4631
|
-
autoValue: context.payments
|
|
4632
|
-
};
|
|
4633
|
-
if (context.backend === "none") return {
|
|
4634
|
-
shouldPrompt: false,
|
|
4635
|
-
mode: "single",
|
|
4636
|
-
options: [],
|
|
4637
|
-
autoValue: "none"
|
|
4638
|
-
};
|
|
4639
|
-
const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
4640
|
-
const options = [];
|
|
4641
|
-
if (isPolarCompatible) options.push({
|
|
4642
|
-
value: "polar",
|
|
4643
|
-
label: "Polar",
|
|
4644
|
-
hint: "Turn your software into a business. 6 lines of code."
|
|
4645
|
-
});
|
|
4646
|
-
options.push({
|
|
4647
|
-
value: "stripe",
|
|
4648
|
-
label: "Stripe",
|
|
4649
|
-
hint: "Payment processing platform for internet businesses."
|
|
4650
|
-
}, {
|
|
4651
|
-
value: "lemon-squeezy",
|
|
4652
|
-
label: "Lemon Squeezy",
|
|
4653
|
-
hint: "All-in-one platform for SaaS, digital products, and subscriptions."
|
|
4654
|
-
}, {
|
|
4655
|
-
value: "paddle",
|
|
4656
|
-
label: "Paddle",
|
|
4657
|
-
hint: "Complete payments infrastructure for SaaS."
|
|
4658
|
-
}, {
|
|
4659
|
-
value: "dodo",
|
|
4660
|
-
label: "Dodo Payments",
|
|
4661
|
-
hint: "Simple payment infrastructure for developers."
|
|
4662
|
-
}, {
|
|
4663
|
-
value: "none",
|
|
4664
|
-
label: "None",
|
|
4665
|
-
hint: "No payments integration"
|
|
4666
|
-
});
|
|
4667
|
-
return {
|
|
4668
|
-
shouldPrompt: true,
|
|
4669
|
-
mode: "single",
|
|
4670
|
-
options,
|
|
4671
|
-
initialValue: DEFAULT_CONFIG.payments
|
|
4672
|
-
};
|
|
4673
|
-
}
|
|
4674
|
-
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
4675
|
-
const resolution = resolvePaymentsPrompt({
|
|
4676
|
-
payments,
|
|
4677
|
-
auth,
|
|
4678
|
-
backend,
|
|
4679
|
-
frontends
|
|
4680
|
-
});
|
|
4681
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4682
|
-
const response = await navigableSelect({
|
|
4683
|
-
message: "Select payments provider",
|
|
4684
|
-
options: resolution.options,
|
|
4685
|
-
initialValue: resolution.initialValue
|
|
4409
|
+
initialValue: detectedPackageManager
|
|
4686
4410
|
});
|
|
4687
4411
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4688
4412
|
return response;
|
|
@@ -5019,133 +4743,6 @@ async function getPythonQualityChoice(pythonQuality) {
|
|
|
5019
4743
|
return response;
|
|
5020
4744
|
}
|
|
5021
4745
|
|
|
5022
|
-
//#endregion
|
|
5023
|
-
//#region src/prompts/realtime.ts
|
|
5024
|
-
const REALTIME_PROMPT_OPTIONS = [
|
|
5025
|
-
{
|
|
5026
|
-
value: "socket-io",
|
|
5027
|
-
label: "Socket.IO",
|
|
5028
|
-
hint: "Real-time bidirectional communication with fallbacks"
|
|
5029
|
-
},
|
|
5030
|
-
{
|
|
5031
|
-
value: "partykit",
|
|
5032
|
-
label: "PartyKit",
|
|
5033
|
-
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
5034
|
-
},
|
|
5035
|
-
{
|
|
5036
|
-
value: "ably",
|
|
5037
|
-
label: "Ably",
|
|
5038
|
-
hint: "Real-time messaging platform with pub/sub and presence"
|
|
5039
|
-
},
|
|
5040
|
-
{
|
|
5041
|
-
value: "pusher",
|
|
5042
|
-
label: "Pusher",
|
|
5043
|
-
hint: "Real-time communication APIs with channels and events"
|
|
5044
|
-
},
|
|
5045
|
-
{
|
|
5046
|
-
value: "liveblocks",
|
|
5047
|
-
label: "Liveblocks",
|
|
5048
|
-
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
5049
|
-
},
|
|
5050
|
-
{
|
|
5051
|
-
value: "yjs",
|
|
5052
|
-
label: "Y.js",
|
|
5053
|
-
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
5054
|
-
},
|
|
5055
|
-
{
|
|
5056
|
-
value: "none",
|
|
5057
|
-
label: "None",
|
|
5058
|
-
hint: "Skip real-time/WebSocket integration"
|
|
5059
|
-
}
|
|
5060
|
-
];
|
|
5061
|
-
function resolveRealtimePrompt(context = {}) {
|
|
5062
|
-
if (context.backend === "none" || context.backend === "convex") return {
|
|
5063
|
-
shouldPrompt: false,
|
|
5064
|
-
mode: "single",
|
|
5065
|
-
options: [],
|
|
5066
|
-
autoValue: "none"
|
|
5067
|
-
};
|
|
5068
|
-
return context.realtime !== void 0 ? {
|
|
5069
|
-
shouldPrompt: false,
|
|
5070
|
-
mode: "single",
|
|
5071
|
-
options: REALTIME_PROMPT_OPTIONS,
|
|
5072
|
-
autoValue: context.realtime
|
|
5073
|
-
} : {
|
|
5074
|
-
shouldPrompt: true,
|
|
5075
|
-
mode: "single",
|
|
5076
|
-
options: REALTIME_PROMPT_OPTIONS,
|
|
5077
|
-
initialValue: "none"
|
|
5078
|
-
};
|
|
5079
|
-
}
|
|
5080
|
-
async function getRealtimeChoice(realtime, backend) {
|
|
5081
|
-
const resolution = resolveRealtimePrompt({
|
|
5082
|
-
realtime,
|
|
5083
|
-
backend
|
|
5084
|
-
});
|
|
5085
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5086
|
-
const response = await navigableSelect({
|
|
5087
|
-
message: "Select real-time solution",
|
|
5088
|
-
options: resolution.options,
|
|
5089
|
-
initialValue: resolution.initialValue
|
|
5090
|
-
});
|
|
5091
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5092
|
-
return response;
|
|
5093
|
-
}
|
|
5094
|
-
|
|
5095
|
-
//#endregion
|
|
5096
|
-
//#region src/prompts/runtime.ts
|
|
5097
|
-
const RUNTIME_PROMPT_OPTIONS = [
|
|
5098
|
-
{
|
|
5099
|
-
value: "bun",
|
|
5100
|
-
label: "Bun",
|
|
5101
|
-
hint: "Fast all-in-one JavaScript runtime"
|
|
5102
|
-
},
|
|
5103
|
-
{
|
|
5104
|
-
value: "node",
|
|
5105
|
-
label: "Node.js",
|
|
5106
|
-
hint: "Traditional Node.js runtime"
|
|
5107
|
-
},
|
|
5108
|
-
{
|
|
5109
|
-
value: "workers",
|
|
5110
|
-
label: "Cloudflare Workers",
|
|
5111
|
-
hint: "Edge runtime on Cloudflare's global network"
|
|
5112
|
-
}
|
|
5113
|
-
];
|
|
5114
|
-
function resolveRuntimePrompt(context = {}) {
|
|
5115
|
-
if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
|
|
5116
|
-
shouldPrompt: false,
|
|
5117
|
-
mode: "single",
|
|
5118
|
-
options: [],
|
|
5119
|
-
autoValue: "none"
|
|
5120
|
-
};
|
|
5121
|
-
const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
|
|
5122
|
-
return context.runtime !== void 0 ? {
|
|
5123
|
-
shouldPrompt: false,
|
|
5124
|
-
mode: "single",
|
|
5125
|
-
options,
|
|
5126
|
-
autoValue: context.runtime
|
|
5127
|
-
} : {
|
|
5128
|
-
shouldPrompt: true,
|
|
5129
|
-
mode: "single",
|
|
5130
|
-
options,
|
|
5131
|
-
initialValue: DEFAULT_CONFIG.runtime
|
|
5132
|
-
};
|
|
5133
|
-
}
|
|
5134
|
-
async function getRuntimeChoice(runtime, backend) {
|
|
5135
|
-
const resolution = resolveRuntimePrompt({
|
|
5136
|
-
runtime,
|
|
5137
|
-
backend
|
|
5138
|
-
});
|
|
5139
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5140
|
-
const response = await navigableSelect({
|
|
5141
|
-
message: "Select runtime",
|
|
5142
|
-
options: resolution.options,
|
|
5143
|
-
initialValue: resolution.initialValue
|
|
5144
|
-
});
|
|
5145
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5146
|
-
return response;
|
|
5147
|
-
}
|
|
5148
|
-
|
|
5149
4746
|
//#endregion
|
|
5150
4747
|
//#region src/prompts/rust-ecosystem.ts
|
|
5151
4748
|
const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
@@ -5528,133 +5125,51 @@ async function getRustAuthChoice(rustAuth) {
|
|
|
5528
5125
|
}
|
|
5529
5126
|
|
|
5530
5127
|
//#endregion
|
|
5531
|
-
//#region src/prompts/
|
|
5532
|
-
const
|
|
5128
|
+
//#region src/prompts/shadcn-options.ts
|
|
5129
|
+
const BASE_OPTIONS = [{
|
|
5130
|
+
value: "radix",
|
|
5131
|
+
label: "Radix UI",
|
|
5132
|
+
hint: "Battle-tested headless primitives (130M+ monthly downloads)"
|
|
5133
|
+
}, {
|
|
5134
|
+
value: "base",
|
|
5135
|
+
label: "Base UI",
|
|
5136
|
+
hint: "MUI's headless library with cleaner APIs and native multi-select"
|
|
5137
|
+
}];
|
|
5138
|
+
const STYLE_OPTIONS = [
|
|
5533
5139
|
{
|
|
5534
|
-
value: "
|
|
5535
|
-
label: "
|
|
5536
|
-
hint: "
|
|
5140
|
+
value: "vega",
|
|
5141
|
+
label: "Vega",
|
|
5142
|
+
hint: "Classic shadcn/ui look"
|
|
5537
5143
|
},
|
|
5538
5144
|
{
|
|
5539
|
-
value: "
|
|
5540
|
-
label: "
|
|
5541
|
-
hint: "
|
|
5145
|
+
value: "nova",
|
|
5146
|
+
label: "Nova",
|
|
5147
|
+
hint: "Compact layout with reduced padding"
|
|
5542
5148
|
},
|
|
5543
5149
|
{
|
|
5544
|
-
value: "
|
|
5545
|
-
label: "
|
|
5546
|
-
hint: "
|
|
5150
|
+
value: "maia",
|
|
5151
|
+
label: "Maia",
|
|
5152
|
+
hint: "Soft, rounded with generous spacing"
|
|
5547
5153
|
},
|
|
5548
5154
|
{
|
|
5549
|
-
value: "
|
|
5550
|
-
label: "
|
|
5551
|
-
hint: "
|
|
5155
|
+
value: "lyra",
|
|
5156
|
+
label: "Lyra",
|
|
5157
|
+
hint: "Boxy and sharp, pairs well with mono fonts"
|
|
5552
5158
|
},
|
|
5553
5159
|
{
|
|
5554
|
-
value: "
|
|
5555
|
-
label: "
|
|
5556
|
-
hint: "
|
|
5557
|
-
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
5568
|
-
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
5569
|
-
shouldPrompt: false,
|
|
5570
|
-
mode: "single",
|
|
5571
|
-
options: [],
|
|
5572
|
-
autoValue: "none"
|
|
5573
|
-
};
|
|
5574
|
-
return context.search !== void 0 ? {
|
|
5575
|
-
shouldPrompt: false,
|
|
5576
|
-
mode: "single",
|
|
5577
|
-
options,
|
|
5578
|
-
autoValue: context.search
|
|
5579
|
-
} : {
|
|
5580
|
-
shouldPrompt: true,
|
|
5581
|
-
mode: "single",
|
|
5582
|
-
options,
|
|
5583
|
-
initialValue: "none"
|
|
5584
|
-
};
|
|
5585
|
-
}
|
|
5586
|
-
async function getSearchChoice(search, backend, ecosystem) {
|
|
5587
|
-
const resolution = resolveSearchPrompt({
|
|
5588
|
-
search,
|
|
5589
|
-
backend,
|
|
5590
|
-
ecosystem
|
|
5591
|
-
});
|
|
5592
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5593
|
-
const response = await navigableSelect({
|
|
5594
|
-
message: "Select search engine",
|
|
5595
|
-
options: resolution.options,
|
|
5596
|
-
initialValue: resolution.initialValue
|
|
5597
|
-
});
|
|
5598
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5599
|
-
return response;
|
|
5600
|
-
}
|
|
5601
|
-
|
|
5602
|
-
//#endregion
|
|
5603
|
-
//#region src/prompts/server-deploy.ts
|
|
5604
|
-
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
5605
|
-
if (deployment !== void 0) return deployment;
|
|
5606
|
-
if (backend === "none" || backend === "convex") return "none";
|
|
5607
|
-
if (backend !== "hono") return "none";
|
|
5608
|
-
if (runtime === "workers") return "cloudflare";
|
|
5609
|
-
return "none";
|
|
5610
|
-
}
|
|
5611
|
-
|
|
5612
|
-
//#endregion
|
|
5613
|
-
//#region src/prompts/shadcn-options.ts
|
|
5614
|
-
const BASE_OPTIONS = [{
|
|
5615
|
-
value: "radix",
|
|
5616
|
-
label: "Radix UI",
|
|
5617
|
-
hint: "Battle-tested headless primitives (130M+ monthly downloads)"
|
|
5618
|
-
}, {
|
|
5619
|
-
value: "base",
|
|
5620
|
-
label: "Base UI",
|
|
5621
|
-
hint: "MUI's headless library with cleaner APIs and native multi-select"
|
|
5622
|
-
}];
|
|
5623
|
-
const STYLE_OPTIONS = [
|
|
5624
|
-
{
|
|
5625
|
-
value: "vega",
|
|
5626
|
-
label: "Vega",
|
|
5627
|
-
hint: "Classic shadcn/ui look"
|
|
5628
|
-
},
|
|
5629
|
-
{
|
|
5630
|
-
value: "nova",
|
|
5631
|
-
label: "Nova",
|
|
5632
|
-
hint: "Compact layout with reduced padding"
|
|
5633
|
-
},
|
|
5634
|
-
{
|
|
5635
|
-
value: "maia",
|
|
5636
|
-
label: "Maia",
|
|
5637
|
-
hint: "Soft, rounded with generous spacing"
|
|
5638
|
-
},
|
|
5639
|
-
{
|
|
5640
|
-
value: "lyra",
|
|
5641
|
-
label: "Lyra",
|
|
5642
|
-
hint: "Boxy and sharp, pairs well with mono fonts"
|
|
5643
|
-
},
|
|
5644
|
-
{
|
|
5645
|
-
value: "mira",
|
|
5646
|
-
label: "Mira",
|
|
5647
|
-
hint: "Dense, made for data-heavy interfaces"
|
|
5648
|
-
},
|
|
5649
|
-
{
|
|
5650
|
-
value: "luma",
|
|
5651
|
-
label: "Luma",
|
|
5652
|
-
hint: "Modern shadcn/ui v4 preset"
|
|
5653
|
-
},
|
|
5654
|
-
{
|
|
5655
|
-
value: "sera",
|
|
5656
|
-
label: "Sera",
|
|
5657
|
-
hint: "Modern shadcn/ui v4 preset"
|
|
5160
|
+
value: "mira",
|
|
5161
|
+
label: "Mira",
|
|
5162
|
+
hint: "Dense, made for data-heavy interfaces"
|
|
5163
|
+
},
|
|
5164
|
+
{
|
|
5165
|
+
value: "luma",
|
|
5166
|
+
label: "Luma",
|
|
5167
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5168
|
+
},
|
|
5169
|
+
{
|
|
5170
|
+
value: "sera",
|
|
5171
|
+
label: "Sera",
|
|
5172
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5658
5173
|
}
|
|
5659
5174
|
];
|
|
5660
5175
|
const ICON_LIBRARY_OPTIONS = [
|
|
@@ -5941,14 +5456,28 @@ const SHADCN_DEFAULTS = {
|
|
|
5941
5456
|
};
|
|
5942
5457
|
async function getShadcnOptions(flags) {
|
|
5943
5458
|
const fallback = (key) => isSilent() ? SHADCN_DEFAULTS[key] : void 0;
|
|
5459
|
+
const shadcnBase = flags.shadcnBase ?? fallback("shadcnBase") ?? await promptShadcnBase();
|
|
5460
|
+
if (isGoBack(shadcnBase)) return shadcnBase;
|
|
5461
|
+
const shadcnStyle = flags.shadcnStyle ?? fallback("shadcnStyle") ?? await promptShadcnStyle();
|
|
5462
|
+
if (isGoBack(shadcnStyle)) return shadcnStyle;
|
|
5463
|
+
const shadcnIconLibrary = flags.shadcnIconLibrary ?? fallback("shadcnIconLibrary") ?? await promptShadcnIconLibrary();
|
|
5464
|
+
if (isGoBack(shadcnIconLibrary)) return shadcnIconLibrary;
|
|
5465
|
+
const shadcnColorTheme = flags.shadcnColorTheme ?? fallback("shadcnColorTheme") ?? await promptShadcnColorTheme();
|
|
5466
|
+
if (isGoBack(shadcnColorTheme)) return shadcnColorTheme;
|
|
5467
|
+
const shadcnBaseColor = flags.shadcnBaseColor ?? fallback("shadcnBaseColor") ?? await promptShadcnBaseColor();
|
|
5468
|
+
if (isGoBack(shadcnBaseColor)) return shadcnBaseColor;
|
|
5469
|
+
const shadcnFont = flags.shadcnFont ?? fallback("shadcnFont") ?? await promptShadcnFont();
|
|
5470
|
+
if (isGoBack(shadcnFont)) return shadcnFont;
|
|
5471
|
+
const shadcnRadius = flags.shadcnRadius ?? fallback("shadcnRadius") ?? await promptShadcnRadius();
|
|
5472
|
+
if (isGoBack(shadcnRadius)) return shadcnRadius;
|
|
5944
5473
|
return {
|
|
5945
|
-
shadcnBase
|
|
5946
|
-
shadcnStyle
|
|
5947
|
-
shadcnIconLibrary
|
|
5948
|
-
shadcnColorTheme
|
|
5949
|
-
shadcnBaseColor
|
|
5950
|
-
shadcnFont
|
|
5951
|
-
shadcnRadius
|
|
5474
|
+
shadcnBase,
|
|
5475
|
+
shadcnStyle,
|
|
5476
|
+
shadcnIconLibrary,
|
|
5477
|
+
shadcnColorTheme,
|
|
5478
|
+
shadcnBaseColor,
|
|
5479
|
+
shadcnFont,
|
|
5480
|
+
shadcnRadius
|
|
5952
5481
|
};
|
|
5953
5482
|
}
|
|
5954
5483
|
async function promptShadcnBase() {
|
|
@@ -5957,6 +5486,7 @@ async function promptShadcnBase() {
|
|
|
5957
5486
|
options: BASE_OPTIONS,
|
|
5958
5487
|
initialValue: "radix"
|
|
5959
5488
|
});
|
|
5489
|
+
if (isGoBack(selected)) return selected;
|
|
5960
5490
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5961
5491
|
return selected;
|
|
5962
5492
|
}
|
|
@@ -5966,6 +5496,7 @@ async function promptShadcnStyle() {
|
|
|
5966
5496
|
options: STYLE_OPTIONS,
|
|
5967
5497
|
initialValue: "nova"
|
|
5968
5498
|
});
|
|
5499
|
+
if (isGoBack(selected)) return selected;
|
|
5969
5500
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5970
5501
|
return selected;
|
|
5971
5502
|
}
|
|
@@ -5975,6 +5506,7 @@ async function promptShadcnIconLibrary() {
|
|
|
5975
5506
|
options: ICON_LIBRARY_OPTIONS,
|
|
5976
5507
|
initialValue: "lucide"
|
|
5977
5508
|
});
|
|
5509
|
+
if (isGoBack(selected)) return selected;
|
|
5978
5510
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5979
5511
|
return selected;
|
|
5980
5512
|
}
|
|
@@ -5984,6 +5516,7 @@ async function promptShadcnColorTheme() {
|
|
|
5984
5516
|
options: COLOR_THEME_OPTIONS,
|
|
5985
5517
|
initialValue: "neutral"
|
|
5986
5518
|
});
|
|
5519
|
+
if (isGoBack(selected)) return selected;
|
|
5987
5520
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5988
5521
|
return selected;
|
|
5989
5522
|
}
|
|
@@ -5993,6 +5526,7 @@ async function promptShadcnBaseColor() {
|
|
|
5993
5526
|
options: BASE_COLOR_OPTIONS,
|
|
5994
5527
|
initialValue: "neutral"
|
|
5995
5528
|
});
|
|
5529
|
+
if (isGoBack(selected)) return selected;
|
|
5996
5530
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5997
5531
|
return selected;
|
|
5998
5532
|
}
|
|
@@ -6002,6 +5536,7 @@ async function promptShadcnFont() {
|
|
|
6002
5536
|
options: FONT_OPTIONS,
|
|
6003
5537
|
initialValue: "inter"
|
|
6004
5538
|
});
|
|
5539
|
+
if (isGoBack(selected)) return selected;
|
|
6005
5540
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
6006
5541
|
return selected;
|
|
6007
5542
|
}
|
|
@@ -6011,151 +5546,11 @@ async function promptShadcnRadius() {
|
|
|
6011
5546
|
options: RADIUS_OPTIONS,
|
|
6012
5547
|
initialValue: "default"
|
|
6013
5548
|
});
|
|
5549
|
+
if (isGoBack(selected)) return selected;
|
|
6014
5550
|
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
6015
5551
|
return selected;
|
|
6016
5552
|
}
|
|
6017
5553
|
|
|
6018
|
-
//#endregion
|
|
6019
|
-
//#region src/prompts/state-management.ts
|
|
6020
|
-
function resolveStateManagementPrompt(context = {}) {
|
|
6021
|
-
if (context.stateManagement !== void 0) return {
|
|
6022
|
-
shouldPrompt: false,
|
|
6023
|
-
mode: "single",
|
|
6024
|
-
options: [],
|
|
6025
|
-
autoValue: context.stateManagement
|
|
6026
|
-
};
|
|
6027
|
-
const { web } = splitFrontends$1(context.frontends);
|
|
6028
|
-
if (web.length === 0) return {
|
|
6029
|
-
shouldPrompt: false,
|
|
6030
|
-
mode: "single",
|
|
6031
|
-
options: [],
|
|
6032
|
-
autoValue: "none"
|
|
6033
|
-
};
|
|
6034
|
-
const isReact = web.some((f) => [
|
|
6035
|
-
"tanstack-router",
|
|
6036
|
-
"react-router",
|
|
6037
|
-
"react-vite",
|
|
6038
|
-
"tanstack-start",
|
|
6039
|
-
"next",
|
|
6040
|
-
"vinext",
|
|
6041
|
-
"redwood"
|
|
6042
|
-
].includes(f));
|
|
6043
|
-
const isFresh = web.includes("fresh");
|
|
6044
|
-
const options = [];
|
|
6045
|
-
if (isReact) options.push({
|
|
6046
|
-
value: "zustand",
|
|
6047
|
-
label: "Zustand",
|
|
6048
|
-
hint: "Lightweight state management with simple API"
|
|
6049
|
-
}, {
|
|
6050
|
-
value: "jotai",
|
|
6051
|
-
label: "Jotai",
|
|
6052
|
-
hint: "Primitive and flexible atomic state"
|
|
6053
|
-
}, {
|
|
6054
|
-
value: "redux-toolkit",
|
|
6055
|
-
label: "Redux Toolkit",
|
|
6056
|
-
hint: "Enterprise-standard state with excellent TS support"
|
|
6057
|
-
}, {
|
|
6058
|
-
value: "valtio",
|
|
6059
|
-
label: "Valtio",
|
|
6060
|
-
hint: "Proxy-based state management"
|
|
6061
|
-
}, {
|
|
6062
|
-
value: "legend-state",
|
|
6063
|
-
label: "Legend State",
|
|
6064
|
-
hint: "High-performance observable state for React"
|
|
6065
|
-
}, {
|
|
6066
|
-
value: "mobx",
|
|
6067
|
-
label: "MobX",
|
|
6068
|
-
hint: "Observable-based reactive state management"
|
|
6069
|
-
});
|
|
6070
|
-
if (!isFresh) options.push({
|
|
6071
|
-
value: "nanostores",
|
|
6072
|
-
label: "Nanostores",
|
|
6073
|
-
hint: "Tiny state manager (1KB) for all frameworks"
|
|
6074
|
-
}, {
|
|
6075
|
-
value: "xstate",
|
|
6076
|
-
label: "XState",
|
|
6077
|
-
hint: "State machines and statecharts for complex logic"
|
|
6078
|
-
}, {
|
|
6079
|
-
value: "tanstack-store",
|
|
6080
|
-
label: "TanStack Store",
|
|
6081
|
-
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
6082
|
-
});
|
|
6083
|
-
options.push({
|
|
6084
|
-
value: "none",
|
|
6085
|
-
label: "None",
|
|
6086
|
-
hint: "Skip state management setup"
|
|
6087
|
-
});
|
|
6088
|
-
return {
|
|
6089
|
-
shouldPrompt: true,
|
|
6090
|
-
mode: "single",
|
|
6091
|
-
options,
|
|
6092
|
-
initialValue: "none"
|
|
6093
|
-
};
|
|
6094
|
-
}
|
|
6095
|
-
async function getStateManagementChoice(stateManagement, frontends) {
|
|
6096
|
-
const resolution = resolveStateManagementPrompt({
|
|
6097
|
-
stateManagement,
|
|
6098
|
-
frontends
|
|
6099
|
-
});
|
|
6100
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6101
|
-
const response = await navigableSelect({
|
|
6102
|
-
message: "Select state management",
|
|
6103
|
-
options: resolution.options,
|
|
6104
|
-
initialValue: resolution.initialValue
|
|
6105
|
-
});
|
|
6106
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6107
|
-
return response;
|
|
6108
|
-
}
|
|
6109
|
-
|
|
6110
|
-
//#endregion
|
|
6111
|
-
//#region src/prompts/testing.ts
|
|
6112
|
-
const TESTING_PROMPT_OPTIONS = [
|
|
6113
|
-
{
|
|
6114
|
-
value: "vitest",
|
|
6115
|
-
label: "Vitest",
|
|
6116
|
-
hint: "Blazing fast Vite-native unit test framework"
|
|
6117
|
-
},
|
|
6118
|
-
{
|
|
6119
|
-
value: "vitest-playwright",
|
|
6120
|
-
label: "Vitest + Playwright",
|
|
6121
|
-
hint: "Both unit and E2E testing for complete coverage"
|
|
6122
|
-
},
|
|
6123
|
-
{
|
|
6124
|
-
value: "playwright",
|
|
6125
|
-
label: "Playwright",
|
|
6126
|
-
hint: "End-to-end testing framework by Microsoft"
|
|
6127
|
-
},
|
|
6128
|
-
{
|
|
6129
|
-
value: "jest",
|
|
6130
|
-
label: "Jest",
|
|
6131
|
-
hint: "Classic testing framework with wide ecosystem"
|
|
6132
|
-
},
|
|
6133
|
-
{
|
|
6134
|
-
value: "cypress",
|
|
6135
|
-
label: "Cypress",
|
|
6136
|
-
hint: "E2E testing with time travel debugging"
|
|
6137
|
-
},
|
|
6138
|
-
{
|
|
6139
|
-
value: "none",
|
|
6140
|
-
label: "None",
|
|
6141
|
-
hint: "Skip testing framework setup"
|
|
6142
|
-
}
|
|
6143
|
-
];
|
|
6144
|
-
function resolveTestingPrompt(testing) {
|
|
6145
|
-
return createStaticSinglePromptResolution(TESTING_PROMPT_OPTIONS, "vitest", testing);
|
|
6146
|
-
}
|
|
6147
|
-
async function getTestingChoice(testing) {
|
|
6148
|
-
const resolution = resolveTestingPrompt(testing);
|
|
6149
|
-
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6150
|
-
const response = await navigableSelect({
|
|
6151
|
-
message: "Select testing framework",
|
|
6152
|
-
options: resolution.options,
|
|
6153
|
-
initialValue: resolution.initialValue
|
|
6154
|
-
});
|
|
6155
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6156
|
-
return response;
|
|
6157
|
-
}
|
|
6158
|
-
|
|
6159
5554
|
//#endregion
|
|
6160
5555
|
//#region src/prompts/ui-library.ts
|
|
6161
5556
|
const UI_LIBRARY_OPTIONS = {
|
|
@@ -6199,72 +5594,1096 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
6199
5594
|
label: "MUI",
|
|
6200
5595
|
hint: "Popular React component library implementing Material Design"
|
|
6201
5596
|
},
|
|
6202
|
-
antd: {
|
|
6203
|
-
label: "Ant Design",
|
|
6204
|
-
hint: "Enterprise-class React UI component library"
|
|
5597
|
+
antd: {
|
|
5598
|
+
label: "Ant Design",
|
|
5599
|
+
hint: "Enterprise-class React UI component library"
|
|
5600
|
+
},
|
|
5601
|
+
"base-ui": {
|
|
5602
|
+
label: "Base UI",
|
|
5603
|
+
hint: "Unstyled, accessible components from MUI team (Radix successor)"
|
|
5604
|
+
},
|
|
5605
|
+
"ark-ui": {
|
|
5606
|
+
label: "Ark UI",
|
|
5607
|
+
hint: "Headless, accessible UI components for React, Vue, Solid, and Svelte"
|
|
5608
|
+
},
|
|
5609
|
+
"react-aria": {
|
|
5610
|
+
label: "React Aria",
|
|
5611
|
+
hint: "Adobe's accessible, unstyled UI components for React"
|
|
5612
|
+
},
|
|
5613
|
+
none: {
|
|
5614
|
+
label: "None",
|
|
5615
|
+
hint: "No UI component library"
|
|
5616
|
+
}
|
|
5617
|
+
};
|
|
5618
|
+
function resolveUILibraryPrompt(context = {}) {
|
|
5619
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
5620
|
+
if (web.length === 0) return {
|
|
5621
|
+
shouldPrompt: false,
|
|
5622
|
+
mode: "single",
|
|
5623
|
+
options: [],
|
|
5624
|
+
autoValue: "none"
|
|
5625
|
+
};
|
|
5626
|
+
const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
|
|
5627
|
+
if (context.uiLibrary !== void 0) return {
|
|
5628
|
+
shouldPrompt: false,
|
|
5629
|
+
mode: "single",
|
|
5630
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5631
|
+
value: lib,
|
|
5632
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5633
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5634
|
+
})),
|
|
5635
|
+
autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
|
|
5636
|
+
};
|
|
5637
|
+
const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
|
|
5638
|
+
return {
|
|
5639
|
+
shouldPrompt: true,
|
|
5640
|
+
mode: "single",
|
|
5641
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5642
|
+
value: lib,
|
|
5643
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5644
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5645
|
+
})),
|
|
5646
|
+
initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
|
|
5647
|
+
};
|
|
5648
|
+
}
|
|
5649
|
+
async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
|
|
5650
|
+
const resolution = resolveUILibraryPrompt({
|
|
5651
|
+
uiLibrary,
|
|
5652
|
+
frontends,
|
|
5653
|
+
astroIntegration
|
|
5654
|
+
});
|
|
5655
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5656
|
+
const selected = await navigableSelect({
|
|
5657
|
+
message: "Select UI component library",
|
|
5658
|
+
options: resolution.options,
|
|
5659
|
+
initialValue: resolution.initialValue
|
|
5660
|
+
});
|
|
5661
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5662
|
+
return selected;
|
|
5663
|
+
}
|
|
5664
|
+
|
|
5665
|
+
//#endregion
|
|
5666
|
+
//#region src/utils/compatibility.ts
|
|
5667
|
+
const WEB_FRAMEWORKS = [
|
|
5668
|
+
"tanstack-router",
|
|
5669
|
+
"react-router",
|
|
5670
|
+
"react-vite",
|
|
5671
|
+
"tanstack-start",
|
|
5672
|
+
"next",
|
|
5673
|
+
"vinext",
|
|
5674
|
+
"nuxt",
|
|
5675
|
+
"svelte",
|
|
5676
|
+
"solid",
|
|
5677
|
+
"solid-start",
|
|
5678
|
+
"astro",
|
|
5679
|
+
"qwik",
|
|
5680
|
+
"angular",
|
|
5681
|
+
"redwood",
|
|
5682
|
+
"fresh"
|
|
5683
|
+
];
|
|
5684
|
+
|
|
5685
|
+
//#endregion
|
|
5686
|
+
//#region src/prompts/web-deploy.ts
|
|
5687
|
+
function hasWebFrontend(frontends) {
|
|
5688
|
+
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
5689
|
+
}
|
|
5690
|
+
function getDeploymentDisplay(deployment) {
|
|
5691
|
+
if (deployment === "cloudflare") return {
|
|
5692
|
+
label: "Cloudflare",
|
|
5693
|
+
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
5694
|
+
};
|
|
5695
|
+
if (deployment === "vercel") return {
|
|
5696
|
+
label: "Vercel",
|
|
5697
|
+
hint: "Deploy to Vercel's edge network"
|
|
5698
|
+
};
|
|
5699
|
+
return {
|
|
5700
|
+
label: deployment,
|
|
5701
|
+
hint: `Add ${deployment} deployment`
|
|
5702
|
+
};
|
|
5703
|
+
}
|
|
5704
|
+
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
5705
|
+
if (deployment !== void 0) return deployment;
|
|
5706
|
+
if (!hasWebFrontend(frontend)) return "none";
|
|
5707
|
+
const response = await navigableSelect({
|
|
5708
|
+
message: "Select web deployment",
|
|
5709
|
+
options: [
|
|
5710
|
+
"cloudflare",
|
|
5711
|
+
"vercel",
|
|
5712
|
+
"none"
|
|
5713
|
+
].map((deploy) => {
|
|
5714
|
+
const { label, hint } = getDeploymentDisplay(deploy);
|
|
5715
|
+
return {
|
|
5716
|
+
value: deploy,
|
|
5717
|
+
label,
|
|
5718
|
+
hint
|
|
5719
|
+
};
|
|
5720
|
+
}),
|
|
5721
|
+
initialValue: DEFAULT_CONFIG.webDeploy
|
|
5722
|
+
});
|
|
5723
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5724
|
+
return response;
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5727
|
+
//#endregion
|
|
5728
|
+
//#region src/prompts/multi-ecosystem-composer.ts
|
|
5729
|
+
async function getCompositionModeChoice() {
|
|
5730
|
+
const response = await navigableSelect({
|
|
5731
|
+
message: "Select project composition",
|
|
5732
|
+
options: [{
|
|
5733
|
+
value: "single",
|
|
5734
|
+
label: "Single ecosystem",
|
|
5735
|
+
hint: "Use the classic guided flow"
|
|
5736
|
+
}, {
|
|
5737
|
+
value: "multi",
|
|
5738
|
+
label: "Multi ecosystem",
|
|
5739
|
+
hint: "Compose a TypeScript frontend with another backend ecosystem"
|
|
5740
|
+
}],
|
|
5741
|
+
initialValue: "single"
|
|
5742
|
+
});
|
|
5743
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5744
|
+
return response;
|
|
5745
|
+
}
|
|
5746
|
+
async function selectBackendEcosystem() {
|
|
5747
|
+
const response = await navigableSelect({
|
|
5748
|
+
message: "Select backend ecosystem",
|
|
5749
|
+
options: [
|
|
5750
|
+
{
|
|
5751
|
+
value: "go",
|
|
5752
|
+
label: "Go",
|
|
5753
|
+
hint: "Gin, Echo, Fiber, Chi"
|
|
5754
|
+
},
|
|
5755
|
+
{
|
|
5756
|
+
value: "rust",
|
|
5757
|
+
label: "Rust",
|
|
5758
|
+
hint: "Axum, Actix Web, Rocket"
|
|
5759
|
+
},
|
|
5760
|
+
{
|
|
5761
|
+
value: "python",
|
|
5762
|
+
label: "Python",
|
|
5763
|
+
hint: "FastAPI, Django, Flask"
|
|
5764
|
+
},
|
|
5765
|
+
{
|
|
5766
|
+
value: "java",
|
|
5767
|
+
label: "Java",
|
|
5768
|
+
hint: "Spring Boot, Quarkus"
|
|
5769
|
+
},
|
|
5770
|
+
{
|
|
5771
|
+
value: "elixir",
|
|
5772
|
+
label: "Elixir",
|
|
5773
|
+
hint: "Phoenix, LiveView"
|
|
5774
|
+
}
|
|
5775
|
+
],
|
|
5776
|
+
initialValue: "go"
|
|
5777
|
+
});
|
|
5778
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5779
|
+
return response;
|
|
5780
|
+
}
|
|
5781
|
+
async function selectServerDeployment(deployment) {
|
|
5782
|
+
if (deployment !== void 0) return deployment;
|
|
5783
|
+
const response = await navigableSelect({
|
|
5784
|
+
message: "Select server deployment",
|
|
5785
|
+
options: [
|
|
5786
|
+
{
|
|
5787
|
+
value: "none",
|
|
5788
|
+
label: "None",
|
|
5789
|
+
hint: "Skip server deployment setup"
|
|
5790
|
+
},
|
|
5791
|
+
{
|
|
5792
|
+
value: "railway",
|
|
5793
|
+
label: "Railway",
|
|
5794
|
+
hint: "Deploy a standalone backend service"
|
|
5795
|
+
},
|
|
5796
|
+
{
|
|
5797
|
+
value: "docker",
|
|
5798
|
+
label: "Docker",
|
|
5799
|
+
hint: "Containerize the backend service"
|
|
5800
|
+
},
|
|
5801
|
+
{
|
|
5802
|
+
value: "fly",
|
|
5803
|
+
label: "Fly",
|
|
5804
|
+
hint: "Deploy close to users"
|
|
5805
|
+
},
|
|
5806
|
+
{
|
|
5807
|
+
value: "vercel",
|
|
5808
|
+
label: "Vercel",
|
|
5809
|
+
hint: "Deploy from the backend workspace"
|
|
5810
|
+
}
|
|
5811
|
+
],
|
|
5812
|
+
initialValue: "none"
|
|
5813
|
+
});
|
|
5814
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5815
|
+
return response;
|
|
5816
|
+
}
|
|
5817
|
+
function promptValue(value) {
|
|
5818
|
+
if (isCancel$1(value) || isGoBack(value)) return exitCancelled("Operation cancelled");
|
|
5819
|
+
return value;
|
|
5820
|
+
}
|
|
5821
|
+
async function selectDatabaseConfig(flags) {
|
|
5822
|
+
const database = promptValue(await getDatabaseChoice(flags.database, "hono", "bun"));
|
|
5823
|
+
return {
|
|
5824
|
+
database,
|
|
5825
|
+
dbSetup: promptValue(await getDBSetupChoice(database, flags.dbSetup, "none", "none", "none"))
|
|
5826
|
+
};
|
|
5827
|
+
}
|
|
5828
|
+
async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath) {
|
|
5829
|
+
const baseConfig = getDefaultConfig();
|
|
5830
|
+
const frontend = promptValue(await navigableSelect({
|
|
5831
|
+
message: "Select TypeScript web frontend",
|
|
5832
|
+
options: WEB_FRONTEND_PROMPT_OPTIONS,
|
|
5833
|
+
initialValue: flags.frontend?.[0] ?? "next"
|
|
5834
|
+
}));
|
|
5835
|
+
const frontendList = [frontend];
|
|
5836
|
+
const astroIntegration = frontend === "astro" ? promptValue(await getAstroIntegrationChoice(flags.astroIntegration)) : void 0;
|
|
5837
|
+
const uiLibrary = hasWebStyling$1(frontendList) ? promptValue(await getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
|
|
5838
|
+
const shadcnOptions = uiLibrary === "shadcn-ui" ? promptValue(await getShadcnOptions({
|
|
5839
|
+
shadcnBase: flags.shadcnBase,
|
|
5840
|
+
shadcnStyle: flags.shadcnStyle,
|
|
5841
|
+
shadcnIconLibrary: flags.shadcnIconLibrary,
|
|
5842
|
+
shadcnColorTheme: flags.shadcnColorTheme,
|
|
5843
|
+
shadcnBaseColor: flags.shadcnBaseColor,
|
|
5844
|
+
shadcnFont: flags.shadcnFont,
|
|
5845
|
+
shadcnRadius: flags.shadcnRadius
|
|
5846
|
+
})) : void 0;
|
|
5847
|
+
const cssFramework = hasWebStyling$1(frontendList) ? promptValue(await getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
|
|
5848
|
+
const backendEcosystem = await selectBackendEcosystem();
|
|
5849
|
+
const stackPartSpecs = [`frontend:typescript:${frontend}`];
|
|
5850
|
+
const backendChoices = {};
|
|
5851
|
+
let database = "none";
|
|
5852
|
+
let dbSetup = "none";
|
|
5853
|
+
if (backendEcosystem === "go") {
|
|
5854
|
+
const goWebFramework = promptValue(await getGoWebFrameworkChoice(flags.goWebFramework));
|
|
5855
|
+
if (goWebFramework !== "none") {
|
|
5856
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5857
|
+
database = databaseConfig.database;
|
|
5858
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5859
|
+
}
|
|
5860
|
+
const goOrm = database === "none" || goWebFramework === "none" ? "none" : promptValue(await getGoOrmChoice(flags.goOrm));
|
|
5861
|
+
const goApi = goWebFramework === "none" ? "none" : promptValue(await getGoApiChoice(flags.goApi));
|
|
5862
|
+
const goAuth = goWebFramework === "none" ? "none" : promptValue(await getGoAuthChoice(flags.goAuth));
|
|
5863
|
+
const goCli = goWebFramework === "none" ? "none" : promptValue(await getGoCliChoice(flags.goCli));
|
|
5864
|
+
const goLogging = goWebFramework === "none" ? "none" : promptValue(await getGoLoggingChoice(flags.goLogging));
|
|
5865
|
+
Object.assign(backendChoices, {
|
|
5866
|
+
goWebFramework,
|
|
5867
|
+
goOrm,
|
|
5868
|
+
goApi,
|
|
5869
|
+
goAuth,
|
|
5870
|
+
goCli,
|
|
5871
|
+
goLogging
|
|
5872
|
+
});
|
|
5873
|
+
if (goWebFramework !== "none") stackPartSpecs.push(`backend:go:${goWebFramework}`);
|
|
5874
|
+
if (goOrm !== "none") stackPartSpecs.push(`backend.orm:go:${goOrm}`);
|
|
5875
|
+
if (goApi !== "none") stackPartSpecs.push(`backend.api:go:${goApi}`);
|
|
5876
|
+
if (goAuth !== "none") stackPartSpecs.push(`backend.auth:go:${goAuth}`);
|
|
5877
|
+
}
|
|
5878
|
+
if (backendEcosystem === "rust") {
|
|
5879
|
+
const rustWebFramework = promptValue(await getRustWebFrameworkChoice(flags.rustWebFramework));
|
|
5880
|
+
if (rustWebFramework !== "none") {
|
|
5881
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5882
|
+
database = databaseConfig.database;
|
|
5883
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5884
|
+
}
|
|
5885
|
+
const rustOrm = database === "none" || rustWebFramework === "none" ? "none" : promptValue(await getRustOrmChoice(flags.rustOrm));
|
|
5886
|
+
const rustApi = rustWebFramework === "none" ? "none" : promptValue(await getRustApiChoice(flags.rustApi));
|
|
5887
|
+
const rustAuth = rustWebFramework === "none" ? "none" : promptValue(await getRustAuthChoice(flags.rustAuth));
|
|
5888
|
+
const rustFrontend = "none";
|
|
5889
|
+
const rustCli = rustWebFramework === "none" ? "none" : promptValue(await getRustCliChoice(flags.rustCli));
|
|
5890
|
+
const rustLibraries = rustWebFramework === "none" ? [] : promptValue(await getRustLibrariesChoice(flags.rustLibraries));
|
|
5891
|
+
const rustLogging = rustWebFramework === "none" ? "none" : promptValue(await getRustLoggingChoice(flags.rustLogging));
|
|
5892
|
+
const rustErrorHandling = promptValue(await getRustErrorHandlingChoice(flags.rustErrorHandling));
|
|
5893
|
+
const rustCaching = rustWebFramework === "none" ? "none" : promptValue(await getRustCachingChoice(flags.rustCaching));
|
|
5894
|
+
Object.assign(backendChoices, {
|
|
5895
|
+
rustWebFramework,
|
|
5896
|
+
rustOrm,
|
|
5897
|
+
rustApi,
|
|
5898
|
+
rustAuth,
|
|
5899
|
+
rustFrontend,
|
|
5900
|
+
rustCli,
|
|
5901
|
+
rustLibraries,
|
|
5902
|
+
rustLogging,
|
|
5903
|
+
rustErrorHandling,
|
|
5904
|
+
rustCaching
|
|
5905
|
+
});
|
|
5906
|
+
if (rustWebFramework !== "none") stackPartSpecs.push(`backend:rust:${rustWebFramework}`);
|
|
5907
|
+
if (rustOrm !== "none") stackPartSpecs.push(`backend.orm:rust:${rustOrm}`);
|
|
5908
|
+
if (rustApi !== "none") stackPartSpecs.push(`backend.api:rust:${rustApi}`);
|
|
5909
|
+
if (rustAuth !== "none") stackPartSpecs.push(`backend.auth:rust:${rustAuth}`);
|
|
5910
|
+
}
|
|
5911
|
+
if (backendEcosystem === "python") {
|
|
5912
|
+
const pythonWebFramework = promptValue(await getPythonWebFrameworkChoice(flags.pythonWebFramework));
|
|
5913
|
+
if (pythonWebFramework !== "none") {
|
|
5914
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5915
|
+
database = databaseConfig.database;
|
|
5916
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5917
|
+
}
|
|
5918
|
+
const pythonOrm = database === "none" || pythonWebFramework === "none" ? "none" : promptValue(await getPythonOrmChoice(flags.pythonOrm));
|
|
5919
|
+
const pythonValidation = pythonWebFramework === "none" ? "none" : promptValue(await getPythonValidationChoice(flags.pythonValidation));
|
|
5920
|
+
const pythonAi = pythonWebFramework === "none" ? [] : promptValue(await getPythonAiChoice(flags.pythonAi));
|
|
5921
|
+
const pythonAuth = pythonWebFramework === "none" ? "none" : promptValue(await getPythonAuthChoice(flags.pythonAuth));
|
|
5922
|
+
const pythonTaskQueue = pythonWebFramework === "none" ? "none" : promptValue(await getPythonTaskQueueChoice(flags.pythonTaskQueue));
|
|
5923
|
+
const pythonGraphql = pythonWebFramework === "none" ? "none" : promptValue(await getPythonGraphqlChoice(flags.pythonGraphql));
|
|
5924
|
+
const pythonQuality = pythonWebFramework === "none" ? "none" : promptValue(await getPythonQualityChoice(flags.pythonQuality));
|
|
5925
|
+
Object.assign(backendChoices, {
|
|
5926
|
+
pythonWebFramework,
|
|
5927
|
+
pythonOrm,
|
|
5928
|
+
pythonValidation,
|
|
5929
|
+
pythonAi,
|
|
5930
|
+
pythonAuth,
|
|
5931
|
+
pythonTaskQueue,
|
|
5932
|
+
pythonGraphql,
|
|
5933
|
+
pythonQuality
|
|
5934
|
+
});
|
|
5935
|
+
if (pythonWebFramework !== "none") stackPartSpecs.push(`backend:python:${pythonWebFramework}`);
|
|
5936
|
+
if (pythonOrm !== "none") stackPartSpecs.push(`backend.orm:python:${pythonOrm}`);
|
|
5937
|
+
if (pythonAuth !== "none") stackPartSpecs.push(`backend.auth:python:${pythonAuth}`);
|
|
5938
|
+
if (pythonTaskQueue !== "none") stackPartSpecs.push(`backend.jobQueue:python:${pythonTaskQueue}`);
|
|
5939
|
+
if (pythonGraphql !== "none") stackPartSpecs.push(`backend.api:python:${pythonGraphql}`);
|
|
5940
|
+
}
|
|
5941
|
+
if (backendEcosystem === "java") {
|
|
5942
|
+
const javaWebFramework = promptValue(await getJavaWebFrameworkChoice(flags.javaWebFramework));
|
|
5943
|
+
const javaBuildTool = promptValue(await getJavaBuildToolChoice(flags.javaBuildTool));
|
|
5944
|
+
if (javaWebFramework !== "none" && javaBuildTool !== "none") {
|
|
5945
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5946
|
+
database = databaseConfig.database;
|
|
5947
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5948
|
+
}
|
|
5949
|
+
const javaOrm = database === "none" || javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaOrmChoice(flags.javaOrm));
|
|
5950
|
+
const javaAuth = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaAuthChoice(flags.javaAuth));
|
|
5951
|
+
const javaLibraries = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? [] : promptValue(await getJavaLibrariesChoice(flags.javaLibraries));
|
|
5952
|
+
const javaTestingLibraries = promptValue(await getJavaTestingLibrariesChoice(flags.javaTestingLibraries));
|
|
5953
|
+
Object.assign(backendChoices, {
|
|
5954
|
+
javaWebFramework,
|
|
5955
|
+
javaBuildTool,
|
|
5956
|
+
javaOrm,
|
|
5957
|
+
javaAuth,
|
|
5958
|
+
javaLibraries,
|
|
5959
|
+
javaTestingLibraries
|
|
5960
|
+
});
|
|
5961
|
+
if (javaWebFramework !== "none") stackPartSpecs.push(`backend:java:${javaWebFramework}`);
|
|
5962
|
+
if (javaOrm !== "none") stackPartSpecs.push(`backend.orm:java:${javaOrm}`);
|
|
5963
|
+
if (javaAuth !== "none") stackPartSpecs.push(`backend.auth:java:${javaAuth}`);
|
|
5964
|
+
}
|
|
5965
|
+
if (backendEcosystem === "elixir") {
|
|
5966
|
+
const elixirWebFramework = promptValue(await getElixirWebFrameworkChoice(flags.elixirWebFramework));
|
|
5967
|
+
if (elixirWebFramework !== "none") {
|
|
5968
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5969
|
+
database = databaseConfig.database;
|
|
5970
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5971
|
+
}
|
|
5972
|
+
const elixirOrm = database === "none" || elixirWebFramework === "none" ? "none" : promptValue(await getElixirOrmChoice(flags.elixirOrm));
|
|
5973
|
+
const elixirAuth = elixirWebFramework === "none" ? "none" : promptValue(await getElixirAuthChoice(flags.elixirAuth));
|
|
5974
|
+
const elixirApi = elixirWebFramework === "none" ? "none" : promptValue(await getElixirApiChoice(flags.elixirApi));
|
|
5975
|
+
const elixirRealtime = elixirWebFramework === "none" ? "none" : promptValue(await getElixirRealtimeChoice(flags.elixirRealtime));
|
|
5976
|
+
const elixirJobs = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJobsChoice(flags.elixirJobs));
|
|
5977
|
+
const elixirValidation = elixirWebFramework === "none" ? "none" : promptValue(await getElixirValidationChoice(flags.elixirValidation));
|
|
5978
|
+
const elixirHttp = elixirWebFramework === "none" ? "none" : promptValue(await getElixirHttpChoice(flags.elixirHttp));
|
|
5979
|
+
const elixirJson = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJsonChoice(flags.elixirJson));
|
|
5980
|
+
const elixirEmail = elixirWebFramework === "none" ? "none" : promptValue(await getElixirEmailChoice(flags.elixirEmail));
|
|
5981
|
+
const elixirCaching = elixirWebFramework === "none" ? "none" : promptValue(await getElixirCachingChoice(flags.elixirCaching));
|
|
5982
|
+
const elixirObservability = elixirWebFramework === "none" ? "none" : promptValue(await getElixirObservabilityChoice(flags.elixirObservability));
|
|
5983
|
+
const elixirTesting = elixirWebFramework === "none" ? "none" : promptValue(await getElixirTestingChoice(flags.elixirTesting));
|
|
5984
|
+
const elixirQuality = elixirWebFramework === "none" ? "none" : promptValue(await getElixirQualityChoice(flags.elixirQuality));
|
|
5985
|
+
const elixirDeploy = elixirWebFramework === "none" ? "none" : promptValue(await getElixirDeployChoice(flags.elixirDeploy));
|
|
5986
|
+
Object.assign(backendChoices, {
|
|
5987
|
+
elixirWebFramework,
|
|
5988
|
+
elixirOrm,
|
|
5989
|
+
elixirAuth,
|
|
5990
|
+
elixirApi,
|
|
5991
|
+
elixirRealtime,
|
|
5992
|
+
elixirJobs,
|
|
5993
|
+
elixirValidation,
|
|
5994
|
+
elixirHttp,
|
|
5995
|
+
elixirJson,
|
|
5996
|
+
elixirEmail,
|
|
5997
|
+
elixirCaching,
|
|
5998
|
+
elixirObservability,
|
|
5999
|
+
elixirTesting,
|
|
6000
|
+
elixirQuality,
|
|
6001
|
+
elixirDeploy
|
|
6002
|
+
});
|
|
6003
|
+
if (elixirWebFramework !== "none") stackPartSpecs.push(`backend:elixir:${elixirWebFramework}`);
|
|
6004
|
+
if (elixirOrm !== "none") stackPartSpecs.push(`backend.orm:elixir:${elixirOrm}`);
|
|
6005
|
+
if (elixirAuth !== "none") stackPartSpecs.push(`backend.auth:elixir:${elixirAuth}`);
|
|
6006
|
+
if (elixirApi !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirApi}`);
|
|
6007
|
+
if (elixirRealtime !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirRealtime}`);
|
|
6008
|
+
if (elixirJobs !== "none") stackPartSpecs.push(`backend.jobQueue:elixir:${elixirJobs}`);
|
|
6009
|
+
if (elixirEmail !== "none") stackPartSpecs.push(`backend.email:elixir:${elixirEmail}`);
|
|
6010
|
+
if (elixirCaching !== "none") stackPartSpecs.push(`backend.caching:elixir:${elixirCaching}`);
|
|
6011
|
+
if (elixirObservability !== "none") stackPartSpecs.push(`backend.observability:elixir:${elixirObservability}`);
|
|
6012
|
+
if (elixirTesting !== "none") stackPartSpecs.push(`backend.testing:elixir:${elixirTesting}`);
|
|
6013
|
+
if (elixirDeploy !== "none") stackPartSpecs.push(`backend.deploy:elixir:${elixirDeploy}`);
|
|
6014
|
+
}
|
|
6015
|
+
if (database !== "none") stackPartSpecs.push(`database:universal:${database}`);
|
|
6016
|
+
const stackParts = (0, types_exports.parseStackPartSpecs)(stackPartSpecs, "selected");
|
|
6017
|
+
const graphPartial = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
|
|
6018
|
+
const addons = promptValue(await getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
|
|
6019
|
+
const webDeploy = promptValue(await getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
|
|
6020
|
+
const serverDeploy = await selectServerDeployment(flags.serverDeploy);
|
|
6021
|
+
const aiDocs = promptValue(await getAiDocsChoice(flags.aiDocs));
|
|
6022
|
+
const git = promptValue(await getGitChoice(flags.git));
|
|
6023
|
+
const packageManager = promptValue(await getPackageManagerChoice(flags.packageManager));
|
|
6024
|
+
const install = promptValue(await getinstallChoice(flags.install, "typescript", "none"));
|
|
6025
|
+
return {
|
|
6026
|
+
...baseConfig,
|
|
6027
|
+
...flags,
|
|
6028
|
+
...graphPartial,
|
|
6029
|
+
...backendChoices,
|
|
6030
|
+
projectName,
|
|
6031
|
+
projectDir,
|
|
6032
|
+
relativePath,
|
|
6033
|
+
ecosystem: "typescript",
|
|
6034
|
+
frontend: frontendList,
|
|
6035
|
+
backend: "none",
|
|
6036
|
+
runtime: "none",
|
|
6037
|
+
database,
|
|
6038
|
+
orm: "none",
|
|
6039
|
+
api: "none",
|
|
6040
|
+
auth: "none",
|
|
6041
|
+
astroIntegration,
|
|
6042
|
+
uiLibrary,
|
|
6043
|
+
...shadcnOptions,
|
|
6044
|
+
cssFramework,
|
|
6045
|
+
addons,
|
|
6046
|
+
examples: [],
|
|
6047
|
+
dbSetup,
|
|
6048
|
+
webDeploy,
|
|
6049
|
+
serverDeploy,
|
|
6050
|
+
aiDocs,
|
|
6051
|
+
git,
|
|
6052
|
+
packageManager,
|
|
6053
|
+
install,
|
|
6054
|
+
stackParts
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
//#endregion
|
|
6059
|
+
//#region src/prompts/navigable-group.ts
|
|
6060
|
+
/**
|
|
6061
|
+
* Navigable group - a group of prompts that allows going back
|
|
6062
|
+
*/
|
|
6063
|
+
/**
|
|
6064
|
+
* Define a group of prompts that supports going back to previous prompts.
|
|
6065
|
+
* Returns a result object with all the values, or handles cancel/go-back navigation.
|
|
6066
|
+
*/
|
|
6067
|
+
async function navigableGroup(prompts, opts) {
|
|
6068
|
+
const results = {};
|
|
6069
|
+
const promptNames = Object.keys(prompts);
|
|
6070
|
+
let currentIndex = 0;
|
|
6071
|
+
let goingBack = false;
|
|
6072
|
+
while (currentIndex < promptNames.length) {
|
|
6073
|
+
const name = promptNames[currentIndex];
|
|
6074
|
+
const prompt = prompts[name];
|
|
6075
|
+
setIsFirstPrompt$1(currentIndex === 0);
|
|
6076
|
+
setLastPromptShownUI(false);
|
|
6077
|
+
const result = await prompt({ results })?.catch((e) => {
|
|
6078
|
+
throw e;
|
|
6079
|
+
});
|
|
6080
|
+
if (isGoBack(result)) {
|
|
6081
|
+
goingBack = true;
|
|
6082
|
+
if (currentIndex > 0) {
|
|
6083
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6084
|
+
delete results[prevName];
|
|
6085
|
+
currentIndex--;
|
|
6086
|
+
continue;
|
|
6087
|
+
}
|
|
6088
|
+
goingBack = false;
|
|
6089
|
+
continue;
|
|
6090
|
+
}
|
|
6091
|
+
if (isCancel$1(result)) {
|
|
6092
|
+
if (typeof opts?.onCancel === "function") {
|
|
6093
|
+
results[name] = "canceled";
|
|
6094
|
+
opts.onCancel({ results });
|
|
6095
|
+
}
|
|
6096
|
+
setIsFirstPrompt$1(false);
|
|
6097
|
+
return results;
|
|
6098
|
+
}
|
|
6099
|
+
if (goingBack && !didLastPromptShowUI()) {
|
|
6100
|
+
if (currentIndex > 0) {
|
|
6101
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6102
|
+
delete results[prevName];
|
|
6103
|
+
currentIndex--;
|
|
6104
|
+
continue;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
goingBack = false;
|
|
6108
|
+
results[name] = result;
|
|
6109
|
+
currentIndex++;
|
|
6110
|
+
}
|
|
6111
|
+
setIsFirstPrompt$1(false);
|
|
6112
|
+
return results;
|
|
6113
|
+
}
|
|
6114
|
+
|
|
6115
|
+
//#endregion
|
|
6116
|
+
//#region src/prompts/observability.ts
|
|
6117
|
+
const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
6118
|
+
{
|
|
6119
|
+
value: "opentelemetry",
|
|
6120
|
+
label: "OpenTelemetry",
|
|
6121
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
6122
|
+
},
|
|
6123
|
+
{
|
|
6124
|
+
value: "sentry",
|
|
6125
|
+
label: "Sentry",
|
|
6126
|
+
hint: "Error tracking and performance monitoring"
|
|
6127
|
+
},
|
|
6128
|
+
{
|
|
6129
|
+
value: "grafana",
|
|
6130
|
+
label: "Grafana",
|
|
6131
|
+
hint: "Prometheus metrics for Grafana dashboards and alerting"
|
|
6132
|
+
},
|
|
6133
|
+
{
|
|
6134
|
+
value: "none",
|
|
6135
|
+
label: "None",
|
|
6136
|
+
hint: "Skip observability/tracing setup"
|
|
6137
|
+
}
|
|
6138
|
+
];
|
|
6139
|
+
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
6140
|
+
function resolveObservabilityPrompt(context = {}) {
|
|
6141
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6142
|
+
shouldPrompt: false,
|
|
6143
|
+
mode: "single",
|
|
6144
|
+
options: [],
|
|
6145
|
+
autoValue: "none"
|
|
6146
|
+
};
|
|
6147
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
6148
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6149
|
+
shouldPrompt: false,
|
|
6150
|
+
mode: "single",
|
|
6151
|
+
options: [],
|
|
6152
|
+
autoValue: "none"
|
|
6153
|
+
};
|
|
6154
|
+
return context.observability !== void 0 ? {
|
|
6155
|
+
shouldPrompt: false,
|
|
6156
|
+
mode: "single",
|
|
6157
|
+
options,
|
|
6158
|
+
autoValue: context.observability
|
|
6159
|
+
} : {
|
|
6160
|
+
shouldPrompt: true,
|
|
6161
|
+
mode: "single",
|
|
6162
|
+
options,
|
|
6163
|
+
initialValue: "none"
|
|
6164
|
+
};
|
|
6165
|
+
}
|
|
6166
|
+
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
6167
|
+
const resolution = resolveObservabilityPrompt({
|
|
6168
|
+
observability,
|
|
6169
|
+
backend,
|
|
6170
|
+
ecosystem
|
|
6171
|
+
});
|
|
6172
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6173
|
+
const response = await navigableSelect({
|
|
6174
|
+
message: "Select observability solution",
|
|
6175
|
+
options: resolution.options,
|
|
6176
|
+
initialValue: resolution.initialValue
|
|
6177
|
+
});
|
|
6178
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6179
|
+
return response;
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
//#endregion
|
|
6183
|
+
//#region src/prompts/orm.ts
|
|
6184
|
+
const ormOptions = {
|
|
6185
|
+
prisma: {
|
|
6186
|
+
value: "prisma",
|
|
6187
|
+
label: "Prisma",
|
|
6188
|
+
hint: "Powerful, feature-rich ORM"
|
|
6189
|
+
},
|
|
6190
|
+
mongoose: {
|
|
6191
|
+
value: "mongoose",
|
|
6192
|
+
label: "Mongoose",
|
|
6193
|
+
hint: "Elegant object modeling tool"
|
|
6194
|
+
},
|
|
6195
|
+
drizzle: {
|
|
6196
|
+
value: "drizzle",
|
|
6197
|
+
label: "Drizzle",
|
|
6198
|
+
hint: "Lightweight and performant TypeScript ORM"
|
|
6199
|
+
},
|
|
6200
|
+
typeorm: {
|
|
6201
|
+
value: "typeorm",
|
|
6202
|
+
label: "TypeORM",
|
|
6203
|
+
hint: "Traditional ORM with Active Record/Data Mapper"
|
|
6204
|
+
},
|
|
6205
|
+
kysely: {
|
|
6206
|
+
value: "kysely",
|
|
6207
|
+
label: "Kysely",
|
|
6208
|
+
hint: "Type-safe SQL query builder"
|
|
6209
|
+
},
|
|
6210
|
+
mikroorm: {
|
|
6211
|
+
value: "mikroorm",
|
|
6212
|
+
label: "MikroORM",
|
|
6213
|
+
hint: "Data Mapper ORM for DDD"
|
|
6214
|
+
},
|
|
6215
|
+
sequelize: {
|
|
6216
|
+
value: "sequelize",
|
|
6217
|
+
label: "Sequelize",
|
|
6218
|
+
hint: "Mature ORM with wide adoption"
|
|
6219
|
+
}
|
|
6220
|
+
};
|
|
6221
|
+
function resolveORMPrompt(context) {
|
|
6222
|
+
if (context.backend === "convex" || !context.hasDatabase) return {
|
|
6223
|
+
shouldPrompt: false,
|
|
6224
|
+
mode: "single",
|
|
6225
|
+
options: [],
|
|
6226
|
+
autoValue: "none"
|
|
6227
|
+
};
|
|
6228
|
+
if (context.database === "edgedb" || context.database === "redis") return {
|
|
6229
|
+
shouldPrompt: false,
|
|
6230
|
+
mode: "single",
|
|
6231
|
+
options: [],
|
|
6232
|
+
autoValue: "none"
|
|
6233
|
+
};
|
|
6234
|
+
if (context.orm !== void 0) return {
|
|
6235
|
+
shouldPrompt: false,
|
|
6236
|
+
mode: "single",
|
|
6237
|
+
options: [],
|
|
6238
|
+
autoValue: context.orm
|
|
6239
|
+
};
|
|
6240
|
+
return {
|
|
6241
|
+
shouldPrompt: true,
|
|
6242
|
+
mode: "single",
|
|
6243
|
+
options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
|
|
6244
|
+
ormOptions.drizzle,
|
|
6245
|
+
ormOptions.prisma,
|
|
6246
|
+
ormOptions.typeorm,
|
|
6247
|
+
ormOptions.kysely,
|
|
6248
|
+
ormOptions.mikroorm,
|
|
6249
|
+
ormOptions.sequelize
|
|
6250
|
+
],
|
|
6251
|
+
initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
6252
|
+
};
|
|
6253
|
+
}
|
|
6254
|
+
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
6255
|
+
const resolution = resolveORMPrompt({
|
|
6256
|
+
orm,
|
|
6257
|
+
hasDatabase,
|
|
6258
|
+
database,
|
|
6259
|
+
backend,
|
|
6260
|
+
runtime
|
|
6261
|
+
});
|
|
6262
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6263
|
+
const response = await navigableSelect({
|
|
6264
|
+
message: "Select ORM",
|
|
6265
|
+
options: resolution.options,
|
|
6266
|
+
initialValue: resolution.initialValue
|
|
6267
|
+
});
|
|
6268
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6269
|
+
return response;
|
|
6270
|
+
}
|
|
6271
|
+
|
|
6272
|
+
//#endregion
|
|
6273
|
+
//#region src/prompts/payments.ts
|
|
6274
|
+
function resolvePaymentsPrompt(context = {}) {
|
|
6275
|
+
if (context.payments !== void 0) return {
|
|
6276
|
+
shouldPrompt: false,
|
|
6277
|
+
mode: "single",
|
|
6278
|
+
options: [],
|
|
6279
|
+
autoValue: context.payments
|
|
6280
|
+
};
|
|
6281
|
+
if (context.backend === "none") return {
|
|
6282
|
+
shouldPrompt: false,
|
|
6283
|
+
mode: "single",
|
|
6284
|
+
options: [],
|
|
6285
|
+
autoValue: "none"
|
|
6286
|
+
};
|
|
6287
|
+
const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
6288
|
+
const options = [];
|
|
6289
|
+
if (isPolarCompatible) options.push({
|
|
6290
|
+
value: "polar",
|
|
6291
|
+
label: "Polar",
|
|
6292
|
+
hint: "Turn your software into a business. 6 lines of code."
|
|
6293
|
+
});
|
|
6294
|
+
options.push({
|
|
6295
|
+
value: "stripe",
|
|
6296
|
+
label: "Stripe",
|
|
6297
|
+
hint: "Payment processing platform for internet businesses."
|
|
6298
|
+
}, {
|
|
6299
|
+
value: "lemon-squeezy",
|
|
6300
|
+
label: "Lemon Squeezy",
|
|
6301
|
+
hint: "All-in-one platform for SaaS, digital products, and subscriptions."
|
|
6302
|
+
}, {
|
|
6303
|
+
value: "paddle",
|
|
6304
|
+
label: "Paddle",
|
|
6305
|
+
hint: "Complete payments infrastructure for SaaS."
|
|
6306
|
+
}, {
|
|
6307
|
+
value: "dodo",
|
|
6308
|
+
label: "Dodo Payments",
|
|
6309
|
+
hint: "Simple payment infrastructure for developers."
|
|
6310
|
+
}, {
|
|
6311
|
+
value: "none",
|
|
6312
|
+
label: "None",
|
|
6313
|
+
hint: "No payments integration"
|
|
6314
|
+
});
|
|
6315
|
+
return {
|
|
6316
|
+
shouldPrompt: true,
|
|
6317
|
+
mode: "single",
|
|
6318
|
+
options,
|
|
6319
|
+
initialValue: DEFAULT_CONFIG.payments
|
|
6320
|
+
};
|
|
6321
|
+
}
|
|
6322
|
+
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
6323
|
+
const resolution = resolvePaymentsPrompt({
|
|
6324
|
+
payments,
|
|
6325
|
+
auth,
|
|
6326
|
+
backend,
|
|
6327
|
+
frontends
|
|
6328
|
+
});
|
|
6329
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6330
|
+
const response = await navigableSelect({
|
|
6331
|
+
message: "Select payments provider",
|
|
6332
|
+
options: resolution.options,
|
|
6333
|
+
initialValue: resolution.initialValue
|
|
6334
|
+
});
|
|
6335
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6336
|
+
return response;
|
|
6337
|
+
}
|
|
6338
|
+
|
|
6339
|
+
//#endregion
|
|
6340
|
+
//#region src/prompts/realtime.ts
|
|
6341
|
+
const REALTIME_PROMPT_OPTIONS = [
|
|
6342
|
+
{
|
|
6343
|
+
value: "socket-io",
|
|
6344
|
+
label: "Socket.IO",
|
|
6345
|
+
hint: "Real-time bidirectional communication with fallbacks"
|
|
6346
|
+
},
|
|
6347
|
+
{
|
|
6348
|
+
value: "partykit",
|
|
6349
|
+
label: "PartyKit",
|
|
6350
|
+
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
6351
|
+
},
|
|
6352
|
+
{
|
|
6353
|
+
value: "ably",
|
|
6354
|
+
label: "Ably",
|
|
6355
|
+
hint: "Real-time messaging platform with pub/sub and presence"
|
|
6356
|
+
},
|
|
6357
|
+
{
|
|
6358
|
+
value: "pusher",
|
|
6359
|
+
label: "Pusher",
|
|
6360
|
+
hint: "Real-time communication APIs with channels and events"
|
|
6361
|
+
},
|
|
6362
|
+
{
|
|
6363
|
+
value: "liveblocks",
|
|
6364
|
+
label: "Liveblocks",
|
|
6365
|
+
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
6366
|
+
},
|
|
6367
|
+
{
|
|
6368
|
+
value: "yjs",
|
|
6369
|
+
label: "Y.js",
|
|
6370
|
+
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
6371
|
+
},
|
|
6372
|
+
{
|
|
6373
|
+
value: "none",
|
|
6374
|
+
label: "None",
|
|
6375
|
+
hint: "Skip real-time/WebSocket integration"
|
|
6376
|
+
}
|
|
6377
|
+
];
|
|
6378
|
+
function resolveRealtimePrompt(context = {}) {
|
|
6379
|
+
if (context.backend === "none" || context.backend === "convex") return {
|
|
6380
|
+
shouldPrompt: false,
|
|
6381
|
+
mode: "single",
|
|
6382
|
+
options: [],
|
|
6383
|
+
autoValue: "none"
|
|
6384
|
+
};
|
|
6385
|
+
return context.realtime !== void 0 ? {
|
|
6386
|
+
shouldPrompt: false,
|
|
6387
|
+
mode: "single",
|
|
6388
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6389
|
+
autoValue: context.realtime
|
|
6390
|
+
} : {
|
|
6391
|
+
shouldPrompt: true,
|
|
6392
|
+
mode: "single",
|
|
6393
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6394
|
+
initialValue: "none"
|
|
6395
|
+
};
|
|
6396
|
+
}
|
|
6397
|
+
async function getRealtimeChoice(realtime, backend) {
|
|
6398
|
+
const resolution = resolveRealtimePrompt({
|
|
6399
|
+
realtime,
|
|
6400
|
+
backend
|
|
6401
|
+
});
|
|
6402
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6403
|
+
const response = await navigableSelect({
|
|
6404
|
+
message: "Select real-time solution",
|
|
6405
|
+
options: resolution.options,
|
|
6406
|
+
initialValue: resolution.initialValue
|
|
6407
|
+
});
|
|
6408
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6409
|
+
return response;
|
|
6410
|
+
}
|
|
6411
|
+
|
|
6412
|
+
//#endregion
|
|
6413
|
+
//#region src/prompts/runtime.ts
|
|
6414
|
+
const RUNTIME_PROMPT_OPTIONS = [
|
|
6415
|
+
{
|
|
6416
|
+
value: "bun",
|
|
6417
|
+
label: "Bun",
|
|
6418
|
+
hint: "Fast all-in-one JavaScript runtime"
|
|
6419
|
+
},
|
|
6420
|
+
{
|
|
6421
|
+
value: "node",
|
|
6422
|
+
label: "Node.js",
|
|
6423
|
+
hint: "Traditional Node.js runtime"
|
|
6424
|
+
},
|
|
6425
|
+
{
|
|
6426
|
+
value: "workers",
|
|
6427
|
+
label: "Cloudflare Workers",
|
|
6428
|
+
hint: "Edge runtime on Cloudflare's global network"
|
|
6429
|
+
}
|
|
6430
|
+
];
|
|
6431
|
+
function resolveRuntimePrompt(context = {}) {
|
|
6432
|
+
if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
|
|
6433
|
+
shouldPrompt: false,
|
|
6434
|
+
mode: "single",
|
|
6435
|
+
options: [],
|
|
6436
|
+
autoValue: "none"
|
|
6437
|
+
};
|
|
6438
|
+
const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
|
|
6439
|
+
return context.runtime !== void 0 ? {
|
|
6440
|
+
shouldPrompt: false,
|
|
6441
|
+
mode: "single",
|
|
6442
|
+
options,
|
|
6443
|
+
autoValue: context.runtime
|
|
6444
|
+
} : {
|
|
6445
|
+
shouldPrompt: true,
|
|
6446
|
+
mode: "single",
|
|
6447
|
+
options,
|
|
6448
|
+
initialValue: DEFAULT_CONFIG.runtime
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
async function getRuntimeChoice(runtime, backend) {
|
|
6452
|
+
const resolution = resolveRuntimePrompt({
|
|
6453
|
+
runtime,
|
|
6454
|
+
backend
|
|
6455
|
+
});
|
|
6456
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6457
|
+
const response = await navigableSelect({
|
|
6458
|
+
message: "Select runtime",
|
|
6459
|
+
options: resolution.options,
|
|
6460
|
+
initialValue: resolution.initialValue
|
|
6461
|
+
});
|
|
6462
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6463
|
+
return response;
|
|
6464
|
+
}
|
|
6465
|
+
|
|
6466
|
+
//#endregion
|
|
6467
|
+
//#region src/prompts/search.ts
|
|
6468
|
+
const SEARCH_PROMPT_OPTIONS = [
|
|
6469
|
+
{
|
|
6470
|
+
value: "meilisearch",
|
|
6471
|
+
label: "Meilisearch",
|
|
6472
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
6473
|
+
},
|
|
6474
|
+
{
|
|
6475
|
+
value: "typesense",
|
|
6476
|
+
label: "Typesense",
|
|
6477
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
6478
|
+
},
|
|
6479
|
+
{
|
|
6480
|
+
value: "elasticsearch",
|
|
6481
|
+
label: "Elasticsearch",
|
|
6482
|
+
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
6483
|
+
},
|
|
6484
|
+
{
|
|
6485
|
+
value: "algolia",
|
|
6486
|
+
label: "Algolia",
|
|
6487
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
6488
|
+
},
|
|
6489
|
+
{
|
|
6490
|
+
value: "none",
|
|
6491
|
+
label: "None",
|
|
6492
|
+
hint: "Skip search engine setup"
|
|
6493
|
+
}
|
|
6494
|
+
];
|
|
6495
|
+
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
6496
|
+
function resolveSearchPrompt(context = {}) {
|
|
6497
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6498
|
+
shouldPrompt: false,
|
|
6499
|
+
mode: "single",
|
|
6500
|
+
options: [],
|
|
6501
|
+
autoValue: "none"
|
|
6502
|
+
};
|
|
6503
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
6504
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6505
|
+
shouldPrompt: false,
|
|
6506
|
+
mode: "single",
|
|
6507
|
+
options: [],
|
|
6508
|
+
autoValue: "none"
|
|
6509
|
+
};
|
|
6510
|
+
return context.search !== void 0 ? {
|
|
6511
|
+
shouldPrompt: false,
|
|
6512
|
+
mode: "single",
|
|
6513
|
+
options,
|
|
6514
|
+
autoValue: context.search
|
|
6515
|
+
} : {
|
|
6516
|
+
shouldPrompt: true,
|
|
6517
|
+
mode: "single",
|
|
6518
|
+
options,
|
|
6519
|
+
initialValue: "none"
|
|
6520
|
+
};
|
|
6521
|
+
}
|
|
6522
|
+
async function getSearchChoice(search, backend, ecosystem) {
|
|
6523
|
+
const resolution = resolveSearchPrompt({
|
|
6524
|
+
search,
|
|
6525
|
+
backend,
|
|
6526
|
+
ecosystem
|
|
6527
|
+
});
|
|
6528
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6529
|
+
const response = await navigableSelect({
|
|
6530
|
+
message: "Select search engine",
|
|
6531
|
+
options: resolution.options,
|
|
6532
|
+
initialValue: resolution.initialValue
|
|
6533
|
+
});
|
|
6534
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6535
|
+
return response;
|
|
6536
|
+
}
|
|
6537
|
+
|
|
6538
|
+
//#endregion
|
|
6539
|
+
//#region src/prompts/server-deploy.ts
|
|
6540
|
+
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
6541
|
+
if (deployment !== void 0) return deployment;
|
|
6542
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
6543
|
+
if (backend !== "hono") return "none";
|
|
6544
|
+
if (runtime === "workers") return "cloudflare";
|
|
6545
|
+
return "none";
|
|
6546
|
+
}
|
|
6547
|
+
|
|
6548
|
+
//#endregion
|
|
6549
|
+
//#region src/prompts/state-management.ts
|
|
6550
|
+
function resolveStateManagementPrompt(context = {}) {
|
|
6551
|
+
if (context.stateManagement !== void 0) return {
|
|
6552
|
+
shouldPrompt: false,
|
|
6553
|
+
mode: "single",
|
|
6554
|
+
options: [],
|
|
6555
|
+
autoValue: context.stateManagement
|
|
6556
|
+
};
|
|
6557
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
6558
|
+
if (web.length === 0) return {
|
|
6559
|
+
shouldPrompt: false,
|
|
6560
|
+
mode: "single",
|
|
6561
|
+
options: [],
|
|
6562
|
+
autoValue: "none"
|
|
6563
|
+
};
|
|
6564
|
+
const isReact = web.some((f) => [
|
|
6565
|
+
"tanstack-router",
|
|
6566
|
+
"react-router",
|
|
6567
|
+
"react-vite",
|
|
6568
|
+
"tanstack-start",
|
|
6569
|
+
"next",
|
|
6570
|
+
"vinext",
|
|
6571
|
+
"redwood"
|
|
6572
|
+
].includes(f));
|
|
6573
|
+
const isFresh = web.includes("fresh");
|
|
6574
|
+
const options = [];
|
|
6575
|
+
if (isReact) options.push({
|
|
6576
|
+
value: "zustand",
|
|
6577
|
+
label: "Zustand",
|
|
6578
|
+
hint: "Lightweight state management with simple API"
|
|
6579
|
+
}, {
|
|
6580
|
+
value: "jotai",
|
|
6581
|
+
label: "Jotai",
|
|
6582
|
+
hint: "Primitive and flexible atomic state"
|
|
6583
|
+
}, {
|
|
6584
|
+
value: "redux-toolkit",
|
|
6585
|
+
label: "Redux Toolkit",
|
|
6586
|
+
hint: "Enterprise-standard state with excellent TS support"
|
|
6587
|
+
}, {
|
|
6588
|
+
value: "valtio",
|
|
6589
|
+
label: "Valtio",
|
|
6590
|
+
hint: "Proxy-based state management"
|
|
6591
|
+
}, {
|
|
6592
|
+
value: "legend-state",
|
|
6593
|
+
label: "Legend State",
|
|
6594
|
+
hint: "High-performance observable state for React"
|
|
6595
|
+
}, {
|
|
6596
|
+
value: "mobx",
|
|
6597
|
+
label: "MobX",
|
|
6598
|
+
hint: "Observable-based reactive state management"
|
|
6599
|
+
});
|
|
6600
|
+
if (!isFresh) options.push({
|
|
6601
|
+
value: "nanostores",
|
|
6602
|
+
label: "Nanostores",
|
|
6603
|
+
hint: "Tiny state manager (1KB) for all frameworks"
|
|
6604
|
+
}, {
|
|
6605
|
+
value: "xstate",
|
|
6606
|
+
label: "XState",
|
|
6607
|
+
hint: "State machines and statecharts for complex logic"
|
|
6608
|
+
}, {
|
|
6609
|
+
value: "tanstack-store",
|
|
6610
|
+
label: "TanStack Store",
|
|
6611
|
+
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
6612
|
+
});
|
|
6613
|
+
options.push({
|
|
6614
|
+
value: "none",
|
|
6615
|
+
label: "None",
|
|
6616
|
+
hint: "Skip state management setup"
|
|
6617
|
+
});
|
|
6618
|
+
return {
|
|
6619
|
+
shouldPrompt: true,
|
|
6620
|
+
mode: "single",
|
|
6621
|
+
options,
|
|
6622
|
+
initialValue: "none"
|
|
6623
|
+
};
|
|
6624
|
+
}
|
|
6625
|
+
async function getStateManagementChoice(stateManagement, frontends) {
|
|
6626
|
+
const resolution = resolveStateManagementPrompt({
|
|
6627
|
+
stateManagement,
|
|
6628
|
+
frontends
|
|
6629
|
+
});
|
|
6630
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6631
|
+
const response = await navigableSelect({
|
|
6632
|
+
message: "Select state management",
|
|
6633
|
+
options: resolution.options,
|
|
6634
|
+
initialValue: resolution.initialValue
|
|
6635
|
+
});
|
|
6636
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6637
|
+
return response;
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
//#endregion
|
|
6641
|
+
//#region src/prompts/testing.ts
|
|
6642
|
+
const TESTING_PROMPT_OPTIONS = [
|
|
6643
|
+
{
|
|
6644
|
+
value: "vitest",
|
|
6645
|
+
label: "Vitest",
|
|
6646
|
+
hint: "Blazing fast Vite-native unit test framework"
|
|
6647
|
+
},
|
|
6648
|
+
{
|
|
6649
|
+
value: "vitest-playwright",
|
|
6650
|
+
label: "Vitest + Playwright",
|
|
6651
|
+
hint: "Both unit and E2E testing for complete coverage"
|
|
6205
6652
|
},
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6653
|
+
{
|
|
6654
|
+
value: "playwright",
|
|
6655
|
+
label: "Playwright",
|
|
6656
|
+
hint: "End-to-end testing framework by Microsoft"
|
|
6209
6657
|
},
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6658
|
+
{
|
|
6659
|
+
value: "jest",
|
|
6660
|
+
label: "Jest",
|
|
6661
|
+
hint: "Classic testing framework with wide ecosystem"
|
|
6213
6662
|
},
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6663
|
+
{
|
|
6664
|
+
value: "cypress",
|
|
6665
|
+
label: "Cypress",
|
|
6666
|
+
hint: "E2E testing with time travel debugging"
|
|
6217
6667
|
},
|
|
6218
|
-
|
|
6668
|
+
{
|
|
6669
|
+
value: "none",
|
|
6219
6670
|
label: "None",
|
|
6220
|
-
hint: "
|
|
6671
|
+
hint: "Skip testing framework setup"
|
|
6221
6672
|
}
|
|
6222
|
-
|
|
6223
|
-
function
|
|
6224
|
-
|
|
6225
|
-
if (web.length === 0) return {
|
|
6226
|
-
shouldPrompt: false,
|
|
6227
|
-
mode: "single",
|
|
6228
|
-
options: [],
|
|
6229
|
-
autoValue: "none"
|
|
6230
|
-
};
|
|
6231
|
-
const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
|
|
6232
|
-
if (context.uiLibrary !== void 0) return {
|
|
6233
|
-
shouldPrompt: false,
|
|
6234
|
-
mode: "single",
|
|
6235
|
-
options: compatibleLibraries.map((lib) => ({
|
|
6236
|
-
value: lib,
|
|
6237
|
-
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
6238
|
-
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
6239
|
-
})),
|
|
6240
|
-
autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
|
|
6241
|
-
};
|
|
6242
|
-
const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
|
|
6243
|
-
return {
|
|
6244
|
-
shouldPrompt: true,
|
|
6245
|
-
mode: "single",
|
|
6246
|
-
options: compatibleLibraries.map((lib) => ({
|
|
6247
|
-
value: lib,
|
|
6248
|
-
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
6249
|
-
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
6250
|
-
})),
|
|
6251
|
-
initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
|
|
6252
|
-
};
|
|
6673
|
+
];
|
|
6674
|
+
function resolveTestingPrompt(testing) {
|
|
6675
|
+
return createStaticSinglePromptResolution(TESTING_PROMPT_OPTIONS, "vitest", testing);
|
|
6253
6676
|
}
|
|
6254
|
-
async function
|
|
6255
|
-
const resolution =
|
|
6256
|
-
uiLibrary,
|
|
6257
|
-
frontends,
|
|
6258
|
-
astroIntegration
|
|
6259
|
-
});
|
|
6677
|
+
async function getTestingChoice(testing) {
|
|
6678
|
+
const resolution = resolveTestingPrompt(testing);
|
|
6260
6679
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6261
|
-
const
|
|
6262
|
-
message: "Select
|
|
6680
|
+
const response = await navigableSelect({
|
|
6681
|
+
message: "Select testing framework",
|
|
6263
6682
|
options: resolution.options,
|
|
6264
6683
|
initialValue: resolution.initialValue
|
|
6265
6684
|
});
|
|
6266
|
-
if (isCancel$1(
|
|
6267
|
-
return
|
|
6685
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6686
|
+
return response;
|
|
6268
6687
|
}
|
|
6269
6688
|
|
|
6270
6689
|
//#endregion
|
|
@@ -6326,71 +6745,12 @@ async function getValidationChoice(validation) {
|
|
|
6326
6745
|
return response;
|
|
6327
6746
|
}
|
|
6328
6747
|
|
|
6329
|
-
//#endregion
|
|
6330
|
-
//#region src/utils/compatibility.ts
|
|
6331
|
-
const WEB_FRAMEWORKS = [
|
|
6332
|
-
"tanstack-router",
|
|
6333
|
-
"react-router",
|
|
6334
|
-
"react-vite",
|
|
6335
|
-
"tanstack-start",
|
|
6336
|
-
"next",
|
|
6337
|
-
"vinext",
|
|
6338
|
-
"nuxt",
|
|
6339
|
-
"svelte",
|
|
6340
|
-
"solid",
|
|
6341
|
-
"solid-start",
|
|
6342
|
-
"astro",
|
|
6343
|
-
"qwik",
|
|
6344
|
-
"angular",
|
|
6345
|
-
"redwood",
|
|
6346
|
-
"fresh"
|
|
6347
|
-
];
|
|
6348
|
-
|
|
6349
|
-
//#endregion
|
|
6350
|
-
//#region src/prompts/web-deploy.ts
|
|
6351
|
-
function hasWebFrontend(frontends) {
|
|
6352
|
-
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
6353
|
-
}
|
|
6354
|
-
function getDeploymentDisplay(deployment) {
|
|
6355
|
-
if (deployment === "cloudflare") return {
|
|
6356
|
-
label: "Cloudflare",
|
|
6357
|
-
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
6358
|
-
};
|
|
6359
|
-
if (deployment === "vercel") return {
|
|
6360
|
-
label: "Vercel",
|
|
6361
|
-
hint: "Deploy to Vercel's edge network"
|
|
6362
|
-
};
|
|
6363
|
-
return {
|
|
6364
|
-
label: deployment,
|
|
6365
|
-
hint: `Add ${deployment} deployment`
|
|
6366
|
-
};
|
|
6367
|
-
}
|
|
6368
|
-
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
6369
|
-
if (deployment !== void 0) return deployment;
|
|
6370
|
-
if (!hasWebFrontend(frontend)) return "none";
|
|
6371
|
-
const response = await navigableSelect({
|
|
6372
|
-
message: "Select web deployment",
|
|
6373
|
-
options: [
|
|
6374
|
-
"cloudflare",
|
|
6375
|
-
"vercel",
|
|
6376
|
-
"none"
|
|
6377
|
-
].map((deploy) => {
|
|
6378
|
-
const { label, hint } = getDeploymentDisplay(deploy);
|
|
6379
|
-
return {
|
|
6380
|
-
value: deploy,
|
|
6381
|
-
label,
|
|
6382
|
-
hint
|
|
6383
|
-
};
|
|
6384
|
-
}),
|
|
6385
|
-
initialValue: DEFAULT_CONFIG.webDeploy
|
|
6386
|
-
});
|
|
6387
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6388
|
-
return response;
|
|
6389
|
-
}
|
|
6390
|
-
|
|
6391
6748
|
//#endregion
|
|
6392
6749
|
//#region src/prompts/config-prompts.ts
|
|
6393
6750
|
async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
6751
|
+
if (flags.ecosystem === void 0 && flags.stackParts === void 0) {
|
|
6752
|
+
if (await getCompositionModeChoice() === "multi") return gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath);
|
|
6753
|
+
}
|
|
6394
6754
|
const result = await navigableGroup({
|
|
6395
6755
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
6396
6756
|
frontend: ({ results }) => {
|
|
@@ -7017,6 +7377,15 @@ async function trackProjectCreation(config, disableAnalytics = false) {
|
|
|
7017
7377
|
|
|
7018
7378
|
//#endregion
|
|
7019
7379
|
//#region src/utils/display-config.ts
|
|
7380
|
+
function getSelectedGraphPart(config, role, ownerPartId) {
|
|
7381
|
+
return config.stackParts?.find((part) => part.role === role && part.ownerPartId === ownerPartId && part.source !== "provided");
|
|
7382
|
+
}
|
|
7383
|
+
function getGraphDisplayValue(config, role) {
|
|
7384
|
+
const primaryBackend = getSelectedGraphPart(config, "backend");
|
|
7385
|
+
const part = role === "backend" ? primaryBackend : getSelectedGraphPart(config, role, primaryBackend?.id) ?? getSelectedGraphPart(config, role);
|
|
7386
|
+
if (!part) return null;
|
|
7387
|
+
return `${part.ecosystem}:${part.toolId}`;
|
|
7388
|
+
}
|
|
7020
7389
|
function displayConfig(config) {
|
|
7021
7390
|
const configDisplay = [];
|
|
7022
7391
|
if (config.projectName) configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`);
|
|
@@ -7027,14 +7396,29 @@ function displayConfig(config) {
|
|
|
7027
7396
|
}
|
|
7028
7397
|
if (config.uiLibrary !== void 0) configDisplay.push(`${pc.blue("UI Library:")} ${String(config.uiLibrary)}`);
|
|
7029
7398
|
if (config.cssFramework !== void 0) configDisplay.push(`${pc.blue("CSS Framework:")} ${String(config.cssFramework)}`);
|
|
7030
|
-
if (config.backend !== void 0)
|
|
7399
|
+
if (config.backend !== void 0) {
|
|
7400
|
+
const graphBackend = config.backend === "none" ? getGraphDisplayValue(config, "backend") : null;
|
|
7401
|
+
configDisplay.push(`${pc.blue("Backend:")} ${graphBackend ?? String(config.backend)}`);
|
|
7402
|
+
}
|
|
7031
7403
|
if (config.runtime !== void 0) configDisplay.push(`${pc.blue("Runtime:")} ${String(config.runtime)}`);
|
|
7032
|
-
if (config.api !== void 0)
|
|
7404
|
+
if (config.api !== void 0) {
|
|
7405
|
+
const graphApi = config.api === "none" ? getGraphDisplayValue(config, "api") : null;
|
|
7406
|
+
configDisplay.push(`${pc.blue("API:")} ${graphApi ?? String(config.api)}`);
|
|
7407
|
+
}
|
|
7033
7408
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
7034
|
-
if (config.orm !== void 0)
|
|
7035
|
-
|
|
7409
|
+
if (config.orm !== void 0) {
|
|
7410
|
+
const graphOrm = config.orm === "none" ? getGraphDisplayValue(config, "orm") : null;
|
|
7411
|
+
configDisplay.push(`${pc.blue("ORM:")} ${graphOrm ?? String(config.orm)}`);
|
|
7412
|
+
}
|
|
7413
|
+
if (config.auth !== void 0) {
|
|
7414
|
+
const graphAuth = config.auth === "none" ? getGraphDisplayValue(config, "auth") : null;
|
|
7415
|
+
configDisplay.push(`${pc.blue("Auth:")} ${graphAuth ?? String(config.auth)}`);
|
|
7416
|
+
}
|
|
7036
7417
|
if (config.payments !== void 0) configDisplay.push(`${pc.blue("Payments:")} ${String(config.payments)}`);
|
|
7037
|
-
if (config.email !== void 0)
|
|
7418
|
+
if (config.email !== void 0) {
|
|
7419
|
+
const graphEmail = config.email === "none" ? getGraphDisplayValue(config, "email") : null;
|
|
7420
|
+
configDisplay.push(`${pc.blue("Email:")} ${graphEmail ?? String(config.email)}`);
|
|
7421
|
+
}
|
|
7038
7422
|
if (config.fileUpload !== void 0) configDisplay.push(`${pc.blue("File Upload:")} ${String(config.fileUpload)}`);
|
|
7039
7423
|
if (config.effect !== void 0) configDisplay.push(`${pc.blue("Effect:")} ${String(config.effect)}`);
|
|
7040
7424
|
if (config.ai !== void 0) configDisplay.push(`${pc.blue("AI:")} ${String(config.ai)}`);
|
|
@@ -7046,6 +7430,42 @@ function displayConfig(config) {
|
|
|
7046
7430
|
if (config.realtime !== void 0) configDisplay.push(`${pc.blue("Realtime:")} ${String(config.realtime)}`);
|
|
7047
7431
|
if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
|
|
7048
7432
|
if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
|
|
7433
|
+
const graphBackendPart = getSelectedGraphPart(config, "backend");
|
|
7434
|
+
if (graphBackendPart?.ecosystem === "go") {
|
|
7435
|
+
if (config.goCli && config.goCli !== "none") configDisplay.push(`${pc.blue("Go CLI:")} ${String(config.goCli)}`);
|
|
7436
|
+
if (config.goLogging && config.goLogging !== "none") configDisplay.push(`${pc.blue("Go Logging:")} ${String(config.goLogging)}`);
|
|
7437
|
+
}
|
|
7438
|
+
if (graphBackendPart?.ecosystem === "rust") {
|
|
7439
|
+
if (config.rustCli && config.rustCli !== "none") configDisplay.push(`${pc.blue("Rust CLI:")} ${String(config.rustCli)}`);
|
|
7440
|
+
if (config.rustLibraries && config.rustLibraries.length > 0) configDisplay.push(`${pc.blue("Rust Libraries:")} ${config.rustLibraries.join(", ")}`);
|
|
7441
|
+
if (config.rustLogging && config.rustLogging !== "none") configDisplay.push(`${pc.blue("Rust Logging:")} ${String(config.rustLogging)}`);
|
|
7442
|
+
if (config.rustErrorHandling && config.rustErrorHandling !== "none") configDisplay.push(`${pc.blue("Rust Error Handling:")} ${String(config.rustErrorHandling)}`);
|
|
7443
|
+
if (config.rustCaching && config.rustCaching !== "none") configDisplay.push(`${pc.blue("Rust Caching:")} ${String(config.rustCaching)}`);
|
|
7444
|
+
}
|
|
7445
|
+
if (graphBackendPart?.ecosystem === "python") {
|
|
7446
|
+
if (config.pythonValidation && config.pythonValidation !== "none") configDisplay.push(`${pc.blue("Python Validation:")} ${String(config.pythonValidation)}`);
|
|
7447
|
+
if (config.pythonAi && config.pythonAi.length > 0) configDisplay.push(`${pc.blue("Python AI:")} ${config.pythonAi.join(", ")}`);
|
|
7448
|
+
if (config.pythonTaskQueue && config.pythonTaskQueue !== "none") configDisplay.push(`${pc.blue("Python Task Queue:")} ${String(config.pythonTaskQueue)}`);
|
|
7449
|
+
if (config.pythonGraphql && config.pythonGraphql !== "none") configDisplay.push(`${pc.blue("Python GraphQL:")} ${String(config.pythonGraphql)}`);
|
|
7450
|
+
if (config.pythonQuality && config.pythonQuality !== "none") configDisplay.push(`${pc.blue("Python Quality:")} ${String(config.pythonQuality)}`);
|
|
7451
|
+
}
|
|
7452
|
+
if (graphBackendPart?.ecosystem === "java") {
|
|
7453
|
+
if (config.javaBuildTool && config.javaBuildTool !== "none") configDisplay.push(`${pc.blue("Java Build Tool:")} ${String(config.javaBuildTool)}`);
|
|
7454
|
+
if (config.javaLibraries && config.javaLibraries.length > 0) configDisplay.push(`${pc.blue("Java Libraries:")} ${config.javaLibraries.join(", ")}`);
|
|
7455
|
+
if (config.javaTestingLibraries && config.javaTestingLibraries.length > 0) configDisplay.push(`${pc.blue("Java Testing Libraries:")} ${config.javaTestingLibraries.join(", ")}`);
|
|
7456
|
+
}
|
|
7457
|
+
if (graphBackendPart?.ecosystem === "elixir") {
|
|
7458
|
+
if (config.elixirRealtime && config.elixirRealtime !== "none") configDisplay.push(`${pc.blue("Elixir Realtime:")} ${String(config.elixirRealtime)}`);
|
|
7459
|
+
if (config.elixirJobs && config.elixirJobs !== "none") configDisplay.push(`${pc.blue("Elixir Jobs:")} ${String(config.elixirJobs)}`);
|
|
7460
|
+
if (config.elixirValidation && config.elixirValidation !== "none") configDisplay.push(`${pc.blue("Elixir Validation:")} ${String(config.elixirValidation)}`);
|
|
7461
|
+
if (config.elixirHttp && config.elixirHttp !== "none") configDisplay.push(`${pc.blue("Elixir HTTP:")} ${String(config.elixirHttp)}`);
|
|
7462
|
+
if (config.elixirJson && config.elixirJson !== "none") configDisplay.push(`${pc.blue("Elixir JSON:")} ${String(config.elixirJson)}`);
|
|
7463
|
+
if (config.elixirCaching && config.elixirCaching !== "none") configDisplay.push(`${pc.blue("Elixir Caching:")} ${String(config.elixirCaching)}`);
|
|
7464
|
+
if (config.elixirObservability && config.elixirObservability !== "none") configDisplay.push(`${pc.blue("Elixir Observability:")} ${String(config.elixirObservability)}`);
|
|
7465
|
+
if (config.elixirTesting && config.elixirTesting !== "none") configDisplay.push(`${pc.blue("Elixir Testing:")} ${String(config.elixirTesting)}`);
|
|
7466
|
+
if (config.elixirQuality && config.elixirQuality !== "none") configDisplay.push(`${pc.blue("Elixir Quality:")} ${String(config.elixirQuality)}`);
|
|
7467
|
+
if (config.elixirDeploy && config.elixirDeploy !== "none") configDisplay.push(`${pc.blue("Elixir Deploy:")} ${String(config.elixirDeploy)}`);
|
|
7468
|
+
}
|
|
7049
7469
|
if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
|
|
7050
7470
|
if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
|
|
7051
7471
|
if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
|
|
@@ -7089,6 +7509,10 @@ function displayConfig(config) {
|
|
|
7089
7509
|
if (config.dbSetup !== void 0) configDisplay.push(`${pc.blue("Database Setup:")} ${String(config.dbSetup)}`);
|
|
7090
7510
|
if (config.webDeploy !== void 0) configDisplay.push(`${pc.blue("Web Deployment:")} ${String(config.webDeploy)}`);
|
|
7091
7511
|
if (config.serverDeploy !== void 0) configDisplay.push(`${pc.blue("Server Deployment:")} ${String(config.serverDeploy)}`);
|
|
7512
|
+
if (config.stackParts?.length) {
|
|
7513
|
+
const stackParts = config.stackParts.filter((part) => part.source !== "provided").map((part) => formatStackPartSpec(part, config.stackParts ?? []));
|
|
7514
|
+
if (stackParts.length > 0) configDisplay.push(`${pc.blue("Stack Parts:")} ${stackParts.join(", ")}`);
|
|
7515
|
+
}
|
|
7092
7516
|
if (configDisplay.length === 0) return pc.yellow("No configuration selected.");
|
|
7093
7517
|
return configDisplay.join("\n");
|
|
7094
7518
|
}
|
|
@@ -7117,6 +7541,107 @@ function appendCommonFlags(flags, config) {
|
|
|
7117
7541
|
if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
|
|
7118
7542
|
flags.push(config.install ? "--install" : "--no-install");
|
|
7119
7543
|
}
|
|
7544
|
+
function hasGraphPrimaryPart(config, role, ecosystem) {
|
|
7545
|
+
return config.stackParts?.some((part) => part.source !== "provided" && part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
|
|
7546
|
+
}
|
|
7547
|
+
function appendChangedStringFlag(flags, flag, value, defaultValue) {
|
|
7548
|
+
if (value !== defaultValue) flags.push(`--${flag} ${value}`);
|
|
7549
|
+
}
|
|
7550
|
+
function appendChangedArrayFlag(flags, flag, values, defaultValues) {
|
|
7551
|
+
if (values.length !== defaultValues.length || values.some((value, index) => value !== defaultValues[index])) flags.push(formatArrayFlag(flag, values));
|
|
7552
|
+
}
|
|
7553
|
+
function appendAstroIntegrationFlag(flags, config) {
|
|
7554
|
+
if (config.frontend.includes("astro") && config.astroIntegration !== "none") flags.push(`--astro-integration ${config.astroIntegration}`);
|
|
7555
|
+
}
|
|
7556
|
+
function appendGraphExtraFlags(flags, config) {
|
|
7557
|
+
appendChangedArrayFlag(flags, "addons", config.addons, ["turborepo"]);
|
|
7558
|
+
appendChangedArrayFlag(flags, "examples", config.examples, []);
|
|
7559
|
+
appendChangedStringFlag(flags, "db-setup", config.dbSetup, "none");
|
|
7560
|
+
appendChangedStringFlag(flags, "web-deploy", config.webDeploy, "none");
|
|
7561
|
+
appendChangedStringFlag(flags, "server-deploy", config.serverDeploy, "none");
|
|
7562
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript")) {
|
|
7563
|
+
appendAstroIntegrationFlag(flags, config);
|
|
7564
|
+
appendChangedStringFlag(flags, "css-framework", config.cssFramework, "tailwind");
|
|
7565
|
+
appendChangedStringFlag(flags, "ui-library", config.uiLibrary, "shadcn-ui");
|
|
7566
|
+
if (config.uiLibrary === "shadcn-ui") {
|
|
7567
|
+
appendChangedStringFlag(flags, "shadcn-base", config.shadcnBase ?? "radix", "radix");
|
|
7568
|
+
appendChangedStringFlag(flags, "shadcn-style", config.shadcnStyle ?? "nova", "nova");
|
|
7569
|
+
appendChangedStringFlag(flags, "shadcn-icon-library", config.shadcnIconLibrary ?? "lucide", "lucide");
|
|
7570
|
+
appendChangedStringFlag(flags, "shadcn-color-theme", config.shadcnColorTheme ?? "neutral", "neutral");
|
|
7571
|
+
appendChangedStringFlag(flags, "shadcn-base-color", config.shadcnBaseColor ?? "neutral", "neutral");
|
|
7572
|
+
appendChangedStringFlag(flags, "shadcn-font", config.shadcnFont ?? "inter", "inter");
|
|
7573
|
+
appendChangedStringFlag(flags, "shadcn-radius", config.shadcnRadius ?? "default", "default");
|
|
7574
|
+
}
|
|
7575
|
+
appendChangedStringFlag(flags, "state-management", config.stateManagement, "none");
|
|
7576
|
+
appendChangedStringFlag(flags, "forms", config.forms, "react-hook-form");
|
|
7577
|
+
appendChangedStringFlag(flags, "validation", config.validation, "zod");
|
|
7578
|
+
appendChangedStringFlag(flags, "testing", config.testing, "vitest");
|
|
7579
|
+
appendChangedStringFlag(flags, "animation", config.animation, "none");
|
|
7580
|
+
}
|
|
7581
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript") || hasGraphPrimaryPart(config, "backend", "typescript")) {
|
|
7582
|
+
appendChangedStringFlag(flags, "payments", config.payments, "none");
|
|
7583
|
+
appendChangedStringFlag(flags, "email", config.email, "none");
|
|
7584
|
+
appendChangedStringFlag(flags, "file-upload", config.fileUpload, "none");
|
|
7585
|
+
appendChangedStringFlag(flags, "effect", config.effect, "none");
|
|
7586
|
+
appendChangedStringFlag(flags, "ai", config.ai, "none");
|
|
7587
|
+
appendChangedStringFlag(flags, "realtime", config.realtime, "none");
|
|
7588
|
+
appendChangedStringFlag(flags, "job-queue", config.jobQueue, "none");
|
|
7589
|
+
appendChangedStringFlag(flags, "logging", config.logging, "none");
|
|
7590
|
+
appendChangedStringFlag(flags, "observability", config.observability, "none");
|
|
7591
|
+
appendChangedStringFlag(flags, "feature-flags", config.featureFlags, "none");
|
|
7592
|
+
appendChangedStringFlag(flags, "caching", config.caching, "none");
|
|
7593
|
+
appendChangedStringFlag(flags, "i18n", config.i18n, "none");
|
|
7594
|
+
appendChangedStringFlag(flags, "cms", config.cms, "none");
|
|
7595
|
+
appendChangedStringFlag(flags, "search", config.search, "none");
|
|
7596
|
+
appendChangedStringFlag(flags, "file-storage", config.fileStorage, "none");
|
|
7597
|
+
}
|
|
7598
|
+
if (hasGraphPrimaryPart(config, "mobile")) {
|
|
7599
|
+
appendChangedStringFlag(flags, "mobile-navigation", config.mobileNavigation, "expo-router");
|
|
7600
|
+
appendChangedStringFlag(flags, "mobile-ui", config.mobileUI, "none");
|
|
7601
|
+
appendChangedStringFlag(flags, "mobile-storage", config.mobileStorage, "none");
|
|
7602
|
+
appendChangedStringFlag(flags, "mobile-testing", config.mobileTesting, "none");
|
|
7603
|
+
appendChangedStringFlag(flags, "mobile-push", config.mobilePush, "none");
|
|
7604
|
+
appendChangedStringFlag(flags, "mobile-ota", config.mobileOTA, "none");
|
|
7605
|
+
appendChangedStringFlag(flags, "mobile-deep-linking", config.mobileDeepLinking, "none");
|
|
7606
|
+
}
|
|
7607
|
+
if (hasGraphPrimaryPart(config, "frontend", "rust")) appendChangedStringFlag(flags, "rust-frontend", config.rustFrontend, "none");
|
|
7608
|
+
if (hasGraphPrimaryPart(config, "backend", "rust")) {
|
|
7609
|
+
appendChangedStringFlag(flags, "rust-cli", config.rustCli, "none");
|
|
7610
|
+
appendChangedArrayFlag(flags, "rust-libraries", config.rustLibraries, []);
|
|
7611
|
+
appendChangedStringFlag(flags, "rust-logging", config.rustLogging, "tracing");
|
|
7612
|
+
appendChangedStringFlag(flags, "rust-error-handling", config.rustErrorHandling, "anyhow-thiserror");
|
|
7613
|
+
appendChangedStringFlag(flags, "rust-caching", config.rustCaching, "none");
|
|
7614
|
+
}
|
|
7615
|
+
if (hasGraphPrimaryPart(config, "backend", "python")) {
|
|
7616
|
+
appendChangedStringFlag(flags, "python-validation", config.pythonValidation, "none");
|
|
7617
|
+
appendChangedArrayFlag(flags, "python-ai", config.pythonAi, []);
|
|
7618
|
+
appendChangedStringFlag(flags, "python-task-queue", config.pythonTaskQueue, "none");
|
|
7619
|
+
appendChangedStringFlag(flags, "python-graphql", config.pythonGraphql, "none");
|
|
7620
|
+
appendChangedStringFlag(flags, "python-quality", config.pythonQuality, "none");
|
|
7621
|
+
}
|
|
7622
|
+
if (hasGraphPrimaryPart(config, "backend", "go")) {
|
|
7623
|
+
appendChangedStringFlag(flags, "go-cli", config.goCli, "none");
|
|
7624
|
+
appendChangedStringFlag(flags, "go-logging", config.goLogging, "none");
|
|
7625
|
+
}
|
|
7626
|
+
if (hasGraphPrimaryPart(config, "backend", "java")) {
|
|
7627
|
+
appendChangedStringFlag(flags, "java-build-tool", config.javaBuildTool, "maven");
|
|
7628
|
+
appendChangedArrayFlag(flags, "java-libraries", config.javaLibraries, []);
|
|
7629
|
+
appendChangedArrayFlag(flags, "java-testing-libraries", config.javaTestingLibraries, ["junit5"]);
|
|
7630
|
+
}
|
|
7631
|
+
if (hasGraphPrimaryPart(config, "backend", "elixir")) {
|
|
7632
|
+
appendChangedStringFlag(flags, "elixir-realtime", config.elixirRealtime, "channels");
|
|
7633
|
+
appendChangedStringFlag(flags, "elixir-jobs", config.elixirJobs, "none");
|
|
7634
|
+
appendChangedStringFlag(flags, "elixir-validation", config.elixirValidation, "ecto-changesets");
|
|
7635
|
+
appendChangedStringFlag(flags, "elixir-http", config.elixirHttp, "req");
|
|
7636
|
+
appendChangedStringFlag(flags, "elixir-json", config.elixirJson, "jason");
|
|
7637
|
+
if (!hasGraphPart(config, "email", "elixir")) appendChangedStringFlag(flags, "elixir-email", config.elixirEmail, "none");
|
|
7638
|
+
appendChangedStringFlag(flags, "elixir-caching", config.elixirCaching, "none");
|
|
7639
|
+
appendChangedStringFlag(flags, "elixir-observability", config.elixirObservability, "telemetry");
|
|
7640
|
+
appendChangedStringFlag(flags, "elixir-testing", config.elixirTesting, "ex_unit");
|
|
7641
|
+
appendChangedStringFlag(flags, "elixir-quality", config.elixirQuality, "credo");
|
|
7642
|
+
appendChangedStringFlag(flags, "elixir-deploy", config.elixirDeploy, "none");
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7120
7645
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
7121
7646
|
flags.push(`--email ${config.email}`);
|
|
7122
7647
|
flags.push(`--observability ${config.observability}`);
|
|
@@ -7132,6 +7657,7 @@ function getTypeScriptFlags(config) {
|
|
|
7132
7657
|
const flags = [];
|
|
7133
7658
|
if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
|
|
7134
7659
|
else flags.push("--frontend none");
|
|
7660
|
+
appendAstroIntegrationFlag(flags, config);
|
|
7135
7661
|
flags.push(`--backend ${config.backend}`);
|
|
7136
7662
|
flags.push(`--runtime ${config.runtime}`);
|
|
7137
7663
|
flags.push(`--database ${config.database}`);
|
|
@@ -7279,6 +7805,12 @@ function getElixirFlags(config) {
|
|
|
7279
7805
|
}
|
|
7280
7806
|
function generateReproducibleCommand(config) {
|
|
7281
7807
|
let flags;
|
|
7808
|
+
if (config.stackParts && config.stackParts.length > 0) {
|
|
7809
|
+
flags = config.stackParts.filter((part) => part.source !== "provided").map((part) => `--part ${(0, types_exports.formatStackPartSpec)(part, config.stackParts ?? [])}`);
|
|
7810
|
+
appendGraphExtraFlags(flags, config);
|
|
7811
|
+
appendCommonFlags(flags, config);
|
|
7812
|
+
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7813
|
+
}
|
|
7282
7814
|
switch (config.ecosystem) {
|
|
7283
7815
|
case "react-native":
|
|
7284
7816
|
flags = getReactNativeFlags(config);
|
|
@@ -7306,6 +7838,98 @@ function generateReproducibleCommand(config) {
|
|
|
7306
7838
|
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7307
7839
|
}
|
|
7308
7840
|
|
|
7841
|
+
//#endregion
|
|
7842
|
+
//#region src/utils/generated-checks.ts
|
|
7843
|
+
function getGraphTarget(config) {
|
|
7844
|
+
const backend = getPrimaryGraphPart(config, "backend");
|
|
7845
|
+
if (!backend || backend.ecosystem === "typescript" || backend.ecosystem === "react-native" || backend.ecosystem === "universal") return null;
|
|
7846
|
+
return {
|
|
7847
|
+
ecosystem: backend.ecosystem,
|
|
7848
|
+
projectDir: path.join(config.projectDir, backend.targetPath ?? "apps/server")
|
|
7849
|
+
};
|
|
7850
|
+
}
|
|
7851
|
+
function getSingleEcosystemTarget(config) {
|
|
7852
|
+
if (config.ecosystem === "typescript" || config.ecosystem === "react-native" || config.ecosystem === "java") return null;
|
|
7853
|
+
return {
|
|
7854
|
+
ecosystem: config.ecosystem,
|
|
7855
|
+
projectDir: config.projectDir
|
|
7856
|
+
};
|
|
7857
|
+
}
|
|
7858
|
+
async function runCommand(cwd, command, args) {
|
|
7859
|
+
await $({
|
|
7860
|
+
cwd,
|
|
7861
|
+
stdout: "inherit",
|
|
7862
|
+
stderr: "inherit"
|
|
7863
|
+
})`${command} ${args}`;
|
|
7864
|
+
}
|
|
7865
|
+
async function verifyTarget(target) {
|
|
7866
|
+
const s = spinner();
|
|
7867
|
+
const cwd = target.projectDir;
|
|
7868
|
+
switch (target.ecosystem) {
|
|
7869
|
+
case "go":
|
|
7870
|
+
s.start("Verifying generated Go server...");
|
|
7871
|
+
await runCommand(cwd, "go", ["mod", "tidy"]);
|
|
7872
|
+
await runCommand(cwd, "go", ["test", "./..."]);
|
|
7873
|
+
s.stop("Generated Go server checks passed");
|
|
7874
|
+
return;
|
|
7875
|
+
case "rust":
|
|
7876
|
+
s.start("Verifying generated Rust server...");
|
|
7877
|
+
await runCommand(cwd, "cargo", ["check"]);
|
|
7878
|
+
s.stop("Generated Rust server checks passed");
|
|
7879
|
+
return;
|
|
7880
|
+
case "python":
|
|
7881
|
+
s.start("Verifying generated Python server...");
|
|
7882
|
+
await runCommand(cwd, "uv", ["sync"]);
|
|
7883
|
+
await runCommand(cwd, "uv", [
|
|
7884
|
+
"run",
|
|
7885
|
+
"ruff",
|
|
7886
|
+
"check",
|
|
7887
|
+
"."
|
|
7888
|
+
]);
|
|
7889
|
+
s.stop("Generated Python server checks passed");
|
|
7890
|
+
return;
|
|
7891
|
+
case "elixir":
|
|
7892
|
+
if (!await commandExists("mix")) {
|
|
7893
|
+
log.warn(pc.yellow("Skipping Elixir verification because mix is not on PATH"));
|
|
7894
|
+
return;
|
|
7895
|
+
}
|
|
7896
|
+
s.start("Verifying generated Elixir server...");
|
|
7897
|
+
await runCommand(cwd, "mix", ["deps.get"]);
|
|
7898
|
+
await runCommand(cwd, "mix", ["compile"]);
|
|
7899
|
+
s.stop("Generated Elixir server checks passed");
|
|
7900
|
+
return;
|
|
7901
|
+
default: log.warn(pc.yellow(`No generated checks are configured for ${target.ecosystem}`));
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
async function runGeneratedChecks(config) {
|
|
7905
|
+
const target = getGraphTarget(config) ?? getSingleEcosystemTarget(config);
|
|
7906
|
+
if (!target) {
|
|
7907
|
+
log.warn(pc.yellow("No generated checks are configured for this stack"));
|
|
7908
|
+
return;
|
|
7909
|
+
}
|
|
7910
|
+
await verifyTarget(target);
|
|
7911
|
+
}
|
|
7912
|
+
|
|
7913
|
+
//#endregion
|
|
7914
|
+
//#region src/utils/preflight-display.ts
|
|
7915
|
+
function displayPreflightWarnings({ warnings }) {
|
|
7916
|
+
if (warnings.length === 0) return;
|
|
7917
|
+
const count = warnings.length;
|
|
7918
|
+
const lines = [pc.bold(pc.yellow(`${count} feature${count > 1 ? "s" : ""} will not generate templates:`)), ""];
|
|
7919
|
+
warnings.forEach((w, i) => {
|
|
7920
|
+
const selected = Array.isArray(w.selectedValue) ? w.selectedValue.join(", ") : w.selectedValue;
|
|
7921
|
+
lines.push(` ${pc.yellow(`${i + 1}.`)} ${pc.bold(w.featureDisplayName)} ${pc.dim(`(${selected})`)}`);
|
|
7922
|
+
lines.push(` ${w.reason}`);
|
|
7923
|
+
w.suggestions.forEach((s) => lines.push(` ${pc.green("•")} ${s}`));
|
|
7924
|
+
if (i < count - 1) lines.push("");
|
|
7925
|
+
});
|
|
7926
|
+
consola.box({
|
|
7927
|
+
title: pc.yellow("Pre-flight Check"),
|
|
7928
|
+
message: lines.join("\n"),
|
|
7929
|
+
style: { borderColor: "yellow" }
|
|
7930
|
+
});
|
|
7931
|
+
}
|
|
7932
|
+
|
|
7309
7933
|
//#endregion
|
|
7310
7934
|
//#region src/utils/project-directory.ts
|
|
7311
7935
|
async function handleDirectoryConflict(currentPathInput) {
|
|
@@ -7656,6 +8280,10 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7656
8280
|
const db = cfg.database;
|
|
7657
8281
|
const orm = cfg.orm;
|
|
7658
8282
|
const has = (k) => flags ? flags.has(k) : true;
|
|
8283
|
+
const hasGraphOrm = cfg.stackParts?.some((part) => part.role === "orm" && part.source !== "provided");
|
|
8284
|
+
const ecosystemOrm = getEcosystemOrm(cfg);
|
|
8285
|
+
const hasEcosystemOrm = ecosystemOrm !== void 0 && ecosystemOrm !== "none";
|
|
8286
|
+
const isNonTypeScriptSqliteDefault = cfg.ecosystem !== void 0 && cfg.ecosystem !== "typescript" && cfg.ecosystem !== "react-native" && db === "sqlite" && !hasEcosystemOrm;
|
|
7659
8287
|
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") incompatibilityError({
|
|
7660
8288
|
message: "Mongoose ORM requires MongoDB database.",
|
|
7661
8289
|
provided: {
|
|
@@ -7712,7 +8340,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7712
8340
|
},
|
|
7713
8341
|
suggestions: ["Use --orm mongoose", "Use --orm prisma"]
|
|
7714
8342
|
});
|
|
7715
|
-
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none") missingRequirementError({
|
|
8343
|
+
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none" && !hasGraphOrm && !hasEcosystemOrm && !isNonTypeScriptSqliteDefault) missingRequirementError({
|
|
7716
8344
|
message: "Database selection requires an ORM.",
|
|
7717
8345
|
provided: {
|
|
7718
8346
|
database: db,
|
|
@@ -7754,6 +8382,26 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7754
8382
|
]
|
|
7755
8383
|
});
|
|
7756
8384
|
}
|
|
8385
|
+
function getEcosystemOrm(cfg) {
|
|
8386
|
+
switch (cfg.ecosystem) {
|
|
8387
|
+
case "rust": return cfg.rustOrm;
|
|
8388
|
+
case "python": return cfg.pythonOrm;
|
|
8389
|
+
case "go": return cfg.goOrm;
|
|
8390
|
+
case "java": return cfg.javaOrm;
|
|
8391
|
+
case "elixir": return cfg.elixirOrm;
|
|
8392
|
+
default: return;
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
function getEcosystemBackend(cfg) {
|
|
8396
|
+
switch (cfg.ecosystem) {
|
|
8397
|
+
case "rust": return cfg.rustWebFramework && cfg.rustWebFramework !== "none" ? cfg.rustWebFramework : void 0;
|
|
8398
|
+
case "python": return cfg.pythonWebFramework && cfg.pythonWebFramework !== "none" ? cfg.pythonWebFramework : void 0;
|
|
8399
|
+
case "go": return cfg.goWebFramework && cfg.goWebFramework !== "none" ? cfg.goWebFramework : void 0;
|
|
8400
|
+
case "java": return cfg.javaWebFramework && cfg.javaWebFramework !== "none" ? cfg.javaWebFramework : void 0;
|
|
8401
|
+
case "elixir": return cfg.elixirWebFramework && cfg.elixirWebFramework !== "none" ? cfg.elixirWebFramework : void 0;
|
|
8402
|
+
default: return;
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
7757
8405
|
function validateDatabaseSetup(config, providedFlags) {
|
|
7758
8406
|
const { dbSetup, database, runtime } = config;
|
|
7759
8407
|
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'.");
|
|
@@ -7879,7 +8527,9 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
7879
8527
|
}
|
|
7880
8528
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
7881
8529
|
const { backend } = config;
|
|
7882
|
-
|
|
8530
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8531
|
+
const hasEcosystemBackend = getEcosystemBackend(config) !== void 0;
|
|
8532
|
+
if (backend !== "none" || hasGraphBackend || hasEcosystemBackend) return;
|
|
7883
8533
|
const has = (k) => providedFlags.has(k);
|
|
7884
8534
|
if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
|
|
7885
8535
|
if (has("database") && config.database !== "none") exitWithError("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
|
|
@@ -8223,6 +8873,10 @@ function validatePythonApiConstraints(config) {
|
|
|
8223
8873
|
});
|
|
8224
8874
|
}
|
|
8225
8875
|
function validateFullConfig(config, providedFlags, options) {
|
|
8876
|
+
if (config.stackParts && !options.yolo) {
|
|
8877
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8878
|
+
if (graphValidation.issues.length > 0) exitWithError(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8879
|
+
}
|
|
8226
8880
|
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
8227
8881
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
8228
8882
|
validateDatabaseSetup(config, providedFlags);
|
|
@@ -8241,7 +8895,8 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8241
8895
|
validateSearchConstraints(config);
|
|
8242
8896
|
validateJavaConstraints(config, providedFlags);
|
|
8243
8897
|
validateElixirConstraints(config);
|
|
8244
|
-
|
|
8898
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8899
|
+
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));
|
|
8245
8900
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
8246
8901
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
8247
8902
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
|
|
@@ -8272,6 +8927,10 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8272
8927
|
}
|
|
8273
8928
|
function validateConfigForProgrammaticUse(config) {
|
|
8274
8929
|
try {
|
|
8930
|
+
if (config.stackParts) {
|
|
8931
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8932
|
+
if (graphValidation.issues.length > 0) throw new Error(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8933
|
+
}
|
|
8275
8934
|
validateEcosystemAuthCompatibility(config);
|
|
8276
8935
|
validateDatabaseOrmAuth(config);
|
|
8277
8936
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
@@ -8363,7 +9022,14 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
8363
9022
|
return config;
|
|
8364
9023
|
}
|
|
8365
9024
|
function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
8366
|
-
if (!options.yolo)
|
|
9025
|
+
if (!options.yolo) {
|
|
9026
|
+
validateYesFlagCombination(options, getProvidedFlags(options));
|
|
9027
|
+
try {
|
|
9028
|
+
validateArrayOptions(options);
|
|
9029
|
+
} catch (error) {
|
|
9030
|
+
exitWithError(error instanceof Error ? error.message : String(error));
|
|
9031
|
+
}
|
|
9032
|
+
}
|
|
8367
9033
|
const config = processFlags(options, projectName);
|
|
8368
9034
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
8369
9035
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
@@ -8375,26 +9041,6 @@ function validateConfigCompatibility(config, providedFlags, options) {
|
|
|
8375
9041
|
else validateConfigForProgrammaticUse(config);
|
|
8376
9042
|
}
|
|
8377
9043
|
|
|
8378
|
-
//#endregion
|
|
8379
|
-
//#region src/utils/preflight-display.ts
|
|
8380
|
-
function displayPreflightWarnings({ warnings }) {
|
|
8381
|
-
if (warnings.length === 0) return;
|
|
8382
|
-
const count = warnings.length;
|
|
8383
|
-
const lines = [pc.bold(pc.yellow(`${count} feature${count > 1 ? "s" : ""} will not generate templates:`)), ""];
|
|
8384
|
-
warnings.forEach((w, i) => {
|
|
8385
|
-
const selected = Array.isArray(w.selectedValue) ? w.selectedValue.join(", ") : w.selectedValue;
|
|
8386
|
-
lines.push(` ${pc.yellow(`${i + 1}.`)} ${pc.bold(w.featureDisplayName)} ${pc.dim(`(${selected})`)}`);
|
|
8387
|
-
lines.push(` ${w.reason}`);
|
|
8388
|
-
w.suggestions.forEach((s) => lines.push(` ${pc.green("•")} ${s}`));
|
|
8389
|
-
if (i < count - 1) lines.push("");
|
|
8390
|
-
});
|
|
8391
|
-
consola.box({
|
|
8392
|
-
title: pc.yellow("Pre-flight Check"),
|
|
8393
|
-
message: lines.join("\n"),
|
|
8394
|
-
style: { borderColor: "yellow" }
|
|
8395
|
-
});
|
|
8396
|
-
}
|
|
8397
|
-
|
|
8398
9044
|
//#endregion
|
|
8399
9045
|
//#region src/utils/file-formatter.ts
|
|
8400
9046
|
const formatOptions = {
|
|
@@ -9666,7 +10312,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
9666
10312
|
const hasHusky = addons?.includes("husky");
|
|
9667
10313
|
const hasLefthook = addons?.includes("lefthook");
|
|
9668
10314
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
|
|
9669
|
-
const
|
|
10315
|
+
const graphOrmPart = getGraphPart(config, "orm");
|
|
10316
|
+
const databaseInstructions = !isConvex && database !== "none" && !graphOrmPart ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
10317
|
+
const graphDatabaseInstructions = !isConvex && database !== "none" && graphOrmPart ? getGraphDatabaseInstructions(database, graphOrmPart.ecosystem, graphOrmPart.toolId) : "";
|
|
9670
10318
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
9671
10319
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
9672
10320
|
const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
|
|
@@ -9680,14 +10328,24 @@ async function displayPostInstallInstructions(config) {
|
|
|
9680
10328
|
const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
|
|
9681
10329
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
9682
10330
|
const vercelDeployInstructions = getVercelDeployInstructions(webDeploy, serverDeploy, backend);
|
|
10331
|
+
const graphBackendDeployInstructions = getGraphBackendDeployInstructions(config);
|
|
9683
10332
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
9684
10333
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
9685
10334
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
9686
|
-
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
10335
|
+
const noOrmWarning = !isConvex && database !== "none" && orm === "none" && !hasGraphPart(config, "orm") ? getNoOrmWarning() : "";
|
|
9687
10336
|
const hasFresh = frontend?.includes("fresh");
|
|
9688
10337
|
const webPort = String(getLocalWebDevPort(frontend ?? []));
|
|
9689
10338
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
9690
|
-
|
|
10339
|
+
const graphSummary = getGraphSummary(config);
|
|
10340
|
+
const graphBackendUrl = getGraphBackendUrl(config);
|
|
10341
|
+
const graphBackendPart = getGraphPart(config, "backend");
|
|
10342
|
+
const graphBackendNeedsSetup = Boolean(graphBackendUrl) && [
|
|
10343
|
+
"python",
|
|
10344
|
+
"go",
|
|
10345
|
+
"elixir"
|
|
10346
|
+
].includes(graphBackendPart?.ecosystem ?? "");
|
|
10347
|
+
let output = graphSummary ? `${pc.bold("Generated:")} ${graphSummary}\n\n` : "";
|
|
10348
|
+
output += `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
9691
10349
|
let stepCounter = 2;
|
|
9692
10350
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
9693
10351
|
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,6 +10354,13 @@ async function displayPostInstallInstructions(config) {
|
|
|
9696
10354
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
9697
10355
|
output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
|
|
9698
10356
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
10357
|
+
} else if (graphBackendUrl) {
|
|
10358
|
+
if (graphBackendNeedsSetup) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} setup:server\n`;
|
|
10359
|
+
if (hasWeb) {
|
|
10360
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:web\n`;
|
|
10361
|
+
output += `${pc.dim(" (run the backend in a second terminal)")}\n`;
|
|
10362
|
+
}
|
|
10363
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:server\n`;
|
|
9699
10364
|
} else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
9700
10365
|
else {
|
|
9701
10366
|
if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
@@ -9704,13 +10369,13 @@ async function displayPostInstallInstructions(config) {
|
|
|
9704
10369
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
9705
10370
|
}
|
|
9706
10371
|
}
|
|
9707
|
-
const hasStandaloneBackend = backend !== "none";
|
|
10372
|
+
const hasStandaloneBackend = backend !== "none" || Boolean(graphBackendUrl);
|
|
9708
10373
|
if (hasWeb || hasStandaloneBackend || addons?.includes("starlight") || addons?.includes("fumadocs")) {
|
|
9709
10374
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
9710
10375
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
9711
10376
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
9712
10377
|
if (!isConvex && !isBackendSelf && hasStandaloneBackend) {
|
|
9713
|
-
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
10378
|
+
output += `${pc.cyan("•")} Backend API: ${graphBackendUrl ?? "http://localhost:3000"}\n`;
|
|
9714
10379
|
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api-reference\n`;
|
|
9715
10380
|
}
|
|
9716
10381
|
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
@@ -9719,6 +10384,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9719
10384
|
}
|
|
9720
10385
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
9721
10386
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
10387
|
+
if (graphDatabaseInstructions) output += `\n${graphDatabaseInstructions.trim()}\n`;
|
|
9722
10388
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
9723
10389
|
if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
|
|
9724
10390
|
if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
|
|
@@ -9726,6 +10392,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9726
10392
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
9727
10393
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
9728
10394
|
if (vercelDeployInstructions) output += `\n${vercelDeployInstructions.trim()}\n`;
|
|
10395
|
+
if (graphBackendDeployInstructions) output += `\n${graphBackendDeployInstructions.trim()}\n`;
|
|
9729
10396
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
9730
10397
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
9731
10398
|
if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
|
|
@@ -9817,6 +10484,10 @@ function getStarlightInstructions(runCmd) {
|
|
|
9817
10484
|
function getNoOrmWarning() {
|
|
9818
10485
|
return `\n${pc.yellow("WARNING:")} Database selected without an ORM. Features requiring\n database access (e.g., examples, auth) need manual setup.`;
|
|
9819
10486
|
}
|
|
10487
|
+
function getGraphDatabaseInstructions(database, ecosystem, ormTool) {
|
|
10488
|
+
if (ecosystem === "java" && ormTool === "spring-data-jpa") return `${pc.bold("Database setup:")}\n${pc.cyan("•")} Selected database: ${database}\n${pc.cyan("•")} ORM: ${ecosystem}:${ormTool}\n${pc.cyan("•")} The generated Spring Data JPA example uses an embedded ${pc.white("H2")} dev database. Update ${pc.white("apps/server/src/main/resources/application.yml")} when switching to external ${database}.`;
|
|
10489
|
+
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")}`;
|
|
10490
|
+
}
|
|
9820
10491
|
function getBunWebNativeWarning() {
|
|
9821
10492
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
9822
10493
|
}
|
|
@@ -10075,14 +10746,14 @@ function displayJavaInstructions(config) {
|
|
|
10075
10746
|
}
|
|
10076
10747
|
const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
|
|
10077
10748
|
const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
|
|
10078
|
-
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;
|
|
10749
|
+
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;
|
|
10079
10750
|
const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
|
|
10080
10751
|
const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
|
|
10081
10752
|
const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
|
|
10082
10753
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10083
10754
|
let stepCounter = 2;
|
|
10084
10755
|
if (!depsInstalled && buildToolCommand && effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan(`${stepCounter++}.`)} ${buildToolCommand} test\n`;
|
|
10085
|
-
if (runCommand) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10756
|
+
if (runCommand$1) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10086
10757
|
else if (sourceCompileCommand && sourceRunCommand) {
|
|
10087
10758
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceCompileCommand}\n`;
|
|
10088
10759
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
|
|
@@ -10130,9 +10801,9 @@ function displayJavaInstructions(config) {
|
|
|
10130
10801
|
output += `${pc.cyan("•")} Testing: ${testingList}\n`;
|
|
10131
10802
|
}
|
|
10132
10803
|
output += `\n${pc.bold("Common Java commands:")}\n`;
|
|
10133
|
-
if (buildToolCommand && runCommand && packageCommand) {
|
|
10804
|
+
if (buildToolCommand && runCommand$1 && packageCommand) {
|
|
10134
10805
|
if (effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan("•")} Test: ${buildToolCommand} test\n`;
|
|
10135
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10806
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10136
10807
|
output += `${pc.cyan("•")} Package: ${packageCommand}\n`;
|
|
10137
10808
|
} else if (sourceCompileCommand && sourceRunCommand) {
|
|
10138
10809
|
output += `${pc.cyan("•")} Compile: ${sourceCompileCommand}\n`;
|
|
@@ -10150,14 +10821,14 @@ function displayJavaInstructions(config) {
|
|
|
10150
10821
|
function displayPythonInstructions(config) {
|
|
10151
10822
|
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
|
|
10152
10823
|
const cdCmd = `cd ${relativePath}`;
|
|
10153
|
-
let runCommand = "uv run uvicorn app.main:app --reload";
|
|
10154
|
-
if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
|
|
10155
|
-
else if (pythonWebFramework === "flask") runCommand = "uv run flask --app app.main run --reload";
|
|
10156
|
-
else if (pythonWebFramework === "litestar") runCommand = "litestar --app src.app.main:app run --reload --port 3001";
|
|
10824
|
+
let runCommand$1 = "uv run uvicorn app.main:app --reload";
|
|
10825
|
+
if (pythonWebFramework === "django") runCommand$1 = "uv run python manage.py runserver";
|
|
10826
|
+
else if (pythonWebFramework === "flask") runCommand$1 = "uv run flask --app app.main run --reload";
|
|
10827
|
+
else if (pythonWebFramework === "litestar") runCommand$1 = "litestar --app src.app.main:app run --reload --port 3001";
|
|
10157
10828
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10158
10829
|
let stepCounter = 2;
|
|
10159
10830
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} uv sync\n`;
|
|
10160
|
-
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10831
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10161
10832
|
output += `\n${pc.bold("Your Python project includes:")}\n`;
|
|
10162
10833
|
if (pythonWebFramework && pythonWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
|
|
10163
10834
|
fastapi: "FastAPI",
|
|
@@ -10195,7 +10866,7 @@ function displayPythonInstructions(config) {
|
|
|
10195
10866
|
}[pythonQuality] || pythonQuality}\n`;
|
|
10196
10867
|
output += `\n${pc.bold("Common Python commands:")}\n`;
|
|
10197
10868
|
output += `${pc.cyan("•")} Install: uv sync\n`;
|
|
10198
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10869
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10199
10870
|
output += `${pc.cyan("•")} Test: uv run pytest\n`;
|
|
10200
10871
|
if (pythonQuality === "ruff") {
|
|
10201
10872
|
output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
|
|
@@ -10357,7 +11028,7 @@ function getYesBaseConfig(flagConfig) {
|
|
|
10357
11028
|
};
|
|
10358
11029
|
}
|
|
10359
11030
|
function shouldPromptForVersionChannel(input) {
|
|
10360
|
-
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
11031
|
+
if (input.yes || input.part?.length || input.versionChannel !== void 0 || isSilent()) return false;
|
|
10361
11032
|
return canPromptInteractively();
|
|
10362
11033
|
}
|
|
10363
11034
|
async function createProjectHandler(input, options = {}) {
|
|
@@ -10542,7 +11213,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10542
11213
|
}
|
|
10543
11214
|
}
|
|
10544
11215
|
let config;
|
|
10545
|
-
if (cliInput.yes) {
|
|
11216
|
+
if (cliInput.yes || cliInput.part?.length) {
|
|
10546
11217
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
10547
11218
|
config = {
|
|
10548
11219
|
...getYesBaseConfig(flagConfig),
|
|
@@ -10571,6 +11242,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10571
11242
|
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, currentPathInput),
|
|
10572
11243
|
versionChannel
|
|
10573
11244
|
};
|
|
11245
|
+
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
10574
11246
|
}
|
|
10575
11247
|
const preflight = validatePreflightConfig(config);
|
|
10576
11248
|
if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
|
|
@@ -10617,6 +11289,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10617
11289
|
};
|
|
10618
11290
|
}
|
|
10619
11291
|
await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
|
|
11292
|
+
if (cliInput.verify ?? input.verify) await runGeneratedChecks(config);
|
|
10620
11293
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
10621
11294
|
if (!isSilent()) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
10622
11295
|
await trackProjectCreation(config, input.disableAnalytics);
|
|
@@ -10946,113 +11619,116 @@ async function createVirtual(options) {
|
|
|
10946
11619
|
const isReactNative = ecosystem === "react-native";
|
|
10947
11620
|
const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
|
|
10948
11621
|
const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
|
|
11622
|
+
const config = {
|
|
11623
|
+
ecosystem,
|
|
11624
|
+
projectName: options.projectName || "my-project",
|
|
11625
|
+
projectDir: "/virtual",
|
|
11626
|
+
relativePath: "./virtual",
|
|
11627
|
+
database: options.database || "none",
|
|
11628
|
+
orm: options.orm || "none",
|
|
11629
|
+
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
11630
|
+
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
11631
|
+
frontend,
|
|
11632
|
+
addons: options.addons || [],
|
|
11633
|
+
examples: options.examples || [],
|
|
11634
|
+
auth: options.auth || "none",
|
|
11635
|
+
payments: options.payments || "none",
|
|
11636
|
+
email: options.email || "none",
|
|
11637
|
+
fileUpload: options.fileUpload || "none",
|
|
11638
|
+
effect: options.effect || "none",
|
|
11639
|
+
git: options.git ?? false,
|
|
11640
|
+
packageManager: options.packageManager || "bun",
|
|
11641
|
+
versionChannel: options.versionChannel || "stable",
|
|
11642
|
+
install: false,
|
|
11643
|
+
dbSetup: options.dbSetup || "none",
|
|
11644
|
+
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
11645
|
+
webDeploy: options.webDeploy || "none",
|
|
11646
|
+
serverDeploy: options.serverDeploy || "none",
|
|
11647
|
+
astroIntegration: options.astroIntegration || "none",
|
|
11648
|
+
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
11649
|
+
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
11650
|
+
shadcnBase: options.shadcnBase ?? "radix",
|
|
11651
|
+
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
11652
|
+
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
11653
|
+
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
11654
|
+
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
11655
|
+
shadcnFont: options.shadcnFont ?? "inter",
|
|
11656
|
+
shadcnRadius: options.shadcnRadius ?? "default",
|
|
11657
|
+
ai: options.ai || "none",
|
|
11658
|
+
stateManagement: options.stateManagement || "none",
|
|
11659
|
+
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
11660
|
+
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
11661
|
+
validation: options.validation || "zod",
|
|
11662
|
+
realtime: options.realtime || "none",
|
|
11663
|
+
jobQueue: options.jobQueue || "none",
|
|
11664
|
+
animation: options.animation || "none",
|
|
11665
|
+
logging: options.logging || "none",
|
|
11666
|
+
observability: options.observability || "none",
|
|
11667
|
+
featureFlags: options.featureFlags || "none",
|
|
11668
|
+
analytics: options.analytics || "none",
|
|
11669
|
+
mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
|
|
11670
|
+
mobileUI: options.mobileUI || "none",
|
|
11671
|
+
mobileStorage: options.mobileStorage || "none",
|
|
11672
|
+
mobileTesting: options.mobileTesting || "none",
|
|
11673
|
+
mobilePush: options.mobilePush || "none",
|
|
11674
|
+
mobileOTA: options.mobileOTA || "none",
|
|
11675
|
+
mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
|
|
11676
|
+
cms: options.cms || "none",
|
|
11677
|
+
caching: options.caching || "none",
|
|
11678
|
+
i18n: options.i18n || "none",
|
|
11679
|
+
search: options.search || "none",
|
|
11680
|
+
fileStorage: options.fileStorage || "none",
|
|
11681
|
+
rustWebFramework: options.rustWebFramework || "none",
|
|
11682
|
+
rustFrontend: options.rustFrontend || "none",
|
|
11683
|
+
rustOrm: options.rustOrm || "none",
|
|
11684
|
+
rustApi: options.rustApi || "none",
|
|
11685
|
+
rustCli: options.rustCli || "none",
|
|
11686
|
+
rustLibraries: options.rustLibraries || [],
|
|
11687
|
+
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
11688
|
+
rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
|
|
11689
|
+
rustCaching: options.rustCaching || "none",
|
|
11690
|
+
rustAuth: options.rustAuth || "none",
|
|
11691
|
+
pythonWebFramework: options.pythonWebFramework || "none",
|
|
11692
|
+
pythonOrm: options.pythonOrm || "none",
|
|
11693
|
+
pythonValidation: options.pythonValidation || "none",
|
|
11694
|
+
pythonAi: options.pythonAi || [],
|
|
11695
|
+
pythonAuth: options.pythonAuth || "none",
|
|
11696
|
+
pythonApi: options.pythonApi || "none",
|
|
11697
|
+
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
11698
|
+
pythonGraphql: options.pythonGraphql || "none",
|
|
11699
|
+
pythonQuality: options.pythonQuality || "none",
|
|
11700
|
+
goWebFramework: options.goWebFramework || "none",
|
|
11701
|
+
goOrm: options.goOrm || "none",
|
|
11702
|
+
goApi: options.goApi || "none",
|
|
11703
|
+
goCli: options.goCli || "none",
|
|
11704
|
+
goLogging: options.goLogging || "none",
|
|
11705
|
+
goAuth: options.goAuth || "none",
|
|
11706
|
+
javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
|
|
11707
|
+
javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
|
|
11708
|
+
javaOrm: options.javaOrm || "none",
|
|
11709
|
+
javaAuth: options.javaAuth || "none",
|
|
11710
|
+
javaLibraries: options.javaLibraries || [],
|
|
11711
|
+
javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
|
|
11712
|
+
elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
|
|
11713
|
+
elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
|
|
11714
|
+
elixirAuth: options.elixirAuth || "none",
|
|
11715
|
+
elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
|
|
11716
|
+
elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
|
|
11717
|
+
elixirJobs: options.elixirJobs || "none",
|
|
11718
|
+
elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
|
|
11719
|
+
elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
|
|
11720
|
+
elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
|
|
11721
|
+
elixirEmail: options.elixirEmail || "none",
|
|
11722
|
+
elixirCaching: options.elixirCaching || "none",
|
|
11723
|
+
elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
|
|
11724
|
+
elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
|
|
11725
|
+
elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
|
|
11726
|
+
elixirDeploy: options.elixirDeploy || "none",
|
|
11727
|
+
aiDocs: options.aiDocs || ["claude-md"]
|
|
11728
|
+
};
|
|
11729
|
+
if (options.stackParts) config.stackParts = options.stackParts;
|
|
10949
11730
|
const result = await generateVirtualProject$1({
|
|
10950
|
-
config
|
|
10951
|
-
ecosystem,
|
|
10952
|
-
projectName: options.projectName || "my-project",
|
|
10953
|
-
projectDir: "/virtual",
|
|
10954
|
-
relativePath: "./virtual",
|
|
10955
|
-
database: options.database || "none",
|
|
10956
|
-
orm: options.orm || "none",
|
|
10957
|
-
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
10958
|
-
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
10959
|
-
frontend,
|
|
10960
|
-
addons: options.addons || [],
|
|
10961
|
-
examples: options.examples || [],
|
|
10962
|
-
auth: options.auth || "none",
|
|
10963
|
-
payments: options.payments || "none",
|
|
10964
|
-
email: options.email || "none",
|
|
10965
|
-
fileUpload: options.fileUpload || "none",
|
|
10966
|
-
effect: options.effect || "none",
|
|
10967
|
-
git: options.git ?? false,
|
|
10968
|
-
packageManager: options.packageManager || "bun",
|
|
10969
|
-
versionChannel: options.versionChannel || "stable",
|
|
10970
|
-
install: false,
|
|
10971
|
-
dbSetup: options.dbSetup || "none",
|
|
10972
|
-
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
10973
|
-
webDeploy: options.webDeploy || "none",
|
|
10974
|
-
serverDeploy: options.serverDeploy || "none",
|
|
10975
|
-
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
10976
|
-
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
10977
|
-
shadcnBase: options.shadcnBase ?? "radix",
|
|
10978
|
-
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
10979
|
-
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
10980
|
-
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
10981
|
-
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
10982
|
-
shadcnFont: options.shadcnFont ?? "inter",
|
|
10983
|
-
shadcnRadius: options.shadcnRadius ?? "default",
|
|
10984
|
-
ai: options.ai || "none",
|
|
10985
|
-
stateManagement: options.stateManagement || "none",
|
|
10986
|
-
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
10987
|
-
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
10988
|
-
validation: options.validation || "zod",
|
|
10989
|
-
realtime: options.realtime || "none",
|
|
10990
|
-
jobQueue: options.jobQueue || "none",
|
|
10991
|
-
animation: options.animation || "none",
|
|
10992
|
-
logging: options.logging || "none",
|
|
10993
|
-
observability: options.observability || "none",
|
|
10994
|
-
featureFlags: options.featureFlags || "none",
|
|
10995
|
-
analytics: options.analytics || "none",
|
|
10996
|
-
mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
|
|
10997
|
-
mobileUI: options.mobileUI || "none",
|
|
10998
|
-
mobileStorage: options.mobileStorage || "none",
|
|
10999
|
-
mobileTesting: options.mobileTesting || "none",
|
|
11000
|
-
mobilePush: options.mobilePush || "none",
|
|
11001
|
-
mobileOTA: options.mobileOTA || "none",
|
|
11002
|
-
mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
|
|
11003
|
-
cms: options.cms || "none",
|
|
11004
|
-
caching: options.caching || "none",
|
|
11005
|
-
i18n: options.i18n || "none",
|
|
11006
|
-
search: options.search || "none",
|
|
11007
|
-
fileStorage: options.fileStorage || "none",
|
|
11008
|
-
rustWebFramework: options.rustWebFramework || "none",
|
|
11009
|
-
rustFrontend: options.rustFrontend || "none",
|
|
11010
|
-
rustOrm: options.rustOrm || "none",
|
|
11011
|
-
rustApi: options.rustApi || "none",
|
|
11012
|
-
rustCli: options.rustCli || "none",
|
|
11013
|
-
rustLibraries: options.rustLibraries || [],
|
|
11014
|
-
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
11015
|
-
rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
|
|
11016
|
-
rustCaching: options.rustCaching || "none",
|
|
11017
|
-
rustAuth: options.rustAuth || "none",
|
|
11018
|
-
pythonWebFramework: options.pythonWebFramework || "none",
|
|
11019
|
-
pythonOrm: options.pythonOrm || "none",
|
|
11020
|
-
pythonValidation: options.pythonValidation || "none",
|
|
11021
|
-
pythonAi: options.pythonAi || [],
|
|
11022
|
-
pythonAuth: options.pythonAuth || "none",
|
|
11023
|
-
pythonApi: options.pythonApi || "none",
|
|
11024
|
-
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
11025
|
-
pythonGraphql: options.pythonGraphql || "none",
|
|
11026
|
-
pythonQuality: options.pythonQuality || "none",
|
|
11027
|
-
goWebFramework: options.goWebFramework || "none",
|
|
11028
|
-
goOrm: options.goOrm || "none",
|
|
11029
|
-
goApi: options.goApi || "none",
|
|
11030
|
-
goCli: options.goCli || "none",
|
|
11031
|
-
goLogging: options.goLogging || "none",
|
|
11032
|
-
goAuth: options.goAuth || "none",
|
|
11033
|
-
javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
|
|
11034
|
-
javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
|
|
11035
|
-
javaOrm: options.javaOrm || "none",
|
|
11036
|
-
javaAuth: options.javaAuth || "none",
|
|
11037
|
-
javaLibraries: options.javaLibraries || [],
|
|
11038
|
-
javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
|
|
11039
|
-
elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
|
|
11040
|
-
elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
|
|
11041
|
-
elixirAuth: options.elixirAuth || "none",
|
|
11042
|
-
elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
|
|
11043
|
-
elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
|
|
11044
|
-
elixirJobs: options.elixirJobs || "none",
|
|
11045
|
-
elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
|
|
11046
|
-
elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
|
|
11047
|
-
elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
|
|
11048
|
-
elixirEmail: options.elixirEmail || "none",
|
|
11049
|
-
elixirCaching: options.elixirCaching || "none",
|
|
11050
|
-
elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
|
|
11051
|
-
elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
|
|
11052
|
-
elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
|
|
11053
|
-
elixirDeploy: options.elixirDeploy || "none",
|
|
11054
|
-
aiDocs: options.aiDocs || ["claude-md"]
|
|
11055
|
-
},
|
|
11731
|
+
config,
|
|
11056
11732
|
templates: EMBEDDED_TEMPLATES$1
|
|
11057
11733
|
});
|
|
11058
11734
|
if (result.success && result.tree) return {
|