create-better-t-stack 2.36.3 → 2.37.0-canary.28c5b8e0
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-CN1zi2A-.js → src-CzVr9ZoP.js} +285 -231
- 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 +1 -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 +3 -18
- 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/deploy/alchemy/wrangler.jsonc.hbs +0 -11
- /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",
|
|
@@ -129,7 +133,7 @@ const dependencyVersionMap = {
|
|
|
129
133
|
"nitro-cloudflare-dev": "^0.2.2",
|
|
130
134
|
"@sveltejs/adapter-cloudflare": "^7.2.1",
|
|
131
135
|
"@cloudflare/workers-types": "^4.20250822.0",
|
|
132
|
-
alchemy: "^0.
|
|
136
|
+
alchemy: "^0.63.0",
|
|
133
137
|
nitropack: "^2.12.4",
|
|
134
138
|
dotenv: "^17.2.1"
|
|
135
139
|
};
|
|
@@ -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,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
495
504
|
}
|
|
496
505
|
function coerceBackendPresets(config) {
|
|
497
506
|
if (config.backend === "convex") {
|
|
498
|
-
config.auth =
|
|
507
|
+
if (config.auth !== "clerk") config.auth = "none";
|
|
499
508
|
config.database = "none";
|
|
500
509
|
config.orm = "none";
|
|
501
510
|
config.api = "none";
|
|
@@ -504,7 +513,7 @@ function coerceBackendPresets(config) {
|
|
|
504
513
|
config.examples = ["todo"];
|
|
505
514
|
}
|
|
506
515
|
if (config.backend === "none") {
|
|
507
|
-
config.auth =
|
|
516
|
+
config.auth = "none";
|
|
508
517
|
config.database = "none";
|
|
509
518
|
config.orm = "none";
|
|
510
519
|
config.api = "none";
|
|
@@ -516,7 +525,7 @@ function coerceBackendPresets(config) {
|
|
|
516
525
|
function incompatibleFlagsForBackend(backend, providedFlags, options) {
|
|
517
526
|
const list = [];
|
|
518
527
|
if (backend === "convex") {
|
|
519
|
-
if (providedFlags.has("auth") && options.auth
|
|
528
|
+
if (providedFlags.has("auth") && options.auth && options.auth !== "none" && options.auth !== "clerk") list.push(`--auth ${options.auth}`);
|
|
520
529
|
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
521
530
|
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
522
531
|
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
@@ -524,7 +533,7 @@ function incompatibleFlagsForBackend(backend, providedFlags, options) {
|
|
|
524
533
|
if (providedFlags.has("dbSetup") && options.dbSetup !== "none") list.push(`--db-setup ${options.dbSetup}`);
|
|
525
534
|
}
|
|
526
535
|
if (backend === "none") {
|
|
527
|
-
if (providedFlags.has("auth") && options.auth
|
|
536
|
+
if (providedFlags.has("auth") && options.auth && options.auth !== "none") list.push(`--auth ${options.auth}`);
|
|
528
537
|
if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
|
|
529
538
|
if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
|
|
530
539
|
if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
|
|
@@ -592,7 +601,7 @@ function validateAlchemyCompatibility(webDeploy, serverDeploy, frontends = []) {
|
|
|
592
601
|
const isAlchemyWebDeploy = webDeploy === "alchemy";
|
|
593
602
|
const isAlchemyServerDeploy = serverDeploy === "alchemy";
|
|
594
603
|
if (isAlchemyWebDeploy || isAlchemyServerDeploy) {
|
|
595
|
-
const incompatibleFrontends = frontends.filter((f) => f === "next"
|
|
604
|
+
const incompatibleFrontends = frontends.filter((f) => f === "next");
|
|
596
605
|
if (incompatibleFrontends.length > 0) {
|
|
597
606
|
const deployType = isAlchemyWebDeploy && isAlchemyServerDeploy ? "web and server deployment" : isAlchemyWebDeploy ? "web deployment" : "server deployment";
|
|
598
607
|
exitWithError(`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")} frontend(s). Please choose a different frontend or deployment option.`);
|
|
@@ -631,11 +640,32 @@ async function getApiChoice(Api, frontend, backend) {
|
|
|
631
640
|
//#endregion
|
|
632
641
|
//#region src/prompts/auth.ts
|
|
633
642
|
async function getAuthChoice(auth, hasDatabase, backend) {
|
|
634
|
-
if (backend === "convex") return false;
|
|
635
|
-
if (!hasDatabase) return false;
|
|
636
643
|
if (auth !== void 0) return auth;
|
|
637
|
-
|
|
638
|
-
|
|
644
|
+
if (backend === "convex") {
|
|
645
|
+
const response$1 = await select({
|
|
646
|
+
message: "Select authentication provider",
|
|
647
|
+
options: [{
|
|
648
|
+
value: "clerk",
|
|
649
|
+
label: "Clerk"
|
|
650
|
+
}, {
|
|
651
|
+
value: "none",
|
|
652
|
+
label: "None"
|
|
653
|
+
}],
|
|
654
|
+
initialValue: "clerk"
|
|
655
|
+
});
|
|
656
|
+
if (isCancel(response$1)) return exitCancelled("Operation cancelled");
|
|
657
|
+
return response$1;
|
|
658
|
+
}
|
|
659
|
+
if (!hasDatabase) return "none";
|
|
660
|
+
const response = await select({
|
|
661
|
+
message: "Select authentication provider",
|
|
662
|
+
options: [{
|
|
663
|
+
value: "better-auth",
|
|
664
|
+
label: "Better-Auth"
|
|
665
|
+
}, {
|
|
666
|
+
value: "none",
|
|
667
|
+
label: "None"
|
|
668
|
+
}],
|
|
639
669
|
initialValue: DEFAULT_CONFIG.auth
|
|
640
670
|
});
|
|
641
671
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
@@ -1152,7 +1182,7 @@ function getDeploymentDisplay(deployment) {
|
|
|
1152
1182
|
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
1153
1183
|
if (deployment !== void 0) return deployment;
|
|
1154
1184
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1155
|
-
const hasIncompatibleFrontend = frontend.some((f) => f === "next"
|
|
1185
|
+
const hasIncompatibleFrontend = frontend.some((f) => f === "next");
|
|
1156
1186
|
const availableDeployments = hasIncompatibleFrontend ? ["wrangler", "none"] : [
|
|
1157
1187
|
"wrangler",
|
|
1158
1188
|
"alchemy",
|
|
@@ -1176,7 +1206,7 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1176
1206
|
}
|
|
1177
1207
|
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
1178
1208
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1179
|
-
const hasIncompatibleFrontend = frontend.some((f) => f === "next"
|
|
1209
|
+
const hasIncompatibleFrontend = frontend.some((f) => f === "next");
|
|
1180
1210
|
const options = [];
|
|
1181
1211
|
if (existingDeployment !== "wrangler") {
|
|
1182
1212
|
const { label, hint } = getDeploymentDisplay("wrangler");
|
|
@@ -1235,7 +1265,6 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1235
1265
|
result.database = "none";
|
|
1236
1266
|
result.orm = "none";
|
|
1237
1267
|
result.api = "none";
|
|
1238
|
-
result.auth = false;
|
|
1239
1268
|
result.dbSetup = "none";
|
|
1240
1269
|
result.examples = ["todo"];
|
|
1241
1270
|
}
|
|
@@ -1244,7 +1273,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1244
1273
|
result.database = "none";
|
|
1245
1274
|
result.orm = "none";
|
|
1246
1275
|
result.api = "none";
|
|
1247
|
-
result.auth =
|
|
1276
|
+
result.auth = "none";
|
|
1248
1277
|
result.dbSetup = "none";
|
|
1249
1278
|
result.examples = [];
|
|
1250
1279
|
}
|
|
@@ -1345,7 +1374,7 @@ const getLatestCLIVersion = () => {
|
|
|
1345
1374
|
*/
|
|
1346
1375
|
function isTelemetryEnabled() {
|
|
1347
1376
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1348
|
-
const BTS_TELEMETRY = "
|
|
1377
|
+
const BTS_TELEMETRY = "0";
|
|
1349
1378
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1350
1379
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1351
1380
|
return true;
|
|
@@ -1353,8 +1382,8 @@ function isTelemetryEnabled() {
|
|
|
1353
1382
|
|
|
1354
1383
|
//#endregion
|
|
1355
1384
|
//#region src/utils/analytics.ts
|
|
1356
|
-
const POSTHOG_API_KEY = "
|
|
1357
|
-
const POSTHOG_HOST = "
|
|
1385
|
+
const POSTHOG_API_KEY = "random";
|
|
1386
|
+
const POSTHOG_HOST = "random";
|
|
1358
1387
|
function generateSessionId() {
|
|
1359
1388
|
const rand = Math.random().toString(36).slice(2);
|
|
1360
1389
|
const now = Date.now().toString(36);
|
|
@@ -1400,10 +1429,7 @@ function displayConfig(config) {
|
|
|
1400
1429
|
if (config.api !== void 0) configDisplay.push(`${pc.blue("API:")} ${String(config.api)}`);
|
|
1401
1430
|
if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
|
|
1402
1431
|
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
|
-
}
|
|
1432
|
+
if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
|
|
1407
1433
|
if (config.addons !== void 0) {
|
|
1408
1434
|
const addons = Array.isArray(config.addons) ? config.addons : [config.addons];
|
|
1409
1435
|
const addonsText = addons.length > 0 && addons[0] !== void 0 ? addons.join(", ") : "none";
|
|
@@ -1441,7 +1467,7 @@ function generateReproducibleCommand(config) {
|
|
|
1441
1467
|
flags.push(`--database ${config.database}`);
|
|
1442
1468
|
flags.push(`--orm ${config.orm}`);
|
|
1443
1469
|
flags.push(`--api ${config.api}`);
|
|
1444
|
-
flags.push(config.auth
|
|
1470
|
+
flags.push(`--auth ${config.auth}`);
|
|
1445
1471
|
if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
|
|
1446
1472
|
else flags.push("--addons none");
|
|
1447
1473
|
if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
|
|
@@ -1649,8 +1675,8 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
1649
1675
|
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
1676
|
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
1677
|
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 '--
|
|
1678
|
+
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'.");
|
|
1679
|
+
if (cfg.auth !== "none" && db === "none" && cfg.backend !== "convex") exitWithError("Authentication requires a database. Please choose a database or set '--auth none'.");
|
|
1654
1680
|
if (orm && orm !== "none" && db === "none") exitWithError("ORM selection requires a database. Please choose a database or set '--orm none'.");
|
|
1655
1681
|
}
|
|
1656
1682
|
function validateDatabaseSetup(config, providedFlags) {
|
|
@@ -1697,6 +1723,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
1697
1723
|
}
|
|
1698
1724
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
1699
1725
|
const { backend } = config;
|
|
1726
|
+
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.");
|
|
1700
1727
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
|
|
1701
1728
|
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
1729
|
}
|
|
@@ -2151,7 +2178,7 @@ async function setupDbOrmTemplates(projectDir, context) {
|
|
|
2151
2178
|
if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, serverAppDir, context);
|
|
2152
2179
|
}
|
|
2153
2180
|
async function setupAuthTemplate(projectDir, context) {
|
|
2154
|
-
if (context.
|
|
2181
|
+
if (!context.auth || context.auth === "none") return;
|
|
2155
2182
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2156
2183
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
2157
2184
|
const nativeAppDir = path.join(projectDir, "apps/native");
|
|
@@ -2170,27 +2197,61 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2170
2197
|
const hasNativeWind = context.frontend.includes("native-nativewind");
|
|
2171
2198
|
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
2172
2199
|
const hasNative = hasNativeWind || hasUnistyles;
|
|
2173
|
-
|
|
2174
|
-
|
|
2200
|
+
const authProvider = context.auth;
|
|
2201
|
+
if (context.backend === "convex" && authProvider === "clerk") {
|
|
2202
|
+
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
2203
|
+
const convexClerkBackendSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/backend");
|
|
2204
|
+
if (await fs.pathExists(convexClerkBackendSrc)) {
|
|
2205
|
+
await fs.ensureDir(convexBackendDestDir);
|
|
2206
|
+
await processAndCopyFiles("**/*", convexClerkBackendSrc, convexBackendDestDir, context);
|
|
2207
|
+
}
|
|
2208
|
+
if (webAppDirExists) {
|
|
2209
|
+
const reactFramework = context.frontend.find((f) => [
|
|
2210
|
+
"tanstack-router",
|
|
2211
|
+
"react-router",
|
|
2212
|
+
"tanstack-start",
|
|
2213
|
+
"next"
|
|
2214
|
+
].includes(f));
|
|
2215
|
+
if (reactFramework) {
|
|
2216
|
+
const convexClerkWebSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/web/react/${reactFramework}`);
|
|
2217
|
+
if (await fs.pathExists(convexClerkWebSrc)) await processAndCopyFiles("**/*", convexClerkWebSrc, webAppDir, context);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
if (nativeAppDirExists) {
|
|
2221
|
+
const convexClerkNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/native/base");
|
|
2222
|
+
if (await fs.pathExists(convexClerkNativeBaseSrc)) await processAndCopyFiles("**/*", convexClerkNativeBaseSrc, nativeAppDir, context);
|
|
2223
|
+
const hasNativeWind$1 = context.frontend.includes("native-nativewind");
|
|
2224
|
+
const hasUnistyles$1 = context.frontend.includes("native-unistyles");
|
|
2225
|
+
let nativeFrameworkPath = "";
|
|
2226
|
+
if (hasNativeWind$1) nativeFrameworkPath = "nativewind";
|
|
2227
|
+
else if (hasUnistyles$1) nativeFrameworkPath = "unistyles";
|
|
2228
|
+
if (nativeFrameworkPath) {
|
|
2229
|
+
const convexClerkNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/native/${nativeFrameworkPath}`);
|
|
2230
|
+
if (await fs.pathExists(convexClerkNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexClerkNativeFrameworkSrc, nativeAppDir, context);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
if (serverAppDirExists && context.backend !== "convex") {
|
|
2236
|
+
const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
|
|
2175
2237
|
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
|
|
2176
2238
|
if (context.backend === "next") {
|
|
2177
|
-
const authServerNextSrc = path.join(PKG_ROOT,
|
|
2239
|
+
const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
|
|
2178
2240
|
if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
|
|
2179
2241
|
}
|
|
2180
2242
|
if (context.orm !== "none" && context.database !== "none") {
|
|
2181
2243
|
const orm = context.orm;
|
|
2182
2244
|
const db = context.database;
|
|
2183
2245
|
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}`);
|
|
2246
|
+
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
|
|
2247
|
+
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
|
|
2248
|
+
else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
|
|
2187
2249
|
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);
|
|
2188
|
-
else if (authDbSrc) {}
|
|
2189
2250
|
}
|
|
2190
2251
|
}
|
|
2191
2252
|
if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
|
|
2192
2253
|
if (hasReactWeb) {
|
|
2193
|
-
const authWebBaseSrc = path.join(PKG_ROOT,
|
|
2254
|
+
const authWebBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/base`);
|
|
2194
2255
|
if (await fs.pathExists(authWebBaseSrc)) await processAndCopyFiles("**/*", authWebBaseSrc, webAppDir, context);
|
|
2195
2256
|
const reactFramework = context.frontend.find((f) => [
|
|
2196
2257
|
"tanstack-router",
|
|
@@ -2199,28 +2260,28 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2199
2260
|
"next"
|
|
2200
2261
|
].includes(f));
|
|
2201
2262
|
if (reactFramework) {
|
|
2202
|
-
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/web/react/${reactFramework}`);
|
|
2263
|
+
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2203
2264
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2204
2265
|
}
|
|
2205
2266
|
} else if (hasNuxtWeb) {
|
|
2206
|
-
const authWebNuxtSrc = path.join(PKG_ROOT,
|
|
2267
|
+
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
2207
2268
|
if (await fs.pathExists(authWebNuxtSrc)) await processAndCopyFiles("**/*", authWebNuxtSrc, webAppDir, context);
|
|
2208
2269
|
} else if (hasSvelteWeb) {
|
|
2209
|
-
const authWebSvelteSrc = path.join(PKG_ROOT,
|
|
2270
|
+
const authWebSvelteSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/svelte`);
|
|
2210
2271
|
if (await fs.pathExists(authWebSvelteSrc)) await processAndCopyFiles("**/*", authWebSvelteSrc, webAppDir, context);
|
|
2211
2272
|
} else if (hasSolidWeb) {
|
|
2212
|
-
const authWebSolidSrc = path.join(PKG_ROOT,
|
|
2273
|
+
const authWebSolidSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/solid`);
|
|
2213
2274
|
if (await fs.pathExists(authWebSolidSrc)) await processAndCopyFiles("**/*", authWebSolidSrc, webAppDir, context);
|
|
2214
2275
|
}
|
|
2215
2276
|
}
|
|
2216
2277
|
if (hasNative && nativeAppDirExists) {
|
|
2217
|
-
const authNativeBaseSrc = path.join(PKG_ROOT,
|
|
2278
|
+
const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/native-base`);
|
|
2218
2279
|
if (await fs.pathExists(authNativeBaseSrc)) await processAndCopyFiles("**/*", authNativeBaseSrc, nativeAppDir, context);
|
|
2219
2280
|
let nativeFrameworkAuthPath = "";
|
|
2220
2281
|
if (hasNativeWind) nativeFrameworkAuthPath = "nativewind";
|
|
2221
2282
|
else if (hasUnistyles) nativeFrameworkAuthPath = "unistyles";
|
|
2222
2283
|
if (nativeFrameworkAuthPath) {
|
|
2223
|
-
const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/native/${nativeFrameworkAuthPath}`);
|
|
2284
|
+
const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/${nativeFrameworkAuthPath}`);
|
|
2224
2285
|
if (await fs.pathExists(authNativeFrameworkSrc)) await processAndCopyFiles("**/*", authNativeFrameworkSrc, nativeAppDir, context);
|
|
2225
2286
|
}
|
|
2226
2287
|
}
|
|
@@ -2350,10 +2411,7 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
2350
2411
|
if (await fs.pathExists(alchemyTemplateSrc)) {
|
|
2351
2412
|
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
|
|
2352
2413
|
const serverAppDir = path.join(projectDir, "apps/server");
|
|
2353
|
-
if (await fs.pathExists(serverAppDir))
|
|
2354
|
-
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2355
|
-
await processAndCopyFiles("wrangler.jsonc.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2356
|
-
}
|
|
2414
|
+
if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2357
2415
|
}
|
|
2358
2416
|
} else {
|
|
2359
2417
|
if (context.webDeploy === "alchemy") {
|
|
@@ -2367,7 +2425,6 @@ async function setupDeploymentTemplates(projectDir, context) {
|
|
|
2367
2425
|
if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
|
|
2368
2426
|
await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2369
2427
|
await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2370
|
-
await processAndCopyFiles("wrangler.jsonc.hbs", alchemyTemplateSrc, serverAppDir, context);
|
|
2371
2428
|
}
|
|
2372
2429
|
}
|
|
2373
2430
|
}
|
|
@@ -3000,11 +3057,9 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
|
3000
3057
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
3001
3058
|
packageJson.scripts = {
|
|
3002
3059
|
...packageJson.scripts,
|
|
3003
|
-
dev: "
|
|
3004
|
-
build: "wrangler deploy --dry-run",
|
|
3060
|
+
dev: "alchemy dev",
|
|
3005
3061
|
deploy: "alchemy deploy",
|
|
3006
|
-
destroy: "alchemy destroy"
|
|
3007
|
-
"alchemy:dev": "alchemy dev"
|
|
3062
|
+
destroy: "alchemy destroy"
|
|
3008
3063
|
};
|
|
3009
3064
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3010
3065
|
}
|
|
@@ -3012,7 +3067,7 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
|
|
|
3012
3067
|
|
|
3013
3068
|
//#endregion
|
|
3014
3069
|
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
3015
|
-
async function setupNextAlchemyDeploy(projectDir, _packageManager) {
|
|
3070
|
+
async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3016
3071
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3017
3072
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3018
3073
|
await addPackageDependency({
|
|
@@ -3022,11 +3077,11 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3022
3077
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3023
3078
|
if (await fs.pathExists(pkgPath)) {
|
|
3024
3079
|
const pkg = await fs.readJson(pkgPath);
|
|
3025
|
-
pkg.scripts = {
|
|
3080
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3026
3081
|
...pkg.scripts,
|
|
3027
3082
|
deploy: "alchemy deploy",
|
|
3028
3083
|
destroy: "alchemy destroy",
|
|
3029
|
-
|
|
3084
|
+
dev: "alchemy dev"
|
|
3030
3085
|
};
|
|
3031
3086
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3032
3087
|
}
|
|
@@ -3034,7 +3089,7 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3034
3089
|
|
|
3035
3090
|
//#endregion
|
|
3036
3091
|
//#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
|
|
3037
|
-
async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
|
|
3092
|
+
async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3038
3093
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3039
3094
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3040
3095
|
await addPackageDependency({
|
|
@@ -3048,11 +3103,11 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3048
3103
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3049
3104
|
if (await fs.pathExists(pkgPath)) {
|
|
3050
3105
|
const pkg = await fs.readJson(pkgPath);
|
|
3051
|
-
pkg.scripts = {
|
|
3106
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3052
3107
|
...pkg.scripts,
|
|
3053
3108
|
deploy: "alchemy deploy",
|
|
3054
3109
|
destroy: "alchemy destroy",
|
|
3055
|
-
|
|
3110
|
+
dev: "alchemy dev"
|
|
3056
3111
|
};
|
|
3057
3112
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3058
3113
|
}
|
|
@@ -3102,112 +3157,29 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3102
3157
|
|
|
3103
3158
|
//#endregion
|
|
3104
3159
|
//#region src/helpers/deployment/alchemy/alchemy-react-router-setup.ts
|
|
3105
|
-
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager) {
|
|
3160
|
+
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3106
3161
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3107
3162
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3108
3163
|
await addPackageDependency({
|
|
3109
|
-
devDependencies: [
|
|
3110
|
-
"alchemy",
|
|
3111
|
-
"@cloudflare/vite-plugin",
|
|
3112
|
-
"dotenv"
|
|
3113
|
-
],
|
|
3164
|
+
devDependencies: ["alchemy", "dotenv"],
|
|
3114
3165
|
projectDir: webAppDir
|
|
3115
3166
|
});
|
|
3116
3167
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3117
3168
|
if (await fs.pathExists(pkgPath)) {
|
|
3118
3169
|
const pkg = await fs.readJson(pkgPath);
|
|
3119
|
-
pkg.scripts = {
|
|
3170
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3120
3171
|
...pkg.scripts,
|
|
3121
3172
|
deploy: "alchemy deploy",
|
|
3122
3173
|
destroy: "alchemy destroy",
|
|
3123
|
-
|
|
3174
|
+
dev: "alchemy dev"
|
|
3124
3175
|
};
|
|
3125
3176
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3126
3177
|
}
|
|
3127
|
-
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
3128
|
-
if (await fs.pathExists(viteConfigPath)) try {
|
|
3129
|
-
const project = new Project({ manipulationSettings: {
|
|
3130
|
-
indentationText: IndentationText.TwoSpaces,
|
|
3131
|
-
quoteKind: QuoteKind.Double
|
|
3132
|
-
} });
|
|
3133
|
-
project.addSourceFileAtPath(viteConfigPath);
|
|
3134
|
-
const sourceFile = project.getSourceFileOrThrow(viteConfigPath);
|
|
3135
|
-
const alchemyImport = sourceFile.getImportDeclaration("alchemy/cloudflare/react-router");
|
|
3136
|
-
if (!alchemyImport) sourceFile.addImportDeclaration({
|
|
3137
|
-
moduleSpecifier: "alchemy/cloudflare/react-router",
|
|
3138
|
-
defaultImport: "alchemy"
|
|
3139
|
-
});
|
|
3140
|
-
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
3141
|
-
if (!exportAssignment) return;
|
|
3142
|
-
const defineConfigCall = exportAssignment.getExpression();
|
|
3143
|
-
if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineConfig") return;
|
|
3144
|
-
let configObject = defineConfigCall.getArguments()[0];
|
|
3145
|
-
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
3146
|
-
if (Node.isObjectLiteralExpression(configObject)) {
|
|
3147
|
-
const pluginsProperty = configObject.getProperty("plugins");
|
|
3148
|
-
if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
|
|
3149
|
-
const initializer = pluginsProperty.getInitializer();
|
|
3150
|
-
if (Node.isArrayLiteralExpression(initializer)) {
|
|
3151
|
-
const hasCloudflarePlugin = initializer.getElements().some((el) => el.getText().includes("cloudflare("));
|
|
3152
|
-
if (!hasCloudflarePlugin) initializer.addElement("alchemy()");
|
|
3153
|
-
}
|
|
3154
|
-
} else if (!pluginsProperty) configObject.addPropertyAssignment({
|
|
3155
|
-
name: "plugins",
|
|
3156
|
-
initializer: "[alchemy()]"
|
|
3157
|
-
});
|
|
3158
|
-
}
|
|
3159
|
-
await project.save();
|
|
3160
|
-
} catch (error) {
|
|
3161
|
-
console.warn("Failed to update vite.config.ts:", error);
|
|
3162
|
-
}
|
|
3163
|
-
const reactRouterConfigPath = path.join(webAppDir, "react-router.config.ts");
|
|
3164
|
-
if (await fs.pathExists(reactRouterConfigPath)) try {
|
|
3165
|
-
const project = new Project({ manipulationSettings: {
|
|
3166
|
-
indentationText: IndentationText.TwoSpaces,
|
|
3167
|
-
quoteKind: QuoteKind.Double
|
|
3168
|
-
} });
|
|
3169
|
-
project.addSourceFileAtPath(reactRouterConfigPath);
|
|
3170
|
-
const sourceFile = project.getSourceFileOrThrow(reactRouterConfigPath);
|
|
3171
|
-
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
3172
|
-
if (!exportAssignment) return;
|
|
3173
|
-
const configExpression = exportAssignment.getExpression();
|
|
3174
|
-
let configObject;
|
|
3175
|
-
if (Node.isObjectLiteralExpression(configExpression)) configObject = configExpression;
|
|
3176
|
-
else if (Node.isSatisfiesExpression(configExpression)) {
|
|
3177
|
-
const expression = configExpression.getExpression();
|
|
3178
|
-
if (Node.isObjectLiteralExpression(expression)) configObject = expression;
|
|
3179
|
-
}
|
|
3180
|
-
if (!configObject || !Node.isObjectLiteralExpression(configObject)) return;
|
|
3181
|
-
const futureProperty = configObject.getProperty("future");
|
|
3182
|
-
if (!futureProperty) configObject.addPropertyAssignment({
|
|
3183
|
-
name: "future",
|
|
3184
|
-
initializer: `{
|
|
3185
|
-
unstable_viteEnvironmentApi: true,
|
|
3186
|
-
}`
|
|
3187
|
-
});
|
|
3188
|
-
else if (Node.isPropertyAssignment(futureProperty)) {
|
|
3189
|
-
const futureInitializer = futureProperty.getInitializer();
|
|
3190
|
-
if (Node.isObjectLiteralExpression(futureInitializer)) {
|
|
3191
|
-
const viteEnvApiProp = futureInitializer.getProperty("unstable_viteEnvironmentApi");
|
|
3192
|
-
if (!viteEnvApiProp) futureInitializer.addPropertyAssignment({
|
|
3193
|
-
name: "unstable_viteEnvironmentApi",
|
|
3194
|
-
initializer: "true"
|
|
3195
|
-
});
|
|
3196
|
-
else if (Node.isPropertyAssignment(viteEnvApiProp)) {
|
|
3197
|
-
const value = viteEnvApiProp.getInitializer()?.getText();
|
|
3198
|
-
if (value === "false") viteEnvApiProp.setInitializer("true");
|
|
3199
|
-
}
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
await project.save();
|
|
3203
|
-
} catch (error) {
|
|
3204
|
-
console.warn("Failed to update react-router.config.ts:", error);
|
|
3205
|
-
}
|
|
3206
3178
|
}
|
|
3207
3179
|
|
|
3208
3180
|
//#endregion
|
|
3209
3181
|
//#region src/helpers/deployment/alchemy/alchemy-solid-setup.ts
|
|
3210
|
-
async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
|
|
3182
|
+
async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3211
3183
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3212
3184
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3213
3185
|
await addPackageDependency({
|
|
@@ -3217,11 +3189,11 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3217
3189
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3218
3190
|
if (await fs.pathExists(pkgPath)) {
|
|
3219
3191
|
const pkg = await fs.readJson(pkgPath);
|
|
3220
|
-
pkg.scripts = {
|
|
3192
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3221
3193
|
...pkg.scripts,
|
|
3222
3194
|
deploy: "alchemy deploy",
|
|
3223
3195
|
destroy: "alchemy destroy",
|
|
3224
|
-
|
|
3196
|
+
dev: "alchemy dev"
|
|
3225
3197
|
};
|
|
3226
3198
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3227
3199
|
}
|
|
@@ -3229,7 +3201,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3229
3201
|
|
|
3230
3202
|
//#endregion
|
|
3231
3203
|
//#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
|
|
3232
|
-
async function setupSvelteAlchemyDeploy(projectDir, _packageManager) {
|
|
3204
|
+
async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3233
3205
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3234
3206
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3235
3207
|
await addPackageDependency({
|
|
@@ -3243,11 +3215,11 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3243
3215
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3244
3216
|
if (await fs.pathExists(pkgPath)) {
|
|
3245
3217
|
const pkg = await fs.readJson(pkgPath);
|
|
3246
|
-
pkg.scripts = {
|
|
3218
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3247
3219
|
...pkg.scripts,
|
|
3248
3220
|
deploy: "alchemy deploy",
|
|
3249
3221
|
destroy: "alchemy destroy",
|
|
3250
|
-
|
|
3222
|
+
dev: "alchemy dev"
|
|
3251
3223
|
};
|
|
3252
3224
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3253
3225
|
}
|
|
@@ -3300,7 +3272,7 @@ function updateAdapterInConfig(configObject) {
|
|
|
3300
3272
|
|
|
3301
3273
|
//#endregion
|
|
3302
3274
|
//#region src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts
|
|
3303
|
-
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
|
|
3275
|
+
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3304
3276
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3305
3277
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3306
3278
|
await addPackageDependency({
|
|
@@ -3310,11 +3282,11 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3310
3282
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3311
3283
|
if (await fs.pathExists(pkgPath)) {
|
|
3312
3284
|
const pkg = await fs.readJson(pkgPath);
|
|
3313
|
-
pkg.scripts = {
|
|
3285
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3314
3286
|
...pkg.scripts,
|
|
3315
3287
|
deploy: "alchemy deploy",
|
|
3316
3288
|
destroy: "alchemy destroy",
|
|
3317
|
-
|
|
3289
|
+
dev: "alchemy dev"
|
|
3318
3290
|
};
|
|
3319
3291
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3320
3292
|
}
|
|
@@ -3322,7 +3294,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3322
3294
|
|
|
3323
3295
|
//#endregion
|
|
3324
3296
|
//#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
|
|
3325
|
-
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
|
|
3297
|
+
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, options) {
|
|
3326
3298
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3327
3299
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3328
3300
|
await addPackageDependency({
|
|
@@ -3336,11 +3308,11 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
|
|
|
3336
3308
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
3337
3309
|
if (await fs.pathExists(pkgPath)) {
|
|
3338
3310
|
const pkg = await fs.readJson(pkgPath);
|
|
3339
|
-
pkg.scripts = {
|
|
3311
|
+
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3340
3312
|
...pkg.scripts,
|
|
3341
3313
|
deploy: "alchemy deploy",
|
|
3342
3314
|
destroy: "alchemy destroy",
|
|
3343
|
-
|
|
3315
|
+
dev: "alchemy dev"
|
|
3344
3316
|
};
|
|
3345
3317
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3346
3318
|
}
|
|
@@ -3456,7 +3428,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3456
3428
|
...pkg.scripts,
|
|
3457
3429
|
deploy: "alchemy deploy",
|
|
3458
3430
|
destroy: "alchemy destroy",
|
|
3459
|
-
|
|
3431
|
+
dev: "alchemy dev"
|
|
3460
3432
|
};
|
|
3461
3433
|
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
3462
3434
|
}
|
|
@@ -3470,13 +3442,13 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
|
3470
3442
|
const isTanstackStart = frontend.includes("tanstack-start");
|
|
3471
3443
|
const isReactRouter = frontend.includes("react-router");
|
|
3472
3444
|
const isSolid = frontend.includes("solid");
|
|
3473
|
-
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager);
|
|
3474
|
-
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
|
|
3475
|
-
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
|
|
3476
|
-
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
|
|
3477
|
-
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
3478
|
-
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
3479
|
-
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
3445
|
+
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3446
|
+
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3447
|
+
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3448
|
+
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3449
|
+
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3450
|
+
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3451
|
+
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
3480
3452
|
}
|
|
3481
3453
|
|
|
3482
3454
|
//#endregion
|
|
@@ -3838,58 +3810,6 @@ async function formatProjectWithBiome(projectDir) {
|
|
|
3838
3810
|
} catch {}
|
|
3839
3811
|
}
|
|
3840
3812
|
|
|
3841
|
-
//#endregion
|
|
3842
|
-
//#region src/helpers/addons/auth-setup.ts
|
|
3843
|
-
async function setupAuth(config) {
|
|
3844
|
-
const { auth, frontend, backend, projectDir } = config;
|
|
3845
|
-
if (backend === "convex" || !auth) return;
|
|
3846
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
3847
|
-
const clientDir = path.join(projectDir, "apps/web");
|
|
3848
|
-
const nativeDir = path.join(projectDir, "apps/native");
|
|
3849
|
-
const clientDirExists = await fs.pathExists(clientDir);
|
|
3850
|
-
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
3851
|
-
const serverDirExists = await fs.pathExists(serverDir);
|
|
3852
|
-
try {
|
|
3853
|
-
if (serverDirExists) await addPackageDependency({
|
|
3854
|
-
dependencies: ["better-auth"],
|
|
3855
|
-
projectDir: serverDir
|
|
3856
|
-
});
|
|
3857
|
-
const hasWebFrontend$1 = frontend.some((f) => [
|
|
3858
|
-
"react-router",
|
|
3859
|
-
"tanstack-router",
|
|
3860
|
-
"tanstack-start",
|
|
3861
|
-
"next",
|
|
3862
|
-
"nuxt",
|
|
3863
|
-
"svelte",
|
|
3864
|
-
"solid"
|
|
3865
|
-
].includes(f));
|
|
3866
|
-
if (hasWebFrontend$1 && clientDirExists) await addPackageDependency({
|
|
3867
|
-
dependencies: ["better-auth"],
|
|
3868
|
-
projectDir: clientDir
|
|
3869
|
-
});
|
|
3870
|
-
if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
|
|
3871
|
-
await addPackageDependency({
|
|
3872
|
-
dependencies: ["better-auth", "@better-auth/expo"],
|
|
3873
|
-
projectDir: nativeDir
|
|
3874
|
-
});
|
|
3875
|
-
if (serverDirExists) await addPackageDependency({
|
|
3876
|
-
dependencies: ["@better-auth/expo"],
|
|
3877
|
-
projectDir: serverDir
|
|
3878
|
-
});
|
|
3879
|
-
}
|
|
3880
|
-
} catch (error) {
|
|
3881
|
-
consola.error(pc.red("Failed to configure authentication dependencies"));
|
|
3882
|
-
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
function generateAuthSecret(length = 32) {
|
|
3886
|
-
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
3887
|
-
let result = "";
|
|
3888
|
-
const charactersLength = 62;
|
|
3889
|
-
for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
3890
|
-
return result;
|
|
3891
|
-
}
|
|
3892
|
-
|
|
3893
3813
|
//#endregion
|
|
3894
3814
|
//#region src/helpers/addons/examples-setup.ts
|
|
3895
3815
|
async function setupExamples(config) {
|
|
@@ -4139,6 +4059,88 @@ async function setupBackendDependencies(config) {
|
|
|
4139
4059
|
});
|
|
4140
4060
|
}
|
|
4141
4061
|
|
|
4062
|
+
//#endregion
|
|
4063
|
+
//#region src/helpers/core/auth-setup.ts
|
|
4064
|
+
async function setupAuth(config) {
|
|
4065
|
+
const { auth, frontend, backend, projectDir } = config;
|
|
4066
|
+
if (!auth || auth === "none") return;
|
|
4067
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
4068
|
+
const clientDir = path.join(projectDir, "apps/web");
|
|
4069
|
+
const nativeDir = path.join(projectDir, "apps/native");
|
|
4070
|
+
const clientDirExists = await fs.pathExists(clientDir);
|
|
4071
|
+
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4072
|
+
const serverDirExists = await fs.pathExists(serverDir);
|
|
4073
|
+
try {
|
|
4074
|
+
if (backend === "convex") {
|
|
4075
|
+
if (auth === "clerk" && clientDirExists) {
|
|
4076
|
+
const hasNextJs = frontend.includes("next");
|
|
4077
|
+
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
4078
|
+
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4079
|
+
if (hasNextJs) await addPackageDependency({
|
|
4080
|
+
dependencies: ["@clerk/nextjs"],
|
|
4081
|
+
projectDir: clientDir
|
|
4082
|
+
});
|
|
4083
|
+
else if (hasTanStackStart) await addPackageDependency({
|
|
4084
|
+
dependencies: ["@clerk/tanstack-react-start"],
|
|
4085
|
+
projectDir: clientDir
|
|
4086
|
+
});
|
|
4087
|
+
else if (hasViteReactOther) await addPackageDependency({
|
|
4088
|
+
dependencies: ["@clerk/clerk-react"],
|
|
4089
|
+
projectDir: clientDir
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
const hasNativeWind = frontend.includes("native-nativewind");
|
|
4093
|
+
const hasUnistyles = frontend.includes("native-unistyles");
|
|
4094
|
+
if (auth === "clerk" && nativeDirExists && (hasNativeWind || hasUnistyles)) await addPackageDependency({
|
|
4095
|
+
dependencies: ["@clerk/clerk-expo"],
|
|
4096
|
+
projectDir: nativeDir
|
|
4097
|
+
});
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
if (serverDirExists && auth === "better-auth") await addPackageDependency({
|
|
4101
|
+
dependencies: ["better-auth"],
|
|
4102
|
+
projectDir: serverDir
|
|
4103
|
+
});
|
|
4104
|
+
const hasWebFrontend$1 = frontend.some((f) => [
|
|
4105
|
+
"react-router",
|
|
4106
|
+
"tanstack-router",
|
|
4107
|
+
"tanstack-start",
|
|
4108
|
+
"next",
|
|
4109
|
+
"nuxt",
|
|
4110
|
+
"svelte",
|
|
4111
|
+
"solid"
|
|
4112
|
+
].includes(f));
|
|
4113
|
+
if (hasWebFrontend$1 && clientDirExists) {
|
|
4114
|
+
if (auth === "better-auth") await addPackageDependency({
|
|
4115
|
+
dependencies: ["better-auth"],
|
|
4116
|
+
projectDir: clientDir
|
|
4117
|
+
});
|
|
4118
|
+
}
|
|
4119
|
+
if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
|
|
4120
|
+
if (auth === "better-auth") {
|
|
4121
|
+
await addPackageDependency({
|
|
4122
|
+
dependencies: ["better-auth", "@better-auth/expo"],
|
|
4123
|
+
projectDir: nativeDir
|
|
4124
|
+
});
|
|
4125
|
+
if (serverDirExists) await addPackageDependency({
|
|
4126
|
+
dependencies: ["@better-auth/expo"],
|
|
4127
|
+
projectDir: serverDir
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
} catch (error) {
|
|
4132
|
+
consola.error(pc.red("Failed to configure authentication dependencies"));
|
|
4133
|
+
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
function generateAuthSecret(length = 32) {
|
|
4137
|
+
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
4138
|
+
let result = "";
|
|
4139
|
+
const charactersLength = 62;
|
|
4140
|
+
for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
4141
|
+
return result;
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4142
4144
|
//#endregion
|
|
4143
4145
|
//#region src/helpers/core/env-setup.ts
|
|
4144
4146
|
async function addEnvVariablesToFile(filePath, variables) {
|
|
@@ -4217,6 +4219,33 @@ async function setupEnvironmentVariables(config) {
|
|
|
4217
4219
|
value: serverUrl,
|
|
4218
4220
|
condition: true
|
|
4219
4221
|
}];
|
|
4222
|
+
if (backend === "convex" && auth === "clerk") {
|
|
4223
|
+
if (hasNextJs) clientVars.push({
|
|
4224
|
+
key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
|
|
4225
|
+
value: "",
|
|
4226
|
+
condition: true
|
|
4227
|
+
}, {
|
|
4228
|
+
key: "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
4229
|
+
value: "",
|
|
4230
|
+
condition: true
|
|
4231
|
+
}, {
|
|
4232
|
+
key: "CLERK_SECRET_KEY",
|
|
4233
|
+
value: "",
|
|
4234
|
+
condition: true
|
|
4235
|
+
});
|
|
4236
|
+
else if (hasReactRouter || hasTanStackRouter || hasTanStackStart) {
|
|
4237
|
+
clientVars.push({
|
|
4238
|
+
key: "VITE_CLERK_PUBLISHABLE_KEY",
|
|
4239
|
+
value: "",
|
|
4240
|
+
condition: true
|
|
4241
|
+
});
|
|
4242
|
+
if (hasTanStackStart) clientVars.push({
|
|
4243
|
+
key: "CLERK_SECRET_KEY",
|
|
4244
|
+
value: "",
|
|
4245
|
+
condition: true
|
|
4246
|
+
});
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4220
4249
|
await addEnvVariablesToFile(path.join(clientDir, ".env"), clientVars);
|
|
4221
4250
|
}
|
|
4222
4251
|
}
|
|
@@ -4234,6 +4263,11 @@ async function setupEnvironmentVariables(config) {
|
|
|
4234
4263
|
value: serverUrl,
|
|
4235
4264
|
condition: true
|
|
4236
4265
|
}];
|
|
4266
|
+
if (backend === "convex" && auth === "clerk") nativeVars.push({
|
|
4267
|
+
key: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
|
|
4268
|
+
value: "",
|
|
4269
|
+
condition: true
|
|
4270
|
+
});
|
|
4237
4271
|
await addEnvVariablesToFile(path.join(nativeDir, ".env"), nativeVars);
|
|
4238
4272
|
}
|
|
4239
4273
|
}
|
|
@@ -5282,7 +5316,7 @@ async function createReadme(projectDir, options) {
|
|
|
5282
5316
|
}
|
|
5283
5317
|
}
|
|
5284
5318
|
function generateReadmeContent(options) {
|
|
5285
|
-
const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc" } = options;
|
|
5319
|
+
const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc", webDeploy, serverDeploy } = options;
|
|
5286
5320
|
const isConvex = backend === "convex";
|
|
5287
5321
|
const hasReactRouter = frontend.includes("react-router");
|
|
5288
5322
|
const hasNative = frontend.includes("native-nativewind") || frontend.includes("native-unistyles");
|
|
@@ -5315,7 +5349,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
|
|
|
5315
5349
|
${packageManagerRunCmd} dev:setup
|
|
5316
5350
|
\`\`\`
|
|
5317
5351
|
|
|
5318
|
-
Follow the prompts to create a new Convex project and connect it to your application.` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup)}
|
|
5352
|
+
Follow the prompts to create a new Convex project and connect it to your application.` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
|
|
5319
5353
|
|
|
5320
5354
|
Then, run the development server:
|
|
5321
5355
|
|
|
@@ -5327,6 +5361,8 @@ ${generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex)}
|
|
|
5327
5361
|
|
|
5328
5362
|
${addons.includes("pwa") && hasReactRouter ? "\n## PWA Support with React Router v7\n\nThere is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809\n" : ""}
|
|
5329
5363
|
|
|
5364
|
+
${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
|
|
5365
|
+
|
|
5330
5366
|
## Project Structure
|
|
5331
5367
|
|
|
5332
5368
|
\`\`\`
|
|
@@ -5461,7 +5497,10 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5461
5497
|
const dbName = database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : database === "mongodb" ? "MongoDB" : "Database";
|
|
5462
5498
|
addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
|
|
5463
5499
|
}
|
|
5464
|
-
if (auth && !isConvex)
|
|
5500
|
+
if (auth !== "none" && !isConvex) {
|
|
5501
|
+
const authLabel = auth === "clerk" ? "Clerk" : "Better-Auth";
|
|
5502
|
+
addonsList.push(`- **Authentication** - ${authLabel}`);
|
|
5503
|
+
}
|
|
5465
5504
|
for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
|
|
5466
5505
|
else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
|
|
5467
5506
|
else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
|
|
@@ -5470,13 +5509,13 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5470
5509
|
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
5471
5510
|
return addonsList.join("\n");
|
|
5472
5511
|
}
|
|
5473
|
-
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup) {
|
|
5512
|
+
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
|
|
5474
5513
|
if (database === "none") return "";
|
|
5475
5514
|
let setup = "## Database Setup\n\n";
|
|
5476
5515
|
if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
5477
5516
|
|
|
5478
5517
|
1. Start the local SQLite database:
|
|
5479
|
-
${dbSetup === "d1" ? "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
|
|
5518
|
+
${dbSetup === "d1" ? serverDeploy === "alchemy" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
|
|
5480
5519
|
cd apps/server && ${packageManagerRunCmd} db:local
|
|
5481
5520
|
\`\`\`
|
|
5482
5521
|
`}
|
|
@@ -5546,6 +5585,21 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
|
|
|
5546
5585
|
- \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
|
|
5547
5586
|
return scripts;
|
|
5548
5587
|
}
|
|
5588
|
+
function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
|
|
5589
|
+
const lines = [];
|
|
5590
|
+
if (webDeploy === "alchemy" || serverDeploy === "alchemy") {
|
|
5591
|
+
lines.push("## Deployment (Alchemy)");
|
|
5592
|
+
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
|
|
5593
|
+
if (serverDeploy === "alchemy" && webDeploy !== "alchemy") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
|
|
5594
|
+
if (webDeploy === "alchemy" && serverDeploy === "alchemy") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
|
|
5595
|
+
}
|
|
5596
|
+
if (webDeploy === "wrangler" || serverDeploy === "wrangler") {
|
|
5597
|
+
lines.push("\n## Deployment (Cloudflare Wrangler)");
|
|
5598
|
+
if (webDeploy === "wrangler") lines.push(`- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`);
|
|
5599
|
+
if (serverDeploy === "wrangler") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`);
|
|
5600
|
+
}
|
|
5601
|
+
return lines.length ? `\n${lines.join("\n")}\n` : "";
|
|
5602
|
+
}
|
|
5549
5603
|
|
|
5550
5604
|
//#endregion
|
|
5551
5605
|
//#region src/helpers/core/git.ts
|
|
@@ -5761,9 +5815,9 @@ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
|
5761
5815
|
}
|
|
5762
5816
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
|
|
5763
5817
|
const instructions = [];
|
|
5764
|
-
if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd}
|
|
5765
|
-
else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd}
|
|
5766
|
-
else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd}
|
|
5818
|
+
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`}`);
|
|
5819
|
+
else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
|
|
5820
|
+
else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
|
|
5767
5821
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
5768
5822
|
}
|
|
5769
5823
|
|
|
@@ -5961,8 +6015,8 @@ async function createProject(options) {
|
|
|
5961
6015
|
if (!isConvex) {
|
|
5962
6016
|
await setupDbOrmTemplates(projectDir, options);
|
|
5963
6017
|
await setupDockerComposeTemplates(projectDir, options);
|
|
5964
|
-
await setupAuthTemplate(projectDir, options);
|
|
5965
6018
|
}
|
|
6019
|
+
await setupAuthTemplate(projectDir, options);
|
|
5966
6020
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
|
|
5967
6021
|
await setupAddonsTemplate(projectDir, options);
|
|
5968
6022
|
await setupDeploymentTemplates(projectDir, options);
|
|
@@ -5974,12 +6028,12 @@ async function createProject(options) {
|
|
|
5974
6028
|
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
|
|
5975
6029
|
}
|
|
5976
6030
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
5977
|
-
if (
|
|
6031
|
+
if (options.auth && options.auth !== "none") await setupAuth(options);
|
|
5978
6032
|
await handleExtras(projectDir, options);
|
|
5979
|
-
await setupWebDeploy(options);
|
|
5980
|
-
await setupServerDeploy(options);
|
|
5981
6033
|
await setupEnvironmentVariables(options);
|
|
5982
6034
|
await updatePackageConfigurations(projectDir, options);
|
|
6035
|
+
await setupWebDeploy(options);
|
|
6036
|
+
await setupServerDeploy(options);
|
|
5983
6037
|
await createReadme(projectDir, options);
|
|
5984
6038
|
await writeBtsConfig(options);
|
|
5985
6039
|
await formatProjectWithBiome(projectDir);
|
|
@@ -6053,7 +6107,7 @@ async function createProjectHandler(input) {
|
|
|
6053
6107
|
frontend: [],
|
|
6054
6108
|
addons: [],
|
|
6055
6109
|
examples: [],
|
|
6056
|
-
auth:
|
|
6110
|
+
auth: "none",
|
|
6057
6111
|
git: false,
|
|
6058
6112
|
packageManager: "npm",
|
|
6059
6113
|
install: false,
|
|
@@ -6088,8 +6142,8 @@ async function createProjectHandler(input) {
|
|
|
6088
6142
|
};
|
|
6089
6143
|
coerceBackendPresets(config);
|
|
6090
6144
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
6091
|
-
if (config.backend === "convex") log.info("Due to '--backend convex' flag, the following options have been automatically set: auth=
|
|
6092
|
-
else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth
|
|
6145
|
+
if (config.backend === "convex") log.info("Due to '--backend convex' flag, the following options have been automatically set: auth=none, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo");
|
|
6146
|
+
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");
|
|
6093
6147
|
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
6094
6148
|
log.message(displayConfig(config));
|
|
6095
6149
|
log.message("");
|
|
@@ -6295,7 +6349,7 @@ const router = t.router({
|
|
|
6295
6349
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
6296
6350
|
database: DatabaseSchema.optional(),
|
|
6297
6351
|
orm: ORMSchema.optional(),
|
|
6298
|
-
auth:
|
|
6352
|
+
auth: AuthSchema.optional(),
|
|
6299
6353
|
frontend: z.array(FrontendSchema).optional(),
|
|
6300
6354
|
addons: z.array(AddonsSchema).optional(),
|
|
6301
6355
|
examples: z.array(ExamplesSchema).optional(),
|
|
@@ -6386,7 +6440,7 @@ function createBtsCli() {
|
|
|
6386
6440
|
* backend: "hono",
|
|
6387
6441
|
* database: "sqlite",
|
|
6388
6442
|
* orm: "drizzle",
|
|
6389
|
-
* auth:
|
|
6443
|
+
* auth: "better-auth",
|
|
6390
6444
|
* addons: ["biome", "turborepo"],
|
|
6391
6445
|
* packageManager: "bun",
|
|
6392
6446
|
* install: false,
|