create-better-fullstack 1.8.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/addons-setup-Ca069cmy.mjs +5 -0
- package/dist/{addons-setup-CUmA_nra.mjs → addons-setup-DHoByttt.mjs} +1 -1
- package/dist/{bts-config-YcroedMK.mjs → bts-config-BMniWIbd.mjs} +180 -4
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +48 -2
- package/dist/index.mjs +1608 -979
- package/dist/{mcp-DoPutOIG.mjs → mcp-YvDMaVXB.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/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
3
|
-
import { a as
|
|
4
|
-
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CUmA_nra.mjs";
|
|
2
|
+
import { a as getGraphBackendUrl, c as getPrimaryGraphPart, d as getLatestCLIVersion, f as DEFAULT_CONFIG, g as getUserPkgManager, h as getDefaultConfig, i as getGraphBackendDeployInstructions, l as hasGraphPart, n as updateBtsConfig, o as getGraphPart, p as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, s as getGraphSummary, t as readBtsConfig, u as types_exports } from "./bts-config-BMniWIbd.mjs";
|
|
3
|
+
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-DHoByttt.mjs";
|
|
5
4
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
6
5
|
import { createRouterClient, os } from "@orpc/server";
|
|
7
6
|
import pc from "picocolors";
|
|
@@ -10,7 +9,7 @@ import z from "zod";
|
|
|
10
9
|
import envPaths from "env-paths";
|
|
11
10
|
import fs from "fs-extra";
|
|
12
11
|
import path from "node:path";
|
|
13
|
-
import { allowedApisForFrontends, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
12
|
+
import { allowedApisForFrontends, formatStackPartSpec, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
14
13
|
import { ECOSYSTEM_GROUPS, EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, checkAllVersions, generateCliReport, generateVirtualProject, generateVirtualProject as generateVirtualProject$1, listEcosystems, processAddonTemplates, processAddonsDeps, validatePreflightConfig } from "@better-fullstack/template-generator";
|
|
15
14
|
import gradient from "gradient-string";
|
|
16
15
|
import path$1 from "path";
|
|
@@ -349,20 +348,16 @@ function showEcosystems() {
|
|
|
349
348
|
console.log(`\nUsage: update-deps --ecosystem <name>`);
|
|
350
349
|
}
|
|
351
350
|
|
|
352
|
-
//#endregion
|
|
353
|
-
//#region src/types.ts
|
|
354
|
-
var types_exports = {};
|
|
355
|
-
import * as import__better_fullstack_types from "@better-fullstack/types";
|
|
356
|
-
__reExport(types_exports, import__better_fullstack_types);
|
|
357
|
-
|
|
358
351
|
//#endregion
|
|
359
352
|
//#region src/create-command-input.ts
|
|
360
353
|
const CreateCommandOptionsSchema = z.object({
|
|
361
354
|
template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
|
|
362
355
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
363
356
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
357
|
+
part: z.array(z.string()).optional().describe("Stack graph part binding, e.g. frontend:typescript:next or backend.orm:go:gorm"),
|
|
364
358
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
365
359
|
dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
|
|
360
|
+
verify: z.boolean().optional().default(false).describe("Run generated project checks after scaffolding without starting dev servers"),
|
|
366
361
|
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)"),
|
|
367
362
|
database: types_exports.DatabaseSchema.optional(),
|
|
368
363
|
orm: types_exports.ORMSchema.optional(),
|
|
@@ -653,8 +648,8 @@ function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
|
|
|
653
648
|
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
654
649
|
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
655
650
|
}
|
|
656
|
-
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
657
|
-
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
651
|
+
function validateServerDeployRequiresBackend(serverDeploy, backend, hasGraphBackend = false) {
|
|
652
|
+
if (serverDeploy && serverDeploy !== "none" && !hasGraphBackend && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
658
653
|
}
|
|
659
654
|
function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
660
655
|
const baseCompatibility = validateAddonCompatibility(addon, frontend, _auth);
|
|
@@ -2935,42 +2930,48 @@ const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target
|
|
|
2935
2930
|
|
|
2936
2931
|
//#endregion
|
|
2937
2932
|
//#region src/prompts/ecosystem.ts
|
|
2933
|
+
const ECOSYSTEM_PROMPT_OPTIONS = [
|
|
2934
|
+
{
|
|
2935
|
+
value: "typescript",
|
|
2936
|
+
label: "TypeScript",
|
|
2937
|
+
hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
|
|
2938
|
+
},
|
|
2939
|
+
{
|
|
2940
|
+
value: "react-native",
|
|
2941
|
+
label: "React Native",
|
|
2942
|
+
hint: "Expo and React Native mobile apps with native integrations"
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
value: "rust",
|
|
2946
|
+
label: "Rust",
|
|
2947
|
+
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
value: "python",
|
|
2951
|
+
label: "Python",
|
|
2952
|
+
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
value: "go",
|
|
2956
|
+
label: "Go",
|
|
2957
|
+
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2958
|
+
},
|
|
2959
|
+
{
|
|
2960
|
+
value: "java",
|
|
2961
|
+
label: "Java",
|
|
2962
|
+
hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
|
|
2963
|
+
},
|
|
2964
|
+
{
|
|
2965
|
+
value: "elixir",
|
|
2966
|
+
label: "Elixir",
|
|
2967
|
+
hint: "Elixir ecosystem with Phoenix, LiveView, Ecto, and more"
|
|
2968
|
+
}
|
|
2969
|
+
];
|
|
2938
2970
|
async function getEcosystemChoice(ecosystem) {
|
|
2939
2971
|
if (ecosystem !== void 0) return ecosystem;
|
|
2940
2972
|
const response = await navigableSelect({
|
|
2941
2973
|
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
|
-
],
|
|
2974
|
+
options: ECOSYSTEM_PROMPT_OPTIONS,
|
|
2974
2975
|
initialValue: "typescript"
|
|
2975
2976
|
});
|
|
2976
2977
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
@@ -4373,220 +4374,6 @@ function getMobileDeepLinkingChoice(mobileDeepLinking) {
|
|
|
4373
4374
|
return promptMobileOption(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking, "Select mobile deep linking");
|
|
4374
4375
|
}
|
|
4375
4376
|
|
|
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
4377
|
//#endregion
|
|
4591
4378
|
//#region src/prompts/package-manager.ts
|
|
4592
4379
|
async function getPackageManagerChoice(packageManager) {
|
|
@@ -4621,73 +4408,6 @@ async function getPackageManagerChoice(packageManager) {
|
|
|
4621
4408
|
return response;
|
|
4622
4409
|
}
|
|
4623
4410
|
|
|
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
|
|
4686
|
-
});
|
|
4687
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4688
|
-
return response;
|
|
4689
|
-
}
|
|
4690
|
-
|
|
4691
4411
|
//#endregion
|
|
4692
4412
|
//#region src/prompts/python-ecosystem.ts
|
|
4693
4413
|
const PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
@@ -5019,133 +4739,6 @@ async function getPythonQualityChoice(pythonQuality) {
|
|
|
5019
4739
|
return response;
|
|
5020
4740
|
}
|
|
5021
4741
|
|
|
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
4742
|
//#endregion
|
|
5150
4743
|
//#region src/prompts/rust-ecosystem.ts
|
|
5151
4744
|
const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
@@ -5528,133 +5121,51 @@ async function getRustAuthChoice(rustAuth) {
|
|
|
5528
5121
|
}
|
|
5529
5122
|
|
|
5530
5123
|
//#endregion
|
|
5531
|
-
//#region src/prompts/
|
|
5532
|
-
const
|
|
5124
|
+
//#region src/prompts/shadcn-options.ts
|
|
5125
|
+
const BASE_OPTIONS = [{
|
|
5126
|
+
value: "radix",
|
|
5127
|
+
label: "Radix UI",
|
|
5128
|
+
hint: "Battle-tested headless primitives (130M+ monthly downloads)"
|
|
5129
|
+
}, {
|
|
5130
|
+
value: "base",
|
|
5131
|
+
label: "Base UI",
|
|
5132
|
+
hint: "MUI's headless library with cleaner APIs and native multi-select"
|
|
5133
|
+
}];
|
|
5134
|
+
const STYLE_OPTIONS = [
|
|
5533
5135
|
{
|
|
5534
|
-
value: "
|
|
5535
|
-
label: "
|
|
5536
|
-
hint: "
|
|
5136
|
+
value: "vega",
|
|
5137
|
+
label: "Vega",
|
|
5138
|
+
hint: "Classic shadcn/ui look"
|
|
5537
5139
|
},
|
|
5538
5140
|
{
|
|
5539
|
-
value: "
|
|
5540
|
-
label: "
|
|
5541
|
-
hint: "
|
|
5141
|
+
value: "nova",
|
|
5142
|
+
label: "Nova",
|
|
5143
|
+
hint: "Compact layout with reduced padding"
|
|
5542
5144
|
},
|
|
5543
5145
|
{
|
|
5544
|
-
value: "
|
|
5545
|
-
label: "
|
|
5546
|
-
hint: "
|
|
5146
|
+
value: "maia",
|
|
5147
|
+
label: "Maia",
|
|
5148
|
+
hint: "Soft, rounded with generous spacing"
|
|
5547
5149
|
},
|
|
5548
5150
|
{
|
|
5549
|
-
value: "
|
|
5550
|
-
label: "
|
|
5551
|
-
hint: "
|
|
5151
|
+
value: "lyra",
|
|
5152
|
+
label: "Lyra",
|
|
5153
|
+
hint: "Boxy and sharp, pairs well with mono fonts"
|
|
5552
5154
|
},
|
|
5553
5155
|
{
|
|
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"
|
|
5156
|
+
value: "mira",
|
|
5157
|
+
label: "Mira",
|
|
5158
|
+
hint: "Dense, made for data-heavy interfaces"
|
|
5159
|
+
},
|
|
5160
|
+
{
|
|
5161
|
+
value: "luma",
|
|
5162
|
+
label: "Luma",
|
|
5163
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5164
|
+
},
|
|
5165
|
+
{
|
|
5166
|
+
value: "sera",
|
|
5167
|
+
label: "Sera",
|
|
5168
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5658
5169
|
}
|
|
5659
5170
|
];
|
|
5660
5171
|
const ICON_LIBRARY_OPTIONS = [
|
|
@@ -6015,147 +5526,6 @@ async function promptShadcnRadius() {
|
|
|
6015
5526
|
return selected;
|
|
6016
5527
|
}
|
|
6017
5528
|
|
|
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
5529
|
//#endregion
|
|
6160
5530
|
//#region src/prompts/ui-library.ts
|
|
6161
5531
|
const UI_LIBRARY_OPTIONS = {
|
|
@@ -6199,72 +5569,1096 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
6199
5569
|
label: "MUI",
|
|
6200
5570
|
hint: "Popular React component library implementing Material Design"
|
|
6201
5571
|
},
|
|
6202
|
-
antd: {
|
|
6203
|
-
label: "Ant Design",
|
|
6204
|
-
hint: "Enterprise-class React UI component library"
|
|
5572
|
+
antd: {
|
|
5573
|
+
label: "Ant Design",
|
|
5574
|
+
hint: "Enterprise-class React UI component library"
|
|
5575
|
+
},
|
|
5576
|
+
"base-ui": {
|
|
5577
|
+
label: "Base UI",
|
|
5578
|
+
hint: "Unstyled, accessible components from MUI team (Radix successor)"
|
|
5579
|
+
},
|
|
5580
|
+
"ark-ui": {
|
|
5581
|
+
label: "Ark UI",
|
|
5582
|
+
hint: "Headless, accessible UI components for React, Vue, Solid, and Svelte"
|
|
5583
|
+
},
|
|
5584
|
+
"react-aria": {
|
|
5585
|
+
label: "React Aria",
|
|
5586
|
+
hint: "Adobe's accessible, unstyled UI components for React"
|
|
5587
|
+
},
|
|
5588
|
+
none: {
|
|
5589
|
+
label: "None",
|
|
5590
|
+
hint: "No UI component library"
|
|
5591
|
+
}
|
|
5592
|
+
};
|
|
5593
|
+
function resolveUILibraryPrompt(context = {}) {
|
|
5594
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
5595
|
+
if (web.length === 0) return {
|
|
5596
|
+
shouldPrompt: false,
|
|
5597
|
+
mode: "single",
|
|
5598
|
+
options: [],
|
|
5599
|
+
autoValue: "none"
|
|
5600
|
+
};
|
|
5601
|
+
const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
|
|
5602
|
+
if (context.uiLibrary !== void 0) return {
|
|
5603
|
+
shouldPrompt: false,
|
|
5604
|
+
mode: "single",
|
|
5605
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5606
|
+
value: lib,
|
|
5607
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5608
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5609
|
+
})),
|
|
5610
|
+
autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
|
|
5611
|
+
};
|
|
5612
|
+
const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
|
|
5613
|
+
return {
|
|
5614
|
+
shouldPrompt: true,
|
|
5615
|
+
mode: "single",
|
|
5616
|
+
options: compatibleLibraries.map((lib) => ({
|
|
5617
|
+
value: lib,
|
|
5618
|
+
label: UI_LIBRARY_OPTIONS[lib].label,
|
|
5619
|
+
hint: UI_LIBRARY_OPTIONS[lib].hint
|
|
5620
|
+
})),
|
|
5621
|
+
initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
|
|
5625
|
+
const resolution = resolveUILibraryPrompt({
|
|
5626
|
+
uiLibrary,
|
|
5627
|
+
frontends,
|
|
5628
|
+
astroIntegration
|
|
5629
|
+
});
|
|
5630
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
5631
|
+
const selected = await navigableSelect({
|
|
5632
|
+
message: "Select UI component library",
|
|
5633
|
+
options: resolution.options,
|
|
5634
|
+
initialValue: resolution.initialValue
|
|
5635
|
+
});
|
|
5636
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5637
|
+
return selected;
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
//#endregion
|
|
5641
|
+
//#region src/utils/compatibility.ts
|
|
5642
|
+
const WEB_FRAMEWORKS = [
|
|
5643
|
+
"tanstack-router",
|
|
5644
|
+
"react-router",
|
|
5645
|
+
"react-vite",
|
|
5646
|
+
"tanstack-start",
|
|
5647
|
+
"next",
|
|
5648
|
+
"vinext",
|
|
5649
|
+
"nuxt",
|
|
5650
|
+
"svelte",
|
|
5651
|
+
"solid",
|
|
5652
|
+
"solid-start",
|
|
5653
|
+
"astro",
|
|
5654
|
+
"qwik",
|
|
5655
|
+
"angular",
|
|
5656
|
+
"redwood",
|
|
5657
|
+
"fresh"
|
|
5658
|
+
];
|
|
5659
|
+
|
|
5660
|
+
//#endregion
|
|
5661
|
+
//#region src/prompts/web-deploy.ts
|
|
5662
|
+
function hasWebFrontend(frontends) {
|
|
5663
|
+
return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
5664
|
+
}
|
|
5665
|
+
function getDeploymentDisplay(deployment) {
|
|
5666
|
+
if (deployment === "cloudflare") return {
|
|
5667
|
+
label: "Cloudflare",
|
|
5668
|
+
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
5669
|
+
};
|
|
5670
|
+
if (deployment === "vercel") return {
|
|
5671
|
+
label: "Vercel",
|
|
5672
|
+
hint: "Deploy to Vercel's edge network"
|
|
5673
|
+
};
|
|
5674
|
+
return {
|
|
5675
|
+
label: deployment,
|
|
5676
|
+
hint: `Add ${deployment} deployment`
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
5680
|
+
if (deployment !== void 0) return deployment;
|
|
5681
|
+
if (!hasWebFrontend(frontend)) return "none";
|
|
5682
|
+
const response = await navigableSelect({
|
|
5683
|
+
message: "Select web deployment",
|
|
5684
|
+
options: [
|
|
5685
|
+
"cloudflare",
|
|
5686
|
+
"vercel",
|
|
5687
|
+
"none"
|
|
5688
|
+
].map((deploy) => {
|
|
5689
|
+
const { label, hint } = getDeploymentDisplay(deploy);
|
|
5690
|
+
return {
|
|
5691
|
+
value: deploy,
|
|
5692
|
+
label,
|
|
5693
|
+
hint
|
|
5694
|
+
};
|
|
5695
|
+
}),
|
|
5696
|
+
initialValue: DEFAULT_CONFIG.webDeploy
|
|
5697
|
+
});
|
|
5698
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
5699
|
+
return response;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
//#endregion
|
|
5703
|
+
//#region src/prompts/multi-ecosystem-composer.ts
|
|
5704
|
+
async function getCompositionModeChoice() {
|
|
5705
|
+
const response = await navigableSelect({
|
|
5706
|
+
message: "Select project composition",
|
|
5707
|
+
options: [{
|
|
5708
|
+
value: "single",
|
|
5709
|
+
label: "Single ecosystem",
|
|
5710
|
+
hint: "Use the classic guided flow"
|
|
5711
|
+
}, {
|
|
5712
|
+
value: "multi",
|
|
5713
|
+
label: "Multi ecosystem",
|
|
5714
|
+
hint: "Compose a TypeScript frontend with another backend ecosystem"
|
|
5715
|
+
}],
|
|
5716
|
+
initialValue: "single"
|
|
5717
|
+
});
|
|
5718
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5719
|
+
return response;
|
|
5720
|
+
}
|
|
5721
|
+
async function selectBackendEcosystem() {
|
|
5722
|
+
const response = await navigableSelect({
|
|
5723
|
+
message: "Select backend ecosystem",
|
|
5724
|
+
options: [
|
|
5725
|
+
{
|
|
5726
|
+
value: "go",
|
|
5727
|
+
label: "Go",
|
|
5728
|
+
hint: "Gin, Echo, Fiber, Chi"
|
|
5729
|
+
},
|
|
5730
|
+
{
|
|
5731
|
+
value: "rust",
|
|
5732
|
+
label: "Rust",
|
|
5733
|
+
hint: "Axum, Actix Web, Rocket"
|
|
5734
|
+
},
|
|
5735
|
+
{
|
|
5736
|
+
value: "python",
|
|
5737
|
+
label: "Python",
|
|
5738
|
+
hint: "FastAPI, Django, Flask"
|
|
5739
|
+
},
|
|
5740
|
+
{
|
|
5741
|
+
value: "java",
|
|
5742
|
+
label: "Java",
|
|
5743
|
+
hint: "Spring Boot, Quarkus"
|
|
5744
|
+
},
|
|
5745
|
+
{
|
|
5746
|
+
value: "elixir",
|
|
5747
|
+
label: "Elixir",
|
|
5748
|
+
hint: "Phoenix, LiveView"
|
|
5749
|
+
}
|
|
5750
|
+
],
|
|
5751
|
+
initialValue: "go"
|
|
5752
|
+
});
|
|
5753
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5754
|
+
return response;
|
|
5755
|
+
}
|
|
5756
|
+
async function selectServerDeployment(deployment) {
|
|
5757
|
+
if (deployment !== void 0) return deployment;
|
|
5758
|
+
const response = await navigableSelect({
|
|
5759
|
+
message: "Select server deployment",
|
|
5760
|
+
options: [
|
|
5761
|
+
{
|
|
5762
|
+
value: "none",
|
|
5763
|
+
label: "None",
|
|
5764
|
+
hint: "Skip server deployment setup"
|
|
5765
|
+
},
|
|
5766
|
+
{
|
|
5767
|
+
value: "railway",
|
|
5768
|
+
label: "Railway",
|
|
5769
|
+
hint: "Deploy a standalone backend service"
|
|
5770
|
+
},
|
|
5771
|
+
{
|
|
5772
|
+
value: "docker",
|
|
5773
|
+
label: "Docker",
|
|
5774
|
+
hint: "Containerize the backend service"
|
|
5775
|
+
},
|
|
5776
|
+
{
|
|
5777
|
+
value: "fly",
|
|
5778
|
+
label: "Fly",
|
|
5779
|
+
hint: "Deploy close to users"
|
|
5780
|
+
},
|
|
5781
|
+
{
|
|
5782
|
+
value: "vercel",
|
|
5783
|
+
label: "Vercel",
|
|
5784
|
+
hint: "Deploy from the backend workspace"
|
|
5785
|
+
}
|
|
5786
|
+
],
|
|
5787
|
+
initialValue: "none"
|
|
5788
|
+
});
|
|
5789
|
+
if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
|
|
5790
|
+
return response;
|
|
5791
|
+
}
|
|
5792
|
+
function promptValue(value) {
|
|
5793
|
+
if (isCancel$1(value) || isGoBack(value)) return exitCancelled("Operation cancelled");
|
|
5794
|
+
return value;
|
|
5795
|
+
}
|
|
5796
|
+
async function selectDatabaseConfig(flags) {
|
|
5797
|
+
const database = promptValue(await getDatabaseChoice(flags.database, "hono", "bun"));
|
|
5798
|
+
return {
|
|
5799
|
+
database,
|
|
5800
|
+
dbSetup: promptValue(await getDBSetupChoice(database, flags.dbSetup, "none", "none", "none"))
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath) {
|
|
5804
|
+
const baseConfig = getDefaultConfig();
|
|
5805
|
+
const frontend = promptValue(await navigableSelect({
|
|
5806
|
+
message: "Select TypeScript web frontend",
|
|
5807
|
+
options: WEB_FRONTEND_PROMPT_OPTIONS,
|
|
5808
|
+
initialValue: flags.frontend?.[0] ?? "next"
|
|
5809
|
+
}));
|
|
5810
|
+
const frontendList = [frontend];
|
|
5811
|
+
const astroIntegration = frontend === "astro" ? promptValue(await getAstroIntegrationChoice(flags.astroIntegration)) : void 0;
|
|
5812
|
+
const uiLibrary = hasWebStyling$1(frontendList) ? promptValue(await getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
|
|
5813
|
+
const shadcnOptions = uiLibrary === "shadcn-ui" ? await getShadcnOptions({
|
|
5814
|
+
shadcnBase: flags.shadcnBase,
|
|
5815
|
+
shadcnStyle: flags.shadcnStyle,
|
|
5816
|
+
shadcnIconLibrary: flags.shadcnIconLibrary,
|
|
5817
|
+
shadcnColorTheme: flags.shadcnColorTheme,
|
|
5818
|
+
shadcnBaseColor: flags.shadcnBaseColor,
|
|
5819
|
+
shadcnFont: flags.shadcnFont,
|
|
5820
|
+
shadcnRadius: flags.shadcnRadius
|
|
5821
|
+
}) : void 0;
|
|
5822
|
+
const cssFramework = hasWebStyling$1(frontendList) ? promptValue(await getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
|
|
5823
|
+
const backendEcosystem = await selectBackendEcosystem();
|
|
5824
|
+
const stackPartSpecs = [`frontend:typescript:${frontend}`];
|
|
5825
|
+
const backendChoices = {};
|
|
5826
|
+
let database = "none";
|
|
5827
|
+
let dbSetup = "none";
|
|
5828
|
+
if (backendEcosystem === "go") {
|
|
5829
|
+
const goWebFramework = promptValue(await getGoWebFrameworkChoice(flags.goWebFramework));
|
|
5830
|
+
if (goWebFramework !== "none") {
|
|
5831
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5832
|
+
database = databaseConfig.database;
|
|
5833
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5834
|
+
}
|
|
5835
|
+
const goOrm = database === "none" || goWebFramework === "none" ? "none" : promptValue(await getGoOrmChoice(flags.goOrm));
|
|
5836
|
+
const goApi = goWebFramework === "none" ? "none" : promptValue(await getGoApiChoice(flags.goApi));
|
|
5837
|
+
const goAuth = goWebFramework === "none" ? "none" : promptValue(await getGoAuthChoice(flags.goAuth));
|
|
5838
|
+
const goCli = goWebFramework === "none" ? "none" : promptValue(await getGoCliChoice(flags.goCli));
|
|
5839
|
+
const goLogging = goWebFramework === "none" ? "none" : promptValue(await getGoLoggingChoice(flags.goLogging));
|
|
5840
|
+
Object.assign(backendChoices, {
|
|
5841
|
+
goWebFramework,
|
|
5842
|
+
goOrm,
|
|
5843
|
+
goApi,
|
|
5844
|
+
goAuth,
|
|
5845
|
+
goCli,
|
|
5846
|
+
goLogging
|
|
5847
|
+
});
|
|
5848
|
+
if (goWebFramework !== "none") stackPartSpecs.push(`backend:go:${goWebFramework}`);
|
|
5849
|
+
if (goOrm !== "none") stackPartSpecs.push(`backend.orm:go:${goOrm}`);
|
|
5850
|
+
if (goApi !== "none") stackPartSpecs.push(`backend.api:go:${goApi}`);
|
|
5851
|
+
if (goAuth !== "none") stackPartSpecs.push(`backend.auth:go:${goAuth}`);
|
|
5852
|
+
}
|
|
5853
|
+
if (backendEcosystem === "rust") {
|
|
5854
|
+
const rustWebFramework = promptValue(await getRustWebFrameworkChoice(flags.rustWebFramework));
|
|
5855
|
+
if (rustWebFramework !== "none") {
|
|
5856
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5857
|
+
database = databaseConfig.database;
|
|
5858
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5859
|
+
}
|
|
5860
|
+
const rustOrm = database === "none" || rustWebFramework === "none" ? "none" : promptValue(await getRustOrmChoice(flags.rustOrm));
|
|
5861
|
+
const rustApi = rustWebFramework === "none" ? "none" : promptValue(await getRustApiChoice(flags.rustApi));
|
|
5862
|
+
const rustAuth = rustWebFramework === "none" ? "none" : promptValue(await getRustAuthChoice(flags.rustAuth));
|
|
5863
|
+
const rustFrontend = "none";
|
|
5864
|
+
const rustCli = rustWebFramework === "none" ? "none" : promptValue(await getRustCliChoice(flags.rustCli));
|
|
5865
|
+
const rustLibraries = rustWebFramework === "none" ? [] : promptValue(await getRustLibrariesChoice(flags.rustLibraries));
|
|
5866
|
+
const rustLogging = rustWebFramework === "none" ? "none" : promptValue(await getRustLoggingChoice(flags.rustLogging));
|
|
5867
|
+
const rustErrorHandling = promptValue(await getRustErrorHandlingChoice(flags.rustErrorHandling));
|
|
5868
|
+
const rustCaching = rustWebFramework === "none" ? "none" : promptValue(await getRustCachingChoice(flags.rustCaching));
|
|
5869
|
+
Object.assign(backendChoices, {
|
|
5870
|
+
rustWebFramework,
|
|
5871
|
+
rustOrm,
|
|
5872
|
+
rustApi,
|
|
5873
|
+
rustAuth,
|
|
5874
|
+
rustFrontend,
|
|
5875
|
+
rustCli,
|
|
5876
|
+
rustLibraries,
|
|
5877
|
+
rustLogging,
|
|
5878
|
+
rustErrorHandling,
|
|
5879
|
+
rustCaching
|
|
5880
|
+
});
|
|
5881
|
+
if (rustWebFramework !== "none") stackPartSpecs.push(`backend:rust:${rustWebFramework}`);
|
|
5882
|
+
if (rustOrm !== "none") stackPartSpecs.push(`backend.orm:rust:${rustOrm}`);
|
|
5883
|
+
if (rustApi !== "none") stackPartSpecs.push(`backend.api:rust:${rustApi}`);
|
|
5884
|
+
if (rustAuth !== "none") stackPartSpecs.push(`backend.auth:rust:${rustAuth}`);
|
|
5885
|
+
}
|
|
5886
|
+
if (backendEcosystem === "python") {
|
|
5887
|
+
const pythonWebFramework = promptValue(await getPythonWebFrameworkChoice(flags.pythonWebFramework));
|
|
5888
|
+
if (pythonWebFramework !== "none") {
|
|
5889
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5890
|
+
database = databaseConfig.database;
|
|
5891
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5892
|
+
}
|
|
5893
|
+
const pythonOrm = database === "none" || pythonWebFramework === "none" ? "none" : promptValue(await getPythonOrmChoice(flags.pythonOrm));
|
|
5894
|
+
const pythonValidation = pythonWebFramework === "none" ? "none" : promptValue(await getPythonValidationChoice(flags.pythonValidation));
|
|
5895
|
+
const pythonAi = pythonWebFramework === "none" ? [] : promptValue(await getPythonAiChoice(flags.pythonAi));
|
|
5896
|
+
const pythonAuth = pythonWebFramework === "none" ? "none" : promptValue(await getPythonAuthChoice(flags.pythonAuth));
|
|
5897
|
+
const pythonTaskQueue = pythonWebFramework === "none" ? "none" : promptValue(await getPythonTaskQueueChoice(flags.pythonTaskQueue));
|
|
5898
|
+
const pythonGraphql = pythonWebFramework === "none" ? "none" : promptValue(await getPythonGraphqlChoice(flags.pythonGraphql));
|
|
5899
|
+
const pythonQuality = pythonWebFramework === "none" ? "none" : promptValue(await getPythonQualityChoice(flags.pythonQuality));
|
|
5900
|
+
Object.assign(backendChoices, {
|
|
5901
|
+
pythonWebFramework,
|
|
5902
|
+
pythonOrm,
|
|
5903
|
+
pythonValidation,
|
|
5904
|
+
pythonAi,
|
|
5905
|
+
pythonAuth,
|
|
5906
|
+
pythonTaskQueue,
|
|
5907
|
+
pythonGraphql,
|
|
5908
|
+
pythonQuality
|
|
5909
|
+
});
|
|
5910
|
+
if (pythonWebFramework !== "none") stackPartSpecs.push(`backend:python:${pythonWebFramework}`);
|
|
5911
|
+
if (pythonOrm !== "none") stackPartSpecs.push(`backend.orm:python:${pythonOrm}`);
|
|
5912
|
+
if (pythonAuth !== "none") stackPartSpecs.push(`backend.auth:python:${pythonAuth}`);
|
|
5913
|
+
if (pythonTaskQueue !== "none") stackPartSpecs.push(`backend.jobQueue:python:${pythonTaskQueue}`);
|
|
5914
|
+
if (pythonGraphql !== "none") stackPartSpecs.push(`backend.api:python:${pythonGraphql}`);
|
|
5915
|
+
}
|
|
5916
|
+
if (backendEcosystem === "java") {
|
|
5917
|
+
const javaWebFramework = promptValue(await getJavaWebFrameworkChoice(flags.javaWebFramework));
|
|
5918
|
+
const javaBuildTool = promptValue(await getJavaBuildToolChoice(flags.javaBuildTool));
|
|
5919
|
+
if (javaWebFramework !== "none" && javaBuildTool !== "none") {
|
|
5920
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5921
|
+
database = databaseConfig.database;
|
|
5922
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5923
|
+
}
|
|
5924
|
+
const javaOrm = database === "none" || javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaOrmChoice(flags.javaOrm));
|
|
5925
|
+
const javaAuth = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaAuthChoice(flags.javaAuth));
|
|
5926
|
+
const javaLibraries = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? [] : promptValue(await getJavaLibrariesChoice(flags.javaLibraries));
|
|
5927
|
+
const javaTestingLibraries = promptValue(await getJavaTestingLibrariesChoice(flags.javaTestingLibraries));
|
|
5928
|
+
Object.assign(backendChoices, {
|
|
5929
|
+
javaWebFramework,
|
|
5930
|
+
javaBuildTool,
|
|
5931
|
+
javaOrm,
|
|
5932
|
+
javaAuth,
|
|
5933
|
+
javaLibraries,
|
|
5934
|
+
javaTestingLibraries
|
|
5935
|
+
});
|
|
5936
|
+
if (javaWebFramework !== "none") stackPartSpecs.push(`backend:java:${javaWebFramework}`);
|
|
5937
|
+
if (javaOrm !== "none") stackPartSpecs.push(`backend.orm:java:${javaOrm}`);
|
|
5938
|
+
if (javaAuth !== "none") stackPartSpecs.push(`backend.auth:java:${javaAuth}`);
|
|
5939
|
+
}
|
|
5940
|
+
if (backendEcosystem === "elixir") {
|
|
5941
|
+
const elixirWebFramework = promptValue(await getElixirWebFrameworkChoice(flags.elixirWebFramework));
|
|
5942
|
+
if (elixirWebFramework !== "none") {
|
|
5943
|
+
const databaseConfig = await selectDatabaseConfig(flags);
|
|
5944
|
+
database = databaseConfig.database;
|
|
5945
|
+
dbSetup = databaseConfig.dbSetup;
|
|
5946
|
+
}
|
|
5947
|
+
const elixirOrm = database === "none" || elixirWebFramework === "none" ? "none" : promptValue(await getElixirOrmChoice(flags.elixirOrm));
|
|
5948
|
+
const elixirAuth = elixirWebFramework === "none" ? "none" : promptValue(await getElixirAuthChoice(flags.elixirAuth));
|
|
5949
|
+
const elixirApi = elixirWebFramework === "none" ? "none" : promptValue(await getElixirApiChoice(flags.elixirApi));
|
|
5950
|
+
const elixirRealtime = elixirWebFramework === "none" ? "none" : promptValue(await getElixirRealtimeChoice(flags.elixirRealtime));
|
|
5951
|
+
const elixirJobs = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJobsChoice(flags.elixirJobs));
|
|
5952
|
+
const elixirValidation = elixirWebFramework === "none" ? "none" : promptValue(await getElixirValidationChoice(flags.elixirValidation));
|
|
5953
|
+
const elixirHttp = elixirWebFramework === "none" ? "none" : promptValue(await getElixirHttpChoice(flags.elixirHttp));
|
|
5954
|
+
const elixirJson = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJsonChoice(flags.elixirJson));
|
|
5955
|
+
const elixirEmail = elixirWebFramework === "none" ? "none" : promptValue(await getElixirEmailChoice(flags.elixirEmail));
|
|
5956
|
+
const elixirCaching = elixirWebFramework === "none" ? "none" : promptValue(await getElixirCachingChoice(flags.elixirCaching));
|
|
5957
|
+
const elixirObservability = elixirWebFramework === "none" ? "none" : promptValue(await getElixirObservabilityChoice(flags.elixirObservability));
|
|
5958
|
+
const elixirTesting = elixirWebFramework === "none" ? "none" : promptValue(await getElixirTestingChoice(flags.elixirTesting));
|
|
5959
|
+
const elixirQuality = elixirWebFramework === "none" ? "none" : promptValue(await getElixirQualityChoice(flags.elixirQuality));
|
|
5960
|
+
const elixirDeploy = elixirWebFramework === "none" ? "none" : promptValue(await getElixirDeployChoice(flags.elixirDeploy));
|
|
5961
|
+
Object.assign(backendChoices, {
|
|
5962
|
+
elixirWebFramework,
|
|
5963
|
+
elixirOrm,
|
|
5964
|
+
elixirAuth,
|
|
5965
|
+
elixirApi,
|
|
5966
|
+
elixirRealtime,
|
|
5967
|
+
elixirJobs,
|
|
5968
|
+
elixirValidation,
|
|
5969
|
+
elixirHttp,
|
|
5970
|
+
elixirJson,
|
|
5971
|
+
elixirEmail,
|
|
5972
|
+
elixirCaching,
|
|
5973
|
+
elixirObservability,
|
|
5974
|
+
elixirTesting,
|
|
5975
|
+
elixirQuality,
|
|
5976
|
+
elixirDeploy
|
|
5977
|
+
});
|
|
5978
|
+
if (elixirWebFramework !== "none") stackPartSpecs.push(`backend:elixir:${elixirWebFramework}`);
|
|
5979
|
+
if (elixirOrm !== "none") stackPartSpecs.push(`backend.orm:elixir:${elixirOrm}`);
|
|
5980
|
+
if (elixirAuth !== "none") stackPartSpecs.push(`backend.auth:elixir:${elixirAuth}`);
|
|
5981
|
+
if (elixirApi !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirApi}`);
|
|
5982
|
+
if (elixirRealtime !== "none") stackPartSpecs.push(`backend.api:elixir:${elixirRealtime}`);
|
|
5983
|
+
if (elixirJobs !== "none") stackPartSpecs.push(`backend.jobQueue:elixir:${elixirJobs}`);
|
|
5984
|
+
if (elixirEmail !== "none") stackPartSpecs.push(`backend.email:elixir:${elixirEmail}`);
|
|
5985
|
+
if (elixirCaching !== "none") stackPartSpecs.push(`backend.caching:elixir:${elixirCaching}`);
|
|
5986
|
+
if (elixirObservability !== "none") stackPartSpecs.push(`backend.observability:elixir:${elixirObservability}`);
|
|
5987
|
+
if (elixirTesting !== "none") stackPartSpecs.push(`backend.testing:elixir:${elixirTesting}`);
|
|
5988
|
+
if (elixirDeploy !== "none") stackPartSpecs.push(`backend.deploy:elixir:${elixirDeploy}`);
|
|
5989
|
+
}
|
|
5990
|
+
if (database !== "none") stackPartSpecs.push(`database:universal:${database}`);
|
|
5991
|
+
const stackParts = (0, types_exports.parseStackPartSpecs)(stackPartSpecs, "selected");
|
|
5992
|
+
const graphPartial = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
|
|
5993
|
+
const addons = promptValue(await getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
|
|
5994
|
+
const webDeploy = promptValue(await getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
|
|
5995
|
+
const serverDeploy = await selectServerDeployment(flags.serverDeploy);
|
|
5996
|
+
const aiDocs = promptValue(await getAiDocsChoice(flags.aiDocs));
|
|
5997
|
+
const git = promptValue(await getGitChoice(flags.git));
|
|
5998
|
+
const packageManager = promptValue(await getPackageManagerChoice(flags.packageManager));
|
|
5999
|
+
const install = promptValue(await getinstallChoice(flags.install, "typescript", "none"));
|
|
6000
|
+
return {
|
|
6001
|
+
...baseConfig,
|
|
6002
|
+
...flags,
|
|
6003
|
+
...graphPartial,
|
|
6004
|
+
...backendChoices,
|
|
6005
|
+
projectName,
|
|
6006
|
+
projectDir,
|
|
6007
|
+
relativePath,
|
|
6008
|
+
ecosystem: "typescript",
|
|
6009
|
+
frontend: frontendList,
|
|
6010
|
+
backend: "none",
|
|
6011
|
+
runtime: "none",
|
|
6012
|
+
database,
|
|
6013
|
+
orm: "none",
|
|
6014
|
+
api: "none",
|
|
6015
|
+
auth: "none",
|
|
6016
|
+
astroIntegration,
|
|
6017
|
+
uiLibrary,
|
|
6018
|
+
...shadcnOptions,
|
|
6019
|
+
cssFramework,
|
|
6020
|
+
addons,
|
|
6021
|
+
examples: [],
|
|
6022
|
+
dbSetup,
|
|
6023
|
+
webDeploy,
|
|
6024
|
+
serverDeploy,
|
|
6025
|
+
aiDocs,
|
|
6026
|
+
git,
|
|
6027
|
+
packageManager,
|
|
6028
|
+
install,
|
|
6029
|
+
stackParts
|
|
6030
|
+
};
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
//#endregion
|
|
6034
|
+
//#region src/prompts/navigable-group.ts
|
|
6035
|
+
/**
|
|
6036
|
+
* Navigable group - a group of prompts that allows going back
|
|
6037
|
+
*/
|
|
6038
|
+
/**
|
|
6039
|
+
* Define a group of prompts that supports going back to previous prompts.
|
|
6040
|
+
* Returns a result object with all the values, or handles cancel/go-back navigation.
|
|
6041
|
+
*/
|
|
6042
|
+
async function navigableGroup(prompts, opts) {
|
|
6043
|
+
const results = {};
|
|
6044
|
+
const promptNames = Object.keys(prompts);
|
|
6045
|
+
let currentIndex = 0;
|
|
6046
|
+
let goingBack = false;
|
|
6047
|
+
while (currentIndex < promptNames.length) {
|
|
6048
|
+
const name = promptNames[currentIndex];
|
|
6049
|
+
const prompt = prompts[name];
|
|
6050
|
+
setIsFirstPrompt$1(currentIndex === 0);
|
|
6051
|
+
setLastPromptShownUI(false);
|
|
6052
|
+
const result = await prompt({ results })?.catch((e) => {
|
|
6053
|
+
throw e;
|
|
6054
|
+
});
|
|
6055
|
+
if (isGoBack(result)) {
|
|
6056
|
+
goingBack = true;
|
|
6057
|
+
if (currentIndex > 0) {
|
|
6058
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6059
|
+
delete results[prevName];
|
|
6060
|
+
currentIndex--;
|
|
6061
|
+
continue;
|
|
6062
|
+
}
|
|
6063
|
+
goingBack = false;
|
|
6064
|
+
continue;
|
|
6065
|
+
}
|
|
6066
|
+
if (isCancel$1(result)) {
|
|
6067
|
+
if (typeof opts?.onCancel === "function") {
|
|
6068
|
+
results[name] = "canceled";
|
|
6069
|
+
opts.onCancel({ results });
|
|
6070
|
+
}
|
|
6071
|
+
setIsFirstPrompt$1(false);
|
|
6072
|
+
return results;
|
|
6073
|
+
}
|
|
6074
|
+
if (goingBack && !didLastPromptShowUI()) {
|
|
6075
|
+
if (currentIndex > 0) {
|
|
6076
|
+
const prevName = promptNames[currentIndex - 1];
|
|
6077
|
+
delete results[prevName];
|
|
6078
|
+
currentIndex--;
|
|
6079
|
+
continue;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
goingBack = false;
|
|
6083
|
+
results[name] = result;
|
|
6084
|
+
currentIndex++;
|
|
6085
|
+
}
|
|
6086
|
+
setIsFirstPrompt$1(false);
|
|
6087
|
+
return results;
|
|
6088
|
+
}
|
|
6089
|
+
|
|
6090
|
+
//#endregion
|
|
6091
|
+
//#region src/prompts/observability.ts
|
|
6092
|
+
const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
6093
|
+
{
|
|
6094
|
+
value: "opentelemetry",
|
|
6095
|
+
label: "OpenTelemetry",
|
|
6096
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
6097
|
+
},
|
|
6098
|
+
{
|
|
6099
|
+
value: "sentry",
|
|
6100
|
+
label: "Sentry",
|
|
6101
|
+
hint: "Error tracking and performance monitoring"
|
|
6102
|
+
},
|
|
6103
|
+
{
|
|
6104
|
+
value: "grafana",
|
|
6105
|
+
label: "Grafana",
|
|
6106
|
+
hint: "Prometheus metrics for Grafana dashboards and alerting"
|
|
6107
|
+
},
|
|
6108
|
+
{
|
|
6109
|
+
value: "none",
|
|
6110
|
+
label: "None",
|
|
6111
|
+
hint: "Skip observability/tracing setup"
|
|
6112
|
+
}
|
|
6113
|
+
];
|
|
6114
|
+
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
6115
|
+
function resolveObservabilityPrompt(context = {}) {
|
|
6116
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6117
|
+
shouldPrompt: false,
|
|
6118
|
+
mode: "single",
|
|
6119
|
+
options: [],
|
|
6120
|
+
autoValue: "none"
|
|
6121
|
+
};
|
|
6122
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
6123
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6124
|
+
shouldPrompt: false,
|
|
6125
|
+
mode: "single",
|
|
6126
|
+
options: [],
|
|
6127
|
+
autoValue: "none"
|
|
6128
|
+
};
|
|
6129
|
+
return context.observability !== void 0 ? {
|
|
6130
|
+
shouldPrompt: false,
|
|
6131
|
+
mode: "single",
|
|
6132
|
+
options,
|
|
6133
|
+
autoValue: context.observability
|
|
6134
|
+
} : {
|
|
6135
|
+
shouldPrompt: true,
|
|
6136
|
+
mode: "single",
|
|
6137
|
+
options,
|
|
6138
|
+
initialValue: "none"
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
6142
|
+
const resolution = resolveObservabilityPrompt({
|
|
6143
|
+
observability,
|
|
6144
|
+
backend,
|
|
6145
|
+
ecosystem
|
|
6146
|
+
});
|
|
6147
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6148
|
+
const response = await navigableSelect({
|
|
6149
|
+
message: "Select observability solution",
|
|
6150
|
+
options: resolution.options,
|
|
6151
|
+
initialValue: resolution.initialValue
|
|
6152
|
+
});
|
|
6153
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6154
|
+
return response;
|
|
6155
|
+
}
|
|
6156
|
+
|
|
6157
|
+
//#endregion
|
|
6158
|
+
//#region src/prompts/orm.ts
|
|
6159
|
+
const ormOptions = {
|
|
6160
|
+
prisma: {
|
|
6161
|
+
value: "prisma",
|
|
6162
|
+
label: "Prisma",
|
|
6163
|
+
hint: "Powerful, feature-rich ORM"
|
|
6164
|
+
},
|
|
6165
|
+
mongoose: {
|
|
6166
|
+
value: "mongoose",
|
|
6167
|
+
label: "Mongoose",
|
|
6168
|
+
hint: "Elegant object modeling tool"
|
|
6169
|
+
},
|
|
6170
|
+
drizzle: {
|
|
6171
|
+
value: "drizzle",
|
|
6172
|
+
label: "Drizzle",
|
|
6173
|
+
hint: "Lightweight and performant TypeScript ORM"
|
|
6174
|
+
},
|
|
6175
|
+
typeorm: {
|
|
6176
|
+
value: "typeorm",
|
|
6177
|
+
label: "TypeORM",
|
|
6178
|
+
hint: "Traditional ORM with Active Record/Data Mapper"
|
|
6179
|
+
},
|
|
6180
|
+
kysely: {
|
|
6181
|
+
value: "kysely",
|
|
6182
|
+
label: "Kysely",
|
|
6183
|
+
hint: "Type-safe SQL query builder"
|
|
6184
|
+
},
|
|
6185
|
+
mikroorm: {
|
|
6186
|
+
value: "mikroorm",
|
|
6187
|
+
label: "MikroORM",
|
|
6188
|
+
hint: "Data Mapper ORM for DDD"
|
|
6189
|
+
},
|
|
6190
|
+
sequelize: {
|
|
6191
|
+
value: "sequelize",
|
|
6192
|
+
label: "Sequelize",
|
|
6193
|
+
hint: "Mature ORM with wide adoption"
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6196
|
+
function resolveORMPrompt(context) {
|
|
6197
|
+
if (context.backend === "convex" || !context.hasDatabase) return {
|
|
6198
|
+
shouldPrompt: false,
|
|
6199
|
+
mode: "single",
|
|
6200
|
+
options: [],
|
|
6201
|
+
autoValue: "none"
|
|
6202
|
+
};
|
|
6203
|
+
if (context.database === "edgedb" || context.database === "redis") return {
|
|
6204
|
+
shouldPrompt: false,
|
|
6205
|
+
mode: "single",
|
|
6206
|
+
options: [],
|
|
6207
|
+
autoValue: "none"
|
|
6208
|
+
};
|
|
6209
|
+
if (context.orm !== void 0) return {
|
|
6210
|
+
shouldPrompt: false,
|
|
6211
|
+
mode: "single",
|
|
6212
|
+
options: [],
|
|
6213
|
+
autoValue: context.orm
|
|
6214
|
+
};
|
|
6215
|
+
return {
|
|
6216
|
+
shouldPrompt: true,
|
|
6217
|
+
mode: "single",
|
|
6218
|
+
options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
|
|
6219
|
+
ormOptions.drizzle,
|
|
6220
|
+
ormOptions.prisma,
|
|
6221
|
+
ormOptions.typeorm,
|
|
6222
|
+
ormOptions.kysely,
|
|
6223
|
+
ormOptions.mikroorm,
|
|
6224
|
+
ormOptions.sequelize
|
|
6225
|
+
],
|
|
6226
|
+
initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
6227
|
+
};
|
|
6228
|
+
}
|
|
6229
|
+
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
6230
|
+
const resolution = resolveORMPrompt({
|
|
6231
|
+
orm,
|
|
6232
|
+
hasDatabase,
|
|
6233
|
+
database,
|
|
6234
|
+
backend,
|
|
6235
|
+
runtime
|
|
6236
|
+
});
|
|
6237
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6238
|
+
const response = await navigableSelect({
|
|
6239
|
+
message: "Select ORM",
|
|
6240
|
+
options: resolution.options,
|
|
6241
|
+
initialValue: resolution.initialValue
|
|
6242
|
+
});
|
|
6243
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6244
|
+
return response;
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
//#endregion
|
|
6248
|
+
//#region src/prompts/payments.ts
|
|
6249
|
+
function resolvePaymentsPrompt(context = {}) {
|
|
6250
|
+
if (context.payments !== void 0) return {
|
|
6251
|
+
shouldPrompt: false,
|
|
6252
|
+
mode: "single",
|
|
6253
|
+
options: [],
|
|
6254
|
+
autoValue: context.payments
|
|
6255
|
+
};
|
|
6256
|
+
if (context.backend === "none") return {
|
|
6257
|
+
shouldPrompt: false,
|
|
6258
|
+
mode: "single",
|
|
6259
|
+
options: [],
|
|
6260
|
+
autoValue: "none"
|
|
6261
|
+
};
|
|
6262
|
+
const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
6263
|
+
const options = [];
|
|
6264
|
+
if (isPolarCompatible) options.push({
|
|
6265
|
+
value: "polar",
|
|
6266
|
+
label: "Polar",
|
|
6267
|
+
hint: "Turn your software into a business. 6 lines of code."
|
|
6268
|
+
});
|
|
6269
|
+
options.push({
|
|
6270
|
+
value: "stripe",
|
|
6271
|
+
label: "Stripe",
|
|
6272
|
+
hint: "Payment processing platform for internet businesses."
|
|
6273
|
+
}, {
|
|
6274
|
+
value: "lemon-squeezy",
|
|
6275
|
+
label: "Lemon Squeezy",
|
|
6276
|
+
hint: "All-in-one platform for SaaS, digital products, and subscriptions."
|
|
6277
|
+
}, {
|
|
6278
|
+
value: "paddle",
|
|
6279
|
+
label: "Paddle",
|
|
6280
|
+
hint: "Complete payments infrastructure for SaaS."
|
|
6281
|
+
}, {
|
|
6282
|
+
value: "dodo",
|
|
6283
|
+
label: "Dodo Payments",
|
|
6284
|
+
hint: "Simple payment infrastructure for developers."
|
|
6285
|
+
}, {
|
|
6286
|
+
value: "none",
|
|
6287
|
+
label: "None",
|
|
6288
|
+
hint: "No payments integration"
|
|
6289
|
+
});
|
|
6290
|
+
return {
|
|
6291
|
+
shouldPrompt: true,
|
|
6292
|
+
mode: "single",
|
|
6293
|
+
options,
|
|
6294
|
+
initialValue: DEFAULT_CONFIG.payments
|
|
6295
|
+
};
|
|
6296
|
+
}
|
|
6297
|
+
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
6298
|
+
const resolution = resolvePaymentsPrompt({
|
|
6299
|
+
payments,
|
|
6300
|
+
auth,
|
|
6301
|
+
backend,
|
|
6302
|
+
frontends
|
|
6303
|
+
});
|
|
6304
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6305
|
+
const response = await navigableSelect({
|
|
6306
|
+
message: "Select payments provider",
|
|
6307
|
+
options: resolution.options,
|
|
6308
|
+
initialValue: resolution.initialValue
|
|
6309
|
+
});
|
|
6310
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6311
|
+
return response;
|
|
6312
|
+
}
|
|
6313
|
+
|
|
6314
|
+
//#endregion
|
|
6315
|
+
//#region src/prompts/realtime.ts
|
|
6316
|
+
const REALTIME_PROMPT_OPTIONS = [
|
|
6317
|
+
{
|
|
6318
|
+
value: "socket-io",
|
|
6319
|
+
label: "Socket.IO",
|
|
6320
|
+
hint: "Real-time bidirectional communication with fallbacks"
|
|
6321
|
+
},
|
|
6322
|
+
{
|
|
6323
|
+
value: "partykit",
|
|
6324
|
+
label: "PartyKit",
|
|
6325
|
+
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
6326
|
+
},
|
|
6327
|
+
{
|
|
6328
|
+
value: "ably",
|
|
6329
|
+
label: "Ably",
|
|
6330
|
+
hint: "Real-time messaging platform with pub/sub and presence"
|
|
6331
|
+
},
|
|
6332
|
+
{
|
|
6333
|
+
value: "pusher",
|
|
6334
|
+
label: "Pusher",
|
|
6335
|
+
hint: "Real-time communication APIs with channels and events"
|
|
6336
|
+
},
|
|
6337
|
+
{
|
|
6338
|
+
value: "liveblocks",
|
|
6339
|
+
label: "Liveblocks",
|
|
6340
|
+
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
6341
|
+
},
|
|
6342
|
+
{
|
|
6343
|
+
value: "yjs",
|
|
6344
|
+
label: "Y.js",
|
|
6345
|
+
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
6346
|
+
},
|
|
6347
|
+
{
|
|
6348
|
+
value: "none",
|
|
6349
|
+
label: "None",
|
|
6350
|
+
hint: "Skip real-time/WebSocket integration"
|
|
6351
|
+
}
|
|
6352
|
+
];
|
|
6353
|
+
function resolveRealtimePrompt(context = {}) {
|
|
6354
|
+
if (context.backend === "none" || context.backend === "convex") return {
|
|
6355
|
+
shouldPrompt: false,
|
|
6356
|
+
mode: "single",
|
|
6357
|
+
options: [],
|
|
6358
|
+
autoValue: "none"
|
|
6359
|
+
};
|
|
6360
|
+
return context.realtime !== void 0 ? {
|
|
6361
|
+
shouldPrompt: false,
|
|
6362
|
+
mode: "single",
|
|
6363
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6364
|
+
autoValue: context.realtime
|
|
6365
|
+
} : {
|
|
6366
|
+
shouldPrompt: true,
|
|
6367
|
+
mode: "single",
|
|
6368
|
+
options: REALTIME_PROMPT_OPTIONS,
|
|
6369
|
+
initialValue: "none"
|
|
6370
|
+
};
|
|
6371
|
+
}
|
|
6372
|
+
async function getRealtimeChoice(realtime, backend) {
|
|
6373
|
+
const resolution = resolveRealtimePrompt({
|
|
6374
|
+
realtime,
|
|
6375
|
+
backend
|
|
6376
|
+
});
|
|
6377
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6378
|
+
const response = await navigableSelect({
|
|
6379
|
+
message: "Select real-time solution",
|
|
6380
|
+
options: resolution.options,
|
|
6381
|
+
initialValue: resolution.initialValue
|
|
6382
|
+
});
|
|
6383
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6384
|
+
return response;
|
|
6385
|
+
}
|
|
6386
|
+
|
|
6387
|
+
//#endregion
|
|
6388
|
+
//#region src/prompts/runtime.ts
|
|
6389
|
+
const RUNTIME_PROMPT_OPTIONS = [
|
|
6390
|
+
{
|
|
6391
|
+
value: "bun",
|
|
6392
|
+
label: "Bun",
|
|
6393
|
+
hint: "Fast all-in-one JavaScript runtime"
|
|
6394
|
+
},
|
|
6395
|
+
{
|
|
6396
|
+
value: "node",
|
|
6397
|
+
label: "Node.js",
|
|
6398
|
+
hint: "Traditional Node.js runtime"
|
|
6399
|
+
},
|
|
6400
|
+
{
|
|
6401
|
+
value: "workers",
|
|
6402
|
+
label: "Cloudflare Workers",
|
|
6403
|
+
hint: "Edge runtime on Cloudflare's global network"
|
|
6404
|
+
}
|
|
6405
|
+
];
|
|
6406
|
+
function resolveRuntimePrompt(context = {}) {
|
|
6407
|
+
if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
|
|
6408
|
+
shouldPrompt: false,
|
|
6409
|
+
mode: "single",
|
|
6410
|
+
options: [],
|
|
6411
|
+
autoValue: "none"
|
|
6412
|
+
};
|
|
6413
|
+
const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
|
|
6414
|
+
return context.runtime !== void 0 ? {
|
|
6415
|
+
shouldPrompt: false,
|
|
6416
|
+
mode: "single",
|
|
6417
|
+
options,
|
|
6418
|
+
autoValue: context.runtime
|
|
6419
|
+
} : {
|
|
6420
|
+
shouldPrompt: true,
|
|
6421
|
+
mode: "single",
|
|
6422
|
+
options,
|
|
6423
|
+
initialValue: DEFAULT_CONFIG.runtime
|
|
6424
|
+
};
|
|
6425
|
+
}
|
|
6426
|
+
async function getRuntimeChoice(runtime, backend) {
|
|
6427
|
+
const resolution = resolveRuntimePrompt({
|
|
6428
|
+
runtime,
|
|
6429
|
+
backend
|
|
6430
|
+
});
|
|
6431
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6432
|
+
const response = await navigableSelect({
|
|
6433
|
+
message: "Select runtime",
|
|
6434
|
+
options: resolution.options,
|
|
6435
|
+
initialValue: resolution.initialValue
|
|
6436
|
+
});
|
|
6437
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6438
|
+
return response;
|
|
6439
|
+
}
|
|
6440
|
+
|
|
6441
|
+
//#endregion
|
|
6442
|
+
//#region src/prompts/search.ts
|
|
6443
|
+
const SEARCH_PROMPT_OPTIONS = [
|
|
6444
|
+
{
|
|
6445
|
+
value: "meilisearch",
|
|
6446
|
+
label: "Meilisearch",
|
|
6447
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
6448
|
+
},
|
|
6449
|
+
{
|
|
6450
|
+
value: "typesense",
|
|
6451
|
+
label: "Typesense",
|
|
6452
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
6453
|
+
},
|
|
6454
|
+
{
|
|
6455
|
+
value: "elasticsearch",
|
|
6456
|
+
label: "Elasticsearch",
|
|
6457
|
+
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
6458
|
+
},
|
|
6459
|
+
{
|
|
6460
|
+
value: "algolia",
|
|
6461
|
+
label: "Algolia",
|
|
6462
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
6463
|
+
},
|
|
6464
|
+
{
|
|
6465
|
+
value: "none",
|
|
6466
|
+
label: "None",
|
|
6467
|
+
hint: "Skip search engine setup"
|
|
6468
|
+
}
|
|
6469
|
+
];
|
|
6470
|
+
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
6471
|
+
function resolveSearchPrompt(context = {}) {
|
|
6472
|
+
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6473
|
+
shouldPrompt: false,
|
|
6474
|
+
mode: "single",
|
|
6475
|
+
options: [],
|
|
6476
|
+
autoValue: "none"
|
|
6477
|
+
};
|
|
6478
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
6479
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6480
|
+
shouldPrompt: false,
|
|
6481
|
+
mode: "single",
|
|
6482
|
+
options: [],
|
|
6483
|
+
autoValue: "none"
|
|
6484
|
+
};
|
|
6485
|
+
return context.search !== void 0 ? {
|
|
6486
|
+
shouldPrompt: false,
|
|
6487
|
+
mode: "single",
|
|
6488
|
+
options,
|
|
6489
|
+
autoValue: context.search
|
|
6490
|
+
} : {
|
|
6491
|
+
shouldPrompt: true,
|
|
6492
|
+
mode: "single",
|
|
6493
|
+
options,
|
|
6494
|
+
initialValue: "none"
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6497
|
+
async function getSearchChoice(search, backend, ecosystem) {
|
|
6498
|
+
const resolution = resolveSearchPrompt({
|
|
6499
|
+
search,
|
|
6500
|
+
backend,
|
|
6501
|
+
ecosystem
|
|
6502
|
+
});
|
|
6503
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6504
|
+
const response = await navigableSelect({
|
|
6505
|
+
message: "Select search engine",
|
|
6506
|
+
options: resolution.options,
|
|
6507
|
+
initialValue: resolution.initialValue
|
|
6508
|
+
});
|
|
6509
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6510
|
+
return response;
|
|
6511
|
+
}
|
|
6512
|
+
|
|
6513
|
+
//#endregion
|
|
6514
|
+
//#region src/prompts/server-deploy.ts
|
|
6515
|
+
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
6516
|
+
if (deployment !== void 0) return deployment;
|
|
6517
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
6518
|
+
if (backend !== "hono") return "none";
|
|
6519
|
+
if (runtime === "workers") return "cloudflare";
|
|
6520
|
+
return "none";
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
//#endregion
|
|
6524
|
+
//#region src/prompts/state-management.ts
|
|
6525
|
+
function resolveStateManagementPrompt(context = {}) {
|
|
6526
|
+
if (context.stateManagement !== void 0) return {
|
|
6527
|
+
shouldPrompt: false,
|
|
6528
|
+
mode: "single",
|
|
6529
|
+
options: [],
|
|
6530
|
+
autoValue: context.stateManagement
|
|
6531
|
+
};
|
|
6532
|
+
const { web } = splitFrontends$1(context.frontends);
|
|
6533
|
+
if (web.length === 0) return {
|
|
6534
|
+
shouldPrompt: false,
|
|
6535
|
+
mode: "single",
|
|
6536
|
+
options: [],
|
|
6537
|
+
autoValue: "none"
|
|
6538
|
+
};
|
|
6539
|
+
const isReact = web.some((f) => [
|
|
6540
|
+
"tanstack-router",
|
|
6541
|
+
"react-router",
|
|
6542
|
+
"react-vite",
|
|
6543
|
+
"tanstack-start",
|
|
6544
|
+
"next",
|
|
6545
|
+
"vinext",
|
|
6546
|
+
"redwood"
|
|
6547
|
+
].includes(f));
|
|
6548
|
+
const isFresh = web.includes("fresh");
|
|
6549
|
+
const options = [];
|
|
6550
|
+
if (isReact) options.push({
|
|
6551
|
+
value: "zustand",
|
|
6552
|
+
label: "Zustand",
|
|
6553
|
+
hint: "Lightweight state management with simple API"
|
|
6554
|
+
}, {
|
|
6555
|
+
value: "jotai",
|
|
6556
|
+
label: "Jotai",
|
|
6557
|
+
hint: "Primitive and flexible atomic state"
|
|
6558
|
+
}, {
|
|
6559
|
+
value: "redux-toolkit",
|
|
6560
|
+
label: "Redux Toolkit",
|
|
6561
|
+
hint: "Enterprise-standard state with excellent TS support"
|
|
6562
|
+
}, {
|
|
6563
|
+
value: "valtio",
|
|
6564
|
+
label: "Valtio",
|
|
6565
|
+
hint: "Proxy-based state management"
|
|
6566
|
+
}, {
|
|
6567
|
+
value: "legend-state",
|
|
6568
|
+
label: "Legend State",
|
|
6569
|
+
hint: "High-performance observable state for React"
|
|
6570
|
+
}, {
|
|
6571
|
+
value: "mobx",
|
|
6572
|
+
label: "MobX",
|
|
6573
|
+
hint: "Observable-based reactive state management"
|
|
6574
|
+
});
|
|
6575
|
+
if (!isFresh) options.push({
|
|
6576
|
+
value: "nanostores",
|
|
6577
|
+
label: "Nanostores",
|
|
6578
|
+
hint: "Tiny state manager (1KB) for all frameworks"
|
|
6579
|
+
}, {
|
|
6580
|
+
value: "xstate",
|
|
6581
|
+
label: "XState",
|
|
6582
|
+
hint: "State machines and statecharts for complex logic"
|
|
6583
|
+
}, {
|
|
6584
|
+
value: "tanstack-store",
|
|
6585
|
+
label: "TanStack Store",
|
|
6586
|
+
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
6587
|
+
});
|
|
6588
|
+
options.push({
|
|
6589
|
+
value: "none",
|
|
6590
|
+
label: "None",
|
|
6591
|
+
hint: "Skip state management setup"
|
|
6592
|
+
});
|
|
6593
|
+
return {
|
|
6594
|
+
shouldPrompt: true,
|
|
6595
|
+
mode: "single",
|
|
6596
|
+
options,
|
|
6597
|
+
initialValue: "none"
|
|
6598
|
+
};
|
|
6599
|
+
}
|
|
6600
|
+
async function getStateManagementChoice(stateManagement, frontends) {
|
|
6601
|
+
const resolution = resolveStateManagementPrompt({
|
|
6602
|
+
stateManagement,
|
|
6603
|
+
frontends
|
|
6604
|
+
});
|
|
6605
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6606
|
+
const response = await navigableSelect({
|
|
6607
|
+
message: "Select state management",
|
|
6608
|
+
options: resolution.options,
|
|
6609
|
+
initialValue: resolution.initialValue
|
|
6610
|
+
});
|
|
6611
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6612
|
+
return response;
|
|
6613
|
+
}
|
|
6614
|
+
|
|
6615
|
+
//#endregion
|
|
6616
|
+
//#region src/prompts/testing.ts
|
|
6617
|
+
const TESTING_PROMPT_OPTIONS = [
|
|
6618
|
+
{
|
|
6619
|
+
value: "vitest",
|
|
6620
|
+
label: "Vitest",
|
|
6621
|
+
hint: "Blazing fast Vite-native unit test framework"
|
|
6622
|
+
},
|
|
6623
|
+
{
|
|
6624
|
+
value: "vitest-playwright",
|
|
6625
|
+
label: "Vitest + Playwright",
|
|
6626
|
+
hint: "Both unit and E2E testing for complete coverage"
|
|
6205
6627
|
},
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6628
|
+
{
|
|
6629
|
+
value: "playwright",
|
|
6630
|
+
label: "Playwright",
|
|
6631
|
+
hint: "End-to-end testing framework by Microsoft"
|
|
6209
6632
|
},
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6633
|
+
{
|
|
6634
|
+
value: "jest",
|
|
6635
|
+
label: "Jest",
|
|
6636
|
+
hint: "Classic testing framework with wide ecosystem"
|
|
6213
6637
|
},
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6638
|
+
{
|
|
6639
|
+
value: "cypress",
|
|
6640
|
+
label: "Cypress",
|
|
6641
|
+
hint: "E2E testing with time travel debugging"
|
|
6217
6642
|
},
|
|
6218
|
-
|
|
6643
|
+
{
|
|
6644
|
+
value: "none",
|
|
6219
6645
|
label: "None",
|
|
6220
|
-
hint: "
|
|
6646
|
+
hint: "Skip testing framework setup"
|
|
6221
6647
|
}
|
|
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
|
-
};
|
|
6648
|
+
];
|
|
6649
|
+
function resolveTestingPrompt(testing) {
|
|
6650
|
+
return createStaticSinglePromptResolution(TESTING_PROMPT_OPTIONS, "vitest", testing);
|
|
6253
6651
|
}
|
|
6254
|
-
async function
|
|
6255
|
-
const resolution =
|
|
6256
|
-
uiLibrary,
|
|
6257
|
-
frontends,
|
|
6258
|
-
astroIntegration
|
|
6259
|
-
});
|
|
6652
|
+
async function getTestingChoice(testing) {
|
|
6653
|
+
const resolution = resolveTestingPrompt(testing);
|
|
6260
6654
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
6261
|
-
const
|
|
6262
|
-
message: "Select
|
|
6655
|
+
const response = await navigableSelect({
|
|
6656
|
+
message: "Select testing framework",
|
|
6263
6657
|
options: resolution.options,
|
|
6264
6658
|
initialValue: resolution.initialValue
|
|
6265
6659
|
});
|
|
6266
|
-
if (isCancel$1(
|
|
6267
|
-
return
|
|
6660
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6661
|
+
return response;
|
|
6268
6662
|
}
|
|
6269
6663
|
|
|
6270
6664
|
//#endregion
|
|
@@ -6326,71 +6720,12 @@ async function getValidationChoice(validation) {
|
|
|
6326
6720
|
return response;
|
|
6327
6721
|
}
|
|
6328
6722
|
|
|
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
6723
|
//#endregion
|
|
6392
6724
|
//#region src/prompts/config-prompts.ts
|
|
6393
6725
|
async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
6726
|
+
if (flags.ecosystem === void 0 && flags.stackParts === void 0) {
|
|
6727
|
+
if (await getCompositionModeChoice() === "multi") return gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath);
|
|
6728
|
+
}
|
|
6394
6729
|
const result = await navigableGroup({
|
|
6395
6730
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
6396
6731
|
frontend: ({ results }) => {
|
|
@@ -7017,6 +7352,15 @@ async function trackProjectCreation(config, disableAnalytics = false) {
|
|
|
7017
7352
|
|
|
7018
7353
|
//#endregion
|
|
7019
7354
|
//#region src/utils/display-config.ts
|
|
7355
|
+
function getSelectedGraphPart(config, role, ownerPartId) {
|
|
7356
|
+
return config.stackParts?.find((part) => part.role === role && part.ownerPartId === ownerPartId && part.source !== "provided");
|
|
7357
|
+
}
|
|
7358
|
+
function getGraphDisplayValue(config, role) {
|
|
7359
|
+
const primaryBackend = getSelectedGraphPart(config, "backend");
|
|
7360
|
+
const part = role === "backend" ? primaryBackend : getSelectedGraphPart(config, role, primaryBackend?.id) ?? getSelectedGraphPart(config, role);
|
|
7361
|
+
if (!part) return null;
|
|
7362
|
+
return `${part.ecosystem}:${part.toolId}`;
|
|
7363
|
+
}
|
|
7020
7364
|
function displayConfig(config) {
|
|
7021
7365
|
const configDisplay = [];
|
|
7022
7366
|
if (config.projectName) configDisplay.push(`${pc.blue("Project Name:")} ${config.projectName}`);
|
|
@@ -7027,14 +7371,26 @@ function displayConfig(config) {
|
|
|
7027
7371
|
}
|
|
7028
7372
|
if (config.uiLibrary !== void 0) configDisplay.push(`${pc.blue("UI Library:")} ${String(config.uiLibrary)}`);
|
|
7029
7373
|
if (config.cssFramework !== void 0) configDisplay.push(`${pc.blue("CSS Framework:")} ${String(config.cssFramework)}`);
|
|
7030
|
-
if (config.backend !== void 0)
|
|
7374
|
+
if (config.backend !== void 0) {
|
|
7375
|
+
const graphBackend = config.backend === "none" ? getGraphDisplayValue(config, "backend") : null;
|
|
7376
|
+
configDisplay.push(`${pc.blue("Backend:")} ${graphBackend ?? String(config.backend)}`);
|
|
7377
|
+
}
|
|
7031
7378
|
if (config.runtime !== void 0) configDisplay.push(`${pc.blue("Runtime:")} ${String(config.runtime)}`);
|
|
7032
|
-
if (config.api !== void 0)
|
|
7379
|
+
if (config.api !== void 0) {
|
|
7380
|
+
const graphApi = config.api === "none" ? getGraphDisplayValue(config, "api") : null;
|
|
7381
|
+
configDisplay.push(`${pc.blue("API:")} ${graphApi ?? String(config.api)}`);
|
|
7382
|
+
}
|
|
7033
7383
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
7034
|
-
if (config.orm !== void 0)
|
|
7384
|
+
if (config.orm !== void 0) {
|
|
7385
|
+
const graphOrm = config.orm === "none" ? getGraphDisplayValue(config, "orm") : null;
|
|
7386
|
+
configDisplay.push(`${pc.blue("ORM:")} ${graphOrm ?? String(config.orm)}`);
|
|
7387
|
+
}
|
|
7035
7388
|
if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
|
|
7036
7389
|
if (config.payments !== void 0) configDisplay.push(`${pc.blue("Payments:")} ${String(config.payments)}`);
|
|
7037
|
-
if (config.email !== void 0)
|
|
7390
|
+
if (config.email !== void 0) {
|
|
7391
|
+
const graphEmail = config.email === "none" ? getGraphDisplayValue(config, "email") : null;
|
|
7392
|
+
configDisplay.push(`${pc.blue("Email:")} ${graphEmail ?? String(config.email)}`);
|
|
7393
|
+
}
|
|
7038
7394
|
if (config.fileUpload !== void 0) configDisplay.push(`${pc.blue("File Upload:")} ${String(config.fileUpload)}`);
|
|
7039
7395
|
if (config.effect !== void 0) configDisplay.push(`${pc.blue("Effect:")} ${String(config.effect)}`);
|
|
7040
7396
|
if (config.ai !== void 0) configDisplay.push(`${pc.blue("AI:")} ${String(config.ai)}`);
|
|
@@ -7046,6 +7402,42 @@ function displayConfig(config) {
|
|
|
7046
7402
|
if (config.realtime !== void 0) configDisplay.push(`${pc.blue("Realtime:")} ${String(config.realtime)}`);
|
|
7047
7403
|
if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
|
|
7048
7404
|
if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
|
|
7405
|
+
const graphBackendPart = getSelectedGraphPart(config, "backend");
|
|
7406
|
+
if (graphBackendPart?.ecosystem === "go") {
|
|
7407
|
+
if (config.goCli && config.goCli !== "none") configDisplay.push(`${pc.blue("Go CLI:")} ${String(config.goCli)}`);
|
|
7408
|
+
if (config.goLogging && config.goLogging !== "none") configDisplay.push(`${pc.blue("Go Logging:")} ${String(config.goLogging)}`);
|
|
7409
|
+
}
|
|
7410
|
+
if (graphBackendPart?.ecosystem === "rust") {
|
|
7411
|
+
if (config.rustCli && config.rustCli !== "none") configDisplay.push(`${pc.blue("Rust CLI:")} ${String(config.rustCli)}`);
|
|
7412
|
+
if (config.rustLibraries && config.rustLibraries.length > 0) configDisplay.push(`${pc.blue("Rust Libraries:")} ${config.rustLibraries.join(", ")}`);
|
|
7413
|
+
if (config.rustLogging && config.rustLogging !== "none") configDisplay.push(`${pc.blue("Rust Logging:")} ${String(config.rustLogging)}`);
|
|
7414
|
+
if (config.rustErrorHandling && config.rustErrorHandling !== "none") configDisplay.push(`${pc.blue("Rust Error Handling:")} ${String(config.rustErrorHandling)}`);
|
|
7415
|
+
if (config.rustCaching && config.rustCaching !== "none") configDisplay.push(`${pc.blue("Rust Caching:")} ${String(config.rustCaching)}`);
|
|
7416
|
+
}
|
|
7417
|
+
if (graphBackendPart?.ecosystem === "python") {
|
|
7418
|
+
if (config.pythonValidation && config.pythonValidation !== "none") configDisplay.push(`${pc.blue("Python Validation:")} ${String(config.pythonValidation)}`);
|
|
7419
|
+
if (config.pythonAi && config.pythonAi.length > 0) configDisplay.push(`${pc.blue("Python AI:")} ${config.pythonAi.join(", ")}`);
|
|
7420
|
+
if (config.pythonTaskQueue && config.pythonTaskQueue !== "none") configDisplay.push(`${pc.blue("Python Task Queue:")} ${String(config.pythonTaskQueue)}`);
|
|
7421
|
+
if (config.pythonGraphql && config.pythonGraphql !== "none") configDisplay.push(`${pc.blue("Python GraphQL:")} ${String(config.pythonGraphql)}`);
|
|
7422
|
+
if (config.pythonQuality && config.pythonQuality !== "none") configDisplay.push(`${pc.blue("Python Quality:")} ${String(config.pythonQuality)}`);
|
|
7423
|
+
}
|
|
7424
|
+
if (graphBackendPart?.ecosystem === "java") {
|
|
7425
|
+
if (config.javaBuildTool && config.javaBuildTool !== "none") configDisplay.push(`${pc.blue("Java Build Tool:")} ${String(config.javaBuildTool)}`);
|
|
7426
|
+
if (config.javaLibraries && config.javaLibraries.length > 0) configDisplay.push(`${pc.blue("Java Libraries:")} ${config.javaLibraries.join(", ")}`);
|
|
7427
|
+
if (config.javaTestingLibraries && config.javaTestingLibraries.length > 0) configDisplay.push(`${pc.blue("Java Testing Libraries:")} ${config.javaTestingLibraries.join(", ")}`);
|
|
7428
|
+
}
|
|
7429
|
+
if (graphBackendPart?.ecosystem === "elixir") {
|
|
7430
|
+
if (config.elixirRealtime && config.elixirRealtime !== "none") configDisplay.push(`${pc.blue("Elixir Realtime:")} ${String(config.elixirRealtime)}`);
|
|
7431
|
+
if (config.elixirJobs && config.elixirJobs !== "none") configDisplay.push(`${pc.blue("Elixir Jobs:")} ${String(config.elixirJobs)}`);
|
|
7432
|
+
if (config.elixirValidation && config.elixirValidation !== "none") configDisplay.push(`${pc.blue("Elixir Validation:")} ${String(config.elixirValidation)}`);
|
|
7433
|
+
if (config.elixirHttp && config.elixirHttp !== "none") configDisplay.push(`${pc.blue("Elixir HTTP:")} ${String(config.elixirHttp)}`);
|
|
7434
|
+
if (config.elixirJson && config.elixirJson !== "none") configDisplay.push(`${pc.blue("Elixir JSON:")} ${String(config.elixirJson)}`);
|
|
7435
|
+
if (config.elixirCaching && config.elixirCaching !== "none") configDisplay.push(`${pc.blue("Elixir Caching:")} ${String(config.elixirCaching)}`);
|
|
7436
|
+
if (config.elixirObservability && config.elixirObservability !== "none") configDisplay.push(`${pc.blue("Elixir Observability:")} ${String(config.elixirObservability)}`);
|
|
7437
|
+
if (config.elixirTesting && config.elixirTesting !== "none") configDisplay.push(`${pc.blue("Elixir Testing:")} ${String(config.elixirTesting)}`);
|
|
7438
|
+
if (config.elixirQuality && config.elixirQuality !== "none") configDisplay.push(`${pc.blue("Elixir Quality:")} ${String(config.elixirQuality)}`);
|
|
7439
|
+
if (config.elixirDeploy && config.elixirDeploy !== "none") configDisplay.push(`${pc.blue("Elixir Deploy:")} ${String(config.elixirDeploy)}`);
|
|
7440
|
+
}
|
|
7049
7441
|
if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
|
|
7050
7442
|
if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
|
|
7051
7443
|
if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
|
|
@@ -7089,6 +7481,10 @@ function displayConfig(config) {
|
|
|
7089
7481
|
if (config.dbSetup !== void 0) configDisplay.push(`${pc.blue("Database Setup:")} ${String(config.dbSetup)}`);
|
|
7090
7482
|
if (config.webDeploy !== void 0) configDisplay.push(`${pc.blue("Web Deployment:")} ${String(config.webDeploy)}`);
|
|
7091
7483
|
if (config.serverDeploy !== void 0) configDisplay.push(`${pc.blue("Server Deployment:")} ${String(config.serverDeploy)}`);
|
|
7484
|
+
if (config.stackParts?.length) {
|
|
7485
|
+
const stackParts = config.stackParts.filter((part) => part.source !== "provided").map((part) => formatStackPartSpec(part, config.stackParts ?? []));
|
|
7486
|
+
if (stackParts.length > 0) configDisplay.push(`${pc.blue("Stack Parts:")} ${stackParts.join(", ")}`);
|
|
7487
|
+
}
|
|
7092
7488
|
if (configDisplay.length === 0) return pc.yellow("No configuration selected.");
|
|
7093
7489
|
return configDisplay.join("\n");
|
|
7094
7490
|
}
|
|
@@ -7117,6 +7513,103 @@ function appendCommonFlags(flags, config) {
|
|
|
7117
7513
|
if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
|
|
7118
7514
|
flags.push(config.install ? "--install" : "--no-install");
|
|
7119
7515
|
}
|
|
7516
|
+
function hasGraphPrimaryPart(config, role, ecosystem) {
|
|
7517
|
+
return config.stackParts?.some((part) => part.source !== "provided" && part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
|
|
7518
|
+
}
|
|
7519
|
+
function appendChangedStringFlag(flags, flag, value, defaultValue) {
|
|
7520
|
+
if (value !== defaultValue) flags.push(`--${flag} ${value}`);
|
|
7521
|
+
}
|
|
7522
|
+
function appendChangedArrayFlag(flags, flag, values, defaultValues) {
|
|
7523
|
+
if (values.length !== defaultValues.length || values.some((value, index) => value !== defaultValues[index])) flags.push(formatArrayFlag(flag, values));
|
|
7524
|
+
}
|
|
7525
|
+
function appendGraphExtraFlags(flags, config) {
|
|
7526
|
+
appendChangedArrayFlag(flags, "addons", config.addons, ["turborepo"]);
|
|
7527
|
+
appendChangedArrayFlag(flags, "examples", config.examples, []);
|
|
7528
|
+
appendChangedStringFlag(flags, "db-setup", config.dbSetup, "none");
|
|
7529
|
+
appendChangedStringFlag(flags, "web-deploy", config.webDeploy, "none");
|
|
7530
|
+
appendChangedStringFlag(flags, "server-deploy", config.serverDeploy, "none");
|
|
7531
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript")) {
|
|
7532
|
+
appendChangedStringFlag(flags, "css-framework", config.cssFramework, "tailwind");
|
|
7533
|
+
appendChangedStringFlag(flags, "ui-library", config.uiLibrary, "shadcn-ui");
|
|
7534
|
+
if (config.uiLibrary === "shadcn-ui") {
|
|
7535
|
+
appendChangedStringFlag(flags, "shadcn-base", config.shadcnBase ?? "radix", "radix");
|
|
7536
|
+
appendChangedStringFlag(flags, "shadcn-style", config.shadcnStyle ?? "nova", "nova");
|
|
7537
|
+
appendChangedStringFlag(flags, "shadcn-icon-library", config.shadcnIconLibrary ?? "lucide", "lucide");
|
|
7538
|
+
appendChangedStringFlag(flags, "shadcn-color-theme", config.shadcnColorTheme ?? "neutral", "neutral");
|
|
7539
|
+
appendChangedStringFlag(flags, "shadcn-base-color", config.shadcnBaseColor ?? "neutral", "neutral");
|
|
7540
|
+
appendChangedStringFlag(flags, "shadcn-font", config.shadcnFont ?? "inter", "inter");
|
|
7541
|
+
appendChangedStringFlag(flags, "shadcn-radius", config.shadcnRadius ?? "default", "default");
|
|
7542
|
+
}
|
|
7543
|
+
appendChangedStringFlag(flags, "state-management", config.stateManagement, "none");
|
|
7544
|
+
appendChangedStringFlag(flags, "forms", config.forms, "react-hook-form");
|
|
7545
|
+
appendChangedStringFlag(flags, "validation", config.validation, "zod");
|
|
7546
|
+
appendChangedStringFlag(flags, "testing", config.testing, "vitest");
|
|
7547
|
+
appendChangedStringFlag(flags, "animation", config.animation, "none");
|
|
7548
|
+
}
|
|
7549
|
+
if (hasGraphPrimaryPart(config, "frontend", "typescript") || hasGraphPrimaryPart(config, "backend", "typescript")) {
|
|
7550
|
+
appendChangedStringFlag(flags, "payments", config.payments, "none");
|
|
7551
|
+
appendChangedStringFlag(flags, "email", config.email, "none");
|
|
7552
|
+
appendChangedStringFlag(flags, "file-upload", config.fileUpload, "none");
|
|
7553
|
+
appendChangedStringFlag(flags, "effect", config.effect, "none");
|
|
7554
|
+
appendChangedStringFlag(flags, "ai", config.ai, "none");
|
|
7555
|
+
appendChangedStringFlag(flags, "realtime", config.realtime, "none");
|
|
7556
|
+
appendChangedStringFlag(flags, "job-queue", config.jobQueue, "none");
|
|
7557
|
+
appendChangedStringFlag(flags, "logging", config.logging, "none");
|
|
7558
|
+
appendChangedStringFlag(flags, "observability", config.observability, "none");
|
|
7559
|
+
appendChangedStringFlag(flags, "feature-flags", config.featureFlags, "none");
|
|
7560
|
+
appendChangedStringFlag(flags, "caching", config.caching, "none");
|
|
7561
|
+
appendChangedStringFlag(flags, "i18n", config.i18n, "none");
|
|
7562
|
+
appendChangedStringFlag(flags, "cms", config.cms, "none");
|
|
7563
|
+
appendChangedStringFlag(flags, "search", config.search, "none");
|
|
7564
|
+
appendChangedStringFlag(flags, "file-storage", config.fileStorage, "none");
|
|
7565
|
+
}
|
|
7566
|
+
if (hasGraphPrimaryPart(config, "mobile")) {
|
|
7567
|
+
appendChangedStringFlag(flags, "mobile-navigation", config.mobileNavigation, "expo-router");
|
|
7568
|
+
appendChangedStringFlag(flags, "mobile-ui", config.mobileUI, "none");
|
|
7569
|
+
appendChangedStringFlag(flags, "mobile-storage", config.mobileStorage, "none");
|
|
7570
|
+
appendChangedStringFlag(flags, "mobile-testing", config.mobileTesting, "none");
|
|
7571
|
+
appendChangedStringFlag(flags, "mobile-push", config.mobilePush, "none");
|
|
7572
|
+
appendChangedStringFlag(flags, "mobile-ota", config.mobileOTA, "none");
|
|
7573
|
+
appendChangedStringFlag(flags, "mobile-deep-linking", config.mobileDeepLinking, "none");
|
|
7574
|
+
}
|
|
7575
|
+
if (hasGraphPrimaryPart(config, "frontend", "rust")) appendChangedStringFlag(flags, "rust-frontend", config.rustFrontend, "none");
|
|
7576
|
+
if (hasGraphPrimaryPart(config, "backend", "rust")) {
|
|
7577
|
+
appendChangedStringFlag(flags, "rust-cli", config.rustCli, "none");
|
|
7578
|
+
appendChangedArrayFlag(flags, "rust-libraries", config.rustLibraries, []);
|
|
7579
|
+
appendChangedStringFlag(flags, "rust-logging", config.rustLogging, "tracing");
|
|
7580
|
+
appendChangedStringFlag(flags, "rust-error-handling", config.rustErrorHandling, "anyhow-thiserror");
|
|
7581
|
+
appendChangedStringFlag(flags, "rust-caching", config.rustCaching, "none");
|
|
7582
|
+
}
|
|
7583
|
+
if (hasGraphPrimaryPart(config, "backend", "python")) {
|
|
7584
|
+
appendChangedStringFlag(flags, "python-validation", config.pythonValidation, "none");
|
|
7585
|
+
appendChangedArrayFlag(flags, "python-ai", config.pythonAi, []);
|
|
7586
|
+
appendChangedStringFlag(flags, "python-task-queue", config.pythonTaskQueue, "none");
|
|
7587
|
+
appendChangedStringFlag(flags, "python-graphql", config.pythonGraphql, "none");
|
|
7588
|
+
appendChangedStringFlag(flags, "python-quality", config.pythonQuality, "none");
|
|
7589
|
+
}
|
|
7590
|
+
if (hasGraphPrimaryPart(config, "backend", "go")) {
|
|
7591
|
+
appendChangedStringFlag(flags, "go-cli", config.goCli, "none");
|
|
7592
|
+
appendChangedStringFlag(flags, "go-logging", config.goLogging, "none");
|
|
7593
|
+
}
|
|
7594
|
+
if (hasGraphPrimaryPart(config, "backend", "java")) {
|
|
7595
|
+
appendChangedStringFlag(flags, "java-build-tool", config.javaBuildTool, "maven");
|
|
7596
|
+
appendChangedArrayFlag(flags, "java-libraries", config.javaLibraries, []);
|
|
7597
|
+
appendChangedArrayFlag(flags, "java-testing-libraries", config.javaTestingLibraries, ["junit5"]);
|
|
7598
|
+
}
|
|
7599
|
+
if (hasGraphPrimaryPart(config, "backend", "elixir")) {
|
|
7600
|
+
appendChangedStringFlag(flags, "elixir-realtime", config.elixirRealtime, "channels");
|
|
7601
|
+
appendChangedStringFlag(flags, "elixir-jobs", config.elixirJobs, "none");
|
|
7602
|
+
appendChangedStringFlag(flags, "elixir-validation", config.elixirValidation, "ecto-changesets");
|
|
7603
|
+
appendChangedStringFlag(flags, "elixir-http", config.elixirHttp, "req");
|
|
7604
|
+
appendChangedStringFlag(flags, "elixir-json", config.elixirJson, "jason");
|
|
7605
|
+
if (!hasGraphPart(config, "email", "elixir")) appendChangedStringFlag(flags, "elixir-email", config.elixirEmail, "none");
|
|
7606
|
+
appendChangedStringFlag(flags, "elixir-caching", config.elixirCaching, "none");
|
|
7607
|
+
appendChangedStringFlag(flags, "elixir-observability", config.elixirObservability, "telemetry");
|
|
7608
|
+
appendChangedStringFlag(flags, "elixir-testing", config.elixirTesting, "ex_unit");
|
|
7609
|
+
appendChangedStringFlag(flags, "elixir-quality", config.elixirQuality, "credo");
|
|
7610
|
+
appendChangedStringFlag(flags, "elixir-deploy", config.elixirDeploy, "none");
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7120
7613
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
7121
7614
|
flags.push(`--email ${config.email}`);
|
|
7122
7615
|
flags.push(`--observability ${config.observability}`);
|
|
@@ -7279,6 +7772,12 @@ function getElixirFlags(config) {
|
|
|
7279
7772
|
}
|
|
7280
7773
|
function generateReproducibleCommand(config) {
|
|
7281
7774
|
let flags;
|
|
7775
|
+
if (config.stackParts && config.stackParts.length > 0) {
|
|
7776
|
+
flags = config.stackParts.filter((part) => part.source !== "provided").map((part) => `--part ${(0, types_exports.formatStackPartSpec)(part, config.stackParts ?? [])}`);
|
|
7777
|
+
appendGraphExtraFlags(flags, config);
|
|
7778
|
+
appendCommonFlags(flags, config);
|
|
7779
|
+
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7780
|
+
}
|
|
7282
7781
|
switch (config.ecosystem) {
|
|
7283
7782
|
case "react-native":
|
|
7284
7783
|
flags = getReactNativeFlags(config);
|
|
@@ -7306,6 +7805,98 @@ function generateReproducibleCommand(config) {
|
|
|
7306
7805
|
return `${getBaseCommand(config.packageManager)}${config.relativePath ? ` ${config.relativePath}` : ""} ${flags.join(" ")}`;
|
|
7307
7806
|
}
|
|
7308
7807
|
|
|
7808
|
+
//#endregion
|
|
7809
|
+
//#region src/utils/generated-checks.ts
|
|
7810
|
+
function getGraphTarget(config) {
|
|
7811
|
+
const backend = getPrimaryGraphPart(config, "backend");
|
|
7812
|
+
if (!backend || backend.ecosystem === "typescript" || backend.ecosystem === "react-native" || backend.ecosystem === "universal") return null;
|
|
7813
|
+
return {
|
|
7814
|
+
ecosystem: backend.ecosystem,
|
|
7815
|
+
projectDir: path.join(config.projectDir, backend.targetPath ?? "apps/server")
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
function getSingleEcosystemTarget(config) {
|
|
7819
|
+
if (config.ecosystem === "typescript" || config.ecosystem === "react-native" || config.ecosystem === "java") return null;
|
|
7820
|
+
return {
|
|
7821
|
+
ecosystem: config.ecosystem,
|
|
7822
|
+
projectDir: config.projectDir
|
|
7823
|
+
};
|
|
7824
|
+
}
|
|
7825
|
+
async function runCommand(cwd, command, args) {
|
|
7826
|
+
await $({
|
|
7827
|
+
cwd,
|
|
7828
|
+
stdout: "inherit",
|
|
7829
|
+
stderr: "inherit"
|
|
7830
|
+
})`${command} ${args}`;
|
|
7831
|
+
}
|
|
7832
|
+
async function verifyTarget(target) {
|
|
7833
|
+
const s = spinner();
|
|
7834
|
+
const cwd = target.projectDir;
|
|
7835
|
+
switch (target.ecosystem) {
|
|
7836
|
+
case "go":
|
|
7837
|
+
s.start("Verifying generated Go server...");
|
|
7838
|
+
await runCommand(cwd, "go", ["mod", "tidy"]);
|
|
7839
|
+
await runCommand(cwd, "go", ["test", "./..."]);
|
|
7840
|
+
s.stop("Generated Go server checks passed");
|
|
7841
|
+
return;
|
|
7842
|
+
case "rust":
|
|
7843
|
+
s.start("Verifying generated Rust server...");
|
|
7844
|
+
await runCommand(cwd, "cargo", ["check"]);
|
|
7845
|
+
s.stop("Generated Rust server checks passed");
|
|
7846
|
+
return;
|
|
7847
|
+
case "python":
|
|
7848
|
+
s.start("Verifying generated Python server...");
|
|
7849
|
+
await runCommand(cwd, "uv", ["sync"]);
|
|
7850
|
+
await runCommand(cwd, "uv", [
|
|
7851
|
+
"run",
|
|
7852
|
+
"ruff",
|
|
7853
|
+
"check",
|
|
7854
|
+
"."
|
|
7855
|
+
]);
|
|
7856
|
+
s.stop("Generated Python server checks passed");
|
|
7857
|
+
return;
|
|
7858
|
+
case "elixir":
|
|
7859
|
+
if (!await commandExists("mix")) {
|
|
7860
|
+
log.warn(pc.yellow("Skipping Elixir verification because mix is not on PATH"));
|
|
7861
|
+
return;
|
|
7862
|
+
}
|
|
7863
|
+
s.start("Verifying generated Elixir server...");
|
|
7864
|
+
await runCommand(cwd, "mix", ["deps.get"]);
|
|
7865
|
+
await runCommand(cwd, "mix", ["compile"]);
|
|
7866
|
+
s.stop("Generated Elixir server checks passed");
|
|
7867
|
+
return;
|
|
7868
|
+
default: log.warn(pc.yellow(`No generated checks are configured for ${target.ecosystem}`));
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
async function runGeneratedChecks(config) {
|
|
7872
|
+
const target = getGraphTarget(config) ?? getSingleEcosystemTarget(config);
|
|
7873
|
+
if (!target) {
|
|
7874
|
+
log.warn(pc.yellow("No generated checks are configured for this stack"));
|
|
7875
|
+
return;
|
|
7876
|
+
}
|
|
7877
|
+
await verifyTarget(target);
|
|
7878
|
+
}
|
|
7879
|
+
|
|
7880
|
+
//#endregion
|
|
7881
|
+
//#region src/utils/preflight-display.ts
|
|
7882
|
+
function displayPreflightWarnings({ warnings }) {
|
|
7883
|
+
if (warnings.length === 0) return;
|
|
7884
|
+
const count = warnings.length;
|
|
7885
|
+
const lines = [pc.bold(pc.yellow(`${count} feature${count > 1 ? "s" : ""} will not generate templates:`)), ""];
|
|
7886
|
+
warnings.forEach((w, i) => {
|
|
7887
|
+
const selected = Array.isArray(w.selectedValue) ? w.selectedValue.join(", ") : w.selectedValue;
|
|
7888
|
+
lines.push(` ${pc.yellow(`${i + 1}.`)} ${pc.bold(w.featureDisplayName)} ${pc.dim(`(${selected})`)}`);
|
|
7889
|
+
lines.push(` ${w.reason}`);
|
|
7890
|
+
w.suggestions.forEach((s) => lines.push(` ${pc.green("•")} ${s}`));
|
|
7891
|
+
if (i < count - 1) lines.push("");
|
|
7892
|
+
});
|
|
7893
|
+
consola.box({
|
|
7894
|
+
title: pc.yellow("Pre-flight Check"),
|
|
7895
|
+
message: lines.join("\n"),
|
|
7896
|
+
style: { borderColor: "yellow" }
|
|
7897
|
+
});
|
|
7898
|
+
}
|
|
7899
|
+
|
|
7309
7900
|
//#endregion
|
|
7310
7901
|
//#region src/utils/project-directory.ts
|
|
7311
7902
|
async function handleDirectoryConflict(currentPathInput) {
|
|
@@ -7656,6 +8247,10 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7656
8247
|
const db = cfg.database;
|
|
7657
8248
|
const orm = cfg.orm;
|
|
7658
8249
|
const has = (k) => flags ? flags.has(k) : true;
|
|
8250
|
+
const hasGraphOrm = cfg.stackParts?.some((part) => part.role === "orm" && part.source !== "provided");
|
|
8251
|
+
const ecosystemOrm = getEcosystemOrm(cfg);
|
|
8252
|
+
const hasEcosystemOrm = ecosystemOrm !== void 0 && ecosystemOrm !== "none";
|
|
8253
|
+
const isNonTypeScriptSqliteDefault = cfg.ecosystem !== void 0 && cfg.ecosystem !== "typescript" && cfg.ecosystem !== "react-native" && db === "sqlite" && !hasEcosystemOrm;
|
|
7659
8254
|
if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") incompatibilityError({
|
|
7660
8255
|
message: "Mongoose ORM requires MongoDB database.",
|
|
7661
8256
|
provided: {
|
|
@@ -7712,7 +8307,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7712
8307
|
},
|
|
7713
8308
|
suggestions: ["Use --orm mongoose", "Use --orm prisma"]
|
|
7714
8309
|
});
|
|
7715
|
-
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none") missingRequirementError({
|
|
8310
|
+
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none" && !hasGraphOrm && !hasEcosystemOrm && !isNonTypeScriptSqliteDefault) missingRequirementError({
|
|
7716
8311
|
message: "Database selection requires an ORM.",
|
|
7717
8312
|
provided: {
|
|
7718
8313
|
database: db,
|
|
@@ -7754,6 +8349,26 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
7754
8349
|
]
|
|
7755
8350
|
});
|
|
7756
8351
|
}
|
|
8352
|
+
function getEcosystemOrm(cfg) {
|
|
8353
|
+
switch (cfg.ecosystem) {
|
|
8354
|
+
case "rust": return cfg.rustOrm;
|
|
8355
|
+
case "python": return cfg.pythonOrm;
|
|
8356
|
+
case "go": return cfg.goOrm;
|
|
8357
|
+
case "java": return cfg.javaOrm;
|
|
8358
|
+
case "elixir": return cfg.elixirOrm;
|
|
8359
|
+
default: return;
|
|
8360
|
+
}
|
|
8361
|
+
}
|
|
8362
|
+
function getEcosystemBackend(cfg) {
|
|
8363
|
+
switch (cfg.ecosystem) {
|
|
8364
|
+
case "rust": return cfg.rustWebFramework && cfg.rustWebFramework !== "none" ? cfg.rustWebFramework : void 0;
|
|
8365
|
+
case "python": return cfg.pythonWebFramework && cfg.pythonWebFramework !== "none" ? cfg.pythonWebFramework : void 0;
|
|
8366
|
+
case "go": return cfg.goWebFramework && cfg.goWebFramework !== "none" ? cfg.goWebFramework : void 0;
|
|
8367
|
+
case "java": return cfg.javaWebFramework && cfg.javaWebFramework !== "none" ? cfg.javaWebFramework : void 0;
|
|
8368
|
+
case "elixir": return cfg.elixirWebFramework && cfg.elixirWebFramework !== "none" ? cfg.elixirWebFramework : void 0;
|
|
8369
|
+
default: return;
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
7757
8372
|
function validateDatabaseSetup(config, providedFlags) {
|
|
7758
8373
|
const { dbSetup, database, runtime } = config;
|
|
7759
8374
|
if (providedFlags.has("dbSetup") && providedFlags.has("database") && dbSetup && dbSetup !== "none" && database === "none") exitWithError("Database setup requires a database. Please choose a database or set '--db-setup none'.");
|
|
@@ -7879,7 +8494,9 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
7879
8494
|
}
|
|
7880
8495
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
7881
8496
|
const { backend } = config;
|
|
7882
|
-
|
|
8497
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8498
|
+
const hasEcosystemBackend = getEcosystemBackend(config) !== void 0;
|
|
8499
|
+
if (backend !== "none" || hasGraphBackend || hasEcosystemBackend) return;
|
|
7883
8500
|
const has = (k) => providedFlags.has(k);
|
|
7884
8501
|
if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
|
|
7885
8502
|
if (has("database") && config.database !== "none") exitWithError("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
|
|
@@ -8223,6 +8840,10 @@ function validatePythonApiConstraints(config) {
|
|
|
8223
8840
|
});
|
|
8224
8841
|
}
|
|
8225
8842
|
function validateFullConfig(config, providedFlags, options) {
|
|
8843
|
+
if (config.stackParts && !options.yolo) {
|
|
8844
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8845
|
+
if (graphValidation.issues.length > 0) exitWithError(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8846
|
+
}
|
|
8226
8847
|
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
8227
8848
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
8228
8849
|
validateDatabaseSetup(config, providedFlags);
|
|
@@ -8241,7 +8862,8 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8241
8862
|
validateSearchConstraints(config);
|
|
8242
8863
|
validateJavaConstraints(config, providedFlags);
|
|
8243
8864
|
validateElixirConstraints(config);
|
|
8244
|
-
|
|
8865
|
+
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
8866
|
+
if (!(providedFlags.has("serverDeploy") && !options.yes && !options.part?.length && options.ecosystem === void 0 && options.backend === void 0 && config.stackParts === void 0)) validateServerDeployRequiresBackend(config.serverDeploy, config.backend, Boolean(hasGraphBackend));
|
|
8245
8867
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
8246
8868
|
validateWorkersCompatibility(providedFlags, options, config);
|
|
8247
8869
|
if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
|
|
@@ -8272,6 +8894,10 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
8272
8894
|
}
|
|
8273
8895
|
function validateConfigForProgrammaticUse(config) {
|
|
8274
8896
|
try {
|
|
8897
|
+
if (config.stackParts) {
|
|
8898
|
+
const graphValidation = (0, types_exports.validateStackParts)(config.stackParts);
|
|
8899
|
+
if (graphValidation.issues.length > 0) throw new Error(graphValidation.issues.map((issue) => issue.message).join("\n"));
|
|
8900
|
+
}
|
|
8275
8901
|
validateEcosystemAuthCompatibility(config);
|
|
8276
8902
|
validateDatabaseOrmAuth(config);
|
|
8277
8903
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
@@ -8363,7 +8989,14 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
8363
8989
|
return config;
|
|
8364
8990
|
}
|
|
8365
8991
|
function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
8366
|
-
if (!options.yolo)
|
|
8992
|
+
if (!options.yolo) {
|
|
8993
|
+
validateYesFlagCombination(options, getProvidedFlags(options));
|
|
8994
|
+
try {
|
|
8995
|
+
validateArrayOptions(options);
|
|
8996
|
+
} catch (error) {
|
|
8997
|
+
exitWithError(error instanceof Error ? error.message : String(error));
|
|
8998
|
+
}
|
|
8999
|
+
}
|
|
8367
9000
|
const config = processFlags(options, projectName);
|
|
8368
9001
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
8369
9002
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
@@ -8375,26 +9008,6 @@ function validateConfigCompatibility(config, providedFlags, options) {
|
|
|
8375
9008
|
else validateConfigForProgrammaticUse(config);
|
|
8376
9009
|
}
|
|
8377
9010
|
|
|
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
9011
|
//#endregion
|
|
8399
9012
|
//#region src/utils/file-formatter.ts
|
|
8400
9013
|
const formatOptions = {
|
|
@@ -9666,7 +10279,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
9666
10279
|
const hasHusky = addons?.includes("husky");
|
|
9667
10280
|
const hasLefthook = addons?.includes("lefthook");
|
|
9668
10281
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
|
|
9669
|
-
const
|
|
10282
|
+
const graphOrmPart = getGraphPart(config, "orm");
|
|
10283
|
+
const databaseInstructions = !isConvex && database !== "none" && !graphOrmPart ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
10284
|
+
const graphDatabaseInstructions = !isConvex && database !== "none" && graphOrmPart ? getGraphDatabaseInstructions(database, graphOrmPart.ecosystem, graphOrmPart.toolId) : "";
|
|
9670
10285
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
9671
10286
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
9672
10287
|
const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
|
|
@@ -9680,14 +10295,17 @@ async function displayPostInstallInstructions(config) {
|
|
|
9680
10295
|
const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
|
|
9681
10296
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
9682
10297
|
const vercelDeployInstructions = getVercelDeployInstructions(webDeploy, serverDeploy, backend);
|
|
10298
|
+
const graphBackendDeployInstructions = getGraphBackendDeployInstructions(config);
|
|
9683
10299
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
9684
10300
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
9685
10301
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
9686
|
-
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
10302
|
+
const noOrmWarning = !isConvex && database !== "none" && orm === "none" && !hasGraphPart(config, "orm") ? getNoOrmWarning() : "";
|
|
9687
10303
|
const hasFresh = frontend?.includes("fresh");
|
|
9688
10304
|
const webPort = String(getLocalWebDevPort(frontend ?? []));
|
|
9689
10305
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
9690
|
-
|
|
10306
|
+
const graphSummary = getGraphSummary(config);
|
|
10307
|
+
let output = graphSummary ? `${pc.bold("Generated:")} ${graphSummary}\n\n` : "";
|
|
10308
|
+
output += `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
9691
10309
|
let stepCounter = 2;
|
|
9692
10310
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
9693
10311
|
if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH.\n Install: ${pc.underline("https://docs.deno.com/runtime/getting_started/installation/")}\n`;
|
|
@@ -9704,13 +10322,14 @@ async function displayPostInstallInstructions(config) {
|
|
|
9704
10322
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
9705
10323
|
}
|
|
9706
10324
|
}
|
|
9707
|
-
const
|
|
10325
|
+
const graphBackendUrl = getGraphBackendUrl(config);
|
|
10326
|
+
const hasStandaloneBackend = backend !== "none" || Boolean(graphBackendUrl);
|
|
9708
10327
|
if (hasWeb || hasStandaloneBackend || addons?.includes("starlight") || addons?.includes("fumadocs")) {
|
|
9709
10328
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
9710
10329
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
9711
10330
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
9712
10331
|
if (!isConvex && !isBackendSelf && hasStandaloneBackend) {
|
|
9713
|
-
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
10332
|
+
output += `${pc.cyan("•")} Backend API: ${graphBackendUrl ?? "http://localhost:3000"}\n`;
|
|
9714
10333
|
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api-reference\n`;
|
|
9715
10334
|
}
|
|
9716
10335
|
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
@@ -9719,6 +10338,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9719
10338
|
}
|
|
9720
10339
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
9721
10340
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
10341
|
+
if (graphDatabaseInstructions) output += `\n${graphDatabaseInstructions.trim()}\n`;
|
|
9722
10342
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
9723
10343
|
if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
|
|
9724
10344
|
if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
|
|
@@ -9726,6 +10346,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9726
10346
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
9727
10347
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
9728
10348
|
if (vercelDeployInstructions) output += `\n${vercelDeployInstructions.trim()}\n`;
|
|
10349
|
+
if (graphBackendDeployInstructions) output += `\n${graphBackendDeployInstructions.trim()}\n`;
|
|
9729
10350
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
9730
10351
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
9731
10352
|
if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
|
|
@@ -9817,6 +10438,9 @@ function getStarlightInstructions(runCmd) {
|
|
|
9817
10438
|
function getNoOrmWarning() {
|
|
9818
10439
|
return `\n${pc.yellow("WARNING:")} Database selected without an ORM. Features requiring\n database access (e.g., examples, auth) need manual setup.`;
|
|
9819
10440
|
}
|
|
10441
|
+
function getGraphDatabaseInstructions(database, ecosystem, ormTool) {
|
|
10442
|
+
return `${pc.bold("Database setup:")}\n${pc.cyan("•")} Database: ${database}\n${pc.cyan("•")} ORM: ${ecosystem}:${ormTool}\n${pc.cyan("•")} Configure ${pc.white("DATABASE_URL")} in ${pc.white("apps/server/.env")}`;
|
|
10443
|
+
}
|
|
9820
10444
|
function getBunWebNativeWarning() {
|
|
9821
10445
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
9822
10446
|
}
|
|
@@ -10075,14 +10699,14 @@ function displayJavaInstructions(config) {
|
|
|
10075
10699
|
}
|
|
10076
10700
|
const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
|
|
10077
10701
|
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;
|
|
10702
|
+
const runCommand$1 = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : isQuarkus ? javaBuildTool === "gradle" ? `${buildToolCommand} quarkusDev` : `${buildToolCommand} quarkus:dev` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
|
|
10079
10703
|
const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
|
|
10080
10704
|
const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
|
|
10081
10705
|
const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
|
|
10082
10706
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10083
10707
|
let stepCounter = 2;
|
|
10084
10708
|
if (!depsInstalled && buildToolCommand && effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan(`${stepCounter++}.`)} ${buildToolCommand} test\n`;
|
|
10085
|
-
if (runCommand) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10709
|
+
if (runCommand$1) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10086
10710
|
else if (sourceCompileCommand && sourceRunCommand) {
|
|
10087
10711
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceCompileCommand}\n`;
|
|
10088
10712
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
|
|
@@ -10130,9 +10754,9 @@ function displayJavaInstructions(config) {
|
|
|
10130
10754
|
output += `${pc.cyan("•")} Testing: ${testingList}\n`;
|
|
10131
10755
|
}
|
|
10132
10756
|
output += `\n${pc.bold("Common Java commands:")}\n`;
|
|
10133
|
-
if (buildToolCommand && runCommand && packageCommand) {
|
|
10757
|
+
if (buildToolCommand && runCommand$1 && packageCommand) {
|
|
10134
10758
|
if (effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan("•")} Test: ${buildToolCommand} test\n`;
|
|
10135
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10759
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10136
10760
|
output += `${pc.cyan("•")} Package: ${packageCommand}\n`;
|
|
10137
10761
|
} else if (sourceCompileCommand && sourceRunCommand) {
|
|
10138
10762
|
output += `${pc.cyan("•")} Compile: ${sourceCompileCommand}\n`;
|
|
@@ -10150,14 +10774,14 @@ function displayJavaInstructions(config) {
|
|
|
10150
10774
|
function displayPythonInstructions(config) {
|
|
10151
10775
|
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
|
|
10152
10776
|
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";
|
|
10777
|
+
let runCommand$1 = "uv run uvicorn app.main:app --reload";
|
|
10778
|
+
if (pythonWebFramework === "django") runCommand$1 = "uv run python manage.py runserver";
|
|
10779
|
+
else if (pythonWebFramework === "flask") runCommand$1 = "uv run flask --app app.main run --reload";
|
|
10780
|
+
else if (pythonWebFramework === "litestar") runCommand$1 = "litestar --app src.app.main:app run --reload --port 3001";
|
|
10157
10781
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
10158
10782
|
let stepCounter = 2;
|
|
10159
10783
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} uv sync\n`;
|
|
10160
|
-
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
|
|
10784
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand$1}\n`;
|
|
10161
10785
|
output += `\n${pc.bold("Your Python project includes:")}\n`;
|
|
10162
10786
|
if (pythonWebFramework && pythonWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
|
|
10163
10787
|
fastapi: "FastAPI",
|
|
@@ -10195,7 +10819,7 @@ function displayPythonInstructions(config) {
|
|
|
10195
10819
|
}[pythonQuality] || pythonQuality}\n`;
|
|
10196
10820
|
output += `\n${pc.bold("Common Python commands:")}\n`;
|
|
10197
10821
|
output += `${pc.cyan("•")} Install: uv sync\n`;
|
|
10198
|
-
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
10822
|
+
output += `${pc.cyan("•")} Run: ${runCommand$1}\n`;
|
|
10199
10823
|
output += `${pc.cyan("•")} Test: uv run pytest\n`;
|
|
10200
10824
|
if (pythonQuality === "ruff") {
|
|
10201
10825
|
output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
|
|
@@ -10357,7 +10981,7 @@ function getYesBaseConfig(flagConfig) {
|
|
|
10357
10981
|
};
|
|
10358
10982
|
}
|
|
10359
10983
|
function shouldPromptForVersionChannel(input) {
|
|
10360
|
-
if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
|
|
10984
|
+
if (input.yes || input.part?.length || input.versionChannel !== void 0 || isSilent()) return false;
|
|
10361
10985
|
return canPromptInteractively();
|
|
10362
10986
|
}
|
|
10363
10987
|
async function createProjectHandler(input, options = {}) {
|
|
@@ -10542,7 +11166,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10542
11166
|
}
|
|
10543
11167
|
}
|
|
10544
11168
|
let config;
|
|
10545
|
-
if (cliInput.yes) {
|
|
11169
|
+
if (cliInput.yes || cliInput.part?.length) {
|
|
10546
11170
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
10547
11171
|
config = {
|
|
10548
11172
|
...getYesBaseConfig(flagConfig),
|
|
@@ -10571,6 +11195,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10571
11195
|
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, currentPathInput),
|
|
10572
11196
|
versionChannel
|
|
10573
11197
|
};
|
|
11198
|
+
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
10574
11199
|
}
|
|
10575
11200
|
const preflight = validatePreflightConfig(config);
|
|
10576
11201
|
if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
|
|
@@ -10617,6 +11242,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
10617
11242
|
};
|
|
10618
11243
|
}
|
|
10619
11244
|
await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
|
|
11245
|
+
if (cliInput.verify ?? input.verify) await runGeneratedChecks(config);
|
|
10620
11246
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
10621
11247
|
if (!isSilent()) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
10622
11248
|
await trackProjectCreation(config, input.disableAnalytics);
|
|
@@ -10946,113 +11572,116 @@ async function createVirtual(options) {
|
|
|
10946
11572
|
const isReactNative = ecosystem === "react-native";
|
|
10947
11573
|
const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
|
|
10948
11574
|
const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
|
|
11575
|
+
const config = {
|
|
11576
|
+
ecosystem,
|
|
11577
|
+
projectName: options.projectName || "my-project",
|
|
11578
|
+
projectDir: "/virtual",
|
|
11579
|
+
relativePath: "./virtual",
|
|
11580
|
+
database: options.database || "none",
|
|
11581
|
+
orm: options.orm || "none",
|
|
11582
|
+
backend: options.backend || (isReactNative ? "none" : "hono"),
|
|
11583
|
+
runtime: options.runtime || (isReactNative ? "none" : "bun"),
|
|
11584
|
+
frontend,
|
|
11585
|
+
addons: options.addons || [],
|
|
11586
|
+
examples: options.examples || [],
|
|
11587
|
+
auth: options.auth || "none",
|
|
11588
|
+
payments: options.payments || "none",
|
|
11589
|
+
email: options.email || "none",
|
|
11590
|
+
fileUpload: options.fileUpload || "none",
|
|
11591
|
+
effect: options.effect || "none",
|
|
11592
|
+
git: options.git ?? false,
|
|
11593
|
+
packageManager: options.packageManager || "bun",
|
|
11594
|
+
versionChannel: options.versionChannel || "stable",
|
|
11595
|
+
install: false,
|
|
11596
|
+
dbSetup: options.dbSetup || "none",
|
|
11597
|
+
api: options.api || (isReactNative ? "none" : "trpc"),
|
|
11598
|
+
webDeploy: options.webDeploy || "none",
|
|
11599
|
+
serverDeploy: options.serverDeploy || "none",
|
|
11600
|
+
astroIntegration: options.astroIntegration || "none",
|
|
11601
|
+
cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
|
|
11602
|
+
uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
|
|
11603
|
+
shadcnBase: options.shadcnBase ?? "radix",
|
|
11604
|
+
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
11605
|
+
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
11606
|
+
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
11607
|
+
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
11608
|
+
shadcnFont: options.shadcnFont ?? "inter",
|
|
11609
|
+
shadcnRadius: options.shadcnRadius ?? "default",
|
|
11610
|
+
ai: options.ai || "none",
|
|
11611
|
+
stateManagement: options.stateManagement || "none",
|
|
11612
|
+
forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
|
|
11613
|
+
testing: options.testing || (isReactNative ? "none" : "vitest"),
|
|
11614
|
+
validation: options.validation || "zod",
|
|
11615
|
+
realtime: options.realtime || "none",
|
|
11616
|
+
jobQueue: options.jobQueue || "none",
|
|
11617
|
+
animation: options.animation || "none",
|
|
11618
|
+
logging: options.logging || "none",
|
|
11619
|
+
observability: options.observability || "none",
|
|
11620
|
+
featureFlags: options.featureFlags || "none",
|
|
11621
|
+
analytics: options.analytics || "none",
|
|
11622
|
+
mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
|
|
11623
|
+
mobileUI: options.mobileUI || "none",
|
|
11624
|
+
mobileStorage: options.mobileStorage || "none",
|
|
11625
|
+
mobileTesting: options.mobileTesting || "none",
|
|
11626
|
+
mobilePush: options.mobilePush || "none",
|
|
11627
|
+
mobileOTA: options.mobileOTA || "none",
|
|
11628
|
+
mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
|
|
11629
|
+
cms: options.cms || "none",
|
|
11630
|
+
caching: options.caching || "none",
|
|
11631
|
+
i18n: options.i18n || "none",
|
|
11632
|
+
search: options.search || "none",
|
|
11633
|
+
fileStorage: options.fileStorage || "none",
|
|
11634
|
+
rustWebFramework: options.rustWebFramework || "none",
|
|
11635
|
+
rustFrontend: options.rustFrontend || "none",
|
|
11636
|
+
rustOrm: options.rustOrm || "none",
|
|
11637
|
+
rustApi: options.rustApi || "none",
|
|
11638
|
+
rustCli: options.rustCli || "none",
|
|
11639
|
+
rustLibraries: options.rustLibraries || [],
|
|
11640
|
+
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
11641
|
+
rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
|
|
11642
|
+
rustCaching: options.rustCaching || "none",
|
|
11643
|
+
rustAuth: options.rustAuth || "none",
|
|
11644
|
+
pythonWebFramework: options.pythonWebFramework || "none",
|
|
11645
|
+
pythonOrm: options.pythonOrm || "none",
|
|
11646
|
+
pythonValidation: options.pythonValidation || "none",
|
|
11647
|
+
pythonAi: options.pythonAi || [],
|
|
11648
|
+
pythonAuth: options.pythonAuth || "none",
|
|
11649
|
+
pythonApi: options.pythonApi || "none",
|
|
11650
|
+
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
11651
|
+
pythonGraphql: options.pythonGraphql || "none",
|
|
11652
|
+
pythonQuality: options.pythonQuality || "none",
|
|
11653
|
+
goWebFramework: options.goWebFramework || "none",
|
|
11654
|
+
goOrm: options.goOrm || "none",
|
|
11655
|
+
goApi: options.goApi || "none",
|
|
11656
|
+
goCli: options.goCli || "none",
|
|
11657
|
+
goLogging: options.goLogging || "none",
|
|
11658
|
+
goAuth: options.goAuth || "none",
|
|
11659
|
+
javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
|
|
11660
|
+
javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
|
|
11661
|
+
javaOrm: options.javaOrm || "none",
|
|
11662
|
+
javaAuth: options.javaAuth || "none",
|
|
11663
|
+
javaLibraries: options.javaLibraries || [],
|
|
11664
|
+
javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
|
|
11665
|
+
elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
|
|
11666
|
+
elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
|
|
11667
|
+
elixirAuth: options.elixirAuth || "none",
|
|
11668
|
+
elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
|
|
11669
|
+
elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
|
|
11670
|
+
elixirJobs: options.elixirJobs || "none",
|
|
11671
|
+
elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
|
|
11672
|
+
elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
|
|
11673
|
+
elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
|
|
11674
|
+
elixirEmail: options.elixirEmail || "none",
|
|
11675
|
+
elixirCaching: options.elixirCaching || "none",
|
|
11676
|
+
elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
|
|
11677
|
+
elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
|
|
11678
|
+
elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
|
|
11679
|
+
elixirDeploy: options.elixirDeploy || "none",
|
|
11680
|
+
aiDocs: options.aiDocs || ["claude-md"]
|
|
11681
|
+
};
|
|
11682
|
+
if (options.stackParts) config.stackParts = options.stackParts;
|
|
10949
11683
|
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
|
-
},
|
|
11684
|
+
config,
|
|
11056
11685
|
templates: EMBEDDED_TEMPLATES$1
|
|
11057
11686
|
});
|
|
11058
11687
|
if (result.success && result.tree) return {
|