create-better-t-stack 2.37.0 → 2.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.js +1 -1
- package/dist/{src-CGVrE3la.js → src-BR5HLmM2.js} +266 -104
- package/package.json +1 -1
- package/templates/addons/ruler/.ruler/bts.md.hbs +2 -2
- package/templates/api/orpc/native/utils/orpc.ts.hbs +2 -2
- package/templates/api/orpc/server/base/src/lib/context.ts.hbs +10 -10
- package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +1 -1
- package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +2 -2
- package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +11 -1
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
- package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
- package/templates/api/trpc/native/utils/trpc.ts.hbs +2 -2
- package/templates/api/trpc/server/base/src/lib/context.ts.hbs +10 -10
- package/templates/api/trpc/server/base/src/lib/trpc.ts.hbs +1 -1
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +2 -2
- package/templates/auth/clerk/convex/backend/convex/auth.config.ts.hbs +12 -0
- package/templates/auth/clerk/convex/backend/convex/privateData.ts.hbs +16 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs +12 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs +67 -0
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-out.tsx.hbs +110 -0
- package/templates/auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs +27 -0
- package/templates/auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs +29 -0
- package/templates/auth/clerk/convex/web/react/next/src/middleware.ts.hbs +12 -0
- package/templates/auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs +32 -0
- package/templates/auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +37 -0
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +37 -0
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +18 -0
- package/templates/backend/convex/packages/backend/package.json.hbs +1 -0
- package/templates/backend/server/elysia/src/index.ts.hbs +3 -3
- package/templates/backend/server/express/src/index.ts.hbs +6 -6
- package/templates/backend/server/fastify/src/index.ts.hbs +4 -4
- package/templates/backend/server/hono/src/index.ts.hbs +4 -4
- package/templates/backend/server/server-base/src/routers/index.ts.hbs +4 -4
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +2 -2
- package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +35 -3
- package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +28 -0
- package/templates/frontend/native/nativewind/package.json.hbs +1 -0
- package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +32 -0
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +35 -0
- package/templates/frontend/native/unistyles/package.json.hbs +1 -0
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +3 -3
- package/templates/frontend/react/next/src/app/layout.tsx.hbs +23 -15
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +12 -0
- package/templates/frontend/react/react-router/src/root.tsx.hbs +28 -1
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +19 -1
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +8 -4
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +52 -5
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +3 -3
- package/templates/frontend/solid/src/components/header.tsx.hbs +3 -3
- package/templates/frontend/svelte/src/components/Header.svelte.hbs +3 -3
- /package/templates/auth/{native → better-auth/native}/native-base/lib/auth-client.ts.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/app/(drawer)/index.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-in.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-up.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/app/(drawer)/index.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-in.tsx.hbs +0 -0
- /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-up.tsx.hbs +0 -0
- /package/templates/auth/{server → better-auth/server}/base/src/lib/auth.ts.hbs +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/mysql/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/postgres/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/drizzle/sqlite/src/db/schema/auth.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/mongoose/mongodb/src/db/models/auth.model.ts +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/mongodb/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/mysql/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/postgres/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/db/prisma/sqlite/prisma/schema/auth.prisma +0 -0
- /package/templates/auth/{server → better-auth/server}/next/src/app/api/auth/[...all]/route.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignInForm.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignUpForm.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/components/UserMenu.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/middleware/auth.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/dashboard.vue.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/login.vue +0 -0
- /package/templates/auth/{web → better-auth/web}/nuxt/app/plugins/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/react/base/src/lib/auth-client.ts.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/app/dashboard/page.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/app/login/page.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/theme-provider.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/next/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/user-menu.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-in-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-up-form.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/components/user-menu.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/lib/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/routes/dashboard.tsx.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/solid/src/routes/login.tsx +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignInForm.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignUpForm.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/components/UserMenu.svelte +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/lib/auth-client.ts +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/routes/dashboard/+page.svelte.hbs +0 -0
- /package/templates/auth/{web → better-auth/web}/svelte/src/routes/login/+page.svelte +0 -0
|
@@ -35,7 +35,7 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
35
35
|
frontend: ["tanstack-router"],
|
|
36
36
|
database: "sqlite",
|
|
37
37
|
orm: "drizzle",
|
|
38
|
-
auth:
|
|
38
|
+
auth: "better-auth",
|
|
39
39
|
addons: ["turborepo"],
|
|
40
40
|
examples: [],
|
|
41
41
|
git: true,
|
|
@@ -61,6 +61,10 @@ const DEFAULT_CONFIG = getDefaultConfig();
|
|
|
61
61
|
const dependencyVersionMap = {
|
|
62
62
|
"better-auth": "^1.3.7",
|
|
63
63
|
"@better-auth/expo": "^1.3.7",
|
|
64
|
+
"@clerk/nextjs": "^6.31.5",
|
|
65
|
+
"@clerk/clerk-react": "^5.45.0",
|
|
66
|
+
"@clerk/tanstack-react-start": "^0.23.1",
|
|
67
|
+
"@clerk/clerk-expo": "^2.14.25",
|
|
64
68
|
"drizzle-orm": "^0.44.2",
|
|
65
69
|
"drizzle-kit": "^0.31.2",
|
|
66
70
|
"@libsql/client": "^0.15.9",
|
|
@@ -239,6 +243,11 @@ const APISchema = z.enum([
|
|
|
239
243
|
"orpc",
|
|
240
244
|
"none"
|
|
241
245
|
]).describe("API type");
|
|
246
|
+
const AuthSchema = z.enum([
|
|
247
|
+
"better-auth",
|
|
248
|
+
"clerk",
|
|
249
|
+
"none"
|
|
250
|
+
]).describe("Authentication provider");
|
|
242
251
|
const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(255, "Project name must be less than 255 characters").refine((name) => name === "." || !name.startsWith("."), "Project name cannot start with a dot (except for '.')").refine((name) => name === "." || !name.startsWith("-"), "Project name cannot start with a dash").refine((name) => {
|
|
243
252
|
const invalidChars = [
|
|
244
253
|
"<",
|
|
@@ -495,7 +504,6 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
495
504
|
}
|
|
496
505
|
function coerceBackendPresets(config) {
|
|
497
506
|
if (config.backend === "convex") {
|
|
498
|
-
config.auth = false;
|
|
499
507
|
config.database = "none";
|
|
500
508
|
config.orm = "none";
|
|
501
509
|
config.api = "none";
|
|
@@ -504,7 +512,7 @@ function coerceBackendPresets(config) {
|
|
|
504
512
|
config.examples = ["todo"];
|
|
505
513
|
}
|
|
506
514
|
if (config.backend === "none") {
|
|
507
|
-
config.auth =
|
|
515
|
+
config.auth = "none";
|
|
508
516
|
config.database = "none";
|
|
509
517
|
config.orm = "none";
|
|
510
518
|
config.api = "none";
|
|
@@ -516,7 +524,7 @@ function coerceBackendPresets(config) {
|
|
|
516
524
|
function incompatibleFlagsForBackend(backend, providedFlags, options) {
|
|
517
525
|
const list = [];
|
|
518
526
|
if (backend === "convex") {
|
|
519
|
-
if (providedFlags.has("auth") && options.auth
|
|
527
|
+
if (providedFlags.has("auth") && options.auth && options.auth !== "none" && options.auth !== "clerk") list.push(`--auth ${options.auth}`);
|
|
520
528
|
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
521
529
|
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
522
530
|
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
@@ -524,7 +532,7 @@ function incompatibleFlagsForBackend(backend, providedFlags, options) {
|
|
|
524
532
|
if (providedFlags.has("dbSetup") && options.dbSetup !== "none") list.push(`--db-setup ${options.dbSetup}`);
|
|
525
533
|
}
|
|
526
534
|
if (backend === "none") {
|
|
527
|
-
if (providedFlags.has("auth") && options.auth
|
|
535
|
+
if (providedFlags.has("auth") && options.auth && options.auth !== "none") list.push(`--auth ${options.auth}`);
|
|
528
536
|
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
529
537
|
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
530
538
|
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
@@ -543,8 +551,16 @@ function validateApiFrontendCompatibility(api, frontends = []) {
|
|
|
543
551
|
const includesSolid = frontends.includes("solid");
|
|
544
552
|
if ((includesNuxt || includesSvelte || includesSolid) && api === "trpc") exitWithError(`tRPC API is not supported with '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' frontend. Please use --api orpc or --api none or remove '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' from --frontend.`);
|
|
545
553
|
}
|
|
546
|
-
function isFrontendAllowedWithBackend(frontend, backend) {
|
|
554
|
+
function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
547
555
|
if (backend === "convex" && frontend === "solid") return false;
|
|
556
|
+
if (auth === "clerk" && backend === "convex") {
|
|
557
|
+
const incompatibleFrontends = [
|
|
558
|
+
"nuxt",
|
|
559
|
+
"svelte",
|
|
560
|
+
"solid"
|
|
561
|
+
];
|
|
562
|
+
if (incompatibleFrontends.includes(frontend)) return false;
|
|
563
|
+
}
|
|
548
564
|
return true;
|
|
549
565
|
}
|
|
550
566
|
function allowedApisForFrontends(frontends = []) {
|
|
@@ -630,12 +646,41 @@ async function getApiChoice(Api, frontend, backend) {
|
|
|
630
646
|
|
|
631
647
|
//#endregion
|
|
632
648
|
//#region src/prompts/auth.ts
|
|
633
|
-
async function getAuthChoice(auth, hasDatabase, backend) {
|
|
634
|
-
if (backend === "convex") return false;
|
|
635
|
-
if (!hasDatabase) return false;
|
|
649
|
+
async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
636
650
|
if (auth !== void 0) return auth;
|
|
637
|
-
|
|
638
|
-
|
|
651
|
+
if (backend === "convex") {
|
|
652
|
+
const unsupportedFrontends = frontend?.filter((f) => [
|
|
653
|
+
"nuxt",
|
|
654
|
+
"svelte",
|
|
655
|
+
"solid"
|
|
656
|
+
].includes(f));
|
|
657
|
+
if (unsupportedFrontends && unsupportedFrontends.length > 0) return "none";
|
|
658
|
+
const response$1 = await select({
|
|
659
|
+
message: "Select authentication provider",
|
|
660
|
+
options: [{
|
|
661
|
+
value: "clerk",
|
|
662
|
+
label: "Clerk",
|
|
663
|
+
hint: "More than auth, Complete User Management"
|
|
664
|
+
}, {
|
|
665
|
+
value: "none",
|
|
666
|
+
label: "None"
|
|
667
|
+
}],
|
|
668
|
+
initialValue: "clerk"
|
|
669
|
+
});
|
|
670
|
+
if (isCancel(response$1)) return exitCancelled("Operation cancelled");
|
|
671
|
+
return response$1;
|
|
672
|
+
}
|
|
673
|
+
if (!hasDatabase) return "none";
|
|
674
|
+
const response = await select({
|
|
675
|
+
message: "Select authentication provider",
|
|
676
|
+
options: [{
|
|
677
|
+
value: "better-auth",
|
|
678
|
+
label: "Better-Auth",
|
|
679
|
+
hint: "comprehensive auth framework for TypeScript"
|
|
680
|
+
}, {
|
|
681
|
+
value: "none",
|
|
682
|
+
label: "None"
|
|
683
|
+
}],
|
|
639
684
|
initialValue: DEFAULT_CONFIG.auth
|
|
640
685
|
});
|
|
641
686
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
@@ -857,7 +902,7 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
|
|
|
857
902
|
|
|
858
903
|
//#endregion
|
|
859
904
|
//#region src/prompts/frontend.ts
|
|
860
|
-
async function getFrontendChoice(frontendOptions, backend) {
|
|
905
|
+
async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
861
906
|
if (frontendOptions !== void 0) return frontendOptions;
|
|
862
907
|
const frontendTypes = await multiselect({
|
|
863
908
|
message: "Select project type",
|
|
@@ -913,7 +958,7 @@ async function getFrontendChoice(frontendOptions, backend) {
|
|
|
913
958
|
hint: "SSR, Server Functions, API Routes and more with TanStack Router"
|
|
914
959
|
}
|
|
915
960
|
];
|
|
916
|
-
const webOptions = allWebOptions.filter((option) => isFrontendAllowedWithBackend(option.value, backend));
|
|
961
|
+
const webOptions = allWebOptions.filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth));
|
|
917
962
|
const webFramework = await select({
|
|
918
963
|
message: "Choose web",
|
|
919
964
|
options: webOptions,
|
|
@@ -1214,13 +1259,13 @@ async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
|
1214
1259
|
//#region src/prompts/config-prompts.ts
|
|
1215
1260
|
async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
1216
1261
|
const result = await group({
|
|
1217
|
-
frontend: () => getFrontendChoice(flags.frontend, flags.backend),
|
|
1262
|
+
frontend: () => getFrontendChoice(flags.frontend, flags.backend, flags.auth),
|
|
1218
1263
|
backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
|
|
1219
1264
|
runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
|
|
1220
1265
|
database: ({ results }) => getDatabaseChoice(flags.database, results.backend, results.runtime),
|
|
1221
1266
|
orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
|
|
1222
1267
|
api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
|
|
1223
|
-
auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
|
|
1268
|
+
auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend, results.frontend),
|
|
1224
1269
|
addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
|
|
1225
1270
|
examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
|
|
1226
1271
|
dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
|
|
@@ -1235,7 +1280,6 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1235
1280
|
result.database = "none";
|
|
1236
1281
|
result.orm = "none";
|
|
1237
1282
|
result.api = "none";
|
|
1238
|
-
result.auth = false;
|
|
1239
1283
|
result.dbSetup = "none";
|
|
1240
1284
|
result.examples = ["todo"];
|
|
1241
1285
|
}
|
|
@@ -1244,7 +1288,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1244
1288
|
result.database = "none";
|
|
1245
1289
|
result.orm = "none";
|
|
1246
1290
|
result.api = "none";
|
|
1247
|
-
result.auth =
|
|
1291
|
+
result.auth = "none";
|
|
1248
1292
|
result.dbSetup = "none";
|
|
1249
1293
|
result.examples = [];
|
|
1250
1294
|
}
|
|
@@ -1400,10 +1444,7 @@ function displayConfig(config) {
|
|
|
1400
1444
|
if (config.api !== void 0) configDisplay.push(`${pc.blue("API:")} ${String(config.api)}`);
|
|
1401
1445
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
1402
1446
|
if (config.orm !== void 0) configDisplay.push(`${pc.blue("ORM:")} ${String(config.orm)}`);
|
|
1403
|
-
if (config.auth !== void 0) {
|
|
1404
|
-
const authText = typeof config.auth === "boolean" ? config.auth ? "Yes" : "No" : String(config.auth);
|
|
1405
|
-
configDisplay.push(`${pc.blue("Authentication:")} ${authText}`);
|
|
1406
|
-
}
|
|
1447
|
+
if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
|
|
1407
1448
|
if (config.addons !== void 0) {
|
|
1408
1449
|
const addons = Array.isArray(config.addons) ? config.addons : [config.addons];
|
|
1409
1450
|
const addonsText = addons.length > 0 && addons[0] !== void 0 ? addons.join(", ") : "none";
|
|
@@ -1441,7 +1482,7 @@ function generateReproducibleCommand(config) {
|
|
|
1441
1482
|
flags.push(`--database ${config.database}`);
|
|
1442
1483
|
flags.push(`--orm ${config.orm}`);
|
|
1443
1484
|
flags.push(`--api ${config.api}`);
|
|
1444
|
-
flags.push(config.auth
|
|
1485
|
+
flags.push(`--auth ${config.auth}`);
|
|
1445
1486
|
if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
|
|
1446
1487
|
else flags.push("--addons none");
|
|
1447
1488
|
if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
|
|
@@ -1649,8 +1690,8 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
1649
1690
|
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") exitWithError("MongoDB database requires Mongoose or Prisma ORM. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
|
|
1650
1691
|
if (has("database") && has("orm") && db && db !== "none" && orm === "none") exitWithError("Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', or '--orm mongoose'.");
|
|
1651
1692
|
if (has("orm") && has("database") && orm && orm !== "none" && db === "none") exitWithError("ORM selection requires a database. Please choose a database or set '--orm none'.");
|
|
1652
|
-
if (has("auth") && has("database") && cfg.auth && db === "none") exitWithError("Authentication requires a database. Please choose a database or set '--
|
|
1653
|
-
if (cfg.auth && db === "none") exitWithError("Authentication requires a database. Please choose a database or set '--
|
|
1693
|
+
if (has("auth") && has("database") && cfg.auth !== "none" && db === "none" && cfg.backend !== "convex") exitWithError("Authentication requires a database. Please choose a database or set '--auth none'.");
|
|
1694
|
+
if (cfg.auth !== "none" && db === "none" && cfg.backend !== "convex") exitWithError("Authentication requires a database. Please choose a database or set '--auth none'.");
|
|
1654
1695
|
if (orm && orm !== "none" && db === "none") exitWithError("ORM selection requires a database. Please choose a database or set '--orm none'.");
|
|
1655
1696
|
}
|
|
1656
1697
|
function validateDatabaseSetup(config, providedFlags) {
|
|
@@ -1697,6 +1738,16 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
1697
1738
|
}
|
|
1698
1739
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
1699
1740
|
const { backend } = config;
|
|
1741
|
+
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.");
|
|
1742
|
+
if (backend === "convex" && config.auth === "clerk" && config.frontend) {
|
|
1743
|
+
const incompatibleFrontends = config.frontend.filter((f) => [
|
|
1744
|
+
"nuxt",
|
|
1745
|
+
"svelte",
|
|
1746
|
+
"solid"
|
|
1747
|
+
].includes(f));
|
|
1748
|
+
if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
|
|
1749
|
+
}
|
|
1750
|
+
if (backend === "convex" && config.auth === "better-auth" && providedFlags.has("auth")) exitWithError("Better-Auth is not compatible with the Convex backend. Please use '--auth clerk' or '--auth none'.");
|
|
1700
1751
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
|
|
1701
1752
|
if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.");
|
|
1702
1753
|
}
|
|
@@ -2151,7 +2202,7 @@ async function setupDbOrmTemplates(projectDir, context) {
|
|
|
2151
2202
|
if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, serverAppDir, context);
|
|
2152
2203
|
}
|
|
2153
2204
|
async function setupAuthTemplate(projectDir, context) {
|
|
2154
|
-
if (context.
|
|
2205
|
+
if (!context.auth || context.auth === "none") return;
|
|
2155
2206
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2156
2207
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
2157
2208
|
const nativeAppDir = path.join(projectDir, "apps/native");
|
|
@@ -2170,27 +2221,61 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2170
2221
|
const hasNativeWind = context.frontend.includes("native-nativewind");
|
|
2171
2222
|
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
2172
2223
|
const hasNative = hasNativeWind || hasUnistyles;
|
|
2173
|
-
|
|
2174
|
-
|
|
2224
|
+
const authProvider = context.auth;
|
|
2225
|
+
if (context.backend === "convex" && authProvider === "clerk") {
|
|
2226
|
+
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2227
|
+
const convexClerkBackendSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/backend");
|
|
2228
|
+
if (await fs.pathExists(convexClerkBackendSrc)) {
|
|
2229
|
+
await fs.ensureDir(convexBackendDestDir);
|
|
2230
|
+
await processAndCopyFiles("**/*", convexClerkBackendSrc, convexBackendDestDir, context);
|
|
2231
|
+
}
|
|
2232
|
+
if (webAppDirExists) {
|
|
2233
|
+
const reactFramework = context.frontend.find((f) => [
|
|
2234
|
+
"tanstack-router",
|
|
2235
|
+
"react-router",
|
|
2236
|
+
"tanstack-start",
|
|
2237
|
+
"next"
|
|
2238
|
+
].includes(f));
|
|
2239
|
+
if (reactFramework) {
|
|
2240
|
+
const convexClerkWebSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/web/react/${reactFramework}`);
|
|
2241
|
+
if (await fs.pathExists(convexClerkWebSrc)) await processAndCopyFiles("**/*", convexClerkWebSrc, webAppDir, context);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
if (nativeAppDirExists) {
|
|
2245
|
+
const convexClerkNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/native/base");
|
|
2246
|
+
if (await fs.pathExists(convexClerkNativeBaseSrc)) await processAndCopyFiles("**/*", convexClerkNativeBaseSrc, nativeAppDir, context);
|
|
2247
|
+
const hasNativeWind$1 = context.frontend.includes("native-nativewind");
|
|
2248
|
+
const hasUnistyles$1 = context.frontend.includes("native-unistyles");
|
|
2249
|
+
let nativeFrameworkPath = "";
|
|
2250
|
+
if (hasNativeWind$1) nativeFrameworkPath = "nativewind";
|
|
2251
|
+
else if (hasUnistyles$1) nativeFrameworkPath = "unistyles";
|
|
2252
|
+
if (nativeFrameworkPath) {
|
|
2253
|
+
const convexClerkNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/native/${nativeFrameworkPath}`);
|
|
2254
|
+
if (await fs.pathExists(convexClerkNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexClerkNativeFrameworkSrc, nativeAppDir, context);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (serverAppDirExists && context.backend !== "convex") {
|
|
2260
|
+
const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
|
|
2175
2261
|
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
|
|
2176
2262
|
if (context.backend === "next") {
|
|
2177
|
-
const authServerNextSrc = path.join(PKG_ROOT,
|
|
2263
|
+
const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
|
|
2178
2264
|
if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
|
|
2179
2265
|
}
|
|
2180
2266
|
if (context.orm !== "none" && context.database !== "none") {
|
|
2181
2267
|
const orm = context.orm;
|
|
2182
2268
|
const db = context.database;
|
|
2183
2269
|
let authDbSrc = "";
|
|
2184
|
-
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/drizzle/${db}`);
|
|
2185
|
-
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/prisma/${db}`);
|
|
2186
|
-
else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/mongoose/${db}`);
|
|
2270
|
+
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
|
|
2271
|
+
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
|
|
2272
|
+
else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
|
|
2187
2273
|
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);
|
|
2188
|
-
else if (authDbSrc) {}
|
|
2189
2274
|
}
|
|
2190
2275
|
}
|
|
2191
2276
|
if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
|
|
2192
2277
|
if (hasReactWeb) {
|
|
2193
|
-
const authWebBaseSrc = path.join(PKG_ROOT,
|
|
2278
|
+
const authWebBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/base`);
|
|
2194
2279
|
if (await fs.pathExists(authWebBaseSrc)) await processAndCopyFiles("**/*", authWebBaseSrc, webAppDir, context);
|
|
2195
2280
|
const reactFramework = context.frontend.find((f) => [
|
|
2196
2281
|
"tanstack-router",
|
|
@@ -2199,28 +2284,28 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2199
2284
|
"next"
|
|
2200
2285
|
].includes(f));
|
|
2201
2286
|
if (reactFramework) {
|
|
2202
|
-
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/web/react/${reactFramework}`);
|
|
2287
|
+
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2203
2288
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2204
2289
|
}
|
|
2205
2290
|
} else if (hasNuxtWeb) {
|
|
2206
|
-
const authWebNuxtSrc = path.join(PKG_ROOT,
|
|
2291
|
+
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
2207
2292
|
if (await fs.pathExists(authWebNuxtSrc)) await processAndCopyFiles("**/*", authWebNuxtSrc, webAppDir, context);
|
|
2208
2293
|
} else if (hasSvelteWeb) {
|
|
2209
|
-
const authWebSvelteSrc = path.join(PKG_ROOT,
|
|
2294
|
+
const authWebSvelteSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/svelte`);
|
|
2210
2295
|
if (await fs.pathExists(authWebSvelteSrc)) await processAndCopyFiles("**/*", authWebSvelteSrc, webAppDir, context);
|
|
2211
2296
|
} else if (hasSolidWeb) {
|
|
2212
|
-
const authWebSolidSrc = path.join(PKG_ROOT,
|
|
2297
|
+
const authWebSolidSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/solid`);
|
|
2213
2298
|
if (await fs.pathExists(authWebSolidSrc)) await processAndCopyFiles("**/*", authWebSolidSrc, webAppDir, context);
|
|
2214
2299
|
}
|
|
2215
2300
|
}
|
|
2216
2301
|
if (hasNative && nativeAppDirExists) {
|
|
2217
|
-
const authNativeBaseSrc = path.join(PKG_ROOT,
|
|
2302
|
+
const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/native-base`);
|
|
2218
2303
|
if (await fs.pathExists(authNativeBaseSrc)) await processAndCopyFiles("**/*", authNativeBaseSrc, nativeAppDir, context);
|
|
2219
2304
|
let nativeFrameworkAuthPath = "";
|
|
2220
2305
|
if (hasNativeWind) nativeFrameworkAuthPath = "nativewind";
|
|
2221
2306
|
else if (hasUnistyles) nativeFrameworkAuthPath = "unistyles";
|
|
2222
2307
|
if (nativeFrameworkAuthPath) {
|
|
2223
|
-
const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/native/${nativeFrameworkAuthPath}`);
|
|
2308
|
+
const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/${nativeFrameworkAuthPath}`);
|
|
2224
2309
|
if (await fs.pathExists(authNativeFrameworkSrc)) await processAndCopyFiles("**/*", authNativeFrameworkSrc, nativeAppDir, context);
|
|
2225
2310
|
}
|
|
2226
2311
|
}
|
|
@@ -2901,7 +2986,7 @@ async function addAddonsToProject(input) {
|
|
|
2901
2986
|
frontend: detectedConfig.frontend || [],
|
|
2902
2987
|
addons: input.addons,
|
|
2903
2988
|
examples: detectedConfig.examples || [],
|
|
2904
|
-
auth: detectedConfig.auth ||
|
|
2989
|
+
auth: detectedConfig.auth || "none",
|
|
2905
2990
|
git: false,
|
|
2906
2991
|
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
2907
2992
|
install: input.install || false,
|
|
@@ -3673,7 +3758,7 @@ async function addDeploymentToProject(input) {
|
|
|
3673
3758
|
frontend: detectedConfig.frontend || [],
|
|
3674
3759
|
addons: detectedConfig.addons || [],
|
|
3675
3760
|
examples: detectedConfig.examples || [],
|
|
3676
|
-
auth: detectedConfig.auth ||
|
|
3761
|
+
auth: detectedConfig.auth || "none",
|
|
3677
3762
|
git: false,
|
|
3678
3763
|
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
3679
3764
|
install: input.install || false,
|
|
@@ -3749,58 +3834,6 @@ async function formatProjectWithBiome(projectDir) {
|
|
|
3749
3834
|
} catch {}
|
|
3750
3835
|
}
|
|
3751
3836
|
|
|
3752
|
-
//#endregion
|
|
3753
|
-
//#region src/helpers/addons/auth-setup.ts
|
|
3754
|
-
async function setupAuth(config) {
|
|
3755
|
-
const { auth, frontend, backend, projectDir } = config;
|
|
3756
|
-
if (backend === "convex" || !auth) return;
|
|
3757
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
3758
|
-
const clientDir = path.join(projectDir, "apps/web");
|
|
3759
|
-
const nativeDir = path.join(projectDir, "apps/native");
|
|
3760
|
-
const clientDirExists = await fs.pathExists(clientDir);
|
|
3761
|
-
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
3762
|
-
const serverDirExists = await fs.pathExists(serverDir);
|
|
3763
|
-
try {
|
|
3764
|
-
if (serverDirExists) await addPackageDependency({
|
|
3765
|
-
dependencies: ["better-auth"],
|
|
3766
|
-
projectDir: serverDir
|
|
3767
|
-
});
|
|
3768
|
-
const hasWebFrontend$1 = frontend.some((f) => [
|
|
3769
|
-
"react-router",
|
|
3770
|
-
"tanstack-router",
|
|
3771
|
-
"tanstack-start",
|
|
3772
|
-
"next",
|
|
3773
|
-
"nuxt",
|
|
3774
|
-
"svelte",
|
|
3775
|
-
"solid"
|
|
3776
|
-
].includes(f));
|
|
3777
|
-
if (hasWebFrontend$1 && clientDirExists) await addPackageDependency({
|
|
3778
|
-
dependencies: ["better-auth"],
|
|
3779
|
-
projectDir: clientDir
|
|
3780
|
-
});
|
|
3781
|
-
if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
|
|
3782
|
-
await addPackageDependency({
|
|
3783
|
-
dependencies: ["better-auth", "@better-auth/expo"],
|
|
3784
|
-
projectDir: nativeDir
|
|
3785
|
-
});
|
|
3786
|
-
if (serverDirExists) await addPackageDependency({
|
|
3787
|
-
dependencies: ["@better-auth/expo"],
|
|
3788
|
-
projectDir: serverDir
|
|
3789
|
-
});
|
|
3790
|
-
}
|
|
3791
|
-
} catch (error) {
|
|
3792
|
-
consola.error(pc.red("Failed to configure authentication dependencies"));
|
|
3793
|
-
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
3794
|
-
}
|
|
3795
|
-
}
|
|
3796
|
-
function generateAuthSecret(length = 32) {
|
|
3797
|
-
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
3798
|
-
let result = "";
|
|
3799
|
-
const charactersLength = 62;
|
|
3800
|
-
for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
3801
|
-
return result;
|
|
3802
|
-
}
|
|
3803
|
-
|
|
3804
3837
|
//#endregion
|
|
3805
3838
|
//#region src/helpers/addons/examples-setup.ts
|
|
3806
3839
|
async function setupExamples(config) {
|
|
@@ -4050,6 +4083,88 @@ async function setupBackendDependencies(config) {
|
|
|
4050
4083
|
});
|
|
4051
4084
|
}
|
|
4052
4085
|
|
|
4086
|
+
//#endregion
|
|
4087
|
+
//#region src/helpers/core/auth-setup.ts
|
|
4088
|
+
async function setupAuth(config) {
|
|
4089
|
+
const { auth, frontend, backend, projectDir } = config;
|
|
4090
|
+
if (!auth || auth === "none") return;
|
|
4091
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
4092
|
+
const clientDir = path.join(projectDir, "apps/web");
|
|
4093
|
+
const nativeDir = path.join(projectDir, "apps/native");
|
|
4094
|
+
const clientDirExists = await fs.pathExists(clientDir);
|
|
4095
|
+
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4096
|
+
const serverDirExists = await fs.pathExists(serverDir);
|
|
4097
|
+
try {
|
|
4098
|
+
if (backend === "convex") {
|
|
4099
|
+
if (auth === "clerk" && clientDirExists) {
|
|
4100
|
+
const hasNextJs = frontend.includes("next");
|
|
4101
|
+
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
4102
|
+
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4103
|
+
if (hasNextJs) await addPackageDependency({
|
|
4104
|
+
dependencies: ["@clerk/nextjs"],
|
|
4105
|
+
projectDir: clientDir
|
|
4106
|
+
});
|
|
4107
|
+
else if (hasTanStackStart) await addPackageDependency({
|
|
4108
|
+
dependencies: ["@clerk/tanstack-react-start"],
|
|
4109
|
+
projectDir: clientDir
|
|
4110
|
+
});
|
|
4111
|
+
else if (hasViteReactOther) await addPackageDependency({
|
|
4112
|
+
dependencies: ["@clerk/clerk-react"],
|
|
4113
|
+
projectDir: clientDir
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
4116
|
+
const hasNativeWind = frontend.includes("native-nativewind");
|
|
4117
|
+
const hasUnistyles = frontend.includes("native-unistyles");
|
|
4118
|
+
if (auth === "clerk" && nativeDirExists && (hasNativeWind || hasUnistyles)) await addPackageDependency({
|
|
4119
|
+
dependencies: ["@clerk/clerk-expo"],
|
|
4120
|
+
projectDir: nativeDir
|
|
4121
|
+
});
|
|
4122
|
+
return;
|
|
4123
|
+
}
|
|
4124
|
+
if (serverDirExists && auth === "better-auth") await addPackageDependency({
|
|
4125
|
+
dependencies: ["better-auth"],
|
|
4126
|
+
projectDir: serverDir
|
|
4127
|
+
});
|
|
4128
|
+
const hasWebFrontend$1 = frontend.some((f) => [
|
|
4129
|
+
"react-router",
|
|
4130
|
+
"tanstack-router",
|
|
4131
|
+
"tanstack-start",
|
|
4132
|
+
"next",
|
|
4133
|
+
"nuxt",
|
|
4134
|
+
"svelte",
|
|
4135
|
+
"solid"
|
|
4136
|
+
].includes(f));
|
|
4137
|
+
if (hasWebFrontend$1 && clientDirExists) {
|
|
4138
|
+
if (auth === "better-auth") await addPackageDependency({
|
|
4139
|
+
dependencies: ["better-auth"],
|
|
4140
|
+
projectDir: clientDir
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
4143
|
+
if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
|
|
4144
|
+
if (auth === "better-auth") {
|
|
4145
|
+
await addPackageDependency({
|
|
4146
|
+
dependencies: ["better-auth", "@better-auth/expo"],
|
|
4147
|
+
projectDir: nativeDir
|
|
4148
|
+
});
|
|
4149
|
+
if (serverDirExists) await addPackageDependency({
|
|
4150
|
+
dependencies: ["@better-auth/expo"],
|
|
4151
|
+
projectDir: serverDir
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
} catch (error) {
|
|
4156
|
+
consola.error(pc.red("Failed to configure authentication dependencies"));
|
|
4157
|
+
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
function generateAuthSecret(length = 32) {
|
|
4161
|
+
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
4162
|
+
let result = "";
|
|
4163
|
+
const charactersLength = 62;
|
|
4164
|
+
for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
4165
|
+
return result;
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4053
4168
|
//#endregion
|
|
4054
4169
|
//#region src/helpers/core/env-setup.ts
|
|
4055
4170
|
async function addEnvVariablesToFile(filePath, variables) {
|
|
@@ -4128,6 +4243,33 @@ async function setupEnvironmentVariables(config) {
|
|
|
4128
4243
|
value: serverUrl,
|
|
4129
4244
|
condition: true
|
|
4130
4245
|
}];
|
|
4246
|
+
if (backend === "convex" && auth === "clerk") {
|
|
4247
|
+
if (hasNextJs) clientVars.push({
|
|
4248
|
+
key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
|
|
4249
|
+
value: "",
|
|
4250
|
+
condition: true
|
|
4251
|
+
}, {
|
|
4252
|
+
key: "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
4253
|
+
value: "",
|
|
4254
|
+
condition: true
|
|
4255
|
+
}, {
|
|
4256
|
+
key: "CLERK_SECRET_KEY",
|
|
4257
|
+
value: "",
|
|
4258
|
+
condition: true
|
|
4259
|
+
});
|
|
4260
|
+
else if (hasReactRouter || hasTanStackRouter || hasTanStackStart) {
|
|
4261
|
+
clientVars.push({
|
|
4262
|
+
key: "VITE_CLERK_PUBLISHABLE_KEY",
|
|
4263
|
+
value: "",
|
|
4264
|
+
condition: true
|
|
4265
|
+
});
|
|
4266
|
+
if (hasTanStackStart) clientVars.push({
|
|
4267
|
+
key: "CLERK_SECRET_KEY",
|
|
4268
|
+
value: "",
|
|
4269
|
+
condition: true
|
|
4270
|
+
});
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4131
4273
|
await addEnvVariablesToFile(path.join(clientDir, ".env"), clientVars);
|
|
4132
4274
|
}
|
|
4133
4275
|
}
|
|
@@ -4145,6 +4287,11 @@ async function setupEnvironmentVariables(config) {
|
|
|
4145
4287
|
value: serverUrl,
|
|
4146
4288
|
condition: true
|
|
4147
4289
|
}];
|
|
4290
|
+
if (backend === "convex" && auth === "clerk") nativeVars.push({
|
|
4291
|
+
key: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
4292
|
+
value: "",
|
|
4293
|
+
condition: true
|
|
4294
|
+
});
|
|
4148
4295
|
await addEnvVariablesToFile(path.join(nativeDir, ".env"), nativeVars);
|
|
4149
4296
|
}
|
|
4150
4297
|
}
|
|
@@ -5226,7 +5373,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
|
|
|
5226
5373
|
${packageManagerRunCmd} dev:setup
|
|
5227
5374
|
\`\`\`
|
|
5228
5375
|
|
|
5229
|
-
Follow the prompts to create a new Convex project and connect it to your application
|
|
5376
|
+
Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
|
|
5230
5377
|
|
|
5231
5378
|
Then, run the development server:
|
|
5232
5379
|
|
|
@@ -5243,7 +5390,7 @@ ${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
|
|
|
5243
5390
|
## Project Structure
|
|
5244
5391
|
|
|
5245
5392
|
\`\`\`
|
|
5246
|
-
${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api)}
|
|
5393
|
+
${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth)}
|
|
5247
5394
|
\`\`\`
|
|
5248
5395
|
|
|
5249
5396
|
## Available Scripts
|
|
@@ -5293,7 +5440,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
|
|
|
5293
5440
|
else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
5294
5441
|
return instructions.join("\n");
|
|
5295
5442
|
}
|
|
5296
|
-
function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api) {
|
|
5443
|
+
function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
|
|
5297
5444
|
const structure = [`${projectName}/`, "├── apps/"];
|
|
5298
5445
|
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
5299
5446
|
const isBackendNone = backend === "none";
|
|
@@ -5323,6 +5470,7 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
|
|
|
5323
5470
|
if (isConvex) {
|
|
5324
5471
|
structure.push("├── packages/");
|
|
5325
5472
|
structure.push("│ └── backend/ # Convex backend functions and schema");
|
|
5473
|
+
if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
|
|
5326
5474
|
} else if (!isBackendNone) {
|
|
5327
5475
|
const backendName = backend[0].toUpperCase() + backend.slice(1);
|
|
5328
5476
|
const apiName = api !== "none" ? api.toUpperCase() : "";
|
|
@@ -5374,7 +5522,10 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5374
5522
|
const dbName = database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : database === "mongodb" ? "MongoDB" : "Database";
|
|
5375
5523
|
addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
|
|
5376
5524
|
}
|
|
5377
|
-
if (auth
|
|
5525
|
+
if (auth !== "none") {
|
|
5526
|
+
const authLabel = auth === "clerk" ? "Clerk" : "Better-Auth";
|
|
5527
|
+
addonsList.push(`- **Authentication** - ${authLabel}`);
|
|
5528
|
+
}
|
|
5378
5529
|
for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
|
|
5379
5530
|
else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
|
|
5380
5531
|
else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
|
|
@@ -5565,6 +5716,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
5565
5716
|
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
|
|
5566
5717
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
5567
5718
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
5719
|
+
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
5568
5720
|
const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
5569
5721
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
|
|
5570
5722
|
const hasWeb = frontend?.some((f) => [
|
|
@@ -5588,6 +5740,12 @@ async function displayPostInstallInstructions(config) {
|
|
|
5588
5740
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
|
|
5589
5741
|
if (isConvex) {
|
|
5590
5742
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
|
|
5743
|
+
if (config.auth === "clerk") {
|
|
5744
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${pc.bold("Clerk Setup:")}\n${pc.dim(" Follow the Convex + Clerk guide to configure authentication:")}\n${pc.cyan(" ")}${pc.underline("https://docs.convex.dev/auth/clerk")}\n\n`;
|
|
5745
|
+
output += `${pc.cyan(`${stepCounter++}.`)} ${pc.bold("Required Environment Variables:")}\n`;
|
|
5746
|
+
output += `${pc.dim(" •")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n`;
|
|
5747
|
+
output += `${pc.dim(" •")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env\n`;
|
|
5748
|
+
}
|
|
5591
5749
|
output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
|
|
5592
5750
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
|
|
5593
5751
|
} else {
|
|
@@ -5612,6 +5770,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
5612
5770
|
if (wranglerDeployInstructions) output += `\n${wranglerDeployInstructions.trim()}\n`;
|
|
5613
5771
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
5614
5772
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
5773
|
+
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
5615
5774
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
5616
5775
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
5617
5776
|
output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
|
|
@@ -5687,6 +5846,9 @@ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
|
5687
5846
|
if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
|
|
5688
5847
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
5689
5848
|
}
|
|
5849
|
+
function getClerkInstructions() {
|
|
5850
|
+
return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("1.")} Sign up for Clerk at ${pc.underline("https://clerk.com/sign-up")}\n${pc.cyan("2.")} Create a new application in Clerk Dashboard\n${pc.cyan("3.")} Create a JWT template named ${pc.bold("'convex'")} (exact name required)\n${pc.cyan("4.")} Copy your Clerk Frontend API URL (Issuer URL)\n${pc.cyan("5.")} Set environment variables:\n${pc.dim(" •")} CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.dim(" •")} CLERK_PUBLISHABLE_KEY in apps/*/.env\n${pc.cyan("6.")} Follow the complete guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.yellow("NOTE:")} Use Convex's <Authenticated> components instead of Clerk's <SignedIn>`;
|
|
5851
|
+
}
|
|
5690
5852
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
5691
5853
|
const instructions = [];
|
|
5692
5854
|
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
|
|
@@ -5889,8 +6051,8 @@ async function createProject(options) {
|
|
|
5889
6051
|
if (!isConvex) {
|
|
5890
6052
|
await setupDbOrmTemplates(projectDir, options);
|
|
5891
6053
|
await setupDockerComposeTemplates(projectDir, options);
|
|
5892
|
-
await setupAuthTemplate(projectDir, options);
|
|
5893
6054
|
}
|
|
6055
|
+
await setupAuthTemplate(projectDir, options);
|
|
5894
6056
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
5895
6057
|
await setupAddonsTemplate(projectDir, options);
|
|
5896
6058
|
await setupDeploymentTemplates(projectDir, options);
|
|
@@ -5902,7 +6064,7 @@ async function createProject(options) {
|
|
|
5902
6064
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
|
|
5903
6065
|
}
|
|
5904
6066
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
5905
|
-
if (
|
|
6067
|
+
if (options.auth && options.auth !== "none") await setupAuth(options);
|
|
5906
6068
|
await handleExtras(projectDir, options);
|
|
5907
6069
|
await setupEnvironmentVariables(options);
|
|
5908
6070
|
await updatePackageConfigurations(projectDir, options);
|
|
@@ -5981,7 +6143,7 @@ async function createProjectHandler(input) {
|
|
|
5981
6143
|
frontend: [],
|
|
5982
6144
|
addons: [],
|
|
5983
6145
|
examples: [],
|
|
5984
|
-
auth:
|
|
6146
|
+
auth: "none",
|
|
5985
6147
|
git: false,
|
|
5986
6148
|
packageManager: "npm",
|
|
5987
6149
|
install: false,
|
|
@@ -6016,8 +6178,8 @@ async function createProjectHandler(input) {
|
|
|
6016
6178
|
};
|
|
6017
6179
|
coerceBackendPresets(config);
|
|
6018
6180
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
6019
|
-
if (config.backend === "convex") log.info(
|
|
6020
|
-
else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth
|
|
6181
|
+
if (config.backend === "convex") log.info(`Due to '--backend convex' flag, the following options have been automatically set: database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`);
|
|
6182
|
+
else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth none, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none");
|
|
6021
6183
|
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
6022
6184
|
log.message(displayConfig(config));
|
|
6023
6185
|
log.message("");
|
|
@@ -6223,7 +6385,7 @@ const router = t.router({
|
|
|
6223
6385
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
6224
6386
|
database: DatabaseSchema.optional(),
|
|
6225
6387
|
orm: ORMSchema.optional(),
|
|
6226
|
-
auth:
|
|
6388
|
+
auth: AuthSchema.optional(),
|
|
6227
6389
|
frontend: z.array(FrontendSchema).optional(),
|
|
6228
6390
|
addons: z.array(AddonsSchema).optional(),
|
|
6229
6391
|
examples: z.array(ExamplesSchema).optional(),
|
|
@@ -6314,7 +6476,7 @@ function createBtsCli() {
|
|
|
6314
6476
|
* backend: "hono",
|
|
6315
6477
|
* database: "sqlite",
|
|
6316
6478
|
* orm: "drizzle",
|
|
6317
|
-
* auth:
|
|
6479
|
+
* auth: "better-auth",
|
|
6318
6480
|
* addons: ["biome", "turborepo"],
|
|
6319
6481
|
* packageManager: "bun",
|
|
6320
6482
|
* install: false,
|