create-better-fullstack 1.3.17 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +88 -4
- package/dist/index.mjs +1 -1
- package/dist/{src-BYQLJiGD.mjs → src-B5kV0Yns.mjs} +637 -482
- 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, 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,75 @@ function validateApiFrontendCompatibility(api, frontends = [], astroIntegration)
|
|
|
1044
788
|
]
|
|
1045
789
|
});
|
|
1046
790
|
}
|
|
1047
|
-
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
if (
|
|
1052
|
-
if (backend === "convex"
|
|
1053
|
-
|
|
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 ([
|
|
791
|
+
function isFrontendAllowedWithBackend$1(frontend, backend, auth) {
|
|
792
|
+
return isFrontendAllowedWithBackend(frontend, backend, auth);
|
|
793
|
+
}
|
|
794
|
+
function validateClerkCompatibility(auth, backend, frontends = []) {
|
|
795
|
+
if (auth !== "clerk") return;
|
|
796
|
+
if (backend === "convex") {
|
|
797
|
+
const incompatibleFrontends = frontends.filter((f) => [
|
|
1060
798
|
"nuxt",
|
|
1061
799
|
"svelte",
|
|
1062
|
-
"solid"
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
if (backend !== "self") return false;
|
|
800
|
+
"solid",
|
|
801
|
+
"solid-start"
|
|
802
|
+
].includes(f));
|
|
803
|
+
if (incompatibleFrontends.length > 0) exitWithError(`In Better-Fullstack, Clerk + Convex is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
|
|
804
|
+
return;
|
|
1068
805
|
}
|
|
1069
|
-
if (
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
806
|
+
if (backend === "self") {
|
|
807
|
+
if (frontends.some((f) => [
|
|
808
|
+
"native-bare",
|
|
809
|
+
"native-uniwind",
|
|
810
|
+
"native-unistyles"
|
|
811
|
+
].includes(f))) exitWithError("In Better-Fullstack, Clerk with the 'self' backend is currently supported only for web-only Next.js or TanStack Start projects (no native companion app). Please remove the native frontend or choose a different auth provider.");
|
|
812
|
+
const hasNextJs = frontends.includes("next");
|
|
813
|
+
const hasTanStackStart = frontends.includes("tanstack-start");
|
|
814
|
+
if (!hasNextJs && !hasTanStackStart) {
|
|
815
|
+
if (frontends.includes("astro")) exitWithError("In Better-Fullstack, Clerk is not yet supported for Astro fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
|
|
816
|
+
if (frontends.includes("nuxt")) exitWithError("In Better-Fullstack, Clerk is not yet supported for Nuxt fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
|
|
817
|
+
if (frontends.includes("svelte")) exitWithError("In Better-Fullstack, Clerk is not yet supported for SvelteKit fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
|
|
818
|
+
if (frontends.includes("solid-start")) exitWithError("In Better-Fullstack, Clerk is not yet supported for SolidStart fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
|
|
819
|
+
exitWithError("In Better-Fullstack, Clerk with the 'self' backend currently requires the Next.js or TanStack Start frontend. Please use '--frontend next' or '--frontend tanstack-start', or choose a different auth provider.");
|
|
820
|
+
}
|
|
821
|
+
return;
|
|
1072
822
|
}
|
|
1073
|
-
|
|
823
|
+
exitWithError("In Better-Fullstack, Clerk authentication is currently supported with the Convex backend, or with the 'self' backend when using Next.js or TanStack Start. Please choose a supported backend/frontend combination or a different auth provider.");
|
|
1074
824
|
}
|
|
1075
825
|
function validateNextAuthCompatibility(auth, backend, frontends = []) {
|
|
1076
826
|
if (auth !== "nextauth") return;
|
|
1077
827
|
const hasNextJs = frontends.includes("next");
|
|
1078
|
-
if (backend !== "self") exitWithError("Auth.js (NextAuth) is
|
|
1079
|
-
if (!hasNextJs) exitWithError("Auth.js (NextAuth) requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
828
|
+
if (backend !== "self") exitWithError("In Better-Fullstack, Auth.js (NextAuth) is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
829
|
+
if (!hasNextJs) exitWithError("In Better-Fullstack, Auth.js (NextAuth) currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1080
830
|
}
|
|
1081
831
|
function validateStackAuthCompatibility(auth, backend, frontends = []) {
|
|
1082
832
|
if (auth !== "stack-auth") return;
|
|
1083
833
|
const hasNextJs = frontends.includes("next");
|
|
1084
|
-
if (backend !== "self") exitWithError("Stack Auth is
|
|
1085
|
-
if (!hasNextJs) exitWithError("Stack Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
834
|
+
if (backend !== "self") exitWithError("In Better-Fullstack, Stack Auth is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
835
|
+
if (!hasNextJs) exitWithError("In Better-Fullstack, Stack Auth currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1086
836
|
}
|
|
1087
837
|
function validateSupabaseAuthCompatibility(auth, backend, frontends = []) {
|
|
1088
838
|
if (auth !== "supabase-auth") return;
|
|
1089
839
|
const hasNextJs = frontends.includes("next");
|
|
1090
|
-
if (backend !== "self") exitWithError("Supabase Auth is
|
|
1091
|
-
if (!hasNextJs) exitWithError("Supabase Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
840
|
+
if (backend !== "self") exitWithError("In Better-Fullstack, Supabase Auth is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
841
|
+
if (!hasNextJs) exitWithError("In Better-Fullstack, Supabase Auth currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1092
842
|
}
|
|
1093
843
|
function validateAuth0Compatibility(auth, backend, frontends = []) {
|
|
1094
844
|
if (auth !== "auth0") return;
|
|
1095
845
|
const hasNextJs = frontends.includes("next");
|
|
1096
|
-
if (backend !== "self") exitWithError("Auth0 is
|
|
1097
|
-
if (!hasNextJs) exitWithError("Auth0 requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
846
|
+
if (backend !== "self") exitWithError("In Better-Fullstack, Auth0 is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
847
|
+
if (!hasNextJs) exitWithError("In Better-Fullstack, Auth0 currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
1098
848
|
}
|
|
1099
|
-
function allowedApisForFrontends(frontends = [], astroIntegration) {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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;
|
|
849
|
+
function allowedApisForFrontends$1(frontends = [], astroIntegration) {
|
|
850
|
+
return allowedApisForFrontends(frontends, astroIntegration);
|
|
851
|
+
}
|
|
852
|
+
function isExampleAIAllowed$1(backend, frontends = []) {
|
|
853
|
+
return isExampleAIAllowed(backend, frontends);
|
|
854
|
+
}
|
|
855
|
+
function isExampleChatSdkAllowed$1(backend, frontends = [], runtime) {
|
|
856
|
+
return isExampleChatSdkAllowed(backend, frontends, runtime);
|
|
857
|
+
}
|
|
858
|
+
function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
|
|
859
|
+
return requiresChatSdkVercelAIForSelection(backend, frontends, runtime);
|
|
1131
860
|
}
|
|
1132
861
|
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
1133
862
|
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
@@ -1135,28 +864,16 @@ function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
|
1135
864
|
function validateServerDeployRequiresBackend(serverDeploy, backend) {
|
|
1136
865
|
if (serverDeploy && serverDeploy !== "none" && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
1137
866
|
}
|
|
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 };
|
|
867
|
+
function validateAddonCompatibility$1(addon, frontend, _auth) {
|
|
868
|
+
return validateAddonCompatibility(addon, frontend, _auth);
|
|
1147
869
|
}
|
|
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
|
-
});
|
|
870
|
+
function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth) {
|
|
871
|
+
return getCompatibleAddons(allAddons, frontend, existingAddons, auth);
|
|
1155
872
|
}
|
|
1156
873
|
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth) {
|
|
1157
874
|
for (const addon of addons) {
|
|
1158
875
|
if (addon === "none") continue;
|
|
1159
|
-
const { isCompatible, reason } = validateAddonCompatibility(addon, frontends, auth);
|
|
876
|
+
const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth);
|
|
1160
877
|
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
1161
878
|
}
|
|
1162
879
|
}
|
|
@@ -1164,68 +881,63 @@ function validatePaymentsCompatibility(payments, auth, _backend, frontends = [])
|
|
|
1164
881
|
if (!payments || payments === "none") return;
|
|
1165
882
|
if (payments === "polar") {
|
|
1166
883
|
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);
|
|
884
|
+
const { web } = splitFrontends$1(frontends);
|
|
1168
885
|
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
886
|
}
|
|
1170
887
|
}
|
|
1171
|
-
function validateExamplesCompatibility(examples, backend, frontend) {
|
|
888
|
+
function validateExamplesCompatibility(examples, backend, frontend, runtime, ai) {
|
|
1172
889
|
const examplesArr = examples ?? [];
|
|
1173
890
|
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
1174
891
|
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
892
|
+
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid-start")) exitWithError("The 'ai' example is not compatible with the SolidStart frontend.");
|
|
1175
893
|
if (examplesArr.includes("ai") && backend === "convex") {
|
|
1176
894
|
const frontendArr = frontend ?? [];
|
|
1177
895
|
const includesNuxt = frontendArr.includes("nuxt");
|
|
1178
896
|
const includesSvelte = frontendArr.includes("svelte");
|
|
1179
897
|
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
898
|
}
|
|
899
|
+
if (examplesArr.includes("chat-sdk")) {
|
|
900
|
+
const frontendArr = frontend ?? [];
|
|
901
|
+
if (!isExampleChatSdkAllowed$1(backend, frontendArr, runtime)) {
|
|
902
|
+
if (backend === "none") exitWithError("The 'chat-sdk' example requires a backend.");
|
|
903
|
+
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.");
|
|
904
|
+
if (backend === "self") exitWithError("The 'chat-sdk' example with self backend only supports Next.js, TanStack Start, or Nuxt frontends in v1.");
|
|
905
|
+
if (backend === "hono" && runtime !== "node") exitWithError("The 'chat-sdk' example with Hono requires '--runtime node' in v1 (Bun/Workers not supported yet).");
|
|
906
|
+
exitWithError("The 'chat-sdk' example is only supported with self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime in v1.");
|
|
907
|
+
}
|
|
908
|
+
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).");
|
|
909
|
+
}
|
|
1181
910
|
}
|
|
1182
911
|
/**
|
|
1183
912
|
* Validates that a UI library is compatible with the selected frontend(s)
|
|
1184
913
|
*/
|
|
1185
914
|
function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astroIntegration) {
|
|
1186
915
|
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";
|
|
916
|
+
const compatible = getCompatibleUILibraries(frontends, astroIntegration);
|
|
917
|
+
if (!compatible.includes(uiLibrary)) {
|
|
918
|
+
const { web } = splitFrontends$1(frontends);
|
|
919
|
+
const isAstroNonReact = web.includes("astro") && astroIntegration !== "react";
|
|
920
|
+
const supportsAstroReact = getCompatibleUILibraries(["astro"], "react").includes(uiLibrary);
|
|
921
|
+
if (isAstroNonReact && supportsAstroReact) {
|
|
1210
922
|
incompatibilityError({
|
|
1211
923
|
message: `UI library '${uiLibrary}' requires React.`,
|
|
1212
924
|
provided: {
|
|
1213
925
|
"ui-library": uiLibrary,
|
|
1214
|
-
"astro-integration":
|
|
926
|
+
"astro-integration": astroIntegration || "none"
|
|
1215
927
|
},
|
|
1216
928
|
suggestions: ["Use --astro-integration react", "Choose a different UI library (daisyui, ark-ui)"]
|
|
1217
929
|
});
|
|
930
|
+
return;
|
|
1218
931
|
}
|
|
1219
|
-
|
|
932
|
+
incompatibilityError({
|
|
933
|
+
message: `UI library '${uiLibrary}' is not compatible with the selected frontend.`,
|
|
934
|
+
provided: {
|
|
935
|
+
"ui-library": uiLibrary,
|
|
936
|
+
frontend: frontends
|
|
937
|
+
},
|
|
938
|
+
suggestions: [`Supported choices for this stack: ${compatible.join(", ")}`, "Choose a different UI library"]
|
|
939
|
+
});
|
|
1220
940
|
}
|
|
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
941
|
}
|
|
1230
942
|
/**
|
|
1231
943
|
* Validates that a UI library is compatible with the selected CSS framework
|
|
@@ -1233,57 +945,26 @@ function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astro
|
|
|
1233
945
|
function validateUILibraryCSSFrameworkCompatibility(uiLibrary, cssFramework) {
|
|
1234
946
|
if (!uiLibrary || uiLibrary === "none") return;
|
|
1235
947
|
if (!cssFramework) return;
|
|
1236
|
-
const
|
|
1237
|
-
if (!
|
|
948
|
+
const supported = getCompatibleCSSFrameworks(uiLibrary);
|
|
949
|
+
if (!supported.includes(cssFramework)) exitWithError(`UI library '${uiLibrary}' is not compatible with '${cssFramework}' CSS framework. Supported CSS frameworks: ${supported.join(", ")}`);
|
|
1238
950
|
}
|
|
1239
951
|
/**
|
|
1240
952
|
* Gets list of UI libraries compatible with the selected frontend(s)
|
|
1241
953
|
*/
|
|
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
|
-
});
|
|
954
|
+
function getCompatibleUILibraries$1(frontends = [], astroIntegration) {
|
|
955
|
+
return getCompatibleUILibraries(frontends, astroIntegration);
|
|
1267
956
|
}
|
|
1268
957
|
/**
|
|
1269
958
|
* Gets list of CSS frameworks compatible with the selected UI library
|
|
1270
959
|
*/
|
|
1271
|
-
function getCompatibleCSSFrameworks(uiLibrary) {
|
|
1272
|
-
|
|
1273
|
-
"tailwind",
|
|
1274
|
-
"scss",
|
|
1275
|
-
"less",
|
|
1276
|
-
"postcss-only",
|
|
1277
|
-
"none"
|
|
1278
|
-
];
|
|
1279
|
-
return UI_LIBRARY_COMPATIBILITY[uiLibrary].cssFrameworks;
|
|
960
|
+
function getCompatibleCSSFrameworks$1(uiLibrary) {
|
|
961
|
+
return getCompatibleCSSFrameworks(uiLibrary);
|
|
1280
962
|
}
|
|
1281
963
|
/**
|
|
1282
964
|
* Checks if a frontend has web styling (excludes native-only frontends)
|
|
1283
965
|
*/
|
|
1284
|
-
function hasWebStyling(frontends = []) {
|
|
1285
|
-
|
|
1286
|
-
return web.length > 0;
|
|
966
|
+
function hasWebStyling$1(frontends = []) {
|
|
967
|
+
return hasWebStyling(frontends);
|
|
1287
968
|
}
|
|
1288
969
|
|
|
1289
970
|
//#endregion
|
|
@@ -1624,7 +1305,7 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1624
1305
|
};
|
|
1625
1306
|
const frontendsArray = frontends || [];
|
|
1626
1307
|
for (const addon of allAddons) {
|
|
1627
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontendsArray, auth);
|
|
1308
|
+
const { isCompatible } = validateAddonCompatibility$1(addon, frontendsArray, auth);
|
|
1628
1309
|
if (!isCompatible) continue;
|
|
1629
1310
|
const { label, hint } = getAddonDisplay(addon);
|
|
1630
1311
|
const option = {
|
|
@@ -1663,7 +1344,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
|
1663
1344
|
"AI Agents": []
|
|
1664
1345
|
};
|
|
1665
1346
|
const frontendArray = frontend || [];
|
|
1666
|
-
const compatibleAddons = getCompatibleAddons(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
1347
|
+
const compatibleAddons = getCompatibleAddons$1(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
1667
1348
|
for (const addon of compatibleAddons) {
|
|
1668
1349
|
const { label, hint } = getAddonDisplay(addon);
|
|
1669
1350
|
const option = {
|
|
@@ -2408,7 +2089,8 @@ const SKILL_SOURCES = {
|
|
|
2408
2089
|
"prisma/skills": { label: "Prisma" },
|
|
2409
2090
|
"elysiajs/skills": { label: "ElysiaJS" },
|
|
2410
2091
|
"waynesutton/convexskills": { label: "Convex" },
|
|
2411
|
-
"msmps/opentui-skill": { label: "OpenTUI Platform" }
|
|
2092
|
+
"msmps/opentui-skill": { label: "OpenTUI Platform" },
|
|
2093
|
+
"haydenbleasel/ultracite": { label: "Ultracite" }
|
|
2412
2094
|
};
|
|
2413
2095
|
const AVAILABLE_AGENTS = [
|
|
2414
2096
|
{
|
|
@@ -2540,6 +2222,7 @@ function getRecommendedSourceKeys(config) {
|
|
|
2540
2222
|
if (backend === "elysia") sources.push("elysiajs/skills");
|
|
2541
2223
|
if (backend === "convex") sources.push("waynesutton/convexskills");
|
|
2542
2224
|
if (addons.includes("opentui")) sources.push("msmps/opentui-skill");
|
|
2225
|
+
if (addons.includes("ultracite")) sources.push("haydenbleasel/ultracite");
|
|
2543
2226
|
return sources;
|
|
2544
2227
|
}
|
|
2545
2228
|
const CURATED_SKILLS_BY_SOURCE = {
|
|
@@ -2597,7 +2280,8 @@ const CURATED_SKILLS_BY_SOURCE = {
|
|
|
2597
2280
|
"convex-migrations",
|
|
2598
2281
|
"convex-security-check"
|
|
2599
2282
|
],
|
|
2600
|
-
"msmps/opentui-skill": () => ["opentui"]
|
|
2283
|
+
"msmps/opentui-skill": () => ["opentui"],
|
|
2284
|
+
"haydenbleasel/ultracite": () => ["ultracite"]
|
|
2601
2285
|
};
|
|
2602
2286
|
function getCuratedSkillNamesForSourceKey(sourceKey, config) {
|
|
2603
2287
|
return CURATED_SKILLS_BY_SOURCE[sourceKey](config);
|
|
@@ -3389,7 +3073,7 @@ async function getAiDocsChoice(aiDocs) {
|
|
|
3389
3073
|
//#region src/prompts/animation.ts
|
|
3390
3074
|
async function getAnimationChoice(animation, frontends) {
|
|
3391
3075
|
if (animation !== void 0) return animation;
|
|
3392
|
-
const { web } = splitFrontends(frontends);
|
|
3076
|
+
const { web } = splitFrontends$1(frontends);
|
|
3393
3077
|
if (web.length === 0) return "none";
|
|
3394
3078
|
const isReact = web.some((f) => [
|
|
3395
3079
|
"tanstack-router",
|
|
@@ -3441,7 +3125,7 @@ async function getAnimationChoice(animation, frontends) {
|
|
|
3441
3125
|
//#region src/prompts/api.ts
|
|
3442
3126
|
async function getApiChoice(Api, frontend, backend, astroIntegration) {
|
|
3443
3127
|
if (backend === "convex" || backend === "none") return "none";
|
|
3444
|
-
const allowed = allowedApisForFrontends(frontend ?? [], astroIntegration);
|
|
3128
|
+
const allowed = allowedApisForFrontends$1(frontend ?? [], astroIntegration);
|
|
3445
3129
|
if (Api) return allowed.includes(Api) ? Api : allowed[0];
|
|
3446
3130
|
const apiOptionMap = {
|
|
3447
3131
|
trpc: {
|
|
@@ -3567,16 +3251,26 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
3567
3251
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3568
3252
|
return response$1;
|
|
3569
3253
|
}
|
|
3570
|
-
const
|
|
3254
|
+
const hasNextJs = frontend?.includes("next");
|
|
3255
|
+
const hasTanStackStart = frontend?.includes("tanstack-start");
|
|
3256
|
+
const isSelfBackend = backend === "self";
|
|
3257
|
+
const supportsNextJsAuth = hasNextJs && isSelfBackend;
|
|
3258
|
+
const hasNativeFrontend$2 = frontend?.some((f) => [
|
|
3259
|
+
"native-bare",
|
|
3260
|
+
"native-uniwind",
|
|
3261
|
+
"native-unistyles"
|
|
3262
|
+
].includes(f));
|
|
3263
|
+
const supportsClerkSelf = isSelfBackend && !hasNativeFrontend$2 && Boolean(hasNextJs || hasTanStackStart);
|
|
3571
3264
|
const options = [{
|
|
3572
3265
|
value: "better-auth",
|
|
3573
3266
|
label: "Better-Auth",
|
|
3574
3267
|
hint: "comprehensive auth framework for TypeScript"
|
|
3575
|
-
}
|
|
3268
|
+
}];
|
|
3269
|
+
if (supportsClerkSelf) options.push({
|
|
3576
3270
|
value: "clerk",
|
|
3577
3271
|
label: "Clerk",
|
|
3578
3272
|
hint: "More than auth, Complete User Management"
|
|
3579
|
-
}
|
|
3273
|
+
});
|
|
3580
3274
|
if (supportsNextJsAuth) {
|
|
3581
3275
|
options.push({
|
|
3582
3276
|
value: "nextauth",
|
|
@@ -3619,11 +3313,13 @@ const FULLSTACK_FRONTENDS = [
|
|
|
3619
3313
|
"next",
|
|
3620
3314
|
"tanstack-start",
|
|
3621
3315
|
"astro",
|
|
3622
|
-
"nuxt"
|
|
3316
|
+
"nuxt",
|
|
3317
|
+
"svelte",
|
|
3318
|
+
"solid-start"
|
|
3623
3319
|
];
|
|
3624
3320
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
3625
3321
|
if (backendFramework !== void 0) return backendFramework;
|
|
3626
|
-
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
|
|
3322
|
+
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid" || f === "solid-start");
|
|
3627
3323
|
const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
|
|
3628
3324
|
const backendOptions = [];
|
|
3629
3325
|
if (hasFullstackFrontend) backendOptions.push({
|
|
@@ -3769,7 +3465,7 @@ const CSS_FRAMEWORK_OPTIONS = {
|
|
|
3769
3465
|
}
|
|
3770
3466
|
};
|
|
3771
3467
|
async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
|
|
3772
|
-
const compatibleFrameworks = getCompatibleCSSFrameworks(uiLibrary);
|
|
3468
|
+
const compatibleFrameworks = getCompatibleCSSFrameworks$1(uiLibrary);
|
|
3773
3469
|
if (cssFramework !== void 0) return compatibleFrameworks.includes(cssFramework) ? cssFramework : compatibleFrameworks[0];
|
|
3774
3470
|
const selected = await navigableSelect({
|
|
3775
3471
|
message: "Select CSS framework",
|
|
@@ -4080,16 +3776,21 @@ async function getEmailChoice(email, backend) {
|
|
|
4080
3776
|
|
|
4081
3777
|
//#endregion
|
|
4082
3778
|
//#region src/prompts/examples.ts
|
|
4083
|
-
async function getExamplesChoice(examples, frontends, backend) {
|
|
3779
|
+
async function getExamplesChoice(examples, frontends, backend, runtime) {
|
|
4084
3780
|
if (examples !== void 0) return examples;
|
|
4085
3781
|
if (backend === "none") return [];
|
|
4086
3782
|
let response = [];
|
|
4087
3783
|
const options = [];
|
|
4088
|
-
if (isExampleAIAllowed(backend, frontends ?? [])) options.push({
|
|
3784
|
+
if (isExampleAIAllowed$1(backend, frontends ?? [])) options.push({
|
|
4089
3785
|
value: "ai",
|
|
4090
3786
|
label: "AI Chat",
|
|
4091
3787
|
hint: "A simple AI chat interface using AI SDK"
|
|
4092
3788
|
});
|
|
3789
|
+
if (isExampleChatSdkAllowed$1(backend, frontends ?? [], runtime)) options.push({
|
|
3790
|
+
value: "chat-sdk",
|
|
3791
|
+
label: "Chat SDK Bots",
|
|
3792
|
+
hint: "Framework-specific Chat SDK bot example (Slack/Discord/GitHub depending on stack)"
|
|
3793
|
+
});
|
|
4093
3794
|
if (options.length === 0) return [];
|
|
4094
3795
|
response = await navigableMultiselect({
|
|
4095
3796
|
message: "Include examples",
|
|
@@ -4170,7 +3871,7 @@ async function getFileUploadChoice(fileUpload, backend) {
|
|
|
4170
3871
|
//#region src/prompts/forms.ts
|
|
4171
3872
|
async function getFormsChoice(forms, frontends) {
|
|
4172
3873
|
if (forms !== void 0) return forms;
|
|
4173
|
-
const { web } = splitFrontends(frontends);
|
|
3874
|
+
const { web } = splitFrontends$1(frontends);
|
|
4174
3875
|
if (web.length === 0) return "none";
|
|
4175
3876
|
const isReact = web.some((f) => [
|
|
4176
3877
|
"tanstack-router",
|
|
@@ -4283,6 +3984,11 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
4283
3984
|
label: "Solid",
|
|
4284
3985
|
hint: "Simple and performant reactivity for building user interfaces"
|
|
4285
3986
|
},
|
|
3987
|
+
{
|
|
3988
|
+
value: "solid-start",
|
|
3989
|
+
label: "SolidStart",
|
|
3990
|
+
hint: "Full-stack Solid framework with SSR and API routes"
|
|
3991
|
+
},
|
|
4286
3992
|
{
|
|
4287
3993
|
value: "astro",
|
|
4288
3994
|
label: "Astro",
|
|
@@ -4313,7 +4019,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
|
4313
4019
|
label: "Fresh",
|
|
4314
4020
|
hint: "Deno-native framework with islands architecture"
|
|
4315
4021
|
}
|
|
4316
|
-
].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth)),
|
|
4022
|
+
].filter((option) => isFrontendAllowedWithBackend$1(option.value, backend, auth)),
|
|
4317
4023
|
initialValue: DEFAULT_CONFIG.frontend[0]
|
|
4318
4024
|
});
|
|
4319
4025
|
if (isGoBack(webFramework)) shouldRestart = true;
|
|
@@ -4807,7 +4513,7 @@ async function getPackageManagerChoice(packageManager) {
|
|
|
4807
4513
|
async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
4808
4514
|
if (payments !== void 0) return payments;
|
|
4809
4515
|
if (backend === "none") return "none";
|
|
4810
|
-
const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0);
|
|
4516
|
+
const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends$1(frontends).web.length > 0);
|
|
4811
4517
|
const options = [];
|
|
4812
4518
|
if (isPolarCompatible) options.push({
|
|
4813
4519
|
value: "polar",
|
|
@@ -5288,11 +4994,379 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
5288
4994
|
return "none";
|
|
5289
4995
|
}
|
|
5290
4996
|
|
|
4997
|
+
//#endregion
|
|
4998
|
+
//#region src/prompts/shadcn-options.ts
|
|
4999
|
+
const BASE_OPTIONS = [{
|
|
5000
|
+
value: "radix",
|
|
5001
|
+
label: "Radix UI",
|
|
5002
|
+
hint: "Battle-tested headless primitives (130M+ monthly downloads)"
|
|
5003
|
+
}, {
|
|
5004
|
+
value: "base",
|
|
5005
|
+
label: "Base UI",
|
|
5006
|
+
hint: "MUI's headless library with cleaner APIs and native multi-select"
|
|
5007
|
+
}];
|
|
5008
|
+
const STYLE_OPTIONS = [
|
|
5009
|
+
{
|
|
5010
|
+
value: "vega",
|
|
5011
|
+
label: "Vega",
|
|
5012
|
+
hint: "Classic shadcn/ui look"
|
|
5013
|
+
},
|
|
5014
|
+
{
|
|
5015
|
+
value: "nova",
|
|
5016
|
+
label: "Nova",
|
|
5017
|
+
hint: "Compact layout with reduced padding"
|
|
5018
|
+
},
|
|
5019
|
+
{
|
|
5020
|
+
value: "maia",
|
|
5021
|
+
label: "Maia",
|
|
5022
|
+
hint: "Soft, rounded with generous spacing"
|
|
5023
|
+
},
|
|
5024
|
+
{
|
|
5025
|
+
value: "lyra",
|
|
5026
|
+
label: "Lyra",
|
|
5027
|
+
hint: "Boxy and sharp, pairs well with mono fonts"
|
|
5028
|
+
},
|
|
5029
|
+
{
|
|
5030
|
+
value: "mira",
|
|
5031
|
+
label: "Mira",
|
|
5032
|
+
hint: "Dense, made for data-heavy interfaces"
|
|
5033
|
+
}
|
|
5034
|
+
];
|
|
5035
|
+
const ICON_LIBRARY_OPTIONS = [
|
|
5036
|
+
{
|
|
5037
|
+
value: "lucide",
|
|
5038
|
+
label: "Lucide",
|
|
5039
|
+
hint: "Default icon library — clean, consistent icons"
|
|
5040
|
+
},
|
|
5041
|
+
{
|
|
5042
|
+
value: "tabler",
|
|
5043
|
+
label: "Tabler Icons",
|
|
5044
|
+
hint: "2000+ open-source SVG icons"
|
|
5045
|
+
},
|
|
5046
|
+
{
|
|
5047
|
+
value: "hugeicons",
|
|
5048
|
+
label: "HugeIcons",
|
|
5049
|
+
hint: "Modern icon set with wrapper component"
|
|
5050
|
+
},
|
|
5051
|
+
{
|
|
5052
|
+
value: "phosphor",
|
|
5053
|
+
label: "Phosphor Icons",
|
|
5054
|
+
hint: "Flexible, consistent icon family"
|
|
5055
|
+
},
|
|
5056
|
+
{
|
|
5057
|
+
value: "remixicon",
|
|
5058
|
+
label: "Remix Icon",
|
|
5059
|
+
hint: "Open-source neutral style icons"
|
|
5060
|
+
}
|
|
5061
|
+
];
|
|
5062
|
+
const COLOR_THEME_OPTIONS = [
|
|
5063
|
+
{
|
|
5064
|
+
value: "neutral",
|
|
5065
|
+
label: "Neutral",
|
|
5066
|
+
hint: "Clean and minimal"
|
|
5067
|
+
},
|
|
5068
|
+
{
|
|
5069
|
+
value: "stone",
|
|
5070
|
+
label: "Stone",
|
|
5071
|
+
hint: "Warm neutral tones"
|
|
5072
|
+
},
|
|
5073
|
+
{
|
|
5074
|
+
value: "zinc",
|
|
5075
|
+
label: "Zinc",
|
|
5076
|
+
hint: "Cool neutral tones"
|
|
5077
|
+
},
|
|
5078
|
+
{
|
|
5079
|
+
value: "gray",
|
|
5080
|
+
label: "Gray",
|
|
5081
|
+
hint: "Blue-tinted neutral"
|
|
5082
|
+
},
|
|
5083
|
+
{
|
|
5084
|
+
value: "blue",
|
|
5085
|
+
label: "Blue",
|
|
5086
|
+
hint: "Trust and reliability"
|
|
5087
|
+
},
|
|
5088
|
+
{
|
|
5089
|
+
value: "violet",
|
|
5090
|
+
label: "Violet",
|
|
5091
|
+
hint: "Creative and modern"
|
|
5092
|
+
},
|
|
5093
|
+
{
|
|
5094
|
+
value: "green",
|
|
5095
|
+
label: "Green",
|
|
5096
|
+
hint: "Growth and success"
|
|
5097
|
+
},
|
|
5098
|
+
{
|
|
5099
|
+
value: "red",
|
|
5100
|
+
label: "Red",
|
|
5101
|
+
hint: "Bold and energetic"
|
|
5102
|
+
},
|
|
5103
|
+
{
|
|
5104
|
+
value: "rose",
|
|
5105
|
+
label: "Rose",
|
|
5106
|
+
hint: "Warm and inviting"
|
|
5107
|
+
},
|
|
5108
|
+
{
|
|
5109
|
+
value: "orange",
|
|
5110
|
+
label: "Orange",
|
|
5111
|
+
hint: "Friendly and vibrant"
|
|
5112
|
+
},
|
|
5113
|
+
{
|
|
5114
|
+
value: "amber",
|
|
5115
|
+
label: "Amber",
|
|
5116
|
+
hint: "Warm and golden"
|
|
5117
|
+
},
|
|
5118
|
+
{
|
|
5119
|
+
value: "yellow",
|
|
5120
|
+
label: "Yellow",
|
|
5121
|
+
hint: "Bright and optimistic"
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
value: "lime",
|
|
5125
|
+
label: "Lime",
|
|
5126
|
+
hint: "Fresh and lively"
|
|
5127
|
+
},
|
|
5128
|
+
{
|
|
5129
|
+
value: "emerald",
|
|
5130
|
+
label: "Emerald",
|
|
5131
|
+
hint: "Rich and luxurious"
|
|
5132
|
+
},
|
|
5133
|
+
{
|
|
5134
|
+
value: "teal",
|
|
5135
|
+
label: "Teal",
|
|
5136
|
+
hint: "Calm and sophisticated"
|
|
5137
|
+
},
|
|
5138
|
+
{
|
|
5139
|
+
value: "cyan",
|
|
5140
|
+
label: "Cyan",
|
|
5141
|
+
hint: "Cool and refreshing"
|
|
5142
|
+
},
|
|
5143
|
+
{
|
|
5144
|
+
value: "sky",
|
|
5145
|
+
label: "Sky",
|
|
5146
|
+
hint: "Light and airy"
|
|
5147
|
+
},
|
|
5148
|
+
{
|
|
5149
|
+
value: "indigo",
|
|
5150
|
+
label: "Indigo",
|
|
5151
|
+
hint: "Deep and focused"
|
|
5152
|
+
},
|
|
5153
|
+
{
|
|
5154
|
+
value: "purple",
|
|
5155
|
+
label: "Purple",
|
|
5156
|
+
hint: "Royal and elegant"
|
|
5157
|
+
},
|
|
5158
|
+
{
|
|
5159
|
+
value: "fuchsia",
|
|
5160
|
+
label: "Fuchsia",
|
|
5161
|
+
hint: "Playful and bold"
|
|
5162
|
+
},
|
|
5163
|
+
{
|
|
5164
|
+
value: "pink",
|
|
5165
|
+
label: "Pink",
|
|
5166
|
+
hint: "Soft and expressive"
|
|
5167
|
+
}
|
|
5168
|
+
];
|
|
5169
|
+
const BASE_COLOR_OPTIONS = [
|
|
5170
|
+
{
|
|
5171
|
+
value: "neutral",
|
|
5172
|
+
label: "Neutral",
|
|
5173
|
+
hint: "Pure neutral grays"
|
|
5174
|
+
},
|
|
5175
|
+
{
|
|
5176
|
+
value: "stone",
|
|
5177
|
+
label: "Stone",
|
|
5178
|
+
hint: "Warm-tinted grays"
|
|
5179
|
+
},
|
|
5180
|
+
{
|
|
5181
|
+
value: "zinc",
|
|
5182
|
+
label: "Zinc",
|
|
5183
|
+
hint: "Cool-tinted grays"
|
|
5184
|
+
},
|
|
5185
|
+
{
|
|
5186
|
+
value: "gray",
|
|
5187
|
+
label: "Gray",
|
|
5188
|
+
hint: "Blue-tinted grays"
|
|
5189
|
+
}
|
|
5190
|
+
];
|
|
5191
|
+
const FONT_OPTIONS = [
|
|
5192
|
+
{
|
|
5193
|
+
value: "inter",
|
|
5194
|
+
label: "Inter",
|
|
5195
|
+
hint: "Clean variable sans-serif (default)"
|
|
5196
|
+
},
|
|
5197
|
+
{
|
|
5198
|
+
value: "geist",
|
|
5199
|
+
label: "Geist",
|
|
5200
|
+
hint: "Vercel's modern typeface"
|
|
5201
|
+
},
|
|
5202
|
+
{
|
|
5203
|
+
value: "figtree",
|
|
5204
|
+
label: "Figtree",
|
|
5205
|
+
hint: "Friendly geometric sans-serif"
|
|
5206
|
+
},
|
|
5207
|
+
{
|
|
5208
|
+
value: "noto-sans",
|
|
5209
|
+
label: "Noto Sans",
|
|
5210
|
+
hint: "Google's universal typeface"
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
value: "nunito-sans",
|
|
5214
|
+
label: "Nunito Sans",
|
|
5215
|
+
hint: "Rounded, balanced sans-serif"
|
|
5216
|
+
},
|
|
5217
|
+
{
|
|
5218
|
+
value: "roboto",
|
|
5219
|
+
label: "Roboto",
|
|
5220
|
+
hint: "Google's Material Design typeface"
|
|
5221
|
+
},
|
|
5222
|
+
{
|
|
5223
|
+
value: "raleway",
|
|
5224
|
+
label: "Raleway",
|
|
5225
|
+
hint: "Elegant thin-weight display font"
|
|
5226
|
+
},
|
|
5227
|
+
{
|
|
5228
|
+
value: "dm-sans",
|
|
5229
|
+
label: "DM Sans",
|
|
5230
|
+
hint: "Low-contrast geometric sans"
|
|
5231
|
+
},
|
|
5232
|
+
{
|
|
5233
|
+
value: "public-sans",
|
|
5234
|
+
label: "Public Sans",
|
|
5235
|
+
hint: "Neutral, US government typeface"
|
|
5236
|
+
},
|
|
5237
|
+
{
|
|
5238
|
+
value: "outfit",
|
|
5239
|
+
label: "Outfit",
|
|
5240
|
+
hint: "Modern geometric variable font"
|
|
5241
|
+
},
|
|
5242
|
+
{
|
|
5243
|
+
value: "jetbrains-mono",
|
|
5244
|
+
label: "JetBrains Mono",
|
|
5245
|
+
hint: "Developer-focused monospace"
|
|
5246
|
+
},
|
|
5247
|
+
{
|
|
5248
|
+
value: "geist-mono",
|
|
5249
|
+
label: "Geist Mono",
|
|
5250
|
+
hint: "Vercel's monospace typeface"
|
|
5251
|
+
}
|
|
5252
|
+
];
|
|
5253
|
+
const RADIUS_OPTIONS = [
|
|
5254
|
+
{
|
|
5255
|
+
value: "default",
|
|
5256
|
+
label: "Default",
|
|
5257
|
+
hint: "Use the style's default radius"
|
|
5258
|
+
},
|
|
5259
|
+
{
|
|
5260
|
+
value: "none",
|
|
5261
|
+
label: "None",
|
|
5262
|
+
hint: "Sharp corners (0)"
|
|
5263
|
+
},
|
|
5264
|
+
{
|
|
5265
|
+
value: "small",
|
|
5266
|
+
label: "Small",
|
|
5267
|
+
hint: "Subtle rounding (0.45rem)"
|
|
5268
|
+
},
|
|
5269
|
+
{
|
|
5270
|
+
value: "medium",
|
|
5271
|
+
label: "Medium",
|
|
5272
|
+
hint: "Moderate rounding (0.625rem)"
|
|
5273
|
+
},
|
|
5274
|
+
{
|
|
5275
|
+
value: "large",
|
|
5276
|
+
label: "Large",
|
|
5277
|
+
hint: "Generous rounding (0.875rem)"
|
|
5278
|
+
}
|
|
5279
|
+
];
|
|
5280
|
+
const SHADCN_DEFAULTS = {
|
|
5281
|
+
shadcnBase: "radix",
|
|
5282
|
+
shadcnStyle: "nova",
|
|
5283
|
+
shadcnIconLibrary: "lucide",
|
|
5284
|
+
shadcnColorTheme: "neutral",
|
|
5285
|
+
shadcnBaseColor: "neutral",
|
|
5286
|
+
shadcnFont: "inter",
|
|
5287
|
+
shadcnRadius: "default"
|
|
5288
|
+
};
|
|
5289
|
+
async function getShadcnOptions(flags) {
|
|
5290
|
+
const fallback = (key) => isSilent() ? SHADCN_DEFAULTS[key] : void 0;
|
|
5291
|
+
return {
|
|
5292
|
+
shadcnBase: flags.shadcnBase ?? fallback("shadcnBase") ?? await promptShadcnBase(),
|
|
5293
|
+
shadcnStyle: flags.shadcnStyle ?? fallback("shadcnStyle") ?? await promptShadcnStyle(),
|
|
5294
|
+
shadcnIconLibrary: flags.shadcnIconLibrary ?? fallback("shadcnIconLibrary") ?? await promptShadcnIconLibrary(),
|
|
5295
|
+
shadcnColorTheme: flags.shadcnColorTheme ?? fallback("shadcnColorTheme") ?? await promptShadcnColorTheme(),
|
|
5296
|
+
shadcnBaseColor: flags.shadcnBaseColor ?? fallback("shadcnBaseColor") ?? await promptShadcnBaseColor(),
|
|
5297
|
+
shadcnFont: flags.shadcnFont ?? fallback("shadcnFont") ?? await promptShadcnFont(),
|
|
5298
|
+
shadcnRadius: flags.shadcnRadius ?? fallback("shadcnRadius") ?? await promptShadcnRadius()
|
|
5299
|
+
};
|
|
5300
|
+
}
|
|
5301
|
+
async function promptShadcnBase() {
|
|
5302
|
+
const selected = await navigableSelect({
|
|
5303
|
+
message: "Select shadcn/ui base library",
|
|
5304
|
+
options: BASE_OPTIONS,
|
|
5305
|
+
initialValue: "radix"
|
|
5306
|
+
});
|
|
5307
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5308
|
+
return selected;
|
|
5309
|
+
}
|
|
5310
|
+
async function promptShadcnStyle() {
|
|
5311
|
+
const selected = await navigableSelect({
|
|
5312
|
+
message: "Select shadcn/ui visual style",
|
|
5313
|
+
options: STYLE_OPTIONS,
|
|
5314
|
+
initialValue: "nova"
|
|
5315
|
+
});
|
|
5316
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5317
|
+
return selected;
|
|
5318
|
+
}
|
|
5319
|
+
async function promptShadcnIconLibrary() {
|
|
5320
|
+
const selected = await navigableSelect({
|
|
5321
|
+
message: "Select shadcn/ui icon library",
|
|
5322
|
+
options: ICON_LIBRARY_OPTIONS,
|
|
5323
|
+
initialValue: "lucide"
|
|
5324
|
+
});
|
|
5325
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5326
|
+
return selected;
|
|
5327
|
+
}
|
|
5328
|
+
async function promptShadcnColorTheme() {
|
|
5329
|
+
const selected = await navigableSelect({
|
|
5330
|
+
message: "Select shadcn/ui color theme",
|
|
5331
|
+
options: COLOR_THEME_OPTIONS,
|
|
5332
|
+
initialValue: "neutral"
|
|
5333
|
+
});
|
|
5334
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5335
|
+
return selected;
|
|
5336
|
+
}
|
|
5337
|
+
async function promptShadcnBaseColor() {
|
|
5338
|
+
const selected = await navigableSelect({
|
|
5339
|
+
message: "Select shadcn/ui base neutral color",
|
|
5340
|
+
options: BASE_COLOR_OPTIONS,
|
|
5341
|
+
initialValue: "neutral"
|
|
5342
|
+
});
|
|
5343
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5344
|
+
return selected;
|
|
5345
|
+
}
|
|
5346
|
+
async function promptShadcnFont() {
|
|
5347
|
+
const selected = await navigableSelect({
|
|
5348
|
+
message: "Select shadcn/ui font",
|
|
5349
|
+
options: FONT_OPTIONS,
|
|
5350
|
+
initialValue: "inter"
|
|
5351
|
+
});
|
|
5352
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5353
|
+
return selected;
|
|
5354
|
+
}
|
|
5355
|
+
async function promptShadcnRadius() {
|
|
5356
|
+
const selected = await navigableSelect({
|
|
5357
|
+
message: "Select shadcn/ui border radius",
|
|
5358
|
+
options: RADIUS_OPTIONS,
|
|
5359
|
+
initialValue: "default"
|
|
5360
|
+
});
|
|
5361
|
+
if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
|
|
5362
|
+
return selected;
|
|
5363
|
+
}
|
|
5364
|
+
|
|
5291
5365
|
//#endregion
|
|
5292
5366
|
//#region src/prompts/state-management.ts
|
|
5293
5367
|
async function getStateManagementChoice(stateManagement, frontends) {
|
|
5294
5368
|
if (stateManagement !== void 0) return stateManagement;
|
|
5295
|
-
const { web } = splitFrontends(frontends);
|
|
5369
|
+
const { web } = splitFrontends$1(frontends);
|
|
5296
5370
|
if (web.length === 0) return "none";
|
|
5297
5371
|
const isReact = web.some((f) => [
|
|
5298
5372
|
"tanstack-router",
|
|
@@ -5452,9 +5526,9 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
5452
5526
|
}
|
|
5453
5527
|
};
|
|
5454
5528
|
async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
|
|
5455
|
-
const { web } = splitFrontends(frontends);
|
|
5529
|
+
const { web } = splitFrontends$1(frontends);
|
|
5456
5530
|
if (web.length === 0) return "none";
|
|
5457
|
-
const compatibleLibraries = getCompatibleUILibraries(frontends, astroIntegration);
|
|
5531
|
+
const compatibleLibraries = getCompatibleUILibraries$1(frontends, astroIntegration);
|
|
5458
5532
|
if (uiLibrary !== void 0) return compatibleLibraries.includes(uiLibrary) ? uiLibrary : compatibleLibraries[0];
|
|
5459
5533
|
const options = compatibleLibraries.map((lib) => ({
|
|
5460
5534
|
value: lib,
|
|
@@ -5525,6 +5599,24 @@ async function getValidationChoice(validation) {
|
|
|
5525
5599
|
return response;
|
|
5526
5600
|
}
|
|
5527
5601
|
|
|
5602
|
+
//#endregion
|
|
5603
|
+
//#region src/utils/compatibility.ts
|
|
5604
|
+
const WEB_FRAMEWORKS = [
|
|
5605
|
+
"tanstack-router",
|
|
5606
|
+
"react-router",
|
|
5607
|
+
"tanstack-start",
|
|
5608
|
+
"next",
|
|
5609
|
+
"nuxt",
|
|
5610
|
+
"svelte",
|
|
5611
|
+
"solid",
|
|
5612
|
+
"solid-start",
|
|
5613
|
+
"astro",
|
|
5614
|
+
"qwik",
|
|
5615
|
+
"angular",
|
|
5616
|
+
"redwood",
|
|
5617
|
+
"fresh"
|
|
5618
|
+
];
|
|
5619
|
+
|
|
5528
5620
|
//#endregion
|
|
5529
5621
|
//#region src/prompts/web-deploy.ts
|
|
5530
5622
|
function hasWebFrontend(frontends) {
|
|
@@ -5575,12 +5667,24 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5575
5667
|
},
|
|
5576
5668
|
uiLibrary: ({ results }) => {
|
|
5577
5669
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5578
|
-
if (hasWebStyling(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
|
|
5670
|
+
if (hasWebStyling$1(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
|
|
5579
5671
|
return Promise.resolve("none");
|
|
5580
5672
|
},
|
|
5673
|
+
shadcnOptions: ({ results }) => {
|
|
5674
|
+
if (results.uiLibrary !== "shadcn-ui") return Promise.resolve(void 0);
|
|
5675
|
+
return getShadcnOptions({
|
|
5676
|
+
shadcnBase: flags.shadcnBase,
|
|
5677
|
+
shadcnStyle: flags.shadcnStyle,
|
|
5678
|
+
shadcnIconLibrary: flags.shadcnIconLibrary,
|
|
5679
|
+
shadcnColorTheme: flags.shadcnColorTheme,
|
|
5680
|
+
shadcnBaseColor: flags.shadcnBaseColor,
|
|
5681
|
+
shadcnFont: flags.shadcnFont,
|
|
5682
|
+
shadcnRadius: flags.shadcnRadius
|
|
5683
|
+
});
|
|
5684
|
+
},
|
|
5581
5685
|
cssFramework: ({ results }) => {
|
|
5582
5686
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5583
|
-
if (hasWebStyling(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
|
|
5687
|
+
if (hasWebStyling$1(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
|
|
5584
5688
|
return Promise.resolve("none");
|
|
5585
5689
|
},
|
|
5586
5690
|
backend: ({ results }) => {
|
|
@@ -5625,7 +5729,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5625
5729
|
},
|
|
5626
5730
|
examples: ({ results }) => {
|
|
5627
5731
|
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
5628
|
-
return getExamplesChoice(flags.examples, results.frontend, results.backend);
|
|
5732
|
+
return getExamplesChoice(flags.examples, results.frontend, results.backend, results.runtime);
|
|
5629
5733
|
},
|
|
5630
5734
|
dbSetup: ({ results }) => {
|
|
5631
5735
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5641,6 +5745,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5641
5745
|
},
|
|
5642
5746
|
ai: ({ results }) => {
|
|
5643
5747
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5748
|
+
if (flags.ai === void 0 && results.examples?.includes("chat-sdk") && requiresChatSdkVercelAI(results.backend, results.frontend, results.runtime)) return Promise.resolve("vercel-ai");
|
|
5644
5749
|
return getAIChoice(flags.ai);
|
|
5645
5750
|
},
|
|
5646
5751
|
validation: ({ results }) => {
|
|
@@ -5790,6 +5895,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5790
5895
|
frontend: result.frontend,
|
|
5791
5896
|
astroIntegration: result.astroIntegration,
|
|
5792
5897
|
uiLibrary: result.uiLibrary,
|
|
5898
|
+
...result.shadcnOptions ?? {},
|
|
5793
5899
|
cssFramework: result.cssFramework,
|
|
5794
5900
|
backend: result.backend,
|
|
5795
5901
|
runtime: result.runtime,
|
|
@@ -6018,6 +6124,15 @@ function generateReproducibleCommand(config) {
|
|
|
6018
6124
|
flags.push(`--effect ${config.effect}`);
|
|
6019
6125
|
flags.push(`--css-framework ${config.cssFramework}`);
|
|
6020
6126
|
flags.push(`--ui-library ${config.uiLibrary}`);
|
|
6127
|
+
if (config.uiLibrary === "shadcn-ui") {
|
|
6128
|
+
flags.push(`--shadcn-base ${config.shadcnBase}`);
|
|
6129
|
+
flags.push(`--shadcn-style ${config.shadcnStyle}`);
|
|
6130
|
+
flags.push(`--shadcn-icon-library ${config.shadcnIconLibrary}`);
|
|
6131
|
+
flags.push(`--shadcn-color-theme ${config.shadcnColorTheme}`);
|
|
6132
|
+
flags.push(`--shadcn-base-color ${config.shadcnBaseColor}`);
|
|
6133
|
+
flags.push(`--shadcn-font ${config.shadcnFont}`);
|
|
6134
|
+
flags.push(`--shadcn-radius ${config.shadcnRadius}`);
|
|
6135
|
+
}
|
|
6021
6136
|
flags.push(`--ai ${config.ai}`);
|
|
6022
6137
|
flags.push(`--state-management ${config.stateManagement}`);
|
|
6023
6138
|
flags.push(`--forms ${config.forms}`);
|
|
@@ -6273,6 +6388,13 @@ function processFlags(options, projectName) {
|
|
|
6273
6388
|
if (options.astroIntegration) config.astroIntegration = options.astroIntegration;
|
|
6274
6389
|
if (options.cssFramework) config.cssFramework = options.cssFramework;
|
|
6275
6390
|
if (options.uiLibrary) config.uiLibrary = options.uiLibrary;
|
|
6391
|
+
if (options.shadcnBase) config.shadcnBase = options.shadcnBase;
|
|
6392
|
+
if (options.shadcnStyle) config.shadcnStyle = options.shadcnStyle;
|
|
6393
|
+
if (options.shadcnIconLibrary) config.shadcnIconLibrary = options.shadcnIconLibrary;
|
|
6394
|
+
if (options.shadcnColorTheme) config.shadcnColorTheme = options.shadcnColorTheme;
|
|
6395
|
+
if (options.shadcnBaseColor) config.shadcnBaseColor = options.shadcnBaseColor;
|
|
6396
|
+
if (options.shadcnFont) config.shadcnFont = options.shadcnFont;
|
|
6397
|
+
if (options.shadcnRadius) config.shadcnRadius = options.shadcnRadius;
|
|
6276
6398
|
if (options.addons && options.addons.length > 0) config.addons = processArrayOption(options.addons);
|
|
6277
6399
|
if (options.examples && options.examples.length > 0) config.examples = processArrayOption(options.examples);
|
|
6278
6400
|
if (options.aiDocs !== void 0) config.aiDocs = processArrayOption(options.aiDocs);
|
|
@@ -6514,7 +6636,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
6514
6636
|
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
6515
6637
|
});
|
|
6516
6638
|
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") incompatibilityError({
|
|
6517
|
-
message: "MongoDB only
|
|
6639
|
+
message: "In Better-Fullstack, MongoDB is currently supported only with Mongoose or Prisma ORM.",
|
|
6518
6640
|
provided: {
|
|
6519
6641
|
database: "mongodb",
|
|
6520
6642
|
orm
|
|
@@ -6597,7 +6719,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6597
6719
|
runtime: "workers",
|
|
6598
6720
|
errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
|
|
6599
6721
|
},
|
|
6600
|
-
docker: { errorMessage: "Docker setup is not
|
|
6722
|
+
docker: { errorMessage: "In Better-Fullstack, Docker setup is currently not available with SQLite database or Cloudflare Workers runtime." },
|
|
6601
6723
|
none: { errorMessage: "" }
|
|
6602
6724
|
};
|
|
6603
6725
|
if (dbSetup && dbSetup !== "none") {
|
|
@@ -6607,8 +6729,8 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6607
6729
|
} else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
|
|
6608
6730
|
if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
|
|
6609
6731
|
if (dbSetup === "docker") {
|
|
6610
|
-
if (database === "sqlite") exitWithError("Docker setup is not
|
|
6611
|
-
if (runtime === "workers") exitWithError("Docker setup is not
|
|
6732
|
+
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.");
|
|
6733
|
+
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
6734
|
}
|
|
6613
6735
|
}
|
|
6614
6736
|
}
|
|
@@ -6727,15 +6849,6 @@ function validateAdonisJSConstraints(config, providedFlags) {
|
|
|
6727
6849
|
}
|
|
6728
6850
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
6729
6851
|
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
6852
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self" && backend !== "encore") {
|
|
6740
6853
|
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
6854
|
}
|
|
@@ -6750,10 +6863,30 @@ function validateFrontendConstraints(config, providedFlags) {
|
|
|
6750
6863
|
ensureSingleWebAndNative(frontend);
|
|
6751
6864
|
if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
|
|
6752
6865
|
}
|
|
6753
|
-
const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend(f));
|
|
6866
|
+
const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend$1(f));
|
|
6754
6867
|
validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag);
|
|
6755
6868
|
}
|
|
6756
6869
|
function validateApiConstraints(_config, _options) {}
|
|
6870
|
+
function validateShadcnConstraints(config, providedFlags) {
|
|
6871
|
+
const shadcnFlagMap = {
|
|
6872
|
+
shadcnBase: "--shadcn-base",
|
|
6873
|
+
shadcnStyle: "--shadcn-style",
|
|
6874
|
+
shadcnIconLibrary: "--shadcn-icon-library",
|
|
6875
|
+
shadcnColorTheme: "--shadcn-color-theme",
|
|
6876
|
+
shadcnBaseColor: "--shadcn-base-color",
|
|
6877
|
+
shadcnFont: "--shadcn-font",
|
|
6878
|
+
shadcnRadius: "--shadcn-radius"
|
|
6879
|
+
};
|
|
6880
|
+
const providedShadcnFlags = Object.keys(shadcnFlagMap).filter((f) => providedFlags.has(f));
|
|
6881
|
+
if (providedShadcnFlags.length > 0 && config.uiLibrary !== "shadcn-ui") incompatibilityError({
|
|
6882
|
+
message: "shadcn/ui customization flags require --ui-library shadcn-ui.",
|
|
6883
|
+
provided: {
|
|
6884
|
+
"ui-library": config.uiLibrary || "none",
|
|
6885
|
+
...Object.fromEntries(providedShadcnFlags.map((f) => [shadcnFlagMap[f], String(config[f] ?? "")]))
|
|
6886
|
+
},
|
|
6887
|
+
suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
|
|
6888
|
+
});
|
|
6889
|
+
}
|
|
6757
6890
|
function validateFullConfig(config, providedFlags, options) {
|
|
6758
6891
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
6759
6892
|
validateDatabaseSetup(config, providedFlags);
|
|
@@ -6774,14 +6907,16 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
6774
6907
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6775
6908
|
config.addons = [...new Set(config.addons)];
|
|
6776
6909
|
}
|
|
6777
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6910
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6778
6911
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
6912
|
+
validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6779
6913
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6780
6914
|
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6781
6915
|
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6782
6916
|
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6783
6917
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
6784
6918
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
6919
|
+
validateShadcnConstraints(config, providedFlags);
|
|
6785
6920
|
validatePeerDependencies(config);
|
|
6786
6921
|
}
|
|
6787
6922
|
function validateConfigForProgrammaticUse(config) {
|
|
@@ -6790,12 +6925,13 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
6790
6925
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
6791
6926
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
6792
6927
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
6928
|
+
validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6793
6929
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6794
6930
|
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6795
6931
|
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6796
6932
|
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6797
6933
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6798
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6934
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6799
6935
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
6800
6936
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
6801
6937
|
validatePeerDependencies(config);
|
|
@@ -8117,7 +8253,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
8117
8253
|
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
|
|
8118
8254
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
8119
8255
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
8120
|
-
const clerkInstructions =
|
|
8256
|
+
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
|
|
8121
8257
|
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
8122
8258
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
8123
8259
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
@@ -8248,8 +8384,13 @@ function getNoOrmWarning() {
|
|
|
8248
8384
|
function getBunWebNativeWarning() {
|
|
8249
8385
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
8250
8386
|
}
|
|
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`;
|
|
8387
|
+
function getClerkInstructions(backend, frontend) {
|
|
8388
|
+
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`;
|
|
8389
|
+
if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
|
|
8390
|
+
const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
|
|
8391
|
+
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`;
|
|
8392
|
+
}
|
|
8393
|
+
return "";
|
|
8253
8394
|
}
|
|
8254
8395
|
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
8255
8396
|
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
@@ -8437,7 +8578,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
|
|
|
8437
8578
|
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
8438
8579
|
if (!await fs.pathExists(pkgJsonPath)) return;
|
|
8439
8580
|
try {
|
|
8440
|
-
const { stdout } = await
|
|
8581
|
+
const { stdout } = await $({ cwd: os$1.tmpdir() })`${packageManager} -v`;
|
|
8441
8582
|
const version = stdout.trim();
|
|
8442
8583
|
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
8443
8584
|
pkgJson.packageManager = `${packageManager}@${version}`;
|
|
@@ -8803,6 +8944,13 @@ const router = os.router({
|
|
|
8803
8944
|
api: types_exports.APISchema.optional(),
|
|
8804
8945
|
cssFramework: types_exports.CSSFrameworkSchema.optional(),
|
|
8805
8946
|
uiLibrary: types_exports.UILibrarySchema.optional(),
|
|
8947
|
+
shadcnBase: types_exports.ShadcnBaseSchema.optional().describe("shadcn/ui headless library (radix, base)"),
|
|
8948
|
+
shadcnStyle: types_exports.ShadcnStyleSchema.optional().describe("shadcn/ui visual style (vega, nova, maia, lyra, mira)"),
|
|
8949
|
+
shadcnIconLibrary: types_exports.ShadcnIconLibrarySchema.optional().describe("shadcn/ui icon library (lucide, tabler, hugeicons, phosphor, remixicon)"),
|
|
8950
|
+
shadcnColorTheme: types_exports.ShadcnColorThemeSchema.optional().describe("shadcn/ui color theme (neutral, blue, violet, etc.)"),
|
|
8951
|
+
shadcnBaseColor: types_exports.ShadcnBaseColorSchema.optional().describe("shadcn/ui base neutral color (neutral, stone, zinc, gray)"),
|
|
8952
|
+
shadcnFont: types_exports.ShadcnFontSchema.optional().describe("shadcn/ui font (inter, geist, figtree, etc.)"),
|
|
8953
|
+
shadcnRadius: types_exports.ShadcnRadiusSchema.optional().describe("shadcn/ui border radius (default, none, small, medium, large)"),
|
|
8806
8954
|
webDeploy: types_exports.WebDeploySchema.optional(),
|
|
8807
8955
|
serverDeploy: types_exports.ServerDeploySchema.optional(),
|
|
8808
8956
|
directoryConflict: types_exports.DirectoryConflictSchema.optional(),
|
|
@@ -9021,6 +9169,13 @@ async function createVirtual(options) {
|
|
|
9021
9169
|
serverDeploy: options.serverDeploy || "none",
|
|
9022
9170
|
cssFramework: options.cssFramework || "tailwind",
|
|
9023
9171
|
uiLibrary: options.uiLibrary || "shadcn-ui",
|
|
9172
|
+
shadcnBase: options.shadcnBase ?? "radix",
|
|
9173
|
+
shadcnStyle: options.shadcnStyle ?? "nova",
|
|
9174
|
+
shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
|
|
9175
|
+
shadcnColorTheme: options.shadcnColorTheme ?? "neutral",
|
|
9176
|
+
shadcnBaseColor: options.shadcnBaseColor ?? "neutral",
|
|
9177
|
+
shadcnFont: options.shadcnFont ?? "inter",
|
|
9178
|
+
shadcnRadius: options.shadcnRadius ?? "default",
|
|
9024
9179
|
ai: options.ai || "none",
|
|
9025
9180
|
stateManagement: options.stateManagement || "none",
|
|
9026
9181
|
forms: options.forms || "react-hook-form",
|