create-better-fullstack 1.3.17 → 1.3.18
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 +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +96 -6
- package/dist/index.mjs +1 -1
- package/dist/{src-BYQLJiGD.mjs → src-CISBB09-.mjs} +641 -626
- package/package.json +3 -3
|
@@ -13,13 +13,14 @@ import { ECOSYSTEM_GROUPS, EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TE
|
|
|
13
13
|
import gradient from "gradient-string";
|
|
14
14
|
import path$1 from "path";
|
|
15
15
|
import { writeTreeToFilesystem } from "@better-fullstack/template-generator/fs-writer";
|
|
16
|
+
import { allowedApisForFrontends, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
16
17
|
import consola, { consola as consola$1 } from "consola";
|
|
17
18
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
19
|
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
19
20
|
import * as JSONC from "jsonc-parser";
|
|
20
21
|
import { $, execa } from "execa";
|
|
21
|
-
import { format } from "oxfmt";
|
|
22
22
|
import os$1 from "node:os";
|
|
23
|
+
import { format } from "oxfmt";
|
|
23
24
|
|
|
24
25
|
//#region src/utils/get-package-manager.ts
|
|
25
26
|
const getUserPkgManager = () => {
|
|
@@ -74,6 +75,13 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
74
75
|
serverDeploy: "none",
|
|
75
76
|
cssFramework: "tailwind",
|
|
76
77
|
uiLibrary: "shadcn-ui",
|
|
78
|
+
shadcnBase: "radix",
|
|
79
|
+
shadcnStyle: "nova",
|
|
80
|
+
shadcnIconLibrary: "lucide",
|
|
81
|
+
shadcnColorTheme: "neutral",
|
|
82
|
+
shadcnBaseColor: "neutral",
|
|
83
|
+
shadcnFont: "inter",
|
|
84
|
+
shadcnRadius: "default",
|
|
77
85
|
rustWebFramework: "none",
|
|
78
86
|
rustFrontend: "none",
|
|
79
87
|
rustOrm: "none",
|
|
@@ -107,254 +115,6 @@ function getDefaultConfig() {
|
|
|
107
115
|
};
|
|
108
116
|
}
|
|
109
117
|
const DEFAULT_CONFIG = getDefaultConfig();
|
|
110
|
-
const ADDON_COMPATIBILITY = {
|
|
111
|
-
pwa: [
|
|
112
|
-
"tanstack-router",
|
|
113
|
-
"react-router",
|
|
114
|
-
"solid",
|
|
115
|
-
"next",
|
|
116
|
-
"astro",
|
|
117
|
-
"qwik",
|
|
118
|
-
"angular",
|
|
119
|
-
"redwood",
|
|
120
|
-
"fresh"
|
|
121
|
-
],
|
|
122
|
-
tauri: [
|
|
123
|
-
"tanstack-router",
|
|
124
|
-
"react-router",
|
|
125
|
-
"nuxt",
|
|
126
|
-
"svelte",
|
|
127
|
-
"solid",
|
|
128
|
-
"next",
|
|
129
|
-
"astro",
|
|
130
|
-
"qwik",
|
|
131
|
-
"angular",
|
|
132
|
-
"redwood",
|
|
133
|
-
"fresh"
|
|
134
|
-
],
|
|
135
|
-
biome: [],
|
|
136
|
-
husky: [],
|
|
137
|
-
lefthook: [],
|
|
138
|
-
turborepo: [],
|
|
139
|
-
starlight: [],
|
|
140
|
-
ultracite: [],
|
|
141
|
-
ruler: [],
|
|
142
|
-
mcp: [],
|
|
143
|
-
skills: [],
|
|
144
|
-
oxlint: [],
|
|
145
|
-
fumadocs: [],
|
|
146
|
-
opentui: [],
|
|
147
|
-
wxt: [],
|
|
148
|
-
msw: [],
|
|
149
|
-
storybook: [
|
|
150
|
-
"tanstack-router",
|
|
151
|
-
"react-router",
|
|
152
|
-
"next",
|
|
153
|
-
"nuxt",
|
|
154
|
-
"svelte",
|
|
155
|
-
"solid"
|
|
156
|
-
],
|
|
157
|
-
none: []
|
|
158
|
-
};
|
|
159
|
-
/**
|
|
160
|
-
* UI Library compatibility rules
|
|
161
|
-
* Defines which frontends and CSS frameworks each UI library supports
|
|
162
|
-
*/
|
|
163
|
-
const UI_LIBRARY_COMPATIBILITY = {
|
|
164
|
-
"shadcn-ui": {
|
|
165
|
-
frontends: [
|
|
166
|
-
"tanstack-router",
|
|
167
|
-
"react-router",
|
|
168
|
-
"tanstack-start",
|
|
169
|
-
"next",
|
|
170
|
-
"astro"
|
|
171
|
-
],
|
|
172
|
-
cssFrameworks: ["tailwind"]
|
|
173
|
-
},
|
|
174
|
-
daisyui: {
|
|
175
|
-
frontends: [
|
|
176
|
-
"tanstack-router",
|
|
177
|
-
"react-router",
|
|
178
|
-
"tanstack-start",
|
|
179
|
-
"next",
|
|
180
|
-
"nuxt",
|
|
181
|
-
"svelte",
|
|
182
|
-
"solid",
|
|
183
|
-
"astro",
|
|
184
|
-
"qwik",
|
|
185
|
-
"angular",
|
|
186
|
-
"redwood",
|
|
187
|
-
"fresh"
|
|
188
|
-
],
|
|
189
|
-
cssFrameworks: ["tailwind"]
|
|
190
|
-
},
|
|
191
|
-
"radix-ui": {
|
|
192
|
-
frontends: [
|
|
193
|
-
"tanstack-router",
|
|
194
|
-
"react-router",
|
|
195
|
-
"tanstack-start",
|
|
196
|
-
"next",
|
|
197
|
-
"astro"
|
|
198
|
-
],
|
|
199
|
-
cssFrameworks: [
|
|
200
|
-
"tailwind",
|
|
201
|
-
"scss",
|
|
202
|
-
"less",
|
|
203
|
-
"postcss-only",
|
|
204
|
-
"none"
|
|
205
|
-
]
|
|
206
|
-
},
|
|
207
|
-
"headless-ui": {
|
|
208
|
-
frontends: [
|
|
209
|
-
"tanstack-router",
|
|
210
|
-
"react-router",
|
|
211
|
-
"tanstack-start",
|
|
212
|
-
"next",
|
|
213
|
-
"nuxt",
|
|
214
|
-
"astro"
|
|
215
|
-
],
|
|
216
|
-
cssFrameworks: [
|
|
217
|
-
"tailwind",
|
|
218
|
-
"scss",
|
|
219
|
-
"less",
|
|
220
|
-
"postcss-only",
|
|
221
|
-
"none"
|
|
222
|
-
]
|
|
223
|
-
},
|
|
224
|
-
"park-ui": {
|
|
225
|
-
frontends: [
|
|
226
|
-
"tanstack-router",
|
|
227
|
-
"react-router",
|
|
228
|
-
"tanstack-start",
|
|
229
|
-
"next",
|
|
230
|
-
"nuxt",
|
|
231
|
-
"solid",
|
|
232
|
-
"astro"
|
|
233
|
-
],
|
|
234
|
-
cssFrameworks: [
|
|
235
|
-
"tailwind",
|
|
236
|
-
"scss",
|
|
237
|
-
"less",
|
|
238
|
-
"postcss-only"
|
|
239
|
-
]
|
|
240
|
-
},
|
|
241
|
-
"chakra-ui": {
|
|
242
|
-
frontends: [
|
|
243
|
-
"tanstack-router",
|
|
244
|
-
"react-router",
|
|
245
|
-
"tanstack-start",
|
|
246
|
-
"next",
|
|
247
|
-
"astro"
|
|
248
|
-
],
|
|
249
|
-
cssFrameworks: [
|
|
250
|
-
"tailwind",
|
|
251
|
-
"scss",
|
|
252
|
-
"less",
|
|
253
|
-
"postcss-only",
|
|
254
|
-
"none"
|
|
255
|
-
]
|
|
256
|
-
},
|
|
257
|
-
nextui: {
|
|
258
|
-
frontends: [
|
|
259
|
-
"tanstack-router",
|
|
260
|
-
"react-router",
|
|
261
|
-
"tanstack-start",
|
|
262
|
-
"next",
|
|
263
|
-
"astro"
|
|
264
|
-
],
|
|
265
|
-
cssFrameworks: ["tailwind"]
|
|
266
|
-
},
|
|
267
|
-
mantine: {
|
|
268
|
-
frontends: [
|
|
269
|
-
"tanstack-router",
|
|
270
|
-
"react-router",
|
|
271
|
-
"tanstack-start",
|
|
272
|
-
"next",
|
|
273
|
-
"astro"
|
|
274
|
-
],
|
|
275
|
-
cssFrameworks: [
|
|
276
|
-
"tailwind",
|
|
277
|
-
"scss",
|
|
278
|
-
"less",
|
|
279
|
-
"postcss-only",
|
|
280
|
-
"none"
|
|
281
|
-
]
|
|
282
|
-
},
|
|
283
|
-
"base-ui": {
|
|
284
|
-
frontends: [
|
|
285
|
-
"tanstack-router",
|
|
286
|
-
"react-router",
|
|
287
|
-
"tanstack-start",
|
|
288
|
-
"next",
|
|
289
|
-
"astro"
|
|
290
|
-
],
|
|
291
|
-
cssFrameworks: [
|
|
292
|
-
"tailwind",
|
|
293
|
-
"scss",
|
|
294
|
-
"less",
|
|
295
|
-
"postcss-only",
|
|
296
|
-
"none"
|
|
297
|
-
]
|
|
298
|
-
},
|
|
299
|
-
"ark-ui": {
|
|
300
|
-
frontends: [
|
|
301
|
-
"tanstack-router",
|
|
302
|
-
"react-router",
|
|
303
|
-
"tanstack-start",
|
|
304
|
-
"next",
|
|
305
|
-
"nuxt",
|
|
306
|
-
"svelte",
|
|
307
|
-
"solid",
|
|
308
|
-
"astro"
|
|
309
|
-
],
|
|
310
|
-
cssFrameworks: [
|
|
311
|
-
"tailwind",
|
|
312
|
-
"scss",
|
|
313
|
-
"less",
|
|
314
|
-
"postcss-only",
|
|
315
|
-
"none"
|
|
316
|
-
]
|
|
317
|
-
},
|
|
318
|
-
"react-aria": {
|
|
319
|
-
frontends: [
|
|
320
|
-
"tanstack-router",
|
|
321
|
-
"react-router",
|
|
322
|
-
"tanstack-start",
|
|
323
|
-
"next",
|
|
324
|
-
"astro"
|
|
325
|
-
],
|
|
326
|
-
cssFrameworks: [
|
|
327
|
-
"tailwind",
|
|
328
|
-
"scss",
|
|
329
|
-
"less",
|
|
330
|
-
"postcss-only",
|
|
331
|
-
"none"
|
|
332
|
-
]
|
|
333
|
-
},
|
|
334
|
-
none: {
|
|
335
|
-
frontends: [
|
|
336
|
-
"tanstack-router",
|
|
337
|
-
"react-router",
|
|
338
|
-
"tanstack-start",
|
|
339
|
-
"next",
|
|
340
|
-
"nuxt",
|
|
341
|
-
"svelte",
|
|
342
|
-
"solid",
|
|
343
|
-
"astro",
|
|
344
|
-
"qwik",
|
|
345
|
-
"angular",
|
|
346
|
-
"redwood",
|
|
347
|
-
"fresh"
|
|
348
|
-
],
|
|
349
|
-
cssFrameworks: [
|
|
350
|
-
"tailwind",
|
|
351
|
-
"scss",
|
|
352
|
-
"less",
|
|
353
|
-
"postcss-only",
|
|
354
|
-
"none"
|
|
355
|
-
]
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
118
|
/**
|
|
359
119
|
* Default UI library for each frontend framework
|
|
360
120
|
* Falls back based on what's compatible
|
|
@@ -367,6 +127,7 @@ const DEFAULT_UI_LIBRARY_BY_FRONTEND = {
|
|
|
367
127
|
nuxt: "daisyui",
|
|
368
128
|
svelte: "daisyui",
|
|
369
129
|
solid: "daisyui",
|
|
130
|
+
"solid-start": "daisyui",
|
|
370
131
|
astro: "daisyui",
|
|
371
132
|
qwik: "daisyui",
|
|
372
133
|
angular: "daisyui",
|
|
@@ -719,23 +480,6 @@ var types_exports = {};
|
|
|
719
480
|
import * as import__better_fullstack_types from "@better-fullstack/types";
|
|
720
481
|
__reExport(types_exports, import__better_fullstack_types);
|
|
721
482
|
|
|
722
|
-
//#endregion
|
|
723
|
-
//#region src/utils/compatibility.ts
|
|
724
|
-
const WEB_FRAMEWORKS = [
|
|
725
|
-
"tanstack-router",
|
|
726
|
-
"react-router",
|
|
727
|
-
"tanstack-start",
|
|
728
|
-
"next",
|
|
729
|
-
"nuxt",
|
|
730
|
-
"svelte",
|
|
731
|
-
"solid",
|
|
732
|
-
"astro",
|
|
733
|
-
"qwik",
|
|
734
|
-
"angular",
|
|
735
|
-
"redwood",
|
|
736
|
-
"fresh"
|
|
737
|
-
];
|
|
738
|
-
|
|
739
483
|
//#endregion
|
|
740
484
|
//#region src/utils/context.ts
|
|
741
485
|
const cliStorage = new AsyncLocalStorage();
|
|
@@ -883,17 +627,14 @@ function constraintError(opts) {
|
|
|
883
627
|
|
|
884
628
|
//#endregion
|
|
885
629
|
//#region src/utils/compatibility-rules.ts
|
|
886
|
-
function isWebFrontend(value) {
|
|
887
|
-
return
|
|
630
|
+
function isWebFrontend$1(value) {
|
|
631
|
+
return isWebFrontend(value);
|
|
888
632
|
}
|
|
889
|
-
function splitFrontends(values = []) {
|
|
890
|
-
return
|
|
891
|
-
web: values.filter((f) => isWebFrontend(f)),
|
|
892
|
-
native: values.filter((f) => f === "native-bare" || f === "native-uniwind" || f === "native-unistyles")
|
|
893
|
-
};
|
|
633
|
+
function splitFrontends$1(values = []) {
|
|
634
|
+
return splitFrontends(values);
|
|
894
635
|
}
|
|
895
636
|
function ensureSingleWebAndNative(frontends) {
|
|
896
|
-
const { web, native } = splitFrontends(frontends);
|
|
637
|
+
const { web, native } = splitFrontends$1(frontends);
|
|
897
638
|
if (web.length > 1) invalidSelectionError({
|
|
898
639
|
message: "Only one web framework can be selected per project.",
|
|
899
640
|
provided: { frontend: web },
|
|
@@ -909,18 +650,20 @@ const FULLSTACK_FRONTENDS$1 = [
|
|
|
909
650
|
"next",
|
|
910
651
|
"tanstack-start",
|
|
911
652
|
"astro",
|
|
912
|
-
"nuxt"
|
|
653
|
+
"nuxt",
|
|
654
|
+
"svelte",
|
|
655
|
+
"solid-start"
|
|
913
656
|
];
|
|
914
657
|
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
915
658
|
const backend = config.backend || options.backend;
|
|
916
659
|
const frontends = config.frontend || options.frontend || [];
|
|
917
660
|
if (backend === "self") {
|
|
918
|
-
const { web, native } = splitFrontends(frontends);
|
|
919
|
-
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, or
|
|
661
|
+
const { web, native } = splitFrontends$1(frontends);
|
|
662
|
+
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
|
|
920
663
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
|
|
921
664
|
}
|
|
922
665
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
923
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, or
|
|
666
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
|
|
924
667
|
}
|
|
925
668
|
const WORKERS_COMPATIBLE_BACKENDS = [
|
|
926
669
|
"hono",
|
|
@@ -929,7 +672,7 @@ const WORKERS_COMPATIBLE_BACKENDS = [
|
|
|
929
672
|
];
|
|
930
673
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
931
674
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend)) incompatibilityError({
|
|
932
|
-
message: "Cloudflare Workers runtime
|
|
675
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently supported only with compatible backends (Hono, Nitro, or Fets).",
|
|
933
676
|
provided: {
|
|
934
677
|
runtime: "workers",
|
|
935
678
|
backend: config.backend
|
|
@@ -942,7 +685,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
942
685
|
]
|
|
943
686
|
});
|
|
944
687
|
if (providedFlags.has("backend") && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend) && config.runtime === "workers") incompatibilityError({
|
|
945
|
-
message: `
|
|
688
|
+
message: `In Better-Fullstack, backend '${config.backend}' is currently not available with Cloudflare Workers runtime.`,
|
|
946
689
|
provided: {
|
|
947
690
|
backend: config.backend,
|
|
948
691
|
runtime: "workers"
|
|
@@ -950,7 +693,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
950
693
|
suggestions: ["Use --backend hono, --backend nitro, or --backend fets", "Choose a different runtime (node, bun)"]
|
|
951
694
|
});
|
|
952
695
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") incompatibilityError({
|
|
953
|
-
message: "Cloudflare Workers runtime is not
|
|
696
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with MongoDB.",
|
|
954
697
|
provided: {
|
|
955
698
|
runtime: "workers",
|
|
956
699
|
database: "mongodb"
|
|
@@ -958,7 +701,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
958
701
|
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
959
702
|
});
|
|
960
703
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") incompatibilityError({
|
|
961
|
-
message: "Cloudflare Workers runtime is not
|
|
704
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with Docker database setup.",
|
|
962
705
|
provided: {
|
|
963
706
|
runtime: "workers",
|
|
964
707
|
"db-setup": "docker"
|
|
@@ -966,7 +709,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
966
709
|
suggestions: ["Use --db-setup d1 for SQLite", "Choose a different runtime (node, bun)"]
|
|
967
710
|
});
|
|
968
711
|
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") incompatibilityError({
|
|
969
|
-
message: "MongoDB is not
|
|
712
|
+
message: "In Better-Fullstack, MongoDB is currently not available with Cloudflare Workers runtime.",
|
|
970
713
|
provided: {
|
|
971
714
|
database: "mongodb",
|
|
972
715
|
runtime: "workers"
|
|
@@ -983,9 +726,10 @@ function validateApiFrontendCompatibility(api, frontends = [], astroIntegration)
|
|
|
983
726
|
const includesAngular = frontends.includes("angular");
|
|
984
727
|
const includesRedwood = frontends.includes("redwood");
|
|
985
728
|
const includesFresh = frontends.includes("fresh");
|
|
986
|
-
|
|
729
|
+
const includesSolidStart = frontends.includes("solid-start");
|
|
730
|
+
if ((includesNuxt || includesSvelte || includesSolid || includesSolidStart) && (api === "trpc" || api === "ts-rest" || api === "garph")) {
|
|
987
731
|
const apiName = api === "trpc" ? "tRPC" : api === "ts-rest" ? "ts-rest" : "garph";
|
|
988
|
-
const incompatibleFrontend = includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid";
|
|
732
|
+
const incompatibleFrontend = includesNuxt ? "nuxt" : includesSvelte ? "svelte" : includesSolid ? "solid" : "solid-start";
|
|
989
733
|
incompatibilityError({
|
|
990
734
|
message: `${apiName} API requires React-based frontends.`,
|
|
991
735
|
provided: {
|
|
@@ -1044,90 +788,20 @@ function validateApiFrontendCompatibility(api, frontends = [], astroIntegration)
|
|
|
1044
788
|
]
|
|
1045
789
|
});
|
|
1046
790
|
}
|
|
1047
|
-
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
1048
|
-
|
|
1049
|
-
if (backend === "convex" && frontend === "astro") return false;
|
|
1050
|
-
if (backend === "convex" && frontend === "qwik") return false;
|
|
1051
|
-
if (backend === "convex" && frontend === "angular") return false;
|
|
1052
|
-
if (backend === "convex" && frontend === "redwood") return false;
|
|
1053
|
-
if (backend === "convex" && frontend === "fresh") return false;
|
|
1054
|
-
if (frontend === "qwik" && backend && backend !== "none") return false;
|
|
1055
|
-
if (frontend === "angular" && backend && backend !== "none") return false;
|
|
1056
|
-
if (frontend === "redwood" && backend && backend !== "none") return false;
|
|
1057
|
-
if (frontend === "fresh" && backend && backend !== "none") return false;
|
|
1058
|
-
if (auth === "clerk" && backend === "convex") {
|
|
1059
|
-
if ([
|
|
1060
|
-
"nuxt",
|
|
1061
|
-
"svelte",
|
|
1062
|
-
"solid"
|
|
1063
|
-
].includes(frontend)) return false;
|
|
1064
|
-
}
|
|
1065
|
-
if (auth === "nextauth") {
|
|
1066
|
-
if (frontend !== "next") return false;
|
|
1067
|
-
if (backend !== "self") return false;
|
|
1068
|
-
}
|
|
1069
|
-
if (auth === "supabase-auth") {
|
|
1070
|
-
if (frontend !== "next") return false;
|
|
1071
|
-
if (backend !== "self") return false;
|
|
1072
|
-
}
|
|
1073
|
-
return true;
|
|
791
|
+
function isFrontendAllowedWithBackend$1(frontend, backend, auth) {
|
|
792
|
+
return isFrontendAllowedWithBackend(frontend, backend, auth);
|
|
1074
793
|
}
|
|
1075
|
-
function
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
function
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
function validateSupabaseAuthCompatibility(auth, backend, frontends = []) {
|
|
1088
|
-
if (auth !== "supabase-auth") return;
|
|
1089
|
-
const hasNextJs = frontends.includes("next");
|
|
1090
|
-
if (backend !== "self") exitWithError("Supabase Auth is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
1091
|
-
if (!hasNextJs) exitWithError("Supabase Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1092
|
-
}
|
|
1093
|
-
function validateAuth0Compatibility(auth, backend, frontends = []) {
|
|
1094
|
-
if (auth !== "auth0") return;
|
|
1095
|
-
const hasNextJs = frontends.includes("next");
|
|
1096
|
-
if (backend !== "self") exitWithError("Auth0 is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
1097
|
-
if (!hasNextJs) exitWithError("Auth0 requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1098
|
-
}
|
|
1099
|
-
function allowedApisForFrontends(frontends = [], astroIntegration) {
|
|
1100
|
-
const includesNuxt = frontends.includes("nuxt");
|
|
1101
|
-
const includesSvelte = frontends.includes("svelte");
|
|
1102
|
-
const includesSolid = frontends.includes("solid");
|
|
1103
|
-
const includesAstro = frontends.includes("astro");
|
|
1104
|
-
const includesQwik = frontends.includes("qwik");
|
|
1105
|
-
const includesAngular = frontends.includes("angular");
|
|
1106
|
-
const includesRedwood = frontends.includes("redwood");
|
|
1107
|
-
const includesFresh = frontends.includes("fresh");
|
|
1108
|
-
const base = [
|
|
1109
|
-
"trpc",
|
|
1110
|
-
"orpc",
|
|
1111
|
-
"ts-rest",
|
|
1112
|
-
"garph",
|
|
1113
|
-
"none"
|
|
1114
|
-
];
|
|
1115
|
-
if (includesQwik) return ["none"];
|
|
1116
|
-
if (includesAngular) return ["none"];
|
|
1117
|
-
if (includesRedwood) return ["none"];
|
|
1118
|
-
if (includesFresh) return ["none"];
|
|
1119
|
-
if (includesNuxt || includesSvelte || includesSolid) return ["orpc", "none"];
|
|
1120
|
-
if (includesAstro && astroIntegration && astroIntegration !== "react") return ["orpc", "none"];
|
|
1121
|
-
return base;
|
|
1122
|
-
}
|
|
1123
|
-
function isExampleAIAllowed(backend, frontends = []) {
|
|
1124
|
-
if (frontends.includes("solid")) return false;
|
|
1125
|
-
if (backend === "convex") {
|
|
1126
|
-
const includesNuxt = frontends.includes("nuxt");
|
|
1127
|
-
const includesSvelte = frontends.includes("svelte");
|
|
1128
|
-
if (includesNuxt || includesSvelte) return false;
|
|
1129
|
-
}
|
|
1130
|
-
return true;
|
|
794
|
+
function allowedApisForFrontends$1(frontends = [], astroIntegration) {
|
|
795
|
+
return allowedApisForFrontends(frontends, astroIntegration);
|
|
796
|
+
}
|
|
797
|
+
function isExampleAIAllowed$1(backend, frontends = []) {
|
|
798
|
+
return isExampleAIAllowed(backend, frontends);
|
|
799
|
+
}
|
|
800
|
+
function isExampleChatSdkAllowed$1(backend, frontends = [], runtime) {
|
|
801
|
+
return isExampleChatSdkAllowed(backend, frontends, runtime);
|
|
802
|
+
}
|
|
803
|
+
function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
|
|
804
|
+
return requiresChatSdkVercelAIForSelection(backend, frontends, runtime);
|
|
1131
805
|
}
|
|
1132
806
|
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
1133
807
|
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
@@ -1135,28 +809,16 @@ function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
|
1135
809
|
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
1136
810
|
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
1137
811
|
}
|
|
1138
|
-
function validateAddonCompatibility(addon, frontend, _auth) {
|
|
1139
|
-
|
|
1140
|
-
if (compatibleFrontends.length > 0) {
|
|
1141
|
-
if (!frontend.some((f) => compatibleFrontends.includes(f))) return {
|
|
1142
|
-
isCompatible: false,
|
|
1143
|
-
reason: `${addon} addon requires one of these frontends: ${compatibleFrontends.join(", ")}`
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
return { isCompatible: true };
|
|
812
|
+
function validateAddonCompatibility$1(addon, frontend, _auth) {
|
|
813
|
+
return validateAddonCompatibility(addon, frontend, _auth);
|
|
1147
814
|
}
|
|
1148
|
-
function getCompatibleAddons(allAddons, frontend, existingAddons = [], auth) {
|
|
1149
|
-
return allAddons
|
|
1150
|
-
if (existingAddons.includes(addon)) return false;
|
|
1151
|
-
if (addon === "none") return false;
|
|
1152
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontend, auth);
|
|
1153
|
-
return isCompatible;
|
|
1154
|
-
});
|
|
815
|
+
function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth) {
|
|
816
|
+
return getCompatibleAddons(allAddons, frontend, existingAddons, auth);
|
|
1155
817
|
}
|
|
1156
818
|
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth) {
|
|
1157
819
|
for (const addon of addons) {
|
|
1158
820
|
if (addon === "none") continue;
|
|
1159
|
-
const { isCompatible, reason } = validateAddonCompatibility(addon, frontends, auth);
|
|
821
|
+
const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth);
|
|
1160
822
|
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
1161
823
|
}
|
|
1162
824
|
}
|
|
@@ -1164,68 +826,63 @@ function validatePaymentsCompatibility(payments, auth, _backend, frontends = [])
|
|
|
1164
826
|
if (!payments || payments === "none") return;
|
|
1165
827
|
if (payments === "polar") {
|
|
1166
828
|
if (!auth || auth === "none" || auth !== "better-auth") exitWithError("Polar payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.");
|
|
1167
|
-
const { web } = splitFrontends(frontends);
|
|
829
|
+
const { web } = splitFrontends$1(frontends);
|
|
1168
830
|
if (web.length === 0 && frontends.length > 0) exitWithError("Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.");
|
|
1169
831
|
}
|
|
1170
832
|
}
|
|
1171
|
-
function validateExamplesCompatibility(examples, backend, frontend) {
|
|
833
|
+
function validateExamplesCompatibility(examples, backend, frontend, runtime, ai) {
|
|
1172
834
|
const examplesArr = examples ?? [];
|
|
1173
835
|
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
1174
836
|
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
837
|
+
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid-start")) exitWithError("The 'ai' example is not compatible with the SolidStart frontend.");
|
|
1175
838
|
if (examplesArr.includes("ai") && backend === "convex") {
|
|
1176
839
|
const frontendArr = frontend ?? [];
|
|
1177
840
|
const includesNuxt = frontendArr.includes("nuxt");
|
|
1178
841
|
const includesSvelte = frontendArr.includes("svelte");
|
|
1179
842
|
if (includesNuxt || includesSvelte) exitWithError("The 'ai' example with Convex backend only supports React-based frontends (Next.js, TanStack Router, TanStack Start, React Router). Svelte and Nuxt are not supported with Convex AI.");
|
|
1180
843
|
}
|
|
844
|
+
if (examplesArr.includes("chat-sdk")) {
|
|
845
|
+
const frontendArr = frontend ?? [];
|
|
846
|
+
if (!isExampleChatSdkAllowed$1(backend, frontendArr, runtime)) {
|
|
847
|
+
if (backend === "none") exitWithError("The 'chat-sdk' example requires a backend.");
|
|
848
|
+
if (backend === "convex") exitWithError("The 'chat-sdk' example is not supported with the Convex backend in v1. Use self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime.");
|
|
849
|
+
if (backend === "self") exitWithError("The 'chat-sdk' example with self backend only supports Next.js, TanStack Start, or Nuxt frontends in v1.");
|
|
850
|
+
if (backend === "hono" && runtime !== "node") exitWithError("The 'chat-sdk' example with Hono requires '--runtime node' in v1 (Bun/Workers not supported yet).");
|
|
851
|
+
exitWithError("The 'chat-sdk' example is only supported with self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime in v1.");
|
|
852
|
+
}
|
|
853
|
+
if (requiresChatSdkVercelAI(backend, frontendArr, runtime) && ai && ai !== "vercel-ai") exitWithError("The 'chat-sdk' example requires '--ai vercel-ai' for the selected stack in v1 (Nuxt Discord and Hono GitHub profiles).");
|
|
854
|
+
}
|
|
1181
855
|
}
|
|
1182
856
|
/**
|
|
1183
857
|
* Validates that a UI library is compatible with the selected frontend(s)
|
|
1184
858
|
*/
|
|
1185
859
|
function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astroIntegration) {
|
|
1186
860
|
if (!uiLibrary || uiLibrary === "none") return;
|
|
1187
|
-
const
|
|
1188
|
-
if (
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
"tanstack-router",
|
|
1194
|
-
"react-router",
|
|
1195
|
-
"tanstack-start",
|
|
1196
|
-
"next"
|
|
1197
|
-
].includes(f));
|
|
1198
|
-
const supportsNonReact = compatibility.frontends.some((f) => [
|
|
1199
|
-
"nuxt",
|
|
1200
|
-
"svelte",
|
|
1201
|
-
"solid",
|
|
1202
|
-
"qwik",
|
|
1203
|
-
"angular"
|
|
1204
|
-
].includes(f));
|
|
1205
|
-
if (astroIntegration === "react") {
|
|
1206
|
-
if (supportsReact) return;
|
|
1207
|
-
}
|
|
1208
|
-
if (!supportsNonReact) {
|
|
1209
|
-
const integrationName = astroIntegration || "none";
|
|
861
|
+
const compatible = getCompatibleUILibraries(frontends, astroIntegration);
|
|
862
|
+
if (!compatible.includes(uiLibrary)) {
|
|
863
|
+
const { web } = splitFrontends$1(frontends);
|
|
864
|
+
const isAstroNonReact = web.includes("astro") && astroIntegration !== "react";
|
|
865
|
+
const supportsAstroReact = getCompatibleUILibraries(["astro"], "react").includes(uiLibrary);
|
|
866
|
+
if (isAstroNonReact && supportsAstroReact) {
|
|
1210
867
|
incompatibilityError({
|
|
1211
868
|
message: `UI library '${uiLibrary}' requires React.`,
|
|
1212
869
|
provided: {
|
|
1213
870
|
"ui-library": uiLibrary,
|
|
1214
|
-
"astro-integration":
|
|
871
|
+
"astro-integration": astroIntegration || "none"
|
|
1215
872
|
},
|
|
1216
873
|
suggestions: ["Use --astro-integration react", "Choose a different UI library (daisyui, ark-ui)"]
|
|
1217
874
|
});
|
|
875
|
+
return;
|
|
1218
876
|
}
|
|
1219
|
-
|
|
877
|
+
incompatibilityError({
|
|
878
|
+
message: `UI library '${uiLibrary}' is not compatible with the selected frontend.`,
|
|
879
|
+
provided: {
|
|
880
|
+
"ui-library": uiLibrary,
|
|
881
|
+
frontend: frontends
|
|
882
|
+
},
|
|
883
|
+
suggestions: [`Supported choices for this stack: ${compatible.join(", ")}`, "Choose a different UI library"]
|
|
884
|
+
});
|
|
1220
885
|
}
|
|
1221
|
-
if (!compatibility.frontends.includes(webFrontend)) incompatibilityError({
|
|
1222
|
-
message: `UI library '${uiLibrary}' is not compatible with '${webFrontend}'.`,
|
|
1223
|
-
provided: {
|
|
1224
|
-
"ui-library": uiLibrary,
|
|
1225
|
-
frontend: webFrontend
|
|
1226
|
-
},
|
|
1227
|
-
suggestions: [`Supported frontends: ${compatibility.frontends.join(", ")}`, "Choose a different UI library"]
|
|
1228
|
-
});
|
|
1229
886
|
}
|
|
1230
887
|
/**
|
|
1231
888
|
* Validates that a UI library is compatible with the selected CSS framework
|
|
@@ -1233,57 +890,26 @@ function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astro
|
|
|
1233
890
|
function validateUILibraryCSSFrameworkCompatibility(uiLibrary, cssFramework) {
|
|
1234
891
|
if (!uiLibrary || uiLibrary === "none") return;
|
|
1235
892
|
if (!cssFramework) return;
|
|
1236
|
-
const
|
|
1237
|
-
if (!
|
|
893
|
+
const supported = getCompatibleCSSFrameworks(uiLibrary);
|
|
894
|
+
if (!supported.includes(cssFramework)) exitWithError(`UI library '${uiLibrary}' is not compatible with '${cssFramework}' CSS framework. Supported CSS frameworks: ${supported.join(", ")}`);
|
|
1238
895
|
}
|
|
1239
896
|
/**
|
|
1240
897
|
* Gets list of UI libraries compatible with the selected frontend(s)
|
|
1241
898
|
*/
|
|
1242
|
-
function getCompatibleUILibraries(frontends = [], astroIntegration) {
|
|
1243
|
-
|
|
1244
|
-
if (web.length === 0) return ["none"];
|
|
1245
|
-
const webFrontend = web[0];
|
|
1246
|
-
return Object.keys(UI_LIBRARY_COMPATIBILITY).filter((lib) => {
|
|
1247
|
-
if (lib === "none") return true;
|
|
1248
|
-
const compatibility = UI_LIBRARY_COMPATIBILITY[lib];
|
|
1249
|
-
if (webFrontend === "astro") {
|
|
1250
|
-
if (astroIntegration === "react") return compatibility.frontends.some((f) => [
|
|
1251
|
-
"tanstack-router",
|
|
1252
|
-
"react-router",
|
|
1253
|
-
"tanstack-start",
|
|
1254
|
-
"next",
|
|
1255
|
-
"astro"
|
|
1256
|
-
].includes(f));
|
|
1257
|
-
return compatibility.frontends.some((f) => [
|
|
1258
|
-
"nuxt",
|
|
1259
|
-
"svelte",
|
|
1260
|
-
"solid",
|
|
1261
|
-
"qwik",
|
|
1262
|
-
"angular"
|
|
1263
|
-
].includes(f));
|
|
1264
|
-
}
|
|
1265
|
-
return compatibility.frontends.includes(webFrontend);
|
|
1266
|
-
});
|
|
899
|
+
function getCompatibleUILibraries$1(frontends = [], astroIntegration) {
|
|
900
|
+
return getCompatibleUILibraries(frontends, astroIntegration);
|
|
1267
901
|
}
|
|
1268
902
|
/**
|
|
1269
903
|
* Gets list of CSS frameworks compatible with the selected UI library
|
|
1270
904
|
*/
|
|
1271
|
-
function getCompatibleCSSFrameworks(uiLibrary) {
|
|
1272
|
-
|
|
1273
|
-
"tailwind",
|
|
1274
|
-
"scss",
|
|
1275
|
-
"less",
|
|
1276
|
-
"postcss-only",
|
|
1277
|
-
"none"
|
|
1278
|
-
];
|
|
1279
|
-
return UI_LIBRARY_COMPATIBILITY[uiLibrary].cssFrameworks;
|
|
905
|
+
function getCompatibleCSSFrameworks$1(uiLibrary) {
|
|
906
|
+
return getCompatibleCSSFrameworks(uiLibrary);
|
|
1280
907
|
}
|
|
1281
908
|
/**
|
|
1282
909
|
* Checks if a frontend has web styling (excludes native-only frontends)
|
|
1283
910
|
*/
|
|
1284
|
-
function hasWebStyling(frontends = []) {
|
|
1285
|
-
|
|
1286
|
-
return web.length > 0;
|
|
911
|
+
function hasWebStyling$1(frontends = []) {
|
|
912
|
+
return hasWebStyling(frontends);
|
|
1287
913
|
}
|
|
1288
914
|
|
|
1289
915
|
//#endregion
|
|
@@ -1624,7 +1250,7 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1624
1250
|
};
|
|
1625
1251
|
const frontendsArray = frontends || [];
|
|
1626
1252
|
for (const addon of allAddons) {
|
|
1627
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontendsArray, auth);
|
|
1253
|
+
const { isCompatible } = validateAddonCompatibility$1(addon, frontendsArray, auth);
|
|
1628
1254
|
if (!isCompatible) continue;
|
|
1629
1255
|
const { label, hint } = getAddonDisplay(addon);
|
|
1630
1256
|
const option = {
|
|
@@ -1663,7 +1289,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
|
1663
1289
|
"AI Agents": []
|
|
1664
1290
|
};
|
|
1665
1291
|
const frontendArray = frontend || [];
|
|
1666
|
-
const compatibleAddons = getCompatibleAddons(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
1292
|
+
const compatibleAddons = getCompatibleAddons$1(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
1667
1293
|
for (const addon of compatibleAddons) {
|
|
1668
1294
|
const { label, hint } = getAddonDisplay(addon);
|
|
1669
1295
|
const option = {
|
|
@@ -2408,7 +2034,8 @@ const SKILL_SOURCES = {
|
|
|
2408
2034
|
"prisma/skills": { label: "Prisma" },
|
|
2409
2035
|
"elysiajs/skills": { label: "ElysiaJS" },
|
|
2410
2036
|
"waynesutton/convexskills": { label: "Convex" },
|
|
2411
|
-
"msmps/opentui-skill": { label: "OpenTUI Platform" }
|
|
2037
|
+
"msmps/opentui-skill": { label: "OpenTUI Platform" },
|
|
2038
|
+
"haydenbleasel/ultracite": { label: "Ultracite" }
|
|
2412
2039
|
};
|
|
2413
2040
|
const AVAILABLE_AGENTS = [
|
|
2414
2041
|
{
|
|
@@ -2540,6 +2167,7 @@ function getRecommendedSourceKeys(config) {
|
|
|
2540
2167
|
if (backend === "elysia") sources.push("elysiajs/skills");
|
|
2541
2168
|
if (backend === "convex") sources.push("waynesutton/convexskills");
|
|
2542
2169
|
if (addons.includes("opentui")) sources.push("msmps/opentui-skill");
|
|
2170
|
+
if (addons.includes("ultracite")) sources.push("haydenbleasel/ultracite");
|
|
2543
2171
|
return sources;
|
|
2544
2172
|
}
|
|
2545
2173
|
const CURATED_SKILLS_BY_SOURCE = {
|
|
@@ -2597,7 +2225,8 @@ const CURATED_SKILLS_BY_SOURCE = {
|
|
|
2597
2225
|
"convex-migrations",
|
|
2598
2226
|
"convex-security-check"
|
|
2599
2227
|
],
|
|
2600
|
-
"msmps/opentui-skill": () => ["opentui"]
|
|
2228
|
+
"msmps/opentui-skill": () => ["opentui"],
|
|
2229
|
+
"haydenbleasel/ultracite": () => ["ultracite"]
|
|
2601
2230
|
};
|
|
2602
2231
|
function getCuratedSkillNamesForSourceKey(sourceKey, config) {
|
|
2603
2232
|
return CURATED_SKILLS_BY_SOURCE[sourceKey](config);
|
|
@@ -2727,12 +2356,11 @@ async function setupTauri(config) {
|
|
|
2727
2356
|
if (!await fs.pathExists(clientPackageDir)) return;
|
|
2728
2357
|
try {
|
|
2729
2358
|
s.start("Setting up Tauri desktop app support...");
|
|
2730
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
2731
2359
|
const hasNuxt = frontend.includes("nuxt");
|
|
2732
2360
|
const hasSvelte = frontend.includes("svelte");
|
|
2733
2361
|
const hasNext = frontend.includes("next");
|
|
2734
|
-
const devUrl =
|
|
2735
|
-
const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" :
|
|
2362
|
+
const devUrl = `http://localhost:${getLocalWebDevPort(frontend)}`;
|
|
2363
|
+
const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : frontend.includes("react-router") ? "../build/client" : "../dist";
|
|
2736
2364
|
const tauriArgs = [
|
|
2737
2365
|
"@tauri-apps/cli@latest",
|
|
2738
2366
|
"init",
|
|
@@ -3389,7 +3017,7 @@ async function getAiDocsChoice(aiDocs) {
|
|
|
3389
3017
|
//#region src/prompts/animation.ts
|
|
3390
3018
|
async function getAnimationChoice(animation, frontends) {
|
|
3391
3019
|
if (animation !== void 0) return animation;
|
|
3392
|
-
const { web } = splitFrontends(frontends);
|
|
3020
|
+
const { web } = splitFrontends$1(frontends);
|
|
3393
3021
|
if (web.length === 0) return "none";
|
|
3394
3022
|
const isReact = web.some((f) => [
|
|
3395
3023
|
"tanstack-router",
|
|
@@ -3441,7 +3069,7 @@ async function getAnimationChoice(animation, frontends) {
|
|
|
3441
3069
|
//#region src/prompts/api.ts
|
|
3442
3070
|
async function getApiChoice(Api, frontend, backend, astroIntegration) {
|
|
3443
3071
|
if (backend === "convex" || backend === "none") return "none";
|
|
3444
|
-
const allowed = allowedApisForFrontends(frontend ?? [], astroIntegration);
|
|
3072
|
+
const allowed = allowedApisForFrontends$1(frontend ?? [], astroIntegration);
|
|
3445
3073
|
if (Api) return allowed.includes(Api) ? Api : allowed[0];
|
|
3446
3074
|
const apiOptionMap = {
|
|
3447
3075
|
trpc: {
|
|
@@ -3521,93 +3149,36 @@ async function getAstroIntegrationChoice(astroIntegration) {
|
|
|
3521
3149
|
|
|
3522
3150
|
//#endregion
|
|
3523
3151
|
//#region src/prompts/auth.ts
|
|
3524
|
-
async function getAuthChoice(auth, backend, frontend) {
|
|
3152
|
+
async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript") {
|
|
3525
3153
|
if (auth !== void 0) return auth;
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
].includes(f));
|
|
3545
|
-
const options$1 = [];
|
|
3546
|
-
if (supportedBetterAuthFrontends) options$1.push({
|
|
3547
|
-
value: "better-auth",
|
|
3548
|
-
label: "Better-Auth",
|
|
3549
|
-
hint: "comprehensive auth framework for TypeScript"
|
|
3550
|
-
});
|
|
3551
|
-
if (hasClerkCompatibleFrontends) options$1.push({
|
|
3552
|
-
value: "clerk",
|
|
3553
|
-
label: "Clerk",
|
|
3554
|
-
hint: "More than auth, Complete User Management"
|
|
3555
|
-
});
|
|
3556
|
-
if (options$1.length === 0) return "none";
|
|
3557
|
-
options$1.push({
|
|
3558
|
-
value: "none",
|
|
3559
|
-
label: "None",
|
|
3560
|
-
hint: "No auth"
|
|
3561
|
-
});
|
|
3562
|
-
const response$1 = await navigableSelect({
|
|
3563
|
-
message: "Select authentication provider",
|
|
3564
|
-
options: options$1,
|
|
3565
|
-
initialValue: "none"
|
|
3566
|
-
});
|
|
3567
|
-
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3568
|
-
return response$1;
|
|
3569
|
-
}
|
|
3570
|
-
const supportsNextJsAuth = frontend?.includes("next") && backend === "self";
|
|
3571
|
-
const options = [{
|
|
3572
|
-
value: "better-auth",
|
|
3573
|
-
label: "Better-Auth",
|
|
3574
|
-
hint: "comprehensive auth framework for TypeScript"
|
|
3575
|
-
}, {
|
|
3576
|
-
value: "clerk",
|
|
3577
|
-
label: "Clerk",
|
|
3578
|
-
hint: "More than auth, Complete User Management"
|
|
3579
|
-
}];
|
|
3580
|
-
if (supportsNextJsAuth) {
|
|
3581
|
-
options.push({
|
|
3582
|
-
value: "nextauth",
|
|
3583
|
-
label: "Auth.js (NextAuth)",
|
|
3584
|
-
hint: "Authentication for Next.js (formerly NextAuth.js)"
|
|
3585
|
-
});
|
|
3586
|
-
options.push({
|
|
3587
|
-
value: "stack-auth",
|
|
3588
|
-
label: "Stack Auth",
|
|
3589
|
-
hint: "Open-source Auth0/Clerk alternative"
|
|
3590
|
-
});
|
|
3591
|
-
options.push({
|
|
3592
|
-
value: "supabase-auth",
|
|
3593
|
-
label: "Supabase Auth",
|
|
3594
|
-
hint: "Auth with Supabase platform integration"
|
|
3595
|
-
});
|
|
3596
|
-
options.push({
|
|
3597
|
-
value: "auth0",
|
|
3598
|
-
label: "Auth0",
|
|
3599
|
-
hint: "Flexible identity platform for authentication"
|
|
3600
|
-
});
|
|
3601
|
-
}
|
|
3602
|
-
options.push({
|
|
3603
|
-
value: "none",
|
|
3604
|
-
label: "None",
|
|
3605
|
-
hint: "No authentication"
|
|
3154
|
+
const authOptionOrder = [
|
|
3155
|
+
{ value: "better-auth" },
|
|
3156
|
+
{ value: "go-better-auth" },
|
|
3157
|
+
{ value: "clerk" },
|
|
3158
|
+
{ value: "nextauth" },
|
|
3159
|
+
{ value: "stack-auth" },
|
|
3160
|
+
{ value: "supabase-auth" },
|
|
3161
|
+
{ value: "auth0" },
|
|
3162
|
+
{ value: "none" }
|
|
3163
|
+
];
|
|
3164
|
+
const supportedOptions = (0, types_exports.getSupportedCapabilityOptions)("auth", {
|
|
3165
|
+
ecosystem,
|
|
3166
|
+
backend,
|
|
3167
|
+
frontend
|
|
3168
|
+
});
|
|
3169
|
+
const options = authOptionOrder.flatMap(({ value }) => {
|
|
3170
|
+
const option = supportedOptions.find((candidate) => candidate.id === value);
|
|
3171
|
+
return option ? [option] : [];
|
|
3606
3172
|
});
|
|
3173
|
+
if (options.length === 1 && options[0]?.id === "none") return "none";
|
|
3607
3174
|
const response = await navigableSelect({
|
|
3608
3175
|
message: "Select authentication provider",
|
|
3609
|
-
options
|
|
3610
|
-
|
|
3176
|
+
options: options.map((option) => ({
|
|
3177
|
+
value: option.id,
|
|
3178
|
+
label: option.label,
|
|
3179
|
+
hint: option.promptHint
|
|
3180
|
+
})),
|
|
3181
|
+
initialValue: options.some((option) => option.id === DEFAULT_CONFIG.auth) ? DEFAULT_CONFIG.auth : options.find((option) => option.id !== "none")?.id ?? "none"
|
|
3611
3182
|
});
|
|
3612
3183
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3613
3184
|
return response;
|
|
@@ -3619,11 +3190,13 @@ const FULLSTACK_FRONTENDS = [
|
|
|
3619
3190
|
"next",
|
|
3620
3191
|
"tanstack-start",
|
|
3621
3192
|
"astro",
|
|
3622
|
-
"nuxt"
|
|
3193
|
+
"nuxt",
|
|
3194
|
+
"svelte",
|
|
3195
|
+
"solid-start"
|
|
3623
3196
|
];
|
|
3624
3197
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
3625
3198
|
if (backendFramework !== void 0) return backendFramework;
|
|
3626
|
-
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
|
|
3199
|
+
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid" || f === "solid-start");
|
|
3627
3200
|
const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
|
|
3628
3201
|
const backendOptions = [];
|
|
3629
3202
|
if (hasFullstackFrontend) backendOptions.push({
|
|
@@ -3769,7 +3342,7 @@ const CSS_FRAMEWORK_OPTIONS = {
|
|
|
3769
3342
|
}
|
|
3770
3343
|
};
|
|
3771
3344
|
async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
|
|
3772
|
-
const compatibleFrameworks = getCompatibleCSSFrameworks(uiLibrary);
|
|
3345
|
+
const compatibleFrameworks = getCompatibleCSSFrameworks$1(uiLibrary);
|
|
3773
3346
|
if (cssFramework !== void 0) return compatibleFrameworks.includes(cssFramework) ? cssFramework : compatibleFrameworks[0];
|
|
3774
3347
|
const selected = await navigableSelect({
|
|
3775
3348
|
message: "Select CSS framework",
|
|
@@ -4080,16 +3653,21 @@ async function getEmailChoice(email, backend) {
|
|
|
4080
3653
|
|
|
4081
3654
|
//#endregion
|
|
4082
3655
|
//#region src/prompts/examples.ts
|
|
4083
|
-
async function getExamplesChoice(examples, frontends, backend) {
|
|
3656
|
+
async function getExamplesChoice(examples, frontends, backend, runtime) {
|
|
4084
3657
|
if (examples !== void 0) return examples;
|
|
4085
3658
|
if (backend === "none") return [];
|
|
4086
3659
|
let response = [];
|
|
4087
3660
|
const options = [];
|
|
4088
|
-
if (isExampleAIAllowed(backend, frontends ?? [])) options.push({
|
|
3661
|
+
if (isExampleAIAllowed$1(backend, frontends ?? [])) options.push({
|
|
4089
3662
|
value: "ai",
|
|
4090
3663
|
label: "AI Chat",
|
|
4091
3664
|
hint: "A simple AI chat interface using AI SDK"
|
|
4092
3665
|
});
|
|
3666
|
+
if (isExampleChatSdkAllowed$1(backend, frontends ?? [], runtime)) options.push({
|
|
3667
|
+
value: "chat-sdk",
|
|
3668
|
+
label: "Chat SDK Bots",
|
|
3669
|
+
hint: "Framework-specific Chat SDK bot example (Slack/Discord/GitHub depending on stack)"
|
|
3670
|
+
});
|
|
4093
3671
|
if (options.length === 0) return [];
|
|
4094
3672
|
response = await navigableMultiselect({
|
|
4095
3673
|
message: "Include examples",
|
|
@@ -4170,7 +3748,7 @@ async function getFileUploadChoice(fileUpload, backend) {
|
|
|
4170
3748
|
//#region src/prompts/forms.ts
|
|
4171
3749
|
async function getFormsChoice(forms, frontends) {
|
|
4172
3750
|
if (forms !== void 0) return forms;
|
|
4173
|
-
const { web } = splitFrontends(frontends);
|
|
3751
|
+
const { web } = splitFrontends$1(frontends);
|
|
4174
3752
|
if (web.length === 0) return "none";
|
|
4175
3753
|
const isReact = web.some((f) => [
|
|
4176
3754
|
"tanstack-router",
|
|
@@ -4275,14 +3853,19 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
4275
3853
|
},
|
|
4276
3854
|
{
|
|
4277
3855
|
value: "svelte",
|
|
4278
|
-
label: "
|
|
4279
|
-
hint: "
|
|
3856
|
+
label: "SvelteKit",
|
|
3857
|
+
hint: "Full-stack Svelte framework with SSR and server routes"
|
|
4280
3858
|
},
|
|
4281
3859
|
{
|
|
4282
3860
|
value: "solid",
|
|
4283
3861
|
label: "Solid",
|
|
4284
3862
|
hint: "Simple and performant reactivity for building user interfaces"
|
|
4285
3863
|
},
|
|
3864
|
+
{
|
|
3865
|
+
value: "solid-start",
|
|
3866
|
+
label: "SolidStart",
|
|
3867
|
+
hint: "Full-stack Solid framework with SSR and API routes"
|
|
3868
|
+
},
|
|
4286
3869
|
{
|
|
4287
3870
|
value: "astro",
|
|
4288
3871
|
label: "Astro",
|
|
@@ -4313,7 +3896,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
4313
3896
|
label: "Fresh",
|
|
4314
3897
|
hint: "Deno-native framework with islands architecture"
|
|
4315
3898
|
}
|
|
4316
|
-
].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth)),
|
|
3899
|
+
].filter((option) => isFrontendAllowedWithBackend$1(option.value, backend, auth)),
|
|
4317
3900
|
initialValue: DEFAULT_CONFIG.frontend[0]
|
|
4318
3901
|
});
|
|
4319
3902
|
if (isGoBack(webFramework)) shouldRestart = true;
|
|
@@ -4807,7 +4390,7 @@ async function getPackageManagerChoice(packageManager) {
|
|
|
4807
4390
|
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
4808
4391
|
if (payments !== void 0) return payments;
|
|
4809
4392
|
if (backend === "none") return "none";
|
|
4810
|
-
const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0);
|
|
4393
|
+
const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends$1(frontends).web.length > 0);
|
|
4811
4394
|
const options = [];
|
|
4812
4395
|
if (isPolarCompatible) options.push({
|
|
4813
4396
|
value: "polar",
|
|
@@ -5288,11 +4871,379 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
5288
4871
|
return "none";
|
|
5289
4872
|
}
|
|
5290
4873
|
|
|
4874
|
+
//#endregion
|
|
4875
|
+
//#region src/prompts/shadcn-options.ts
|
|
4876
|
+
const BASE_OPTIONS = [{
|
|
4877
|
+
value: "radix",
|
|
4878
|
+
label: "Radix UI",
|
|
4879
|
+
hint: "Battle-tested headless primitives (130M+ monthly downloads)"
|
|
4880
|
+
}, {
|
|
4881
|
+
value: "base",
|
|
4882
|
+
label: "Base UI",
|
|
4883
|
+
hint: "MUI's headless library with cleaner APIs and native multi-select"
|
|
4884
|
+
}];
|
|
4885
|
+
const STYLE_OPTIONS = [
|
|
4886
|
+
{
|
|
4887
|
+
value: "vega",
|
|
4888
|
+
label: "Vega",
|
|
4889
|
+
hint: "Classic shadcn/ui look"
|
|
4890
|
+
},
|
|
4891
|
+
{
|
|
4892
|
+
value: "nova",
|
|
4893
|
+
label: "Nova",
|
|
4894
|
+
hint: "Compact layout with reduced padding"
|
|
4895
|
+
},
|
|
4896
|
+
{
|
|
4897
|
+
value: "maia",
|
|
4898
|
+
label: "Maia",
|
|
4899
|
+
hint: "Soft, rounded with generous spacing"
|
|
4900
|
+
},
|
|
4901
|
+
{
|
|
4902
|
+
value: "lyra",
|
|
4903
|
+
label: "Lyra",
|
|
4904
|
+
hint: "Boxy and sharp, pairs well with mono fonts"
|
|
4905
|
+
},
|
|
4906
|
+
{
|
|
4907
|
+
value: "mira",
|
|
4908
|
+
label: "Mira",
|
|
4909
|
+
hint: "Dense, made for data-heavy interfaces"
|
|
4910
|
+
}
|
|
4911
|
+
];
|
|
4912
|
+
const ICON_LIBRARY_OPTIONS = [
|
|
4913
|
+
{
|
|
4914
|
+
value: "lucide",
|
|
4915
|
+
label: "Lucide",
|
|
4916
|
+
hint: "Default icon library — clean, consistent icons"
|
|
4917
|
+
},
|
|
4918
|
+
{
|
|
4919
|
+
value: "tabler",
|
|
4920
|
+
label: "Tabler Icons",
|
|
4921
|
+
hint: "2000+ open-source SVG icons"
|
|
4922
|
+
},
|
|
4923
|
+
{
|
|
4924
|
+
value: "hugeicons",
|
|
4925
|
+
label: "HugeIcons",
|
|
4926
|
+
hint: "Modern icon set with wrapper component"
|
|
4927
|
+
},
|
|
4928
|
+
{
|
|
4929
|
+
value: "phosphor",
|
|
4930
|
+
label: "Phosphor Icons",
|
|
4931
|
+
hint: "Flexible, consistent icon family"
|
|
4932
|
+
},
|
|
4933
|
+
{
|
|
4934
|
+
value: "remixicon",
|
|
4935
|
+
label: "Remix Icon",
|
|
4936
|
+
hint: "Open-source neutral style icons"
|
|
4937
|
+
}
|
|
4938
|
+
];
|
|
4939
|
+
const COLOR_THEME_OPTIONS = [
|
|
4940
|
+
{
|
|
4941
|
+
value: "neutral",
|
|
4942
|
+
label: "Neutral",
|
|
4943
|
+
hint: "Clean and minimal"
|
|
4944
|
+
},
|
|
4945
|
+
{
|
|
4946
|
+
value: "stone",
|
|
4947
|
+
label: "Stone",
|
|
4948
|
+
hint: "Warm neutral tones"
|
|
4949
|
+
},
|
|
4950
|
+
{
|
|
4951
|
+
value: "zinc",
|
|
4952
|
+
label: "Zinc",
|
|
4953
|
+
hint: "Cool neutral tones"
|
|
4954
|
+
},
|
|
4955
|
+
{
|
|
4956
|
+
value: "gray",
|
|
4957
|
+
label: "Gray",
|
|
4958
|
+
hint: "Blue-tinted neutral"
|
|
4959
|
+
},
|
|
4960
|
+
{
|
|
4961
|
+
value: "blue",
|
|
4962
|
+
label: "Blue",
|
|
4963
|
+
hint: "Trust and reliability"
|
|
4964
|
+
},
|
|
4965
|
+
{
|
|
4966
|
+
value: "violet",
|
|
4967
|
+
label: "Violet",
|
|
4968
|
+
hint: "Creative and modern"
|
|
4969
|
+
},
|
|
4970
|
+
{
|
|
4971
|
+
value: "green",
|
|
4972
|
+
label: "Green",
|
|
4973
|
+
hint: "Growth and success"
|
|
4974
|
+
},
|
|
4975
|
+
{
|
|
4976
|
+
value: "red",
|
|
4977
|
+
label: "Red",
|
|
4978
|
+
hint: "Bold and energetic"
|
|
4979
|
+
},
|
|
4980
|
+
{
|
|
4981
|
+
value: "rose",
|
|
4982
|
+
label: "Rose",
|
|
4983
|
+
hint: "Warm and inviting"
|
|
4984
|
+
},
|
|
4985
|
+
{
|
|
4986
|
+
value: "orange",
|
|
4987
|
+
label: "Orange",
|
|
4988
|
+
hint: "Friendly and vibrant"
|
|
4989
|
+
},
|
|
4990
|
+
{
|
|
4991
|
+
value: "amber",
|
|
4992
|
+
label: "Amber",
|
|
4993
|
+
hint: "Warm and golden"
|
|
4994
|
+
},
|
|
4995
|
+
{
|
|
4996
|
+
value: "yellow",
|
|
4997
|
+
label: "Yellow",
|
|
4998
|
+
hint: "Bright and optimistic"
|
|
4999
|
+
},
|
|
5000
|
+
{
|
|
5001
|
+
value: "lime",
|
|
5002
|
+
label: "Lime",
|
|
5003
|
+
hint: "Fresh and lively"
|
|
5004
|
+
},
|
|
5005
|
+
{
|
|
5006
|
+
value: "emerald",
|
|
5007
|
+
label: "Emerald",
|
|
5008
|
+
hint: "Rich and luxurious"
|
|
5009
|
+
},
|
|
5010
|
+
{
|
|
5011
|
+
value: "teal",
|
|
5012
|
+
label: "Teal",
|
|
5013
|
+
hint: "Calm and sophisticated"
|
|
5014
|
+
},
|
|
5015
|
+
{
|
|
5016
|
+
value: "cyan",
|
|
5017
|
+
label: "Cyan",
|
|
5018
|
+
hint: "Cool and refreshing"
|
|
5019
|
+
},
|
|
5020
|
+
{
|
|
5021
|
+
value: "sky",
|
|
5022
|
+
label: "Sky",
|
|
5023
|
+
hint: "Light and airy"
|
|
5024
|
+
},
|
|
5025
|
+
{
|
|
5026
|
+
value: "indigo",
|
|
5027
|
+
label: "Indigo",
|
|
5028
|
+
hint: "Deep and focused"
|
|
5029
|
+
},
|
|
5030
|
+
{
|
|
5031
|
+
value: "purple",
|
|
5032
|
+
label: "Purple",
|
|
5033
|
+
hint: "Royal and elegant"
|
|
5034
|
+
},
|
|
5035
|
+
{
|
|
5036
|
+
value: "fuchsia",
|
|
5037
|
+
label: "Fuchsia",
|
|
5038
|
+
hint: "Playful and bold"
|
|
5039
|
+
},
|
|
5040
|
+
{
|
|
5041
|
+
value: "pink",
|
|
5042
|
+
label: "Pink",
|
|
5043
|
+
hint: "Soft and expressive"
|
|
5044
|
+
}
|
|
5045
|
+
];
|
|
5046
|
+
const BASE_COLOR_OPTIONS = [
|
|
5047
|
+
{
|
|
5048
|
+
value: "neutral",
|
|
5049
|
+
label: "Neutral",
|
|
5050
|
+
hint: "Pure neutral grays"
|
|
5051
|
+
},
|
|
5052
|
+
{
|
|
5053
|
+
value: "stone",
|
|
5054
|
+
label: "Stone",
|
|
5055
|
+
hint: "Warm-tinted grays"
|
|
5056
|
+
},
|
|
5057
|
+
{
|
|
5058
|
+
value: "zinc",
|
|
5059
|
+
label: "Zinc",
|
|
5060
|
+
hint: "Cool-tinted grays"
|
|
5061
|
+
},
|
|
5062
|
+
{
|
|
5063
|
+
value: "gray",
|
|
5064
|
+
label: "Gray",
|
|
5065
|
+
hint: "Blue-tinted grays"
|
|
5066
|
+
}
|
|
5067
|
+
];
|
|
5068
|
+
const FONT_OPTIONS = [
|
|
5069
|
+
{
|
|
5070
|
+
value: "inter",
|
|
5071
|
+
label: "Inter",
|
|
5072
|
+
hint: "Clean variable sans-serif (default)"
|
|
5073
|
+
},
|
|
5074
|
+
{
|
|
5075
|
+
value: "geist",
|
|
5076
|
+
label: "Geist",
|
|
5077
|
+
hint: "Vercel's modern typeface"
|
|
5078
|
+
},
|
|
5079
|
+
{
|
|
5080
|
+
value: "figtree",
|
|
5081
|
+
label: "Figtree",
|
|
5082
|
+
hint: "Friendly geometric sans-serif"
|
|
5083
|
+
},
|
|
5084
|
+
{
|
|
5085
|
+
value: "noto-sans",
|
|
5086
|
+
label: "Noto Sans",
|
|
5087
|
+
hint: "Google's universal typeface"
|
|
5088
|
+
},
|
|
5089
|
+
{
|
|
5090
|
+
value: "nunito-sans",
|
|
5091
|
+
label: "Nunito Sans",
|
|
5092
|
+
hint: "Rounded, balanced sans-serif"
|
|
5093
|
+
},
|
|
5094
|
+
{
|
|
5095
|
+
value: "roboto",
|
|
5096
|
+
label: "Roboto",
|
|
5097
|
+
hint: "Google's Material Design typeface"
|
|
5098
|
+
},
|
|
5099
|
+
{
|
|
5100
|
+
value: "raleway",
|
|
5101
|
+
label: "Raleway",
|
|
5102
|
+
hint: "Elegant thin-weight display font"
|
|
5103
|
+
},
|
|
5104
|
+
{
|
|
5105
|
+
value: "dm-sans",
|
|
5106
|
+
label: "DM Sans",
|
|
5107
|
+
hint: "Low-contrast geometric sans"
|
|
5108
|
+
},
|
|
5109
|
+
{
|
|
5110
|
+
value: "public-sans",
|
|
5111
|
+
label: "Public Sans",
|
|
5112
|
+
hint: "Neutral, US government typeface"
|
|
5113
|
+
},
|
|
5114
|
+
{
|
|
5115
|
+
value: "outfit",
|
|
5116
|
+
label: "Outfit",
|
|
5117
|
+
hint: "Modern geometric variable font"
|
|
5118
|
+
},
|
|
5119
|
+
{
|
|
5120
|
+
value: "jetbrains-mono",
|
|
5121
|
+
label: "JetBrains Mono",
|
|
5122
|
+
hint: "Developer-focused monospace"
|
|
5123
|
+
},
|
|
5124
|
+
{
|
|
5125
|
+
value: "geist-mono",
|
|
5126
|
+
label: "Geist Mono",
|
|
5127
|
+
hint: "Vercel's monospace typeface"
|
|
5128
|
+
}
|
|
5129
|
+
];
|
|
5130
|
+
const RADIUS_OPTIONS = [
|
|
5131
|
+
{
|
|
5132
|
+
value: "default",
|
|
5133
|
+
label: "Default",
|
|
5134
|
+
hint: "Use the style's default radius"
|
|
5135
|
+
},
|
|
5136
|
+
{
|
|
5137
|
+
value: "none",
|
|
5138
|
+
label: "None",
|
|
5139
|
+
hint: "Sharp corners (0)"
|
|
5140
|
+
},
|
|
5141
|
+
{
|
|
5142
|
+
value: "small",
|
|
5143
|
+
label: "Small",
|
|
5144
|
+
hint: "Subtle rounding (0.45rem)"
|
|
5145
|
+
},
|
|
5146
|
+
{
|
|
5147
|
+
value: "medium",
|
|
5148
|
+
label: "Medium",
|
|
5149
|
+
hint: "Moderate rounding (0.625rem)"
|
|
5150
|
+
},
|
|
5151
|
+
{
|
|
5152
|
+
value: "large",
|
|
5153
|
+
label: "Large",
|
|
5154
|
+
hint: "Generous rounding (0.875rem)"
|
|
5155
|
+
}
|
|
5156
|
+
];
|
|
5157
|
+
const SHADCN_DEFAULTS = {
|
|
5158
|
+
shadcnBase: "radix",
|
|
5159
|
+
shadcnStyle: "nova",
|
|
5160
|
+
shadcnIconLibrary: "lucide",
|
|
5161
|
+
shadcnColorTheme: "neutral",
|
|
5162
|
+
shadcnBaseColor: "neutral",
|
|
5163
|
+
shadcnFont: "inter",
|
|
5164
|
+
shadcnRadius: "default"
|
|
5165
|
+
};
|
|
5166
|
+
async function getShadcnOptions(flags) {
|
|
5167
|
+
const fallback = (key) => isSilent() ? SHADCN_DEFAULTS[key] : void 0;
|
|
5168
|
+
return {
|
|
5169
|
+
shadcnBase: flags.shadcnBase ?? fallback("shadcnBase") ?? await promptShadcnBase(),
|
|
5170
|
+
shadcnStyle: flags.shadcnStyle ?? fallback("shadcnStyle") ?? await promptShadcnStyle(),
|
|
5171
|
+
shadcnIconLibrary: flags.shadcnIconLibrary ?? fallback("shadcnIconLibrary") ?? await promptShadcnIconLibrary(),
|
|
5172
|
+
shadcnColorTheme: flags.shadcnColorTheme ?? fallback("shadcnColorTheme") ?? await promptShadcnColorTheme(),
|
|
5173
|
+
shadcnBaseColor: flags.shadcnBaseColor ?? fallback("shadcnBaseColor") ?? await promptShadcnBaseColor(),
|
|
5174
|
+
shadcnFont: flags.shadcnFont ?? fallback("shadcnFont") ?? await promptShadcnFont(),
|
|
5175
|
+
shadcnRadius: flags.shadcnRadius ?? fallback("shadcnRadius") ?? await promptShadcnRadius()
|
|
5176
|
+
};
|
|
5177
|
+
}
|
|
5178
|
+
async function promptShadcnBase() {
|
|
5179
|
+
const selected = await navigableSelect({
|
|
5180
|
+
message: "Select shadcn/ui base library",
|
|
5181
|
+
options: BASE_OPTIONS,
|
|
5182
|
+
initialValue: "radix"
|
|
5183
|
+
});
|
|
5184
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5185
|
+
return selected;
|
|
5186
|
+
}
|
|
5187
|
+
async function promptShadcnStyle() {
|
|
5188
|
+
const selected = await navigableSelect({
|
|
5189
|
+
message: "Select shadcn/ui visual style",
|
|
5190
|
+
options: STYLE_OPTIONS,
|
|
5191
|
+
initialValue: "nova"
|
|
5192
|
+
});
|
|
5193
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5194
|
+
return selected;
|
|
5195
|
+
}
|
|
5196
|
+
async function promptShadcnIconLibrary() {
|
|
5197
|
+
const selected = await navigableSelect({
|
|
5198
|
+
message: "Select shadcn/ui icon library",
|
|
5199
|
+
options: ICON_LIBRARY_OPTIONS,
|
|
5200
|
+
initialValue: "lucide"
|
|
5201
|
+
});
|
|
5202
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5203
|
+
return selected;
|
|
5204
|
+
}
|
|
5205
|
+
async function promptShadcnColorTheme() {
|
|
5206
|
+
const selected = await navigableSelect({
|
|
5207
|
+
message: "Select shadcn/ui color theme",
|
|
5208
|
+
options: COLOR_THEME_OPTIONS,
|
|
5209
|
+
initialValue: "neutral"
|
|
5210
|
+
});
|
|
5211
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5212
|
+
return selected;
|
|
5213
|
+
}
|
|
5214
|
+
async function promptShadcnBaseColor() {
|
|
5215
|
+
const selected = await navigableSelect({
|
|
5216
|
+
message: "Select shadcn/ui base neutral color",
|
|
5217
|
+
options: BASE_COLOR_OPTIONS,
|
|
5218
|
+
initialValue: "neutral"
|
|
5219
|
+
});
|
|
5220
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5221
|
+
return selected;
|
|
5222
|
+
}
|
|
5223
|
+
async function promptShadcnFont() {
|
|
5224
|
+
const selected = await navigableSelect({
|
|
5225
|
+
message: "Select shadcn/ui font",
|
|
5226
|
+
options: FONT_OPTIONS,
|
|
5227
|
+
initialValue: "inter"
|
|
5228
|
+
});
|
|
5229
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5230
|
+
return selected;
|
|
5231
|
+
}
|
|
5232
|
+
async function promptShadcnRadius() {
|
|
5233
|
+
const selected = await navigableSelect({
|
|
5234
|
+
message: "Select shadcn/ui border radius",
|
|
5235
|
+
options: RADIUS_OPTIONS,
|
|
5236
|
+
initialValue: "default"
|
|
5237
|
+
});
|
|
5238
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5239
|
+
return selected;
|
|
5240
|
+
}
|
|
5241
|
+
|
|
5291
5242
|
//#endregion
|
|
5292
5243
|
//#region src/prompts/state-management.ts
|
|
5293
5244
|
async function getStateManagementChoice(stateManagement, frontends) {
|
|
5294
5245
|
if (stateManagement !== void 0) return stateManagement;
|
|
5295
|
-
const { web } = splitFrontends(frontends);
|
|
5246
|
+
const { web } = splitFrontends$1(frontends);
|
|
5296
5247
|
if (web.length === 0) return "none";
|
|
5297
5248
|
const isReact = web.some((f) => [
|
|
5298
5249
|
"tanstack-router",
|
|
@@ -5452,9 +5403,9 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
5452
5403
|
}
|
|
5453
5404
|
};
|
|
5454
5405
|
async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
|
|
5455
|
-
const { web } = splitFrontends(frontends);
|
|
5406
|
+
const { web } = splitFrontends$1(frontends);
|
|
5456
5407
|
if (web.length === 0) return "none";
|
|
5457
|
-
const compatibleLibraries = getCompatibleUILibraries(frontends, astroIntegration);
|
|
5408
|
+
const compatibleLibraries = getCompatibleUILibraries$1(frontends, astroIntegration);
|
|
5458
5409
|
if (uiLibrary !== void 0) return compatibleLibraries.includes(uiLibrary) ? uiLibrary : compatibleLibraries[0];
|
|
5459
5410
|
const options = compatibleLibraries.map((lib) => ({
|
|
5460
5411
|
value: lib,
|
|
@@ -5525,6 +5476,24 @@ async function getValidationChoice(validation) {
|
|
|
5525
5476
|
return response;
|
|
5526
5477
|
}
|
|
5527
5478
|
|
|
5479
|
+
//#endregion
|
|
5480
|
+
//#region src/utils/compatibility.ts
|
|
5481
|
+
const WEB_FRAMEWORKS = [
|
|
5482
|
+
"tanstack-router",
|
|
5483
|
+
"react-router",
|
|
5484
|
+
"tanstack-start",
|
|
5485
|
+
"next",
|
|
5486
|
+
"nuxt",
|
|
5487
|
+
"svelte",
|
|
5488
|
+
"solid",
|
|
5489
|
+
"solid-start",
|
|
5490
|
+
"astro",
|
|
5491
|
+
"qwik",
|
|
5492
|
+
"angular",
|
|
5493
|
+
"redwood",
|
|
5494
|
+
"fresh"
|
|
5495
|
+
];
|
|
5496
|
+
|
|
5528
5497
|
//#endregion
|
|
5529
5498
|
//#region src/prompts/web-deploy.ts
|
|
5530
5499
|
function hasWebFrontend(frontends) {
|
|
@@ -5575,12 +5544,24 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5575
5544
|
},
|
|
5576
5545
|
uiLibrary: ({ results }) => {
|
|
5577
5546
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5578
|
-
if (hasWebStyling(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
|
|
5547
|
+
if (hasWebStyling$1(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
|
|
5579
5548
|
return Promise.resolve("none");
|
|
5580
5549
|
},
|
|
5550
|
+
shadcnOptions: ({ results }) => {
|
|
5551
|
+
if (results.uiLibrary !== "shadcn-ui") return Promise.resolve(void 0);
|
|
5552
|
+
return getShadcnOptions({
|
|
5553
|
+
shadcnBase: flags.shadcnBase,
|
|
5554
|
+
shadcnStyle: flags.shadcnStyle,
|
|
5555
|
+
shadcnIconLibrary: flags.shadcnIconLibrary,
|
|
5556
|
+
shadcnColorTheme: flags.shadcnColorTheme,
|
|
5557
|
+
shadcnBaseColor: flags.shadcnBaseColor,
|
|
5558
|
+
shadcnFont: flags.shadcnFont,
|
|
5559
|
+
shadcnRadius: flags.shadcnRadius
|
|
5560
|
+
});
|
|
5561
|
+
},
|
|
5581
5562
|
cssFramework: ({ results }) => {
|
|
5582
5563
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5583
|
-
if (hasWebStyling(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
|
|
5564
|
+
if (hasWebStyling$1(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
|
|
5584
5565
|
return Promise.resolve("none");
|
|
5585
5566
|
},
|
|
5586
5567
|
backend: ({ results }) => {
|
|
@@ -5604,8 +5585,9 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5604
5585
|
return getApiChoice(flags.api, results.frontend, results.backend, results.astroIntegration);
|
|
5605
5586
|
},
|
|
5606
5587
|
auth: ({ results }) => {
|
|
5607
|
-
if (results.ecosystem
|
|
5608
|
-
return getAuthChoice(flags.auth,
|
|
5588
|
+
if (results.ecosystem === "typescript") return getAuthChoice(flags.auth, results.backend, results.frontend, "typescript");
|
|
5589
|
+
if (results.ecosystem === "go") return getAuthChoice(flags.auth, void 0, void 0, "go");
|
|
5590
|
+
return Promise.resolve("none");
|
|
5609
5591
|
},
|
|
5610
5592
|
payments: ({ results }) => {
|
|
5611
5593
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5625,7 +5607,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5625
5607
|
},
|
|
5626
5608
|
examples: ({ results }) => {
|
|
5627
5609
|
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
5628
|
-
return getExamplesChoice(flags.examples, results.frontend, results.backend);
|
|
5610
|
+
return getExamplesChoice(flags.examples, results.frontend, results.backend, results.runtime);
|
|
5629
5611
|
},
|
|
5630
5612
|
dbSetup: ({ results }) => {
|
|
5631
5613
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5641,6 +5623,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5641
5623
|
},
|
|
5642
5624
|
ai: ({ results }) => {
|
|
5643
5625
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5626
|
+
if (flags.ai === void 0 && results.examples?.includes("chat-sdk") && requiresChatSdkVercelAI(results.backend, results.frontend, results.runtime)) return Promise.resolve("vercel-ai");
|
|
5644
5627
|
return getAIChoice(flags.ai);
|
|
5645
5628
|
},
|
|
5646
5629
|
validation: ({ results }) => {
|
|
@@ -5790,6 +5773,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5790
5773
|
frontend: result.frontend,
|
|
5791
5774
|
astroIntegration: result.astroIntegration,
|
|
5792
5775
|
uiLibrary: result.uiLibrary,
|
|
5776
|
+
...result.shadcnOptions ?? {},
|
|
5793
5777
|
cssFramework: result.cssFramework,
|
|
5794
5778
|
backend: result.backend,
|
|
5795
5779
|
runtime: result.runtime,
|
|
@@ -6018,6 +6002,15 @@ function generateReproducibleCommand(config) {
|
|
|
6018
6002
|
flags.push(`--effect ${config.effect}`);
|
|
6019
6003
|
flags.push(`--css-framework ${config.cssFramework}`);
|
|
6020
6004
|
flags.push(`--ui-library ${config.uiLibrary}`);
|
|
6005
|
+
if (config.uiLibrary === "shadcn-ui") {
|
|
6006
|
+
flags.push(`--shadcn-base ${config.shadcnBase}`);
|
|
6007
|
+
flags.push(`--shadcn-style ${config.shadcnStyle}`);
|
|
6008
|
+
flags.push(`--shadcn-icon-library ${config.shadcnIconLibrary}`);
|
|
6009
|
+
flags.push(`--shadcn-color-theme ${config.shadcnColorTheme}`);
|
|
6010
|
+
flags.push(`--shadcn-base-color ${config.shadcnBaseColor}`);
|
|
6011
|
+
flags.push(`--shadcn-font ${config.shadcnFont}`);
|
|
6012
|
+
flags.push(`--shadcn-radius ${config.shadcnRadius}`);
|
|
6013
|
+
}
|
|
6021
6014
|
flags.push(`--ai ${config.ai}`);
|
|
6022
6015
|
flags.push(`--state-management ${config.stateManagement}`);
|
|
6023
6016
|
flags.push(`--forms ${config.forms}`);
|
|
@@ -6273,6 +6266,13 @@ function processFlags(options, projectName) {
|
|
|
6273
6266
|
if (options.astroIntegration) config.astroIntegration = options.astroIntegration;
|
|
6274
6267
|
if (options.cssFramework) config.cssFramework = options.cssFramework;
|
|
6275
6268
|
if (options.uiLibrary) config.uiLibrary = options.uiLibrary;
|
|
6269
|
+
if (options.shadcnBase) config.shadcnBase = options.shadcnBase;
|
|
6270
|
+
if (options.shadcnStyle) config.shadcnStyle = options.shadcnStyle;
|
|
6271
|
+
if (options.shadcnIconLibrary) config.shadcnIconLibrary = options.shadcnIconLibrary;
|
|
6272
|
+
if (options.shadcnColorTheme) config.shadcnColorTheme = options.shadcnColorTheme;
|
|
6273
|
+
if (options.shadcnBaseColor) config.shadcnBaseColor = options.shadcnBaseColor;
|
|
6274
|
+
if (options.shadcnFont) config.shadcnFont = options.shadcnFont;
|
|
6275
|
+
if (options.shadcnRadius) config.shadcnRadius = options.shadcnRadius;
|
|
6276
6276
|
if (options.addons && options.addons.length > 0) config.addons = processArrayOption(options.addons);
|
|
6277
6277
|
if (options.examples && options.examples.length > 0) config.examples = processArrayOption(options.examples);
|
|
6278
6278
|
if (options.aiDocs !== void 0) config.aiDocs = processArrayOption(options.aiDocs);
|
|
@@ -6514,7 +6514,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
6514
6514
|
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
6515
6515
|
});
|
|
6516
6516
|
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") incompatibilityError({
|
|
6517
|
-
message: "MongoDB only
|
|
6517
|
+
message: "In Better-Fullstack, MongoDB is currently supported only with Mongoose or Prisma ORM.",
|
|
6518
6518
|
provided: {
|
|
6519
6519
|
database: "mongodb",
|
|
6520
6520
|
orm
|
|
@@ -6597,7 +6597,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6597
6597
|
runtime: "workers",
|
|
6598
6598
|
errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
|
|
6599
6599
|
},
|
|
6600
|
-
docker: { errorMessage: "Docker setup is not
|
|
6600
|
+
docker: { errorMessage: "In Better-Fullstack, Docker setup is currently not available with SQLite database or Cloudflare Workers runtime." },
|
|
6601
6601
|
none: { errorMessage: "" }
|
|
6602
6602
|
};
|
|
6603
6603
|
if (dbSetup && dbSetup !== "none") {
|
|
@@ -6607,11 +6607,23 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6607
6607
|
} else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
|
|
6608
6608
|
if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
|
|
6609
6609
|
if (dbSetup === "docker") {
|
|
6610
|
-
if (database === "sqlite") exitWithError("Docker setup is not
|
|
6611
|
-
if (runtime === "workers") exitWithError("Docker setup is not
|
|
6610
|
+
if (database === "sqlite") exitWithError("In Better-Fullstack, Docker setup is currently not available with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
|
|
6611
|
+
if (runtime === "workers") exitWithError("In Better-Fullstack, Docker setup is currently not available with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
6612
6612
|
}
|
|
6613
6613
|
}
|
|
6614
6614
|
}
|
|
6615
|
+
function validateEcosystemAuthCompatibility(config, providedFlags) {
|
|
6616
|
+
const auth = config.auth;
|
|
6617
|
+
if (!auth || auth === "none") return;
|
|
6618
|
+
const normalized = (0, types_exports.normalizeCapabilitySelection)("auth", {
|
|
6619
|
+
ecosystem: config.ecosystem,
|
|
6620
|
+
backend: config.backend,
|
|
6621
|
+
frontend: config.frontend
|
|
6622
|
+
}, auth);
|
|
6623
|
+
if (!normalized.normalized || normalized.value === auth) return;
|
|
6624
|
+
config.auth = normalized.value;
|
|
6625
|
+
if (providedFlags?.has("auth") && normalized.reason && !isSilent()) consola.warn(pc.yellow(`Unsupported auth selection '${auth}' for the current stack: ${normalized.reason}. Falling back to '--auth ${normalized.value}'.`));
|
|
6626
|
+
}
|
|
6615
6627
|
function validateConvexConstraints(config, providedFlags) {
|
|
6616
6628
|
const { backend } = config;
|
|
6617
6629
|
if (backend !== "convex") return;
|
|
@@ -6664,30 +6676,6 @@ function validateConvexConstraints(config, providedFlags) {
|
|
|
6664
6676
|
},
|
|
6665
6677
|
suggestions: ["Remove --server-deploy flag", "Set --server-deploy none"]
|
|
6666
6678
|
});
|
|
6667
|
-
if (has("auth") && config.auth === "better-auth") {
|
|
6668
|
-
const supportedFrontends = [
|
|
6669
|
-
"tanstack-router",
|
|
6670
|
-
"tanstack-start",
|
|
6671
|
-
"next",
|
|
6672
|
-
"native-bare",
|
|
6673
|
-
"native-uniwind",
|
|
6674
|
-
"native-unistyles"
|
|
6675
|
-
];
|
|
6676
|
-
if (!config.frontend?.some((f) => supportedFrontends.includes(f))) incompatibilityError({
|
|
6677
|
-
message: "Better-Auth with Convex requires specific frontends.",
|
|
6678
|
-
provided: {
|
|
6679
|
-
backend: "convex",
|
|
6680
|
-
auth: "better-auth",
|
|
6681
|
-
frontend: config.frontend || []
|
|
6682
|
-
},
|
|
6683
|
-
suggestions: [
|
|
6684
|
-
"Use --frontend tanstack-router",
|
|
6685
|
-
"Use --frontend tanstack-start",
|
|
6686
|
-
"Use --frontend next",
|
|
6687
|
-
"Use a native frontend"
|
|
6688
|
-
]
|
|
6689
|
-
});
|
|
6690
|
-
}
|
|
6691
6679
|
}
|
|
6692
6680
|
function validateBackendNoneConstraints(config, providedFlags) {
|
|
6693
6681
|
const { backend } = config;
|
|
@@ -6697,7 +6685,6 @@ function validateBackendNoneConstraints(config, providedFlags) {
|
|
|
6697
6685
|
if (has("database") && config.database !== "none") exitWithError("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
|
|
6698
6686
|
if (has("orm") && config.orm !== "none") exitWithError("Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.");
|
|
6699
6687
|
if (has("api") && config.api !== "none") exitWithError("Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.");
|
|
6700
|
-
if (has("auth") && config.auth !== "none") exitWithError("Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.");
|
|
6701
6688
|
if (has("payments") && config.payments !== "none") exitWithError("Backend 'none' requires '--payments none'. Please remove the --payments flag or set it to 'none'.");
|
|
6702
6689
|
if (has("dbSetup") && config.dbSetup !== "none") exitWithError("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
|
|
6703
6690
|
if (has("serverDeploy") && config.serverDeploy !== "none") exitWithError("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
|
|
@@ -6727,15 +6714,6 @@ function validateAdonisJSConstraints(config, providedFlags) {
|
|
|
6727
6714
|
}
|
|
6728
6715
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
6729
6716
|
const { backend } = config;
|
|
6730
|
-
if (config.auth === "clerk" && backend !== "convex") exitWithError("Clerk authentication is only supported with the Convex backend. Please use '--backend convex' or choose a different auth provider.");
|
|
6731
|
-
if (backend === "convex" && config.auth === "clerk" && config.frontend) {
|
|
6732
|
-
const incompatibleFrontends = config.frontend.filter((f) => [
|
|
6733
|
-
"nuxt",
|
|
6734
|
-
"svelte",
|
|
6735
|
-
"solid"
|
|
6736
|
-
].includes(f));
|
|
6737
|
-
if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
|
|
6738
|
-
}
|
|
6739
6717
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self" && backend !== "encore") {
|
|
6740
6718
|
if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex', '--backend none', '--backend self', or '--backend encore'. Please choose 'bun', 'node', or remove the --runtime flag.");
|
|
6741
6719
|
}
|
|
@@ -6750,11 +6728,32 @@ function validateFrontendConstraints(config, providedFlags) {
|
|
|
6750
6728
|
ensureSingleWebAndNative(frontend);
|
|
6751
6729
|
if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
|
|
6752
6730
|
}
|
|
6753
|
-
const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend(f));
|
|
6731
|
+
const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend$1(f));
|
|
6754
6732
|
validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag);
|
|
6755
6733
|
}
|
|
6756
6734
|
function validateApiConstraints(_config, _options) {}
|
|
6735
|
+
function validateShadcnConstraints(config, providedFlags) {
|
|
6736
|
+
const shadcnFlagMap = {
|
|
6737
|
+
shadcnBase: "--shadcn-base",
|
|
6738
|
+
shadcnStyle: "--shadcn-style",
|
|
6739
|
+
shadcnIconLibrary: "--shadcn-icon-library",
|
|
6740
|
+
shadcnColorTheme: "--shadcn-color-theme",
|
|
6741
|
+
shadcnBaseColor: "--shadcn-base-color",
|
|
6742
|
+
shadcnFont: "--shadcn-font",
|
|
6743
|
+
shadcnRadius: "--shadcn-radius"
|
|
6744
|
+
};
|
|
6745
|
+
const providedShadcnFlags = Object.keys(shadcnFlagMap).filter((f) => providedFlags.has(f));
|
|
6746
|
+
if (providedShadcnFlags.length > 0 && config.uiLibrary !== "shadcn-ui") incompatibilityError({
|
|
6747
|
+
message: "shadcn/ui customization flags require --ui-library shadcn-ui.",
|
|
6748
|
+
provided: {
|
|
6749
|
+
"ui-library": config.uiLibrary || "none",
|
|
6750
|
+
...Object.fromEntries(providedShadcnFlags.map((f) => [shadcnFlagMap[f], String(config[f] ?? "")]))
|
|
6751
|
+
},
|
|
6752
|
+
suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
|
|
6753
|
+
});
|
|
6754
|
+
}
|
|
6757
6755
|
function validateFullConfig(config, providedFlags, options) {
|
|
6756
|
+
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
6758
6757
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
6759
6758
|
validateDatabaseSetup(config, providedFlags);
|
|
6760
6759
|
validateConvexConstraints(config, providedFlags);
|
|
@@ -6774,28 +6773,22 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
6774
6773
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6775
6774
|
config.addons = [...new Set(config.addons)];
|
|
6776
6775
|
}
|
|
6777
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6776
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6778
6777
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
6779
|
-
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6780
|
-
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6781
|
-
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6782
|
-
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6783
6778
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
6784
6779
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
6780
|
+
validateShadcnConstraints(config, providedFlags);
|
|
6785
6781
|
validatePeerDependencies(config);
|
|
6786
6782
|
}
|
|
6787
6783
|
function validateConfigForProgrammaticUse(config) {
|
|
6788
6784
|
try {
|
|
6785
|
+
validateEcosystemAuthCompatibility(config);
|
|
6789
6786
|
validateDatabaseOrmAuth(config);
|
|
6790
6787
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
6791
6788
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
6792
6789
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
6793
|
-
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6794
|
-
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6795
|
-
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6796
|
-
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6797
6790
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6798
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6791
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6799
6792
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
6800
6793
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
6801
6794
|
validatePeerDependencies(config);
|
|
@@ -6901,6 +6894,8 @@ async function formatCode(filePath, content) {
|
|
|
6901
6894
|
}
|
|
6902
6895
|
async function formatProject(projectDir) {
|
|
6903
6896
|
async function formatDirectory(dir) {
|
|
6897
|
+
const denoConfigPath = path.join(dir, "deno.json");
|
|
6898
|
+
if (await fs.pathExists(denoConfigPath)) return;
|
|
6904
6899
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
6905
6900
|
await Promise.all(entries.map(async (entry) => {
|
|
6906
6901
|
const fullPath = path.join(dir, entry.name);
|
|
@@ -8117,20 +8112,20 @@ async function displayPostInstallInstructions(config) {
|
|
|
8117
8112
|
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
|
|
8118
8113
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
8119
8114
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
8120
|
-
const clerkInstructions =
|
|
8115
|
+
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
|
|
8121
8116
|
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
8122
8117
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
8123
8118
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
8124
8119
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
8125
8120
|
const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
|
|
8126
8121
|
const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
|
|
8127
|
-
const
|
|
8128
|
-
const
|
|
8129
|
-
const webPort = hasReactRouter || hasSvelte ? "5173" : "3001";
|
|
8122
|
+
const hasFresh = frontend?.includes("fresh");
|
|
8123
|
+
const webPort = String(getLocalWebDevPort(frontend ?? []));
|
|
8130
8124
|
const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
|
|
8131
8125
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
8132
8126
|
let stepCounter = 2;
|
|
8133
8127
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
8128
|
+
if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH\n`;
|
|
8134
8129
|
if (database === "sqlite" && dbSetup !== "d1") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (optional - starts local SQLite database)")}\n`;
|
|
8135
8130
|
if (isConvex) {
|
|
8136
8131
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
@@ -8248,8 +8243,13 @@ function getNoOrmWarning() {
|
|
|
8248
8243
|
function getBunWebNativeWarning() {
|
|
8249
8244
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
8250
8245
|
}
|
|
8251
|
-
function getClerkInstructions() {
|
|
8252
|
-
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
8246
|
+
function getClerkInstructions(backend, frontend) {
|
|
8247
|
+
if (backend === "convex") return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
|
|
8248
|
+
if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
|
|
8249
|
+
const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
|
|
8250
|
+
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Create an application in ${pc.underline("https://dashboard.clerk.com/")}\n${pc.cyan("•")} Set ${publishableKeyVar} in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Set CLERK_SECRET_KEY in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Clerk middleware and a protected dashboard route are already generated`;
|
|
8251
|
+
}
|
|
8252
|
+
return "";
|
|
8253
8253
|
}
|
|
8254
8254
|
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
8255
8255
|
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
@@ -8437,7 +8437,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
|
|
|
8437
8437
|
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
8438
8438
|
if (!await fs.pathExists(pkgJsonPath)) return;
|
|
8439
8439
|
try {
|
|
8440
|
-
const { stdout } = await
|
|
8440
|
+
const { stdout } = await $({ cwd: os$1.tmpdir() })`${packageManager} -v`;
|
|
8441
8441
|
const version = stdout.trim();
|
|
8442
8442
|
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
8443
8443
|
pkgJson.packageManager = `${packageManager}@${version}`;
|
|
@@ -8767,7 +8767,7 @@ const router = os.router({
|
|
|
8767
8767
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
8768
8768
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
8769
8769
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
8770
|
-
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, or
|
|
8770
|
+
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, or go)"),
|
|
8771
8771
|
database: types_exports.DatabaseSchema.optional(),
|
|
8772
8772
|
orm: types_exports.ORMSchema.optional(),
|
|
8773
8773
|
auth: types_exports.AuthSchema.optional(),
|
|
@@ -8785,6 +8785,7 @@ const router = os.router({
|
|
|
8785
8785
|
animation: types_exports.AnimationSchema.optional(),
|
|
8786
8786
|
logging: types_exports.LoggingSchema.optional(),
|
|
8787
8787
|
observability: types_exports.ObservabilitySchema.optional(),
|
|
8788
|
+
featureFlags: types_exports.FeatureFlagsSchema.optional().describe("Feature flags provider"),
|
|
8788
8789
|
analytics: types_exports.AnalyticsSchema.optional().describe("Privacy-focused analytics"),
|
|
8789
8790
|
cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
|
|
8790
8791
|
caching: types_exports.CachingSchema.optional().describe("Caching solution"),
|
|
@@ -8803,6 +8804,13 @@ const router = os.router({
|
|
|
8803
8804
|
api: types_exports.APISchema.optional(),
|
|
8804
8805
|
cssFramework: types_exports.CSSFrameworkSchema.optional(),
|
|
8805
8806
|
uiLibrary: types_exports.UILibrarySchema.optional(),
|
|
8807
|
+
shadcnBase: types_exports.ShadcnBaseSchema.optional().describe("shadcn/ui headless library (radix, base)"),
|
|
8808
|
+
shadcnStyle: types_exports.ShadcnStyleSchema.optional().describe("shadcn/ui visual style (vega, nova, maia, lyra, mira)"),
|
|
8809
|
+
shadcnIconLibrary: types_exports.ShadcnIconLibrarySchema.optional().describe("shadcn/ui icon library (lucide, tabler, hugeicons, phosphor, remixicon)"),
|
|
8810
|
+
shadcnColorTheme: types_exports.ShadcnColorThemeSchema.optional().describe("shadcn/ui color theme (neutral, blue, violet, etc.)"),
|
|
8811
|
+
shadcnBaseColor: types_exports.ShadcnBaseColorSchema.optional().describe("shadcn/ui base neutral color (neutral, stone, zinc, gray)"),
|
|
8812
|
+
shadcnFont: types_exports.ShadcnFontSchema.optional().describe("shadcn/ui font (inter, geist, figtree, etc.)"),
|
|
8813
|
+
shadcnRadius: types_exports.ShadcnRadiusSchema.optional().describe("shadcn/ui border radius (default, none, small, medium, large)"),
|
|
8806
8814
|
webDeploy: types_exports.WebDeploySchema.optional(),
|
|
8807
8815
|
serverDeploy: types_exports.ServerDeploySchema.optional(),
|
|
8808
8816
|
directoryConflict: types_exports.DirectoryConflictSchema.optional(),
|
|
@@ -9021,6 +9029,13 @@ async function createVirtual(options) {
|
|
|
9021
9029
|
serverDeploy: options.serverDeploy || "none",
|
|
9022
9030
|
cssFramework: options.cssFramework || "tailwind",
|
|
9023
9031
|
uiLibrary: options.uiLibrary || "shadcn-ui",
|
|
9032
|
+
shadcnBase: options.shadcnBase ?? "radix",
|
|
9033
|
+
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
9034
|
+
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
9035
|
+
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
9036
|
+
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
9037
|
+
shadcnFont: options.shadcnFont ?? "inter",
|
|
9038
|
+
shadcnRadius: options.shadcnRadius ?? "default",
|
|
9024
9039
|
ai: options.ai || "none",
|
|
9025
9040
|
stateManagement: options.stateManagement || "none",
|
|
9026
9041
|
forms: options.forms || "react-hook-form",
|