create-better-t-stack 3.13.1 → 3.13.2-dev.6a9e17a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +29 -33
- package/dist/index.mjs +2 -2
- package/dist/{src-BpuxNIy_.mjs → src-CCKoHHi-.mjs} +675 -3255
- package/dist/virtual.d.mts +4 -0
- package/dist/virtual.mjs +4 -0
- package/package.json +8 -4
- package/templates/addons/biome/biome.json.hbs +0 -96
- package/templates/addons/husky/.husky/pre-commit +0 -1
- package/templates/addons/pwa/apps/web/next/public/favicon/apple-touch-icon.png +0 -0
- package/templates/addons/pwa/apps/web/next/public/favicon/favicon-96x96.png +0 -0
- package/templates/addons/pwa/apps/web/next/public/favicon/favicon.svg +0 -6
- package/templates/addons/pwa/apps/web/next/public/favicon/site.webmanifest.hbs +0 -21
- package/templates/addons/pwa/apps/web/next/public/favicon/web-app-manifest-192x192.png +0 -0
- package/templates/addons/pwa/apps/web/next/public/favicon/web-app-manifest-512x512.png +0 -0
- package/templates/addons/pwa/apps/web/next/src/app/manifest.ts.hbs +0 -26
- package/templates/addons/pwa/apps/web/vite/public/logo.png +0 -0
- package/templates/addons/pwa/apps/web/vite/pwa-assets.config.ts.hbs +0 -12
- package/templates/addons/ruler/.ruler/bts.md.hbs +0 -142
- package/templates/addons/ruler/.ruler/ruler.toml.hbs +0 -80
- package/templates/addons/turborepo/turbo.json.hbs +0 -74
- package/templates/addons/ultracite/biome.json.hbs +0 -26
- package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +0 -50
- package/templates/api/orpc/fullstack/tanstack-start/src/routes/api/rpc/$.ts.hbs +0 -58
- package/templates/api/orpc/native/utils/orpc.ts.hbs +0 -39
- package/templates/api/orpc/server/_gitignore +0 -34
- package/templates/api/orpc/server/package.json.hbs +0 -15
- package/templates/api/orpc/server/src/context.ts.hbs +0 -148
- package/templates/api/orpc/server/src/index.ts.hbs +0 -21
- package/templates/api/orpc/server/src/routers/index.ts.hbs +0 -55
- package/templates/api/orpc/server/tsconfig.json.hbs +0 -10
- package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +0 -32
- package/templates/api/orpc/web/nuxt/app/plugins/vue-query.ts.hbs +0 -44
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +0 -113
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +0 -30
- package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +0 -30
- package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +0 -14
- package/templates/api/trpc/fullstack/tanstack-start/src/routes/api/trpc/$.ts.hbs +0 -22
- package/templates/api/trpc/native/utils/trpc.ts.hbs +0 -37
- package/templates/api/trpc/server/_gitignore +0 -34
- package/templates/api/trpc/server/package.json.hbs +0 -14
- package/templates/api/trpc/server/src/context.ts.hbs +0 -148
- package/templates/api/trpc/server/src/index.ts.hbs +0 -26
- package/templates/api/trpc/server/src/routers/index.ts.hbs +0 -55
- package/templates/api/trpc/server/tsconfig.json.hbs +0 -10
- package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +0 -97
- package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +0 -6
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +0 -68
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +0 -12
- package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +0 -17
- package/templates/auth/better-auth/convex/native/bare/components/sign-in.tsx.hbs +0 -127
- package/templates/auth/better-auth/convex/native/bare/components/sign-up.tsx.hbs +0 -138
- package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +0 -18
- package/templates/auth/better-auth/convex/native/unistyles/components/sign-in.tsx.hbs +0 -127
- package/templates/auth/better-auth/convex/native/unistyles/components/sign-up.tsx.hbs +0 -145
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +0 -73
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +0 -85
- package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +0 -3
- package/templates/auth/better-auth/convex/web/react/next/src/app/dashboard/page.tsx.hbs +0 -40
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-in-form.tsx.hbs +0 -129
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-up-form.tsx.hbs +0 -154
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +0 -48
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +0 -6
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +0 -16
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/sign-in-form.tsx.hbs +0 -133
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/sign-up-form.tsx.hbs +0 -158
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +0 -52
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +0 -11
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -43
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-in-form.tsx.hbs +0 -133
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-up-form.tsx.hbs +0 -158
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +0 -47
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-client.ts.hbs +0 -6
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +0 -13
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +0 -11
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -43
- package/templates/auth/better-auth/fullstack/next/src/app/api/auth/[...all]/route.ts.hbs +0 -4
- package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +0 -15
- package/templates/auth/better-auth/native/bare/app/(drawer)/index.tsx.hbs +0 -186
- package/templates/auth/better-auth/native/bare/components/sign-in.tsx.hbs +0 -131
- package/templates/auth/better-auth/native/bare/components/sign-up.tsx.hbs +0 -150
- package/templates/auth/better-auth/native/base/lib/auth-client.ts.hbs +0 -16
- package/templates/auth/better-auth/native/unistyles/app/(drawer)/index.tsx.hbs +0 -187
- package/templates/auth/better-auth/native/unistyles/components/sign-in.tsx.hbs +0 -139
- package/templates/auth/better-auth/native/unistyles/components/sign-up.tsx.hbs +0 -157
- package/templates/auth/better-auth/native/uniwind/app/(drawer)/index.tsx.hbs +0 -123
- package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +0 -87
- package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +0 -128
- package/templates/auth/better-auth/server/base/_gitignore +0 -34
- package/templates/auth/better-auth/server/base/package.json.hbs +0 -14
- package/templates/auth/better-auth/server/base/src/index.ts.hbs +0 -304
- package/templates/auth/better-auth/server/base/tsconfig.json.hbs +0 -10
- package/templates/auth/better-auth/server/db/drizzle/mysql/src/schema/auth.ts.hbs +0 -100
- package/templates/auth/better-auth/server/db/drizzle/postgres/src/schema/auth.ts.hbs +0 -93
- package/templates/auth/better-auth/server/db/drizzle/sqlite/src/schema/auth.ts.hbs +0 -107
- package/templates/auth/better-auth/server/db/mongoose/mongodb/src/models/auth.model.ts.hbs +0 -68
- package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/auth.prisma.hbs +0 -62
- package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/auth.prisma.hbs +0 -62
- package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/auth.prisma.hbs +0 -62
- package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/auth.prisma.hbs +0 -62
- package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +0 -82
- package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +0 -91
- package/templates/auth/better-auth/web/nuxt/app/components/UserMenu.vue.hbs +0 -42
- package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +0 -14
- package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +0 -99
- package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +0 -27
- package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +0 -21
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +0 -16
- package/templates/auth/better-auth/web/react/next/src/app/dashboard/dashboard.tsx.hbs +0 -60
- package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +0 -42
- package/templates/auth/better-auth/web/react/next/src/app/login/page.tsx.hbs +0 -16
- package/templates/auth/better-auth/web/react/next/src/components/sign-in-form.tsx.hbs +0 -135
- package/templates/auth/better-auth/web/react/next/src/components/sign-up-form.tsx.hbs +0 -160
- package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +0 -62
- package/templates/auth/better-auth/web/react/react-router/src/components/sign-in-form.tsx.hbs +0 -135
- package/templates/auth/better-auth/web/react/react-router/src/components/sign-up-form.tsx.hbs +0 -160
- package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +0 -61
- package/templates/auth/better-auth/web/react/react-router/src/routes/dashboard.tsx.hbs +0 -80
- package/templates/auth/better-auth/web/react/react-router/src/routes/login.tsx.hbs +0 -13
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-in-form.tsx.hbs +0 -135
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-up-form.tsx.hbs +0 -160
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/user-menu.tsx.hbs +0 -63
- package/templates/auth/better-auth/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -71
- package/templates/auth/better-auth/web/react/tanstack-router/src/routes/login.tsx.hbs +0 -18
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-in-form.tsx.hbs +0 -135
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-up-form.tsx.hbs +0 -160
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/user-menu.tsx.hbs +0 -63
- package/templates/auth/better-auth/web/react/tanstack-start/src/functions/get-user.ts.hbs +0 -6
- package/templates/auth/better-auth/web/react/tanstack-start/src/middleware/auth.ts.hbs +0 -31
- package/templates/auth/better-auth/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -84
- package/templates/auth/better-auth/web/react/tanstack-start/src/routes/login.tsx.hbs +0 -18
- package/templates/auth/better-auth/web/solid/src/components/sign-in-form.tsx.hbs +0 -124
- package/templates/auth/better-auth/web/solid/src/components/sign-up-form.tsx.hbs +0 -148
- package/templates/auth/better-auth/web/solid/src/components/user-menu.tsx.hbs +0 -55
- package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +0 -12
- package/templates/auth/better-auth/web/solid/src/routes/dashboard.tsx.hbs +0 -67
- package/templates/auth/better-auth/web/solid/src/routes/login.tsx.hbs +0 -23
- package/templates/auth/better-auth/web/svelte/src/components/SignInForm.svelte.hbs +0 -108
- package/templates/auth/better-auth/web/svelte/src/components/SignUpForm.svelte.hbs +0 -142
- package/templates/auth/better-auth/web/svelte/src/components/UserMenu.svelte.hbs +0 -52
- package/templates/auth/better-auth/web/svelte/src/lib/auth-client.ts.hbs +0 -12
- package/templates/auth/better-auth/web/svelte/src/routes/dashboard/+page.svelte.hbs +0 -57
- package/templates/auth/better-auth/web/svelte/src/routes/login/+page.svelte.hbs +0 -12
- package/templates/auth/clerk/convex/backend/convex/auth.config.ts.hbs +0 -12
- package/templates/auth/clerk/convex/backend/convex/privateData.ts.hbs +0 -16
- package/templates/auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs +0 -12
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs +0 -67
- package/templates/auth/clerk/convex/native/base/app/(auth)/sign-up.tsx.hbs +0 -110
- package/templates/auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs +0 -27
- package/templates/auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs +0 -29
- package/templates/auth/clerk/convex/web/react/next/src/middleware.ts.hbs +0 -12
- package/templates/auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs +0 -32
- package/templates/auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -37
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -37
- package/templates/auth/clerk/convex/web/react/tanstack-start/src/start.ts.hbs +0 -8
- package/templates/backend/convex/packages/backend/_gitignore +0 -2
- package/templates/backend/convex/packages/backend/convex/README.md +0 -90
- package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +0 -17
- package/templates/backend/convex/packages/backend/convex/healthCheck.ts.hbs +0 -7
- package/templates/backend/convex/packages/backend/convex/schema.ts.hbs +0 -11
- package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +0 -25
- package/templates/backend/convex/packages/backend/package.json.hbs +0 -15
- package/templates/backend/server/base/_gitignore +0 -55
- package/templates/backend/server/base/package.json.hbs +0 -17
- package/templates/backend/server/base/tsconfig.json.hbs +0 -13
- package/templates/backend/server/base/tsdown.config.ts.hbs +0 -9
- package/templates/backend/server/elysia/src/index.ts.hbs +0 -122
- package/templates/backend/server/express/src/index.ts.hbs +0 -126
- package/templates/backend/server/fastify/src/index.ts.hbs +0 -187
- package/templates/backend/server/hono/src/index.ts.hbs +0 -171
- package/templates/base/_gitignore +0 -50
- package/templates/base/package.json.hbs +0 -10
- package/templates/base/tsconfig.json.hbs +0 -3
- package/templates/db/base/_gitignore +0 -35
- package/templates/db/base/package.json.hbs +0 -14
- package/templates/db/base/tsconfig.json.hbs +0 -10
- package/templates/db/drizzle/base/src/schema/index.ts.hbs +0 -7
- package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +0 -19
- package/templates/db/drizzle/mysql/src/index.ts.hbs +0 -54
- package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +0 -19
- package/templates/db/drizzle/postgres/src/index.ts.hbs +0 -44
- package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +0 -28
- package/templates/db/drizzle/sqlite/src/index.ts.hbs +0 -39
- package/templates/db/mongoose/mongodb/src/index.ts.hbs +0 -10
- package/templates/db/prisma/mongodb/prisma/schema/schema.prisma.hbs +0 -19
- package/templates/db/prisma/mongodb/prisma.config.ts.hbs +0 -18
- package/templates/db/prisma/mongodb/src/index.ts.hbs +0 -5
- package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +0 -21
- package/templates/db/prisma/mysql/prisma.config.ts.hbs +0 -21
- package/templates/db/prisma/mysql/src/index.ts.hbs +0 -55
- package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +0 -21
- package/templates/db/prisma/postgres/prisma.config.ts.hbs +0 -21
- package/templates/db/prisma/postgres/src/index.ts.hbs +0 -69
- package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +0 -18
- package/templates/db/prisma/sqlite/prisma.config.ts.hbs +0 -25
- package/templates/db/prisma/sqlite/src/index.ts.hbs +0 -25
- package/templates/db-setup/docker-compose/mongodb/docker-compose.yml.hbs +0 -23
- package/templates/db-setup/docker-compose/mysql/docker-compose.yml.hbs +0 -24
- package/templates/db-setup/docker-compose/postgres/docker-compose.yml.hbs +0 -23
- package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +0 -9
- package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +0 -67
- package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +0 -20
- package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +0 -36
- package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +0 -586
- package/templates/examples/ai/native/bare/polyfills.js +0 -25
- package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +0 -588
- package/templates/examples/ai/native/unistyles/polyfills.js +0 -22
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +0 -331
- package/templates/examples/ai/native/uniwind/polyfills.js +0 -22
- package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +0 -54
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +0 -267
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +0 -235
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +0 -242
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +0 -243
- package/templates/examples/ai/web/svelte/src/routes/ai/+page.svelte.hbs +0 -107
- package/templates/examples/todo/convex/packages/backend/convex/todos.ts.hbs +0 -42
- package/templates/examples/todo/native/bare/app/(drawer)/todos.tsx.hbs +0 -521
- package/templates/examples/todo/native/unistyles/app/(drawer)/todos.tsx.hbs +0 -340
- package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +0 -282
- package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +0 -75
- package/templates/examples/todo/server/drizzle/mysql/src/schema/todo.ts +0 -7
- package/templates/examples/todo/server/drizzle/postgres/src/schema/todo.ts +0 -7
- package/templates/examples/todo/server/drizzle/sqlite/src/schema/todo.ts +0 -7
- package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +0 -66
- package/templates/examples/todo/server/mongoose/mongodb/src/models/todo.model.ts.hbs +0 -24
- package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +0 -116
- package/templates/examples/todo/server/prisma/mongodb/prisma/schema/todo.prisma.hbs +0 -7
- package/templates/examples/todo/server/prisma/mysql/prisma/schema/todo.prisma.hbs +0 -7
- package/templates/examples/todo/server/prisma/postgres/prisma/schema/todo.prisma.hbs +0 -7
- package/templates/examples/todo/server/prisma/sqlite/prisma/schema/todo.prisma.hbs +0 -7
- package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +0 -220
- package/templates/examples/todo/web/react/next/src/app/todos/page.tsx.hbs +0 -245
- package/templates/examples/todo/web/react/react-router/src/routes/todos.tsx.hbs +0 -242
- package/templates/examples/todo/web/react/tanstack-router/src/routes/todos.tsx.hbs +0 -247
- package/templates/examples/todo/web/react/tanstack-start/src/routes/todos.tsx.hbs +0 -272
- package/templates/examples/todo/web/solid/src/routes/todos.tsx.hbs +0 -132
- package/templates/examples/todo/web/svelte/src/routes/todos/+page.svelte.hbs +0 -317
- package/templates/extras/_npmrc.hbs +0 -5
- package/templates/extras/bunfig.toml.hbs +0 -6
- package/templates/extras/pnpm-workspace.yaml +0 -3
- package/templates/frontend/native/bare/_gitignore +0 -18
- package/templates/frontend/native/bare/app/(drawer)/(tabs)/_layout.tsx.hbs +0 -41
- package/templates/frontend/native/bare/app/(drawer)/(tabs)/index.tsx.hbs +0 -43
- package/templates/frontend/native/bare/app/(drawer)/(tabs)/two.tsx.hbs +0 -43
- package/templates/frontend/native/bare/app/(drawer)/_layout.tsx.hbs +0 -90
- package/templates/frontend/native/bare/app/(drawer)/index.tsx.hbs +0 -234
- package/templates/frontend/native/bare/app/+not-found.tsx.hbs +0 -65
- package/templates/frontend/native/bare/app/_layout.tsx.hbs +0 -165
- package/templates/frontend/native/bare/app/modal.tsx.hbs +0 -34
- package/templates/frontend/native/bare/app.json.hbs +0 -50
- package/templates/frontend/native/bare/components/container.tsx.hbs +0 -25
- package/templates/frontend/native/bare/components/header-button.tsx.hbs +0 -47
- package/templates/frontend/native/bare/components/tabbar-icon.tsx.hbs +0 -9
- package/templates/frontend/native/bare/lib/android-navigation-bar.tsx.hbs +0 -12
- package/templates/frontend/native/bare/lib/constants.ts.hbs +0 -19
- package/templates/frontend/native/bare/lib/use-color-scheme.ts.hbs +0 -20
- package/templates/frontend/native/bare/metro.config.js.hbs +0 -9
- package/templates/frontend/native/bare/package.json.hbs +0 -51
- package/templates/frontend/native/bare/tsconfig.json.hbs +0 -11
- package/templates/frontend/native/base/assets/images/android-icon-background.png +0 -0
- package/templates/frontend/native/base/assets/images/android-icon-foreground.png +0 -0
- package/templates/frontend/native/base/assets/images/android-icon-monochrome.png +0 -0
- package/templates/frontend/native/base/assets/images/favicon.png +0 -0
- package/templates/frontend/native/base/assets/images/icon.png +0 -0
- package/templates/frontend/native/base/assets/images/partial-react-logo.png +0 -0
- package/templates/frontend/native/base/assets/images/react-logo.png +0 -0
- package/templates/frontend/native/base/assets/images/react-logo@2x.png +0 -0
- package/templates/frontend/native/base/assets/images/react-logo@3x.png +0 -0
- package/templates/frontend/native/base/assets/images/splash-icon.png +0 -0
- package/templates/frontend/native/unistyles/_gitignore +0 -24
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/_layout.tsx.hbs +0 -39
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/index.tsx.hbs +0 -37
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/two.tsx.hbs +0 -37
- package/templates/frontend/native/unistyles/app/(drawer)/_layout.tsx.hbs +0 -87
- package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +0 -333
- package/templates/frontend/native/unistyles/app/+not-found.tsx.hbs +0 -65
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +0 -169
- package/templates/frontend/native/unistyles/app/modal.tsx.hbs +0 -33
- package/templates/frontend/native/unistyles/app.json.hbs +0 -49
- package/templates/frontend/native/unistyles/babel.config.js.hbs +0 -21
- package/templates/frontend/native/unistyles/breakpoints.ts.hbs +0 -9
- package/templates/frontend/native/unistyles/components/container.tsx.hbs +0 -15
- package/templates/frontend/native/unistyles/components/header-button.tsx.hbs +0 -36
- package/templates/frontend/native/unistyles/components/tabbar-icon.tsx.hbs +0 -8
- package/templates/frontend/native/unistyles/index.js.hbs +0 -2
- package/templates/frontend/native/unistyles/metro.config.js.hbs +0 -5
- package/templates/frontend/native/unistyles/package.json.hbs +0 -51
- package/templates/frontend/native/unistyles/theme.ts.hbs +0 -98
- package/templates/frontend/native/unistyles/tsconfig.json.hbs +0 -12
- package/templates/frontend/native/unistyles/unistyles.ts.hbs +0 -27
- package/templates/frontend/native/uniwind/_gitignore +0 -21
- package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/_layout.tsx.hbs +0 -46
- package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/index.tsx.hbs +0 -15
- package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/two.tsx.hbs +0 -15
- package/templates/frontend/native/uniwind/app/(drawer)/_layout.tsx.hbs +0 -91
- package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +0 -191
- package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +0 -27
- package/templates/frontend/native/uniwind/app/_layout.tsx.hbs +0 -132
- package/templates/frontend/native/uniwind/app/modal.tsx.hbs +0 -37
- package/templates/frontend/native/uniwind/app.json.hbs +0 -19
- package/templates/frontend/native/uniwind/components/container.tsx.hbs +0 -33
- package/templates/frontend/native/uniwind/components/theme-toggle.tsx.hbs +0 -35
- package/templates/frontend/native/uniwind/contexts/app-theme-context.tsx.hbs +0 -62
- package/templates/frontend/native/uniwind/global.css +0 -5
- package/templates/frontend/native/uniwind/metro.config.js.hbs +0 -13
- package/templates/frontend/native/uniwind/package.json.hbs +0 -54
- package/templates/frontend/native/uniwind/tsconfig.json.hbs +0 -14
- package/templates/frontend/nuxt/_gitignore +0 -27
- package/templates/frontend/nuxt/app/app.config.ts.hbs +0 -15
- package/templates/frontend/nuxt/app/app.vue.hbs +0 -17
- package/templates/frontend/nuxt/app/assets/css/main.css +0 -2
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +0 -40
- package/templates/frontend/nuxt/app/layouts/default.vue.hbs +0 -10
- package/templates/frontend/nuxt/app/pages/index.vue.hbs +0 -97
- package/templates/frontend/nuxt/nuxt.config.ts.hbs +0 -29
- package/templates/frontend/nuxt/package.json.hbs +0 -24
- package/templates/frontend/nuxt/public/favicon.ico +0 -0
- package/templates/frontend/nuxt/public/robots.txt +0 -2
- package/templates/frontend/nuxt/server/tsconfig.json +0 -3
- package/templates/frontend/nuxt/tsconfig.json.hbs +0 -18
- package/templates/frontend/react/next/next-env.d.ts.hbs +0 -5
- package/templates/frontend/react/next/next.config.ts.hbs +0 -22
- package/templates/frontend/react/next/package.json.hbs +0 -34
- package/templates/frontend/react/next/postcss.config.mjs.hbs +0 -5
- package/templates/frontend/react/next/src/app/favicon.ico +0 -0
- package/templates/frontend/react/next/src/app/layout.tsx.hbs +0 -76
- package/templates/frontend/react/next/src/app/page.tsx.hbs +0 -79
- package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +0 -37
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +0 -89
- package/templates/frontend/react/next/src/components/theme-provider.tsx.hbs +0 -11
- package/templates/frontend/react/next/tsconfig.json.hbs +0 -41
- package/templates/frontend/react/react-router/package.json.hbs +0 -42
- package/templates/frontend/react/react-router/public/favicon.ico +0 -0
- package/templates/frontend/react/react-router/react-router.config.ts +0 -6
- package/templates/frontend/react/react-router/src/components/mode-toggle.tsx.hbs +0 -29
- package/templates/frontend/react/react-router/src/components/theme-provider.tsx.hbs +0 -11
- package/templates/frontend/react/react-router/src/root.tsx.hbs +0 -190
- package/templates/frontend/react/react-router/src/routes/_index.tsx.hbs +0 -85
- package/templates/frontend/react/react-router/src/routes.ts +0 -4
- package/templates/frontend/react/react-router/tsconfig.json.hbs +0 -27
- package/templates/frontend/react/react-router/vite.config.ts.hbs +0 -12
- package/templates/frontend/react/tanstack-router/index.html.hbs +0 -13
- package/templates/frontend/react/tanstack-router/package.json.hbs +0 -41
- package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx.hbs +0 -29
- package/templates/frontend/react/tanstack-router/src/components/theme-provider.tsx.hbs +0 -11
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +0 -90
- package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +0 -103
- package/templates/frontend/react/tanstack-router/src/routes/index.tsx.hbs +0 -85
- package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +0 -18
- package/templates/frontend/react/tanstack-router/vite.config.ts.hbs +0 -21
- package/templates/frontend/react/tanstack-start/package.json.hbs +0 -43
- package/templates/frontend/react/tanstack-start/public/robots.txt +0 -3
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +0 -144
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +0 -208
- package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +0 -85
- package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +0 -28
- package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +0 -22
- package/templates/frontend/react/web-base/_gitignore +0 -60
- package/templates/frontend/react/web-base/components.json +0 -24
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +0 -78
- package/templates/frontend/react/web-base/src/components/loader.tsx.hbs +0 -9
- package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +0 -57
- package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +0 -103
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +0 -26
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +0 -262
- package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +0 -20
- package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +0 -20
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +0 -13
- package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +0 -44
- package/templates/frontend/react/web-base/src/index.css.hbs +0 -131
- package/templates/frontend/react/web-base/src/lib/utils.ts.hbs +0 -6
- package/templates/frontend/solid/_gitignore +0 -11
- package/templates/frontend/solid/index.html +0 -13
- package/templates/frontend/solid/package.json.hbs +0 -24
- package/templates/frontend/solid/public/robots.txt +0 -3
- package/templates/frontend/solid/src/components/header.tsx.hbs +0 -38
- package/templates/frontend/solid/src/components/loader.tsx +0 -9
- package/templates/frontend/solid/src/main.tsx.hbs +0 -41
- package/templates/frontend/solid/src/routes/__root.tsx.hbs +0 -34
- package/templates/frontend/solid/src/routes/index.tsx.hbs +0 -72
- package/templates/frontend/solid/src/styles.css +0 -5
- package/templates/frontend/solid/tsconfig.json.hbs +0 -29
- package/templates/frontend/solid/vite.config.ts.hbs +0 -21
- package/templates/frontend/svelte/_gitignore +0 -24
- package/templates/frontend/svelte/_npmrc +0 -1
- package/templates/frontend/svelte/package.json.hbs +0 -27
- package/templates/frontend/svelte/src/app.css +0 -5
- package/templates/frontend/svelte/src/app.d.ts +0 -13
- package/templates/frontend/svelte/src/app.html +0 -12
- package/templates/frontend/svelte/src/components/Header.svelte.hbs +0 -40
- package/templates/frontend/svelte/src/lib/index.ts +0 -2
- package/templates/frontend/svelte/src/routes/+layout.svelte.hbs +0 -54
- package/templates/frontend/svelte/src/routes/+page.svelte.hbs +0 -92
- package/templates/frontend/svelte/static/favicon.png +0 -0
- package/templates/frontend/svelte/svelte.config.js.hbs +0 -18
- package/templates/frontend/svelte/tsconfig.json.hbs +0 -19
- package/templates/frontend/svelte/vite.config.ts.hbs +0 -7
- package/templates/packages/config/package.json.hbs +0 -5
- package/templates/packages/config/tsconfig.base.json.hbs +0 -33
- package/templates/packages/env/env.d.ts.hbs +0 -16
- package/templates/packages/env/package.json.hbs +0 -7
- package/templates/packages/env/src/native.ts.hbs +0 -21
- package/templates/packages/env/src/server.ts.hbs +0 -39
- package/templates/packages/env/src/web.ts.hbs +0 -98
- package/templates/packages/env/tsconfig.json.hbs +0 -3
- package/templates/packages/infra/alchemy.run.ts.hbs +0 -271
- package/templates/packages/infra/package.json.hbs +0 -10
- package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +0 -7
- package/templates/payments/polar/web/nuxt/app/pages/success.vue.hbs +0 -11
- package/templates/payments/polar/web/react/next/src/app/success/page.tsx.hbs +0 -15
- package/templates/payments/polar/web/react/react-router/src/routes/success.tsx.hbs +0 -13
- package/templates/payments/polar/web/react/tanstack-router/src/routes/success.tsx.hbs +0 -19
- package/templates/payments/polar/web/react/tanstack-start/src/functions/get-payment.ts.hbs +0 -15
- package/templates/payments/polar/web/react/tanstack-start/src/routes/success.tsx.hbs +0 -19
- package/templates/payments/polar/web/solid/src/routes/success.tsx.hbs +0 -23
- package/templates/payments/polar/web/svelte/src/routes/success/+page.svelte.hbs +0 -12
- /package/dist/{chunk-Dt3mZKp0.mjs → chunk-DPg_XC7m.mjs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as __reExport } from "./chunk-
|
|
2
|
+
import { t as __reExport } from "./chunk-DPg_XC7m.mjs";
|
|
3
3
|
import { autocompleteMultiselect, cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
4
4
|
import { createRouterClient, os } from "@orpc/server";
|
|
5
5
|
import pc from "picocolors";
|
|
@@ -9,16 +9,15 @@ import consola, { consola as consola$1 } from "consola";
|
|
|
9
9
|
import fs from "fs-extra";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
-
import {
|
|
12
|
+
import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, dependencyVersionMap, generateVirtualProject, generateVirtualProject as generateVirtualProject$1 } from "@better-t-stack/template-generator";
|
|
13
13
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
|
+
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
14
15
|
import gradient from "gradient-string";
|
|
15
|
-
import
|
|
16
|
+
import { writeTreeToFilesystem } from "@better-t-stack/template-generator/fs-writer";
|
|
16
17
|
import { $, execa } from "execa";
|
|
17
18
|
import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph";
|
|
18
|
-
import
|
|
19
|
-
import handlebars from "handlebars";
|
|
19
|
+
import * as JSONC from "jsonc-parser";
|
|
20
20
|
import { format } from "oxfmt";
|
|
21
|
-
import yaml from "yaml";
|
|
22
21
|
import os$1 from "node:os";
|
|
23
22
|
|
|
24
23
|
//#region src/utils/get-package-manager.ts
|
|
@@ -64,111 +63,6 @@ function getDefaultConfig() {
|
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
65
|
const DEFAULT_CONFIG = getDefaultConfig();
|
|
67
|
-
const dependencyVersionMap = {
|
|
68
|
-
typescript: "^5",
|
|
69
|
-
"better-auth": "^1.4.9",
|
|
70
|
-
"@better-auth/expo": "^1.4.9",
|
|
71
|
-
"@clerk/nextjs": "^6.31.5",
|
|
72
|
-
"@clerk/clerk-react": "^5.45.0",
|
|
73
|
-
"@clerk/tanstack-react-start": "^0.26.3",
|
|
74
|
-
"@clerk/clerk-expo": "^2.14.25",
|
|
75
|
-
"drizzle-orm": "^0.45.1",
|
|
76
|
-
"drizzle-kit": "^0.31.8",
|
|
77
|
-
"@planetscale/database": "^1.19.0",
|
|
78
|
-
"@libsql/client": "0.15.15",
|
|
79
|
-
libsql: "0.5.22",
|
|
80
|
-
"@neondatabase/serverless": "^1.0.2",
|
|
81
|
-
pg: "^8.16.3",
|
|
82
|
-
"@types/pg": "^8.15.6",
|
|
83
|
-
"@types/ws": "^8.18.1",
|
|
84
|
-
ws: "^8.18.3",
|
|
85
|
-
mysql2: "^3.14.0",
|
|
86
|
-
"@prisma/client": "^7.1.0",
|
|
87
|
-
prisma: "^7.1.0",
|
|
88
|
-
"@prisma/adapter-d1": "^7.1.0",
|
|
89
|
-
"@prisma/adapter-neon": "^7.1.0",
|
|
90
|
-
"@prisma/adapter-mariadb": "^7.1.0",
|
|
91
|
-
"@prisma/adapter-libsql": "^7.1.0",
|
|
92
|
-
"@prisma/adapter-better-sqlite3": "^7.1.0",
|
|
93
|
-
"@prisma/adapter-pg": "^7.1.0",
|
|
94
|
-
"@prisma/adapter-planetscale": "^7.1.0",
|
|
95
|
-
mongoose: "^8.14.0",
|
|
96
|
-
"vite-plugin-pwa": "^1.0.1",
|
|
97
|
-
"@vite-pwa/assets-generator": "^1.0.0",
|
|
98
|
-
"@tauri-apps/cli": "^2.4.0",
|
|
99
|
-
"@biomejs/biome": "^2.2.0",
|
|
100
|
-
oxlint: "^1.34.0",
|
|
101
|
-
oxfmt: "^0.19.0",
|
|
102
|
-
husky: "^9.1.7",
|
|
103
|
-
"lint-staged": "^16.1.2",
|
|
104
|
-
tsx: "^4.19.2",
|
|
105
|
-
"@types/node": "^22.13.14",
|
|
106
|
-
"@types/bun": "^1.3.4",
|
|
107
|
-
"@elysiajs/node": "^1.3.1",
|
|
108
|
-
"@elysiajs/cors": "^1.3.3",
|
|
109
|
-
"@elysiajs/trpc": "^1.1.0",
|
|
110
|
-
elysia: "^1.3.21",
|
|
111
|
-
"@hono/node-server": "^1.14.4",
|
|
112
|
-
"@hono/trpc-server": "^0.4.0",
|
|
113
|
-
hono: "^4.8.2",
|
|
114
|
-
cors: "^2.8.5",
|
|
115
|
-
express: "^5.1.0",
|
|
116
|
-
"@types/express": "^5.0.1",
|
|
117
|
-
"@types/cors": "^2.8.17",
|
|
118
|
-
fastify: "^5.3.3",
|
|
119
|
-
"@fastify/cors": "^11.0.1",
|
|
120
|
-
turbo: "^2.6.3",
|
|
121
|
-
ai: "^6.0.3",
|
|
122
|
-
"@ai-sdk/google": "^3.0.1",
|
|
123
|
-
"@ai-sdk/vue": "^3.0.3",
|
|
124
|
-
"@ai-sdk/svelte": "^4.0.3",
|
|
125
|
-
"@ai-sdk/react": "^3.0.3",
|
|
126
|
-
"@ai-sdk/devtools": "^0.0.2",
|
|
127
|
-
streamdown: "^1.6.10",
|
|
128
|
-
shiki: "^3.20.0",
|
|
129
|
-
"@orpc/server": "^1.12.2",
|
|
130
|
-
"@orpc/client": "^1.12.2",
|
|
131
|
-
"@orpc/openapi": "^1.12.2",
|
|
132
|
-
"@orpc/zod": "^1.12.2",
|
|
133
|
-
"@orpc/tanstack-query": "^1.12.2",
|
|
134
|
-
"@trpc/tanstack-react-query": "^11.7.2",
|
|
135
|
-
"@trpc/server": "^11.7.2",
|
|
136
|
-
"@trpc/client": "^11.7.2",
|
|
137
|
-
next: "^16.1.1",
|
|
138
|
-
convex: "^1.31.2",
|
|
139
|
-
"@convex-dev/react-query": "^0.1.0",
|
|
140
|
-
"@convex-dev/agent": "^0.3.2",
|
|
141
|
-
"convex-svelte": "^0.0.12",
|
|
142
|
-
"convex-nuxt": "0.1.5",
|
|
143
|
-
"convex-vue": "^0.1.5",
|
|
144
|
-
"@convex-dev/better-auth": "^0.10.9",
|
|
145
|
-
"@tanstack/svelte-query": "^5.85.3",
|
|
146
|
-
"@tanstack/svelte-query-devtools": "^5.85.3",
|
|
147
|
-
"@tanstack/vue-query-devtools": "^5.90.2",
|
|
148
|
-
"@tanstack/vue-query": "^5.90.2",
|
|
149
|
-
"@tanstack/react-query-devtools": "^5.91.1",
|
|
150
|
-
"@tanstack/react-query": "^5.90.12",
|
|
151
|
-
"@tanstack/react-router-ssr-query": "^1.142.7",
|
|
152
|
-
"@tanstack/solid-query": "^5.87.4",
|
|
153
|
-
"@tanstack/solid-query-devtools": "^5.87.4",
|
|
154
|
-
"@tanstack/solid-router-devtools": "^1.131.44",
|
|
155
|
-
wrangler: "^4.54.0",
|
|
156
|
-
"@cloudflare/vite-plugin": "^1.17.1",
|
|
157
|
-
"@opennextjs/cloudflare": "^1.14.6",
|
|
158
|
-
"nitro-cloudflare-dev": "^0.2.2",
|
|
159
|
-
"@sveltejs/adapter-cloudflare": "^7.2.4",
|
|
160
|
-
"@cloudflare/workers-types": "^4.20251213.0",
|
|
161
|
-
alchemy: "^0.82.1",
|
|
162
|
-
dotenv: "^17.2.2",
|
|
163
|
-
tsdown: "^0.16.5",
|
|
164
|
-
zod: "^4.1.13",
|
|
165
|
-
"@t3-oss/env-core": "^0.13.1",
|
|
166
|
-
"@t3-oss/env-nextjs": "^0.13.1",
|
|
167
|
-
"@t3-oss/env-nuxt": "^0.13.1",
|
|
168
|
-
srvx: "0.8.15",
|
|
169
|
-
"@polar-sh/better-auth": "^1.1.3",
|
|
170
|
-
"@polar-sh/sdk": "^0.34.16"
|
|
171
|
-
};
|
|
172
66
|
const ADDON_COMPATIBILITY = {
|
|
173
67
|
pwa: [
|
|
174
68
|
"tanstack-router",
|
|
@@ -198,22 +92,57 @@ const ADDON_COMPATIBILITY = {
|
|
|
198
92
|
};
|
|
199
93
|
|
|
200
94
|
//#endregion
|
|
201
|
-
//#region src/
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
95
|
+
//#region src/utils/context.ts
|
|
96
|
+
const cliStorage = new AsyncLocalStorage();
|
|
97
|
+
function defaultContext() {
|
|
98
|
+
return {
|
|
99
|
+
navigation: {
|
|
100
|
+
isFirstPrompt: false,
|
|
101
|
+
lastPromptShownUI: false
|
|
102
|
+
},
|
|
103
|
+
silent: false,
|
|
104
|
+
verbose: false
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function getContext() {
|
|
108
|
+
const ctx = cliStorage.getStore();
|
|
109
|
+
if (!ctx) return defaultContext();
|
|
110
|
+
return ctx;
|
|
111
|
+
}
|
|
112
|
+
function tryGetContext() {
|
|
113
|
+
return cliStorage.getStore();
|
|
114
|
+
}
|
|
115
|
+
function isSilent() {
|
|
116
|
+
return getContext().silent;
|
|
117
|
+
}
|
|
118
|
+
function isFirstPrompt() {
|
|
119
|
+
return getContext().navigation.isFirstPrompt;
|
|
120
|
+
}
|
|
121
|
+
function didLastPromptShowUI() {
|
|
122
|
+
return getContext().navigation.lastPromptShownUI;
|
|
123
|
+
}
|
|
124
|
+
function setIsFirstPrompt$1(value) {
|
|
125
|
+
const ctx = tryGetContext();
|
|
126
|
+
if (ctx) ctx.navigation.isFirstPrompt = value;
|
|
127
|
+
}
|
|
128
|
+
function setLastPromptShownUI(value) {
|
|
129
|
+
const ctx = tryGetContext();
|
|
130
|
+
if (ctx) ctx.navigation.lastPromptShownUI = value;
|
|
131
|
+
}
|
|
132
|
+
async function runWithContextAsync(options, fn) {
|
|
133
|
+
const ctx = {
|
|
134
|
+
navigation: {
|
|
135
|
+
isFirstPrompt: false,
|
|
136
|
+
lastPromptShownUI: false
|
|
137
|
+
},
|
|
138
|
+
silent: options.silent ?? false,
|
|
139
|
+
verbose: options.verbose ?? false,
|
|
140
|
+
projectDir: options.projectDir,
|
|
141
|
+
projectName: options.projectName,
|
|
142
|
+
packageManager: options.packageManager
|
|
143
|
+
};
|
|
144
|
+
return cliStorage.run(ctx, fn);
|
|
145
|
+
}
|
|
217
146
|
|
|
218
147
|
//#endregion
|
|
219
148
|
//#region src/utils/errors.ts
|
|
@@ -230,19 +159,40 @@ var CLIError = class extends Error {
|
|
|
230
159
|
}
|
|
231
160
|
};
|
|
232
161
|
function exitWithError(message) {
|
|
162
|
+
if (isSilent()) throw new CLIError(message);
|
|
233
163
|
consola.error(pc.red(message));
|
|
234
|
-
|
|
164
|
+
process.exit(1);
|
|
235
165
|
}
|
|
236
166
|
function exitCancelled(message = "Operation cancelled") {
|
|
167
|
+
if (isSilent()) throw new UserCancelledError(message);
|
|
237
168
|
cancel(pc.red(message));
|
|
238
|
-
|
|
169
|
+
process.exit(1);
|
|
239
170
|
}
|
|
240
171
|
function handleError(error, fallbackMessage) {
|
|
241
172
|
const message = error instanceof Error ? error.message : fallbackMessage || String(error);
|
|
173
|
+
if (isSilent()) throw error instanceof Error ? error : new Error(message);
|
|
242
174
|
consola.error(pc.red(message));
|
|
243
|
-
|
|
175
|
+
process.exit(1);
|
|
244
176
|
}
|
|
245
177
|
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/types.ts
|
|
180
|
+
var types_exports = {};
|
|
181
|
+
import * as import__better_t_stack_types from "@better-t-stack/types";
|
|
182
|
+
__reExport(types_exports, import__better_t_stack_types);
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/utils/compatibility.ts
|
|
186
|
+
const WEB_FRAMEWORKS = [
|
|
187
|
+
"tanstack-router",
|
|
188
|
+
"react-router",
|
|
189
|
+
"tanstack-start",
|
|
190
|
+
"next",
|
|
191
|
+
"nuxt",
|
|
192
|
+
"svelte",
|
|
193
|
+
"solid"
|
|
194
|
+
];
|
|
195
|
+
|
|
246
196
|
//#endregion
|
|
247
197
|
//#region src/utils/compatibility-rules.ts
|
|
248
198
|
function isWebFrontend(value) {
|
|
@@ -337,14 +287,6 @@ function validateAddonCompatibility(addon, frontend, _auth) {
|
|
|
337
287
|
}
|
|
338
288
|
return { isCompatible: true };
|
|
339
289
|
}
|
|
340
|
-
function getCompatibleAddons(allAddons, frontend, existingAddons = [], auth) {
|
|
341
|
-
return allAddons.filter((addon) => {
|
|
342
|
-
if (existingAddons.includes(addon)) return false;
|
|
343
|
-
if (addon === "none") return false;
|
|
344
|
-
const { isCompatible } = validateAddonCompatibility(addon, frontend, auth);
|
|
345
|
-
return isCompatible;
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
290
|
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth) {
|
|
349
291
|
for (const addon of addons) {
|
|
350
292
|
if (addon === "none") continue;
|
|
@@ -376,64 +318,8 @@ function validateExamplesCompatibility(examples, backend, database, frontend, ap
|
|
|
376
318
|
}
|
|
377
319
|
}
|
|
378
320
|
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region src/utils/context.ts
|
|
381
|
-
const cliStorage = new AsyncLocalStorage();
|
|
382
|
-
function defaultContext() {
|
|
383
|
-
return {
|
|
384
|
-
navigation: {
|
|
385
|
-
isFirstPrompt: false,
|
|
386
|
-
lastPromptShownUI: false
|
|
387
|
-
},
|
|
388
|
-
silent: false,
|
|
389
|
-
verbose: false
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
function getContext() {
|
|
393
|
-
const ctx = cliStorage.getStore();
|
|
394
|
-
if (!ctx) return defaultContext();
|
|
395
|
-
return ctx;
|
|
396
|
-
}
|
|
397
|
-
function tryGetContext() {
|
|
398
|
-
return cliStorage.getStore();
|
|
399
|
-
}
|
|
400
|
-
function isSilent() {
|
|
401
|
-
return getContext().silent;
|
|
402
|
-
}
|
|
403
|
-
function isFirstPrompt() {
|
|
404
|
-
return getContext().navigation.isFirstPrompt;
|
|
405
|
-
}
|
|
406
|
-
function didLastPromptShowUI() {
|
|
407
|
-
return getContext().navigation.lastPromptShownUI;
|
|
408
|
-
}
|
|
409
|
-
function setIsFirstPrompt$1(value) {
|
|
410
|
-
const ctx = tryGetContext();
|
|
411
|
-
if (ctx) ctx.navigation.isFirstPrompt = value;
|
|
412
|
-
}
|
|
413
|
-
function setLastPromptShownUI(value) {
|
|
414
|
-
const ctx = tryGetContext();
|
|
415
|
-
if (ctx) ctx.navigation.lastPromptShownUI = value;
|
|
416
|
-
}
|
|
417
|
-
async function runWithContextAsync(options, fn) {
|
|
418
|
-
const ctx = {
|
|
419
|
-
navigation: {
|
|
420
|
-
isFirstPrompt: false,
|
|
421
|
-
lastPromptShownUI: false
|
|
422
|
-
},
|
|
423
|
-
silent: options.silent ?? false,
|
|
424
|
-
verbose: options.verbose ?? false,
|
|
425
|
-
projectDir: options.projectDir,
|
|
426
|
-
projectName: options.projectName,
|
|
427
|
-
packageManager: options.packageManager
|
|
428
|
-
};
|
|
429
|
-
return cliStorage.run(ctx, fn);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
321
|
//#endregion
|
|
433
322
|
//#region src/utils/navigation.ts
|
|
434
|
-
/**
|
|
435
|
-
* Navigation symbols and utilities for prompt navigation
|
|
436
|
-
*/
|
|
437
323
|
const GO_BACK_SYMBOL = Symbol("clack:goBack");
|
|
438
324
|
function isGoBack(value) {
|
|
439
325
|
return value === GO_BACK_SYMBOL;
|
|
@@ -785,43 +671,6 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
785
671
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
786
672
|
return response;
|
|
787
673
|
}
|
|
788
|
-
async function getAddonsToAdd(frontend, existingAddons = [], auth) {
|
|
789
|
-
const groupedOptions = {
|
|
790
|
-
Documentation: [],
|
|
791
|
-
Linting: [],
|
|
792
|
-
Other: []
|
|
793
|
-
};
|
|
794
|
-
const frontendArray = frontend || [];
|
|
795
|
-
const compatibleAddons = getCompatibleAddons(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
|
|
796
|
-
for (const addon of compatibleAddons) {
|
|
797
|
-
const { label, hint } = getAddonDisplay(addon);
|
|
798
|
-
const option = {
|
|
799
|
-
value: addon,
|
|
800
|
-
label,
|
|
801
|
-
hint
|
|
802
|
-
};
|
|
803
|
-
if (ADDON_GROUPS.Documentation.includes(addon)) groupedOptions.Documentation.push(option);
|
|
804
|
-
else if (ADDON_GROUPS.Linting.includes(addon)) groupedOptions.Linting.push(option);
|
|
805
|
-
else if (ADDON_GROUPS.Other.includes(addon)) groupedOptions.Other.push(option);
|
|
806
|
-
}
|
|
807
|
-
Object.keys(groupedOptions).forEach((group$1) => {
|
|
808
|
-
if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
|
|
809
|
-
else {
|
|
810
|
-
const groupOrder = ADDON_GROUPS[group$1] || [];
|
|
811
|
-
groupedOptions[group$1].sort((a, b) => {
|
|
812
|
-
return groupOrder.indexOf(a.value) - groupOrder.indexOf(b.value);
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
if (Object.keys(groupedOptions).length === 0) return [];
|
|
817
|
-
const response = await navigableGroupMultiselect({
|
|
818
|
-
message: "Select addons to add",
|
|
819
|
-
options: groupedOptions,
|
|
820
|
-
required: false
|
|
821
|
-
});
|
|
822
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
823
|
-
return response;
|
|
824
|
-
}
|
|
825
674
|
|
|
826
675
|
//#endregion
|
|
827
676
|
//#region src/prompts/api.ts
|
|
@@ -1441,16 +1290,6 @@ async function getRuntimeChoice(runtime, backend) {
|
|
|
1441
1290
|
|
|
1442
1291
|
//#endregion
|
|
1443
1292
|
//#region src/prompts/server-deploy.ts
|
|
1444
|
-
function getDeploymentDisplay$1(deployment) {
|
|
1445
|
-
if (deployment === "cloudflare") return {
|
|
1446
|
-
label: "Cloudflare",
|
|
1447
|
-
hint: "Deploy to Cloudflare Workers using Alchemy"
|
|
1448
|
-
};
|
|
1449
|
-
return {
|
|
1450
|
-
label: deployment,
|
|
1451
|
-
hint: `Add ${deployment} deployment`
|
|
1452
|
-
};
|
|
1453
|
-
}
|
|
1454
1293
|
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
1455
1294
|
if (deployment !== void 0) return deployment;
|
|
1456
1295
|
if (backend === "none" || backend === "convex") return "none";
|
|
@@ -1458,29 +1297,6 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
1458
1297
|
if (runtime === "workers") return "cloudflare";
|
|
1459
1298
|
return "none";
|
|
1460
1299
|
}
|
|
1461
|
-
async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
1462
|
-
if (backend !== "hono") return "none";
|
|
1463
|
-
const options = [];
|
|
1464
|
-
if (runtime === "workers") {
|
|
1465
|
-
if (existingDeployment !== "cloudflare") {
|
|
1466
|
-
const { label, hint } = getDeploymentDisplay$1("cloudflare");
|
|
1467
|
-
options.push({
|
|
1468
|
-
value: "cloudflare",
|
|
1469
|
-
label,
|
|
1470
|
-
hint
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
if (existingDeployment && existingDeployment !== "none") return "none";
|
|
1475
|
-
if (options.length === 0) return "none";
|
|
1476
|
-
const response = await navigableSelect({
|
|
1477
|
-
message: "Select server deployment",
|
|
1478
|
-
options,
|
|
1479
|
-
initialValue: DEFAULT_CONFIG.serverDeploy
|
|
1480
|
-
});
|
|
1481
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1482
|
-
return response;
|
|
1483
|
-
}
|
|
1484
1300
|
|
|
1485
1301
|
//#endregion
|
|
1486
1302
|
//#region src/prompts/web-deploy.ts
|
|
@@ -1515,32 +1331,6 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1515
1331
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1516
1332
|
return response;
|
|
1517
1333
|
}
|
|
1518
|
-
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
1519
|
-
if (!hasWebFrontend(frontend)) return "none";
|
|
1520
|
-
const options = [];
|
|
1521
|
-
if (existingDeployment !== "cloudflare") {
|
|
1522
|
-
const { label, hint } = getDeploymentDisplay("cloudflare");
|
|
1523
|
-
options.push({
|
|
1524
|
-
value: "cloudflare",
|
|
1525
|
-
label,
|
|
1526
|
-
hint
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
if (existingDeployment && existingDeployment !== "none") return "none";
|
|
1530
|
-
if (options.length > 0) options.push({
|
|
1531
|
-
value: "none",
|
|
1532
|
-
label: "None",
|
|
1533
|
-
hint: "Skip deployment setup"
|
|
1534
|
-
});
|
|
1535
|
-
if (options.length === 0) return "none";
|
|
1536
|
-
const response = await navigableSelect({
|
|
1537
|
-
message: "Select web deployment",
|
|
1538
|
-
options,
|
|
1539
|
-
initialValue: DEFAULT_CONFIG.webDeploy
|
|
1540
|
-
});
|
|
1541
|
-
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1542
|
-
return response;
|
|
1543
|
-
}
|
|
1544
1334
|
|
|
1545
1335
|
//#endregion
|
|
1546
1336
|
//#region src/prompts/config-prompts.ts
|
|
@@ -1653,7 +1443,7 @@ const getLatestCLIVersion = () => {
|
|
|
1653
1443
|
*/
|
|
1654
1444
|
function isTelemetryEnabled() {
|
|
1655
1445
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1656
|
-
const BTS_TELEMETRY = "
|
|
1446
|
+
const BTS_TELEMETRY = "0";
|
|
1657
1447
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1658
1448
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1659
1449
|
return true;
|
|
@@ -1661,16 +1451,7 @@ function isTelemetryEnabled() {
|
|
|
1661
1451
|
|
|
1662
1452
|
//#endregion
|
|
1663
1453
|
//#region src/utils/analytics.ts
|
|
1664
|
-
|
|
1665
|
-
async function sendConvexEvent(payload) {
|
|
1666
|
-
try {
|
|
1667
|
-
await fetch(CONVEX_INGEST_URL, {
|
|
1668
|
-
method: "POST",
|
|
1669
|
-
headers: { "Content-Type": "application/json" },
|
|
1670
|
-
body: JSON.stringify(payload)
|
|
1671
|
-
});
|
|
1672
|
-
} catch {}
|
|
1673
|
-
}
|
|
1454
|
+
async function sendConvexEvent(payload) {}
|
|
1674
1455
|
async function trackProjectCreation(config, disableAnalytics = false) {
|
|
1675
1456
|
if (!isTelemetryEnabled() || disableAnalytics) return;
|
|
1676
1457
|
const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath, ...safeConfig } = config;
|
|
@@ -2248,40 +2029,97 @@ function validateConfigCompatibility(config, providedFlags, options) {
|
|
|
2248
2029
|
}
|
|
2249
2030
|
|
|
2250
2031
|
//#endregion
|
|
2251
|
-
//#region src/utils/
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2032
|
+
//#region src/utils/ts-morph.ts
|
|
2033
|
+
const tsProject = new Project({
|
|
2034
|
+
useInMemoryFileSystem: false,
|
|
2035
|
+
skipAddingFilesFromTsConfig: true,
|
|
2036
|
+
manipulationSettings: {
|
|
2037
|
+
quoteKind: QuoteKind.Single,
|
|
2038
|
+
indentationText: IndentationText.TwoSpaces
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
function ensureArrayProperty(obj, name) {
|
|
2042
|
+
return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
|
|
2043
|
+
name,
|
|
2044
|
+
initializer: "[]"
|
|
2045
|
+
}).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
//#endregion
|
|
2049
|
+
//#region src/utils/better-auth-plugin-setup.ts
|
|
2050
|
+
async function setupBetterAuthPlugins(projectDir, config) {
|
|
2051
|
+
const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
|
|
2052
|
+
const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
|
|
2053
|
+
if (!authIndexFile) return;
|
|
2054
|
+
const pluginsToAdd = [];
|
|
2055
|
+
const importsToAdd = [];
|
|
2056
|
+
if (config.backend === "self" && config.frontend?.includes("tanstack-start")) {
|
|
2057
|
+
pluginsToAdd.push("tanstackStartCookies()");
|
|
2058
|
+
importsToAdd.push("import { tanstackStartCookies } from \"better-auth/tanstack-start\";");
|
|
2059
|
+
}
|
|
2060
|
+
if (config.backend === "self" && config.frontend?.includes("next")) {
|
|
2061
|
+
pluginsToAdd.push("nextCookies()");
|
|
2062
|
+
importsToAdd.push("import { nextCookies } from \"better-auth/next-js\";");
|
|
2063
|
+
}
|
|
2064
|
+
if (config.frontend?.includes("native-bare") || config.frontend?.includes("native-uniwind") || config.frontend?.includes("native-unistyles")) {
|
|
2065
|
+
pluginsToAdd.push("expo()");
|
|
2066
|
+
importsToAdd.push("import { expo } from \"@better-auth/expo\";");
|
|
2067
|
+
}
|
|
2068
|
+
if (pluginsToAdd.length === 0) return;
|
|
2069
|
+
importsToAdd.forEach((importStatement) => {
|
|
2070
|
+
if (!authIndexFile.getImportDeclaration((declaration) => declaration.getModuleSpecifierValue().includes(importStatement.split("\"")[1]))) authIndexFile.insertImportDeclaration(0, {
|
|
2071
|
+
moduleSpecifier: importStatement.split("\"")[1],
|
|
2072
|
+
namedImports: [importStatement.split("{")[1].split("}")[0].trim()]
|
|
2073
|
+
});
|
|
2074
|
+
});
|
|
2075
|
+
const betterAuthCall = authIndexFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((call) => call.getExpression().getText() === "betterAuth");
|
|
2076
|
+
if (betterAuthCall) {
|
|
2077
|
+
const configObject = betterAuthCall.getArguments()[0];
|
|
2078
|
+
if (configObject && configObject.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
|
2079
|
+
const pluginsArray = ensureArrayProperty(configObject.asKindOrThrow(SyntaxKind.ObjectLiteralExpression), "plugins");
|
|
2080
|
+
pluginsToAdd.forEach((plugin) => {
|
|
2081
|
+
pluginsArray.addElement(plugin);
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
authIndexFile.save();
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
//#endregion
|
|
2089
|
+
//#region src/utils/bts-config.ts
|
|
2090
|
+
const BTS_CONFIG_FILE = "bts.jsonc";
|
|
2091
|
+
async function writeBtsConfig(projectConfig) {
|
|
2092
|
+
const btsConfig = {
|
|
2093
|
+
version: getLatestCLIVersion(),
|
|
2094
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2095
|
+
database: projectConfig.database,
|
|
2096
|
+
orm: projectConfig.orm,
|
|
2097
|
+
backend: projectConfig.backend,
|
|
2098
|
+
runtime: projectConfig.runtime,
|
|
2099
|
+
frontend: projectConfig.frontend,
|
|
2100
|
+
addons: projectConfig.addons,
|
|
2101
|
+
examples: projectConfig.examples,
|
|
2102
|
+
auth: projectConfig.auth,
|
|
2103
|
+
payments: projectConfig.payments,
|
|
2104
|
+
packageManager: projectConfig.packageManager,
|
|
2105
|
+
dbSetup: projectConfig.dbSetup,
|
|
2106
|
+
api: projectConfig.api,
|
|
2107
|
+
webDeploy: projectConfig.webDeploy,
|
|
2108
|
+
serverDeploy: projectConfig.serverDeploy
|
|
2109
|
+
};
|
|
2110
|
+
const baseContent = {
|
|
2111
|
+
$schema: "https://r2.better-t-stack.dev/schema.json",
|
|
2112
|
+
version: btsConfig.version,
|
|
2113
|
+
createdAt: btsConfig.createdAt,
|
|
2114
|
+
database: btsConfig.database,
|
|
2115
|
+
orm: btsConfig.orm,
|
|
2116
|
+
backend: btsConfig.backend,
|
|
2117
|
+
runtime: btsConfig.runtime,
|
|
2118
|
+
frontend: btsConfig.frontend,
|
|
2119
|
+
addons: btsConfig.addons,
|
|
2120
|
+
examples: btsConfig.examples,
|
|
2121
|
+
auth: btsConfig.auth,
|
|
2122
|
+
payments: btsConfig.payments,
|
|
2285
2123
|
packageManager: btsConfig.packageManager,
|
|
2286
2124
|
dbSetup: btsConfig.dbSetup,
|
|
2287
2125
|
api: btsConfig.api,
|
|
@@ -2302,64 +2140,37 @@ ${configContent}`;
|
|
|
2302
2140
|
const configPath = path.join(projectConfig.projectDir, BTS_CONFIG_FILE);
|
|
2303
2141
|
await fs.writeFile(configPath, finalContent, "utf-8");
|
|
2304
2142
|
}
|
|
2305
|
-
|
|
2143
|
+
|
|
2144
|
+
//#endregion
|
|
2145
|
+
//#region src/utils/file-formatter.ts
|
|
2146
|
+
const formatOptions = {
|
|
2147
|
+
experimentalSortPackageJson: true,
|
|
2148
|
+
experimentalSortImports: { order: "asc" }
|
|
2149
|
+
};
|
|
2150
|
+
async function formatCode(filePath, content) {
|
|
2306
2151
|
try {
|
|
2307
|
-
const
|
|
2308
|
-
if (
|
|
2309
|
-
|
|
2310
|
-
const errors = [];
|
|
2311
|
-
const config = JSONC.parse(configContent, errors, {
|
|
2312
|
-
allowTrailingComma: true,
|
|
2313
|
-
disallowComments: false
|
|
2314
|
-
});
|
|
2315
|
-
if (errors.length > 0) {
|
|
2316
|
-
console.warn("Warning: Found errors parsing bts.jsonc:", errors);
|
|
2317
|
-
return null;
|
|
2318
|
-
}
|
|
2319
|
-
return config;
|
|
2152
|
+
const result = await format(path.basename(filePath), content, formatOptions);
|
|
2153
|
+
if (result.errors && result.errors.length > 0) return null;
|
|
2154
|
+
return result.code;
|
|
2320
2155
|
} catch {
|
|
2321
2156
|
return null;
|
|
2322
2157
|
}
|
|
2323
2158
|
}
|
|
2324
|
-
async function
|
|
2325
|
-
|
|
2326
|
-
const
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
} }
|
|
2335
|
-
|
|
2336
|
-
}
|
|
2337
|
-
await fs.writeFile(configPath, modifiedContent, "utf-8");
|
|
2338
|
-
} catch {}
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
//#endregion
|
|
2342
|
-
//#region src/utils/add-package-deps.ts
|
|
2343
|
-
const addPackageDependency = async (opts) => {
|
|
2344
|
-
const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, projectDir } = opts;
|
|
2345
|
-
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
2346
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
2347
|
-
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
2348
|
-
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
2349
|
-
for (const pkgName of dependencies) {
|
|
2350
|
-
const version = dependencyVersionMap[pkgName];
|
|
2351
|
-
if (version) pkgJson.dependencies[pkgName] = version;
|
|
2352
|
-
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
2353
|
-
}
|
|
2354
|
-
for (const pkgName of devDependencies) {
|
|
2355
|
-
const version = dependencyVersionMap[pkgName];
|
|
2356
|
-
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
2357
|
-
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
2159
|
+
async function formatProject(projectDir) {
|
|
2160
|
+
async function formatDirectory(dir) {
|
|
2161
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
2162
|
+
await Promise.all(entries.map(async (entry) => {
|
|
2163
|
+
const fullPath = path.join(dir, entry.name);
|
|
2164
|
+
if (entry.isDirectory()) await formatDirectory(fullPath);
|
|
2165
|
+
else if (entry.isFile()) try {
|
|
2166
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
2167
|
+
const formatted = await formatCode(fullPath, content);
|
|
2168
|
+
if (formatted && formatted !== content) await fs.writeFile(fullPath, formatted, "utf-8");
|
|
2169
|
+
} catch {}
|
|
2170
|
+
}));
|
|
2358
2171
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
2362
|
-
};
|
|
2172
|
+
await formatDirectory(projectDir);
|
|
2173
|
+
}
|
|
2363
2174
|
|
|
2364
2175
|
//#endregion
|
|
2365
2176
|
//#region src/utils/package-runner.ts
|
|
@@ -2487,30 +2298,22 @@ async function setupFumadocs(config) {
|
|
|
2487
2298
|
|
|
2488
2299
|
//#endregion
|
|
2489
2300
|
//#region src/helpers/addons/oxlint-setup.ts
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
2497
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
2498
|
-
packageJson.scripts = {
|
|
2499
|
-
...packageJson.scripts,
|
|
2500
|
-
check: "oxlint && oxfmt --write"
|
|
2501
|
-
};
|
|
2502
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
2503
|
-
}
|
|
2301
|
+
/**
|
|
2302
|
+
* Oxlint setup - CLI-only operations
|
|
2303
|
+
* NOTE: Dependencies are handled by template-generator's addons-deps.ts processor
|
|
2304
|
+
* This file only handles external CLI initialization (oxlint --init, oxfmt --init)
|
|
2305
|
+
*/
|
|
2306
|
+
async function setupOxlint(config) {
|
|
2504
2307
|
const s = spinner();
|
|
2505
|
-
const oxlintArgs = getPackageExecutionArgs(packageManager, "oxlint@latest --init");
|
|
2506
2308
|
s.start("Initializing oxlint and oxfmt...");
|
|
2309
|
+
const oxlintArgs = getPackageExecutionArgs(config.packageManager, "oxlint@latest --init");
|
|
2507
2310
|
await $({
|
|
2508
|
-
cwd: projectDir,
|
|
2311
|
+
cwd: config.projectDir,
|
|
2509
2312
|
env: { CI: "true" }
|
|
2510
2313
|
})`${oxlintArgs}`;
|
|
2511
|
-
const oxfmtArgs = getPackageExecutionArgs(packageManager, "oxfmt@latest --init");
|
|
2314
|
+
const oxfmtArgs = getPackageExecutionArgs(config.packageManager, "oxfmt@latest --init");
|
|
2512
2315
|
await $({
|
|
2513
|
-
cwd: projectDir,
|
|
2316
|
+
cwd: config.projectDir,
|
|
2514
2317
|
env: { CI: "true" }
|
|
2515
2318
|
})`${oxfmtArgs}`;
|
|
2516
2319
|
s.stop("oxlint and oxfmt initialized successfully!");
|
|
@@ -2643,26 +2446,9 @@ async function setupTauri(config) {
|
|
|
2643
2446
|
if (!await fs.pathExists(clientPackageDir)) return;
|
|
2644
2447
|
try {
|
|
2645
2448
|
s.start("Setting up Tauri desktop app support...");
|
|
2646
|
-
await addPackageDependency({
|
|
2647
|
-
devDependencies: ["@tauri-apps/cli"],
|
|
2648
|
-
projectDir: clientPackageDir
|
|
2649
|
-
});
|
|
2650
|
-
const clientPackageJsonPath = path.join(clientPackageDir, "package.json");
|
|
2651
|
-
if (await fs.pathExists(clientPackageJsonPath)) {
|
|
2652
|
-
const packageJson = await fs.readJson(clientPackageJsonPath);
|
|
2653
|
-
packageJson.scripts = {
|
|
2654
|
-
...packageJson.scripts,
|
|
2655
|
-
tauri: "tauri",
|
|
2656
|
-
"desktop:dev": "tauri dev",
|
|
2657
|
-
"desktop:build": "tauri build"
|
|
2658
|
-
};
|
|
2659
|
-
await fs.writeJson(clientPackageJsonPath, packageJson, { spaces: 2 });
|
|
2660
|
-
}
|
|
2661
|
-
frontend.includes("tanstack-router");
|
|
2662
2449
|
const hasReactRouter = frontend.includes("react-router");
|
|
2663
2450
|
const hasNuxt = frontend.includes("nuxt");
|
|
2664
2451
|
const hasSvelte = frontend.includes("svelte");
|
|
2665
|
-
frontend.includes("solid");
|
|
2666
2452
|
const hasNext = frontend.includes("next");
|
|
2667
2453
|
const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
|
|
2668
2454
|
const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
|
|
@@ -2736,33 +2522,53 @@ async function setupTui(config) {
|
|
|
2736
2522
|
|
|
2737
2523
|
//#endregion
|
|
2738
2524
|
//#region src/helpers/addons/ultracite-setup.ts
|
|
2525
|
+
/**
|
|
2526
|
+
* Ultracite setup - CLI-only operations
|
|
2527
|
+
* NOTE: Dependencies (husky, lint-staged) are handled by template-generator's addons-deps.ts
|
|
2528
|
+
* This file handles interactive prompts and external CLI initialization
|
|
2529
|
+
*/
|
|
2739
2530
|
const EDITORS = {
|
|
2740
|
-
vscode: { label: "
|
|
2531
|
+
vscode: { label: "VS Code" },
|
|
2532
|
+
cursor: { label: "Cursor" },
|
|
2533
|
+
windsurf: { label: "Windsurf" },
|
|
2534
|
+
antigravity: { label: "Antigravity" },
|
|
2535
|
+
kiro: { label: "Kiro" },
|
|
2536
|
+
trae: { label: "Trae" },
|
|
2537
|
+
void: { label: "Void" },
|
|
2741
2538
|
zed: { label: "Zed" }
|
|
2742
2539
|
};
|
|
2743
2540
|
const AGENTS = {
|
|
2744
|
-
"vscode-copilot": { label: "VS Code Copilot" },
|
|
2745
|
-
cursor: { label: "Cursor" },
|
|
2746
|
-
windsurf: { label: "Windsurf" },
|
|
2747
|
-
zed: { label: "Zed" },
|
|
2748
2541
|
claude: { label: "Claude" },
|
|
2749
2542
|
codex: { label: "Codex" },
|
|
2750
|
-
|
|
2543
|
+
jules: { label: "Jules" },
|
|
2544
|
+
copilot: { label: "GitHub Copilot" },
|
|
2751
2545
|
cline: { label: "Cline" },
|
|
2752
2546
|
amp: { label: "Amp" },
|
|
2753
2547
|
aider: { label: "Aider" },
|
|
2754
2548
|
"firebase-studio": { label: "Firebase Studio" },
|
|
2755
2549
|
"open-hands": { label: "Open Hands" },
|
|
2756
|
-
|
|
2550
|
+
gemini: { label: "Gemini" },
|
|
2757
2551
|
junie: { label: "Junie" },
|
|
2758
2552
|
augmentcode: { label: "AugmentCode" },
|
|
2759
2553
|
"kilo-code": { label: "Kilo Code" },
|
|
2760
2554
|
goose: { label: "Goose" },
|
|
2761
|
-
"roo-code": { label: "Roo Code" }
|
|
2555
|
+
"roo-code": { label: "Roo Code" },
|
|
2556
|
+
warp: { label: "Warp" },
|
|
2557
|
+
droid: { label: "Droid" },
|
|
2558
|
+
opencode: { label: "OpenCode" },
|
|
2559
|
+
crush: { label: "Crush" },
|
|
2560
|
+
qwen: { label: "Qwen" },
|
|
2561
|
+
"amazon-q-cli": { label: "Amazon Q CLI" },
|
|
2562
|
+
firebender: { label: "Firebender" }
|
|
2762
2563
|
};
|
|
2763
2564
|
const HOOKS = {
|
|
2764
2565
|
cursor: { label: "Cursor" },
|
|
2765
|
-
|
|
2566
|
+
windsurf: { label: "Windsurf" }
|
|
2567
|
+
};
|
|
2568
|
+
const LINTERS = {
|
|
2569
|
+
biome: { label: "Biome (recommended)" },
|
|
2570
|
+
oxlint: { label: "OxLint" },
|
|
2571
|
+
eslint: { label: "ESLint" }
|
|
2766
2572
|
};
|
|
2767
2573
|
function getFrameworksFromFrontend(frontend) {
|
|
2768
2574
|
const frameworkMap = {
|
|
@@ -2785,8 +2591,15 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2785
2591
|
const { packageManager, projectDir, frontend } = config;
|
|
2786
2592
|
try {
|
|
2787
2593
|
log.info("Setting up Ultracite...");
|
|
2788
|
-
await setupBiome(projectDir);
|
|
2789
2594
|
const result = await group({
|
|
2595
|
+
linter: () => select({
|
|
2596
|
+
message: "Choose linter/formatter",
|
|
2597
|
+
options: Object.entries(LINTERS).map(([key, linter$1]) => ({
|
|
2598
|
+
value: key,
|
|
2599
|
+
label: linter$1.label
|
|
2600
|
+
})),
|
|
2601
|
+
initialValue: "biome"
|
|
2602
|
+
}),
|
|
2790
2603
|
editors: () => multiselect({
|
|
2791
2604
|
message: "Choose editors",
|
|
2792
2605
|
options: Object.entries(EDITORS).map(([key, editor]) => ({
|
|
@@ -2804,7 +2617,7 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2804
2617
|
required: true
|
|
2805
2618
|
}),
|
|
2806
2619
|
hooks: () => autocompleteMultiselect({
|
|
2807
|
-
message: "Choose hooks",
|
|
2620
|
+
message: "Choose hooks (optional)",
|
|
2808
2621
|
options: Object.entries(HOOKS).map(([key, hook]) => ({
|
|
2809
2622
|
value: key,
|
|
2810
2623
|
label: hook.label
|
|
@@ -2813,6 +2626,7 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2813
2626
|
}, { onCancel: () => {
|
|
2814
2627
|
exitCancelled("Operation cancelled");
|
|
2815
2628
|
} });
|
|
2629
|
+
const linter = result.linter;
|
|
2816
2630
|
const editors = result.editors;
|
|
2817
2631
|
const agents = result.agents;
|
|
2818
2632
|
const hooks = result.hooks;
|
|
@@ -2820,7 +2634,9 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2820
2634
|
const ultraciteArgs = [
|
|
2821
2635
|
"init",
|
|
2822
2636
|
"--pm",
|
|
2823
|
-
packageManager
|
|
2637
|
+
packageManager,
|
|
2638
|
+
"--linter",
|
|
2639
|
+
linter
|
|
2824
2640
|
];
|
|
2825
2641
|
if (frameworks.length > 0) ultraciteArgs.push("--frameworks", ...frameworks);
|
|
2826
2642
|
if (editors.length > 0) ultraciteArgs.push("--editors", ...editors);
|
|
@@ -2834,10 +2650,6 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2834
2650
|
cwd: projectDir,
|
|
2835
2651
|
env: { CI: "true" }
|
|
2836
2652
|
})`${args}`;
|
|
2837
|
-
if (hasHusky) await addPackageDependency({
|
|
2838
|
-
devDependencies: ["husky", "lint-staged"],
|
|
2839
|
-
projectDir
|
|
2840
|
-
});
|
|
2841
2653
|
s.stop("Ultracite setup successfully!");
|
|
2842
2654
|
} catch (error) {
|
|
2843
2655
|
log.error(pc.red("Failed to set up Ultracite"));
|
|
@@ -2845,54 +2657,6 @@ async function setupUltracite(config, hasHusky) {
|
|
|
2845
2657
|
}
|
|
2846
2658
|
}
|
|
2847
2659
|
|
|
2848
|
-
//#endregion
|
|
2849
|
-
//#region src/utils/ts-morph.ts
|
|
2850
|
-
const tsProject = new Project({
|
|
2851
|
-
useInMemoryFileSystem: false,
|
|
2852
|
-
skipAddingFilesFromTsConfig: true,
|
|
2853
|
-
manipulationSettings: {
|
|
2854
|
-
quoteKind: QuoteKind.Single,
|
|
2855
|
-
indentationText: IndentationText.TwoSpaces
|
|
2856
|
-
}
|
|
2857
|
-
});
|
|
2858
|
-
function ensureArrayProperty(obj, name) {
|
|
2859
|
-
return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
|
|
2860
|
-
name,
|
|
2861
|
-
initializer: "[]"
|
|
2862
|
-
}).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
2863
|
-
}
|
|
2864
|
-
|
|
2865
|
-
//#endregion
|
|
2866
|
-
//#region src/helpers/addons/vite-pwa-setup.ts
|
|
2867
|
-
async function addPwaToViteConfig(viteConfigPath, projectName) {
|
|
2868
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
2869
|
-
if (!sourceFile) throw new Error("vite config not found");
|
|
2870
|
-
if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
|
|
2871
|
-
namedImports: ["VitePWA"],
|
|
2872
|
-
moduleSpecifier: "vite-plugin-pwa"
|
|
2873
|
-
});
|
|
2874
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
2875
|
-
const expression = expr.getExpression();
|
|
2876
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
2877
|
-
});
|
|
2878
|
-
if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
|
|
2879
|
-
const configObject = defineCall.getArguments()[0];
|
|
2880
|
-
if (!configObject) throw new Error("defineConfig argument is not an object literal");
|
|
2881
|
-
const pluginsArray = ensureArrayProperty(configObject, "plugins");
|
|
2882
|
-
if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
|
|
2883
|
-
registerType: "autoUpdate",
|
|
2884
|
-
manifest: {
|
|
2885
|
-
name: "${projectName}",
|
|
2886
|
-
short_name: "${projectName}",
|
|
2887
|
-
description: "${projectName} - PWA Application",
|
|
2888
|
-
theme_color: "#0c0c0c",
|
|
2889
|
-
},
|
|
2890
|
-
pwaAssets: { disabled: false, config: true },
|
|
2891
|
-
devOptions: { enabled: true },
|
|
2892
|
-
})`);
|
|
2893
|
-
await tsProject.save();
|
|
2894
|
-
}
|
|
2895
|
-
|
|
2896
2660
|
//#endregion
|
|
2897
2661
|
//#region src/helpers/addons/wxt-setup.ts
|
|
2898
2662
|
const TEMPLATES = {
|
|
@@ -2957,1877 +2721,50 @@ async function setupWxt(config) {
|
|
|
2957
2721
|
|
|
2958
2722
|
//#endregion
|
|
2959
2723
|
//#region src/helpers/addons/addons-setup.ts
|
|
2960
|
-
async function setupAddons(config
|
|
2961
|
-
const { addons, frontend
|
|
2724
|
+
async function setupAddons(config) {
|
|
2725
|
+
const { addons, frontend } = config;
|
|
2962
2726
|
const hasReactWebFrontend = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next");
|
|
2963
2727
|
const hasNuxtFrontend = frontend.includes("nuxt");
|
|
2964
2728
|
const hasSvelteFrontend = frontend.includes("svelte");
|
|
2965
2729
|
const hasSolidFrontend = frontend.includes("solid");
|
|
2966
2730
|
const hasNextFrontend = frontend.includes("next");
|
|
2967
|
-
if (addons.includes("turborepo")) {
|
|
2968
|
-
await addPackageDependency({
|
|
2969
|
-
devDependencies: ["turbo"],
|
|
2970
|
-
projectDir
|
|
2971
|
-
});
|
|
2972
|
-
if (isAddCommand) log.info(`${pc.yellow("Update your package.json scripts:")}
|
|
2973
|
-
|
|
2974
|
-
${pc.dim("Replace:")} ${pc.yellow("\"pnpm -r dev\"")} ${pc.dim("→")} ${pc.green("\"turbo dev\"")}
|
|
2975
|
-
${pc.dim("Replace:")} ${pc.yellow("\"pnpm --filter web dev\"")} ${pc.dim("→")} ${pc.green("\"turbo -F web dev\"")}
|
|
2976
|
-
|
|
2977
|
-
${pc.cyan("Docs:")} ${pc.underline("https://turborepo.com/docs")}
|
|
2978
|
-
`);
|
|
2979
|
-
}
|
|
2980
|
-
if (addons.includes("pwa") && (hasReactWebFrontend || hasSolidFrontend)) await setupPwa(projectDir, frontend);
|
|
2981
2731
|
if (addons.includes("tauri") && (hasReactWebFrontend || hasNuxtFrontend || hasSvelteFrontend || hasSolidFrontend || hasNextFrontend)) await setupTauri(config);
|
|
2982
2732
|
const hasUltracite = addons.includes("ultracite");
|
|
2983
|
-
const hasBiome = addons.includes("biome");
|
|
2984
2733
|
const hasHusky = addons.includes("husky");
|
|
2985
|
-
|
|
2986
|
-
if (hasUltracite) await setupUltracite(config, hasHusky);
|
|
2987
|
-
else {
|
|
2988
|
-
if (hasBiome) await setupBiome(projectDir);
|
|
2989
|
-
if (hasHusky) {
|
|
2990
|
-
let linter;
|
|
2991
|
-
if (hasOxlint) linter = "oxlint";
|
|
2992
|
-
else if (hasBiome) linter = "biome";
|
|
2993
|
-
await setupHusky(projectDir, linter);
|
|
2994
|
-
}
|
|
2995
|
-
}
|
|
2996
|
-
if (hasOxlint) await setupOxlint(projectDir, packageManager);
|
|
2734
|
+
if (addons.includes("oxlint")) await setupOxlint(config);
|
|
2997
2735
|
if (addons.includes("starlight")) await setupStarlight(config);
|
|
2998
2736
|
if (addons.includes("ruler")) await setupRuler(config);
|
|
2999
2737
|
if (addons.includes("fumadocs")) await setupFumadocs(config);
|
|
3000
2738
|
if (addons.includes("opentui")) await setupTui(config);
|
|
3001
2739
|
if (addons.includes("wxt")) await setupWxt(config);
|
|
3002
|
-
|
|
3003
|
-
function getWebAppDir(projectDir, frontends) {
|
|
3004
|
-
if (frontends.some((f) => [
|
|
3005
|
-
"react-router",
|
|
3006
|
-
"tanstack-router",
|
|
3007
|
-
"nuxt",
|
|
3008
|
-
"svelte",
|
|
3009
|
-
"solid"
|
|
3010
|
-
].includes(f))) return path.join(projectDir, "apps/web");
|
|
3011
|
-
return path.join(projectDir, "apps/web");
|
|
3012
|
-
}
|
|
3013
|
-
async function setupBiome(projectDir) {
|
|
3014
|
-
await addPackageDependency({
|
|
3015
|
-
devDependencies: ["@biomejs/biome"],
|
|
3016
|
-
projectDir
|
|
3017
|
-
});
|
|
3018
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
3019
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3020
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3021
|
-
packageJson.scripts = {
|
|
3022
|
-
...packageJson.scripts,
|
|
3023
|
-
check: "biome check --write ."
|
|
3024
|
-
};
|
|
3025
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
async function setupHusky(projectDir, linter) {
|
|
3029
|
-
await addPackageDependency({
|
|
3030
|
-
devDependencies: ["husky", "lint-staged"],
|
|
3031
|
-
projectDir
|
|
3032
|
-
});
|
|
3033
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
3034
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3035
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3036
|
-
packageJson.scripts = {
|
|
3037
|
-
...packageJson.scripts,
|
|
3038
|
-
prepare: "husky"
|
|
3039
|
-
};
|
|
3040
|
-
if (linter === "oxlint") packageJson["lint-staged"] = { "*": ["oxlint", "oxfmt --write"] };
|
|
3041
|
-
else if (linter === "biome") packageJson["lint-staged"] = { "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": ["biome check --write ."] };
|
|
3042
|
-
else packageJson["lint-staged"] = { "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "" };
|
|
3043
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
async function setupPwa(projectDir, frontends) {
|
|
3047
|
-
if (!frontends.some((f) => [
|
|
3048
|
-
"react-router",
|
|
3049
|
-
"tanstack-router",
|
|
3050
|
-
"solid"
|
|
3051
|
-
].includes(f))) return;
|
|
3052
|
-
const clientPackageDir = getWebAppDir(projectDir, frontends);
|
|
3053
|
-
if (!await fs.pathExists(clientPackageDir)) return;
|
|
3054
|
-
await addPackageDependency({
|
|
3055
|
-
dependencies: ["vite-plugin-pwa"],
|
|
3056
|
-
devDependencies: ["@vite-pwa/assets-generator"],
|
|
3057
|
-
projectDir: clientPackageDir
|
|
3058
|
-
});
|
|
3059
|
-
const clientPackageJsonPath = path.join(clientPackageDir, "package.json");
|
|
3060
|
-
if (await fs.pathExists(clientPackageJsonPath)) {
|
|
3061
|
-
const packageJson = await fs.readJson(clientPackageJsonPath);
|
|
3062
|
-
packageJson.scripts = {
|
|
3063
|
-
...packageJson.scripts,
|
|
3064
|
-
"generate-pwa-assets": "pwa-assets-generator"
|
|
3065
|
-
};
|
|
3066
|
-
await fs.writeJson(clientPackageJsonPath, packageJson, { spaces: 2 });
|
|
3067
|
-
}
|
|
3068
|
-
const viteConfigTs = path.join(clientPackageDir, "vite.config.ts");
|
|
3069
|
-
if (await fs.pathExists(viteConfigTs)) await addPwaToViteConfig(viteConfigTs, path.basename(projectDir));
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
|
-
//#endregion
|
|
3073
|
-
//#region src/helpers/core/detect-project-config.ts
|
|
3074
|
-
async function detectProjectConfig(projectDir) {
|
|
3075
|
-
try {
|
|
3076
|
-
const btsConfig = await readBtsConfig(projectDir);
|
|
3077
|
-
if (btsConfig) return {
|
|
3078
|
-
projectDir,
|
|
3079
|
-
projectName: path.basename(projectDir),
|
|
3080
|
-
database: btsConfig.database,
|
|
3081
|
-
orm: btsConfig.orm,
|
|
3082
|
-
backend: btsConfig.backend,
|
|
3083
|
-
runtime: btsConfig.runtime,
|
|
3084
|
-
frontend: btsConfig.frontend,
|
|
3085
|
-
addons: btsConfig.addons,
|
|
3086
|
-
examples: btsConfig.examples,
|
|
3087
|
-
auth: btsConfig.auth,
|
|
3088
|
-
payments: btsConfig.payments,
|
|
3089
|
-
packageManager: btsConfig.packageManager,
|
|
3090
|
-
dbSetup: btsConfig.dbSetup,
|
|
3091
|
-
api: btsConfig.api,
|
|
3092
|
-
webDeploy: btsConfig.webDeploy,
|
|
3093
|
-
serverDeploy: btsConfig.serverDeploy
|
|
3094
|
-
};
|
|
3095
|
-
return null;
|
|
3096
|
-
} catch {
|
|
3097
|
-
return null;
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
async function isBetterTStackProject(projectDir) {
|
|
3101
|
-
try {
|
|
3102
|
-
return await fs.pathExists(path.join(projectDir, "bts.jsonc"));
|
|
3103
|
-
} catch {
|
|
3104
|
-
return false;
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
|
|
3108
|
-
//#endregion
|
|
3109
|
-
//#region src/helpers/core/install-dependencies.ts
|
|
3110
|
-
async function installDependencies({ projectDir, packageManager }) {
|
|
3111
|
-
const s = spinner();
|
|
3112
|
-
try {
|
|
3113
|
-
s.start(`Running ${packageManager} install...`);
|
|
3114
|
-
await $({
|
|
3115
|
-
cwd: projectDir,
|
|
3116
|
-
stderr: "inherit"
|
|
3117
|
-
})`${packageManager} install`;
|
|
3118
|
-
s.stop("Dependencies installed successfully");
|
|
3119
|
-
} catch (error) {
|
|
3120
|
-
s.stop(pc.red("Failed to install dependencies"));
|
|
3121
|
-
if (error instanceof Error) consola.error(pc.red(`Installation error: ${error.message}`));
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
|
-
|
|
3125
|
-
//#endregion
|
|
3126
|
-
//#region src/utils/file-formatter.ts
|
|
3127
|
-
const formatOptions = {
|
|
3128
|
-
experimentalSortPackageJson: true,
|
|
3129
|
-
experimentalSortImports: { order: "asc" }
|
|
3130
|
-
};
|
|
3131
|
-
async function formatFile(filePath, content) {
|
|
3132
|
-
try {
|
|
3133
|
-
const result = await format(path.basename(filePath), content, formatOptions);
|
|
3134
|
-
if (result.errors && result.errors.length > 0) return null;
|
|
3135
|
-
return result.code;
|
|
3136
|
-
} catch {
|
|
3137
|
-
return null;
|
|
3138
|
-
}
|
|
2740
|
+
if (hasUltracite) await setupUltracite(config, hasHusky);
|
|
3139
2741
|
}
|
|
3140
2742
|
|
|
3141
2743
|
//#endregion
|
|
3142
|
-
//#region src/utils/
|
|
3143
|
-
const
|
|
3144
|
-
|
|
3145
|
-
".
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
const
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
try {
|
|
3154
|
-
await fs.ensureDir(path.dirname(destPath));
|
|
3155
|
-
if (isBinaryFile(srcPath) && !srcPath.endsWith(".hbs")) {
|
|
3156
|
-
await fs.copy(srcPath, destPath);
|
|
3157
|
-
return;
|
|
3158
|
-
}
|
|
3159
|
-
let content;
|
|
3160
|
-
if (srcPath.endsWith(".hbs")) {
|
|
3161
|
-
const templateContent = await fs.readFile(srcPath, "utf-8");
|
|
3162
|
-
content = handlebars.compile(templateContent)(context);
|
|
3163
|
-
} else content = await fs.readFile(srcPath, "utf-8");
|
|
3164
|
-
try {
|
|
3165
|
-
const formattedContent = await formatFile(destPath, content);
|
|
3166
|
-
if (formattedContent) content = formattedContent;
|
|
3167
|
-
} catch (formatError) {
|
|
3168
|
-
consola.debug(`Failed to format ${destPath}:`, formatError);
|
|
3169
|
-
}
|
|
3170
|
-
await fs.writeFile(destPath, content);
|
|
3171
|
-
} catch (error) {
|
|
3172
|
-
consola.error(`Error processing template ${srcPath}:`, error);
|
|
3173
|
-
throw new Error(`Failed to process template ${srcPath}`);
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
handlebars.registerHelper("eq", (a, b) => a === b);
|
|
3177
|
-
handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
3178
|
-
handlebars.registerHelper("and", (...args) => {
|
|
3179
|
-
return args.slice(0, -1).every((value) => value);
|
|
3180
|
-
});
|
|
3181
|
-
handlebars.registerHelper("or", (...args) => {
|
|
3182
|
-
return args.slice(0, -1).some((value) => value);
|
|
3183
|
-
});
|
|
3184
|
-
handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
|
|
3185
|
-
|
|
3186
|
-
//#endregion
|
|
3187
|
-
//#region src/helpers/core/template-manager.ts
|
|
3188
|
-
async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, context, overwrite = true, ignorePatterns) {
|
|
3189
|
-
const sourceFiles = await glob(sourcePattern, {
|
|
3190
|
-
cwd: baseSourceDir,
|
|
3191
|
-
dot: true,
|
|
3192
|
-
onlyFiles: true,
|
|
3193
|
-
absolute: false,
|
|
3194
|
-
ignore: ignorePatterns
|
|
3195
|
-
});
|
|
3196
|
-
for (const relativeSrcPath of sourceFiles) {
|
|
3197
|
-
const srcPath = path.join(baseSourceDir, relativeSrcPath);
|
|
3198
|
-
let relativeDestPath = relativeSrcPath;
|
|
3199
|
-
if (relativeSrcPath.endsWith(".hbs")) relativeDestPath = relativeSrcPath.slice(0, -4);
|
|
3200
|
-
const basename = path.basename(relativeDestPath);
|
|
3201
|
-
if (basename === "_gitignore") relativeDestPath = path.join(path.dirname(relativeDestPath), ".gitignore");
|
|
3202
|
-
else if (basename === "_npmrc") relativeDestPath = path.join(path.dirname(relativeDestPath), ".npmrc");
|
|
3203
|
-
const destPath = path.join(destDir, relativeDestPath);
|
|
3204
|
-
await fs.ensureDir(path.dirname(destPath));
|
|
3205
|
-
if (!overwrite && await fs.pathExists(destPath)) continue;
|
|
3206
|
-
await processTemplate(srcPath, destPath, context);
|
|
3207
|
-
}
|
|
3208
|
-
}
|
|
3209
|
-
async function copyBaseTemplate(projectDir, context) {
|
|
3210
|
-
await processAndCopyFiles(["**/*"], path.join(PKG_ROOT, "templates/base"), projectDir, context);
|
|
3211
|
-
}
|
|
3212
|
-
async function setupFrontendTemplates(projectDir, context) {
|
|
3213
|
-
const hasReactWeb = context.frontend.some((f) => [
|
|
3214
|
-
"tanstack-router",
|
|
3215
|
-
"react-router",
|
|
3216
|
-
"tanstack-start",
|
|
3217
|
-
"next"
|
|
3218
|
-
].includes(f));
|
|
3219
|
-
const hasNuxtWeb = context.frontend.includes("nuxt");
|
|
3220
|
-
const hasSvelteWeb = context.frontend.includes("svelte");
|
|
3221
|
-
const hasSolidWeb = context.frontend.includes("solid");
|
|
3222
|
-
const hasNativeBare = context.frontend.includes("native-bare");
|
|
3223
|
-
const hasNativeUniwind = context.frontend.includes("native-uniwind");
|
|
3224
|
-
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
3225
|
-
const isConvex = context.backend === "convex";
|
|
3226
|
-
if (hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) {
|
|
3227
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3228
|
-
await fs.ensureDir(webAppDir);
|
|
3229
|
-
if (hasReactWeb) {
|
|
3230
|
-
const webBaseDir = path.join(PKG_ROOT, "templates/frontend/react/web-base");
|
|
3231
|
-
if (await fs.pathExists(webBaseDir)) await processAndCopyFiles("**/*", webBaseDir, webAppDir, context);
|
|
3232
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3233
|
-
"tanstack-router",
|
|
3234
|
-
"react-router",
|
|
3235
|
-
"tanstack-start",
|
|
3236
|
-
"next"
|
|
3237
|
-
].includes(f));
|
|
3238
|
-
if (reactFramework) {
|
|
3239
|
-
const frameworkSrcDir = path.join(PKG_ROOT, `templates/frontend/react/${reactFramework}`);
|
|
3240
|
-
if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, webAppDir, context);
|
|
3241
|
-
if (!isConvex && context.api !== "none") {
|
|
3242
|
-
const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
|
|
3243
|
-
if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
|
|
3244
|
-
}
|
|
3245
|
-
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start") && context.api !== "none") {
|
|
3246
|
-
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/${reactFramework}`);
|
|
3247
|
-
if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
} else if (hasNuxtWeb) {
|
|
3251
|
-
const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
|
|
3252
|
-
if (await fs.pathExists(nuxtBaseDir)) await processAndCopyFiles("**/*", nuxtBaseDir, webAppDir, context);
|
|
3253
|
-
if (!isConvex && context.api === "orpc") {
|
|
3254
|
-
const apiWebNuxtDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/nuxt`);
|
|
3255
|
-
if (await fs.pathExists(apiWebNuxtDir)) await processAndCopyFiles("**/*", apiWebNuxtDir, webAppDir, context);
|
|
3256
|
-
}
|
|
3257
|
-
} else if (hasSvelteWeb) {
|
|
3258
|
-
const svelteBaseDir = path.join(PKG_ROOT, "templates/frontend/svelte");
|
|
3259
|
-
if (await fs.pathExists(svelteBaseDir)) await processAndCopyFiles("**/*", svelteBaseDir, webAppDir, context);
|
|
3260
|
-
if (!isConvex && context.api === "orpc") {
|
|
3261
|
-
const apiWebSvelteDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/svelte`);
|
|
3262
|
-
if (await fs.pathExists(apiWebSvelteDir)) await processAndCopyFiles("**/*", apiWebSvelteDir, webAppDir, context);
|
|
3263
|
-
}
|
|
3264
|
-
} else if (hasSolidWeb) {
|
|
3265
|
-
const solidBaseDir = path.join(PKG_ROOT, "templates/frontend/solid");
|
|
3266
|
-
if (await fs.pathExists(solidBaseDir)) await processAndCopyFiles("**/*", solidBaseDir, webAppDir, context);
|
|
3267
|
-
if (!isConvex && context.api === "orpc") {
|
|
3268
|
-
const apiWebSolidDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/solid`);
|
|
3269
|
-
if (await fs.pathExists(apiWebSolidDir)) await processAndCopyFiles("**/*", apiWebSolidDir, webAppDir, context);
|
|
3270
|
-
}
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
if (hasNativeBare || hasNativeUniwind || hasUnistyles) {
|
|
3274
|
-
const nativeAppDir = path.join(projectDir, "apps/native");
|
|
3275
|
-
await fs.ensureDir(nativeAppDir);
|
|
3276
|
-
const nativeBaseCommonDir = path.join(PKG_ROOT, "templates/frontend/native/base");
|
|
3277
|
-
if (await fs.pathExists(nativeBaseCommonDir)) await processAndCopyFiles("**/*", nativeBaseCommonDir, nativeAppDir, context);
|
|
3278
|
-
let nativeFrameworkPath = "";
|
|
3279
|
-
if (hasNativeBare) nativeFrameworkPath = "bare";
|
|
3280
|
-
else if (hasNativeUniwind) nativeFrameworkPath = "uniwind";
|
|
3281
|
-
else if (hasUnistyles) nativeFrameworkPath = "unistyles";
|
|
3282
|
-
const nativeSpecificDir = path.join(PKG_ROOT, `templates/frontend/native/${nativeFrameworkPath}`);
|
|
3283
|
-
if (await fs.pathExists(nativeSpecificDir)) await processAndCopyFiles("**/*", nativeSpecificDir, nativeAppDir, context, true);
|
|
3284
|
-
if (!isConvex && (context.api === "trpc" || context.api === "orpc")) {
|
|
3285
|
-
const apiNativeSrcDir = path.join(PKG_ROOT, `templates/api/${context.api}/native`);
|
|
3286
|
-
if (await fs.pathExists(apiNativeSrcDir)) await processAndCopyFiles("**/*", apiNativeSrcDir, nativeAppDir, context);
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
async function setupApiPackage$1(projectDir, context) {
|
|
3291
|
-
if (context.api === "none") return;
|
|
3292
|
-
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
3293
|
-
await fs.ensureDir(apiPackageDir);
|
|
3294
|
-
const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
|
|
3295
|
-
if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
|
|
3296
|
-
}
|
|
3297
|
-
async function setupConfigPackage(projectDir, context) {
|
|
3298
|
-
const configPackageDir = path.join(projectDir, "packages/config");
|
|
3299
|
-
await fs.ensureDir(configPackageDir);
|
|
3300
|
-
const configBaseDir = path.join(PKG_ROOT, "templates/packages/config");
|
|
3301
|
-
if (await fs.pathExists(configBaseDir)) await processAndCopyFiles("**/*", configBaseDir, configPackageDir, context);
|
|
3302
|
-
}
|
|
3303
|
-
async function setupDbPackage$1(projectDir, context) {
|
|
3304
|
-
if (context.database === "none" || context.orm === "none") return;
|
|
3305
|
-
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3306
|
-
await fs.ensureDir(dbPackageDir);
|
|
3307
|
-
const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
|
|
3308
|
-
if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
|
|
3309
|
-
const dbOrmBaseDir = path.join(PKG_ROOT, `templates/db/${context.orm}/base`);
|
|
3310
|
-
if (await fs.pathExists(dbOrmBaseDir)) await processAndCopyFiles("**/*", dbOrmBaseDir, dbPackageDir, context);
|
|
3311
|
-
const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
|
|
3312
|
-
if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
|
|
3313
|
-
}
|
|
3314
|
-
async function setupConvexBackend(projectDir, context) {
|
|
3315
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3316
|
-
if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
|
|
3317
|
-
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
3318
|
-
const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
|
|
3319
|
-
await fs.ensureDir(convexBackendDestDir);
|
|
3320
|
-
if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
|
|
3321
|
-
}
|
|
3322
|
-
async function setupServerApp(projectDir, context) {
|
|
3323
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3324
|
-
await fs.ensureDir(serverAppDir);
|
|
3325
|
-
const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
|
|
3326
|
-
if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
|
|
3327
|
-
const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
|
|
3328
|
-
if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
|
|
3329
|
-
}
|
|
3330
|
-
async function setupEnvPackage$1(projectDir, context) {
|
|
3331
|
-
const hasWebFrontend$1 = context.frontend.some((f) => [
|
|
3332
|
-
"tanstack-router",
|
|
3333
|
-
"react-router",
|
|
3334
|
-
"tanstack-start",
|
|
3335
|
-
"next",
|
|
3336
|
-
"nuxt",
|
|
3337
|
-
"svelte",
|
|
3338
|
-
"solid"
|
|
3339
|
-
].includes(f));
|
|
3340
|
-
const hasNative = context.frontend.some((f) => [
|
|
3341
|
-
"native-bare",
|
|
3342
|
-
"native-uniwind",
|
|
3343
|
-
"native-unistyles"
|
|
3344
|
-
].includes(f));
|
|
3345
|
-
if (!hasWebFrontend$1 && !hasNative && context.backend === "none") return;
|
|
3346
|
-
const envPackageDir = path.join(projectDir, "packages/env");
|
|
3347
|
-
await fs.ensureDir(envPackageDir);
|
|
3348
|
-
const envBaseDir = path.join(PKG_ROOT, "templates/packages/env");
|
|
3349
|
-
const packageJsonSrc = path.join(envBaseDir, "package.json.hbs");
|
|
3350
|
-
if (await fs.pathExists(packageJsonSrc)) await processAndCopyFiles("package.json.hbs", envBaseDir, envPackageDir, context);
|
|
3351
|
-
const tsconfigSrc = path.join(envBaseDir, "tsconfig.json.hbs");
|
|
3352
|
-
if (await fs.pathExists(tsconfigSrc)) await processAndCopyFiles("tsconfig.json.hbs", envBaseDir, envPackageDir, context);
|
|
3353
|
-
const needsServerEnv = context.backend !== "none" && context.backend !== "convex";
|
|
3354
|
-
if (needsServerEnv) {
|
|
3355
|
-
const serverSrc = path.join(envBaseDir, "src/server.ts.hbs");
|
|
3356
|
-
if (await fs.pathExists(serverSrc)) {
|
|
3357
|
-
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
3358
|
-
await processAndCopyFiles("src/server.ts.hbs", envBaseDir, envPackageDir, context);
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
if (hasWebFrontend$1) {
|
|
3362
|
-
const webSrc = path.join(envBaseDir, "src/web.ts.hbs");
|
|
3363
|
-
if (await fs.pathExists(webSrc)) {
|
|
3364
|
-
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
3365
|
-
await processAndCopyFiles("src/web.ts.hbs", envBaseDir, envPackageDir, context);
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
if (hasNative) {
|
|
3369
|
-
const nativeSrc = path.join(envBaseDir, "src/native.ts.hbs");
|
|
3370
|
-
if (await fs.pathExists(nativeSrc)) {
|
|
3371
|
-
await fs.ensureDir(path.join(envPackageDir, "src"));
|
|
3372
|
-
await processAndCopyFiles("src/native.ts.hbs", envBaseDir, envPackageDir, context);
|
|
3373
|
-
}
|
|
3374
|
-
}
|
|
3375
|
-
const packageJsonPath = path.join(envPackageDir, "package.json");
|
|
3376
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
3377
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
3378
|
-
const exports = {};
|
|
3379
|
-
if (needsServerEnv) exports["./server"] = "./src/server.ts";
|
|
3380
|
-
if (hasWebFrontend$1) exports["./web"] = "./src/web.ts";
|
|
3381
|
-
if (hasNative) exports["./native"] = "./src/native.ts";
|
|
3382
|
-
packageJson.exports = exports;
|
|
3383
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
async function setupBackendFramework(projectDir, context) {
|
|
3387
|
-
await setupConfigPackage(projectDir, context);
|
|
3388
|
-
await setupEnvPackage$1(projectDir, context);
|
|
3389
|
-
if (context.backend === "none") return;
|
|
3390
|
-
if (context.backend === "convex") {
|
|
3391
|
-
await setupConvexBackend(projectDir, context);
|
|
3392
|
-
return;
|
|
3393
|
-
}
|
|
3394
|
-
if (context.backend === "self") {
|
|
3395
|
-
await setupApiPackage$1(projectDir, context);
|
|
3396
|
-
await setupDbPackage$1(projectDir, context);
|
|
3397
|
-
return;
|
|
3398
|
-
}
|
|
3399
|
-
await setupServerApp(projectDir, context);
|
|
3400
|
-
await setupApiPackage$1(projectDir, context);
|
|
3401
|
-
await setupDbPackage$1(projectDir, context);
|
|
3402
|
-
}
|
|
3403
|
-
async function setupAuthTemplate(projectDir, context) {
|
|
3404
|
-
if (!context.auth || context.auth === "none") return;
|
|
3405
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3406
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3407
|
-
const nativeAppDir = path.join(projectDir, "apps/native");
|
|
3408
|
-
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
3409
|
-
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
3410
|
-
const nativeAppDirExists = await fs.pathExists(nativeAppDir);
|
|
3411
|
-
const hasReactWeb = context.frontend.some((f) => [
|
|
3412
|
-
"tanstack-router",
|
|
3413
|
-
"react-router",
|
|
3414
|
-
"tanstack-start",
|
|
3415
|
-
"next"
|
|
3416
|
-
].includes(f));
|
|
3417
|
-
const hasNuxtWeb = context.frontend.includes("nuxt");
|
|
3418
|
-
const hasSvelteWeb = context.frontend.includes("svelte");
|
|
3419
|
-
const hasSolidWeb = context.frontend.includes("solid");
|
|
3420
|
-
const hasNativeBare = context.frontend.includes("native-bare");
|
|
3421
|
-
const hasUniwind = context.frontend.includes("native-uniwind");
|
|
3422
|
-
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
3423
|
-
const hasNative = hasNativeBare || hasUniwind || hasUnistyles;
|
|
3424
|
-
const authProvider = context.auth;
|
|
3425
|
-
if (context.backend === "convex" && authProvider === "clerk") {
|
|
3426
|
-
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
3427
|
-
const convexClerkBackendSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/backend");
|
|
3428
|
-
if (await fs.pathExists(convexClerkBackendSrc)) {
|
|
3429
|
-
await fs.ensureDir(convexBackendDestDir);
|
|
3430
|
-
await processAndCopyFiles("**/*", convexClerkBackendSrc, convexBackendDestDir, context);
|
|
3431
|
-
}
|
|
3432
|
-
if (webAppDirExists) {
|
|
3433
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3434
|
-
"tanstack-router",
|
|
3435
|
-
"react-router",
|
|
3436
|
-
"tanstack-start",
|
|
3437
|
-
"next"
|
|
3438
|
-
].includes(f));
|
|
3439
|
-
if (reactFramework) {
|
|
3440
|
-
const convexClerkWebSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/web/react/${reactFramework}`);
|
|
3441
|
-
if (await fs.pathExists(convexClerkWebSrc)) await processAndCopyFiles("**/*", convexClerkWebSrc, webAppDir, context);
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
if (nativeAppDirExists) {
|
|
3445
|
-
const convexClerkNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/native/base");
|
|
3446
|
-
if (await fs.pathExists(convexClerkNativeBaseSrc)) await processAndCopyFiles("**/*", convexClerkNativeBaseSrc, nativeAppDir, context);
|
|
3447
|
-
let nativeFrameworkPath = "";
|
|
3448
|
-
if (hasNativeBare) nativeFrameworkPath = "bare";
|
|
3449
|
-
else if (hasUniwind) nativeFrameworkPath = "uniwind";
|
|
3450
|
-
else if (hasUnistyles) nativeFrameworkPath = "unistyles";
|
|
3451
|
-
if (nativeFrameworkPath) {
|
|
3452
|
-
const convexClerkNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/native/${nativeFrameworkPath}`);
|
|
3453
|
-
if (await fs.pathExists(convexClerkNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexClerkNativeFrameworkSrc, nativeAppDir, context);
|
|
3454
|
-
}
|
|
3455
|
-
}
|
|
3456
|
-
return;
|
|
3457
|
-
}
|
|
3458
|
-
if (context.backend === "convex" && authProvider === "better-auth") {
|
|
3459
|
-
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
3460
|
-
const convexBetterAuthBackendSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/backend");
|
|
3461
|
-
if (await fs.pathExists(convexBetterAuthBackendSrc)) {
|
|
3462
|
-
await fs.ensureDir(convexBackendDestDir);
|
|
3463
|
-
await processAndCopyFiles("**/*", convexBetterAuthBackendSrc, convexBackendDestDir, context);
|
|
3464
|
-
}
|
|
3465
|
-
if (webAppDirExists && hasReactWeb) {
|
|
3466
|
-
const convexBetterAuthWebBaseSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/web/react/base");
|
|
3467
|
-
if (await fs.pathExists(convexBetterAuthWebBaseSrc)) await processAndCopyFiles("**/*", convexBetterAuthWebBaseSrc, webAppDir, context);
|
|
3468
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3469
|
-
"tanstack-router",
|
|
3470
|
-
"react-router",
|
|
3471
|
-
"tanstack-start",
|
|
3472
|
-
"next"
|
|
3473
|
-
].includes(f));
|
|
3474
|
-
if (reactFramework) {
|
|
3475
|
-
const convexBetterAuthWebSrc = path.join(PKG_ROOT, `templates/auth/better-auth/convex/web/react/${reactFramework}`);
|
|
3476
|
-
if (await fs.pathExists(convexBetterAuthWebSrc)) await processAndCopyFiles("**/*", convexBetterAuthWebSrc, webAppDir, context);
|
|
3477
|
-
}
|
|
3478
|
-
}
|
|
3479
|
-
if (nativeAppDirExists) {
|
|
3480
|
-
const convexBetterAuthNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/better-auth/convex/native/base");
|
|
3481
|
-
if (await fs.pathExists(convexBetterAuthNativeBaseSrc)) await processAndCopyFiles("**/*", convexBetterAuthNativeBaseSrc, nativeAppDir, context);
|
|
3482
|
-
let nativeFrameworkPath = "";
|
|
3483
|
-
if (hasNativeBare) nativeFrameworkPath = "bare";
|
|
3484
|
-
else if (hasUniwind) nativeFrameworkPath = "uniwind";
|
|
3485
|
-
else if (hasUnistyles) nativeFrameworkPath = "unistyles";
|
|
3486
|
-
if (nativeFrameworkPath) {
|
|
3487
|
-
const convexBetterAuthNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/better-auth/convex/native/${nativeFrameworkPath}`);
|
|
3488
|
-
if (await fs.pathExists(convexBetterAuthNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexBetterAuthNativeFrameworkSrc, nativeAppDir, context);
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
3491
|
-
return;
|
|
3492
|
-
}
|
|
3493
|
-
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
3494
|
-
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
3495
|
-
await fs.ensureDir(authPackageDir);
|
|
3496
|
-
const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
|
|
3497
|
-
if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
|
|
3498
|
-
if (context.orm !== "none" && context.database !== "none") {
|
|
3499
|
-
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3500
|
-
await fs.ensureDir(dbPackageDir);
|
|
3501
|
-
const orm = context.orm;
|
|
3502
|
-
const db = context.database;
|
|
3503
|
-
let authDbSrc = "";
|
|
3504
|
-
if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
|
|
3505
|
-
else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
|
|
3506
|
-
else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
|
|
3507
|
-
if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
|
|
3508
|
-
}
|
|
3509
|
-
}
|
|
3510
|
-
if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
|
|
3511
|
-
if (hasReactWeb) {
|
|
3512
|
-
const authWebBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/base`);
|
|
3513
|
-
if (await fs.pathExists(authWebBaseSrc)) await processAndCopyFiles("**/*", authWebBaseSrc, webAppDir, context);
|
|
3514
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3515
|
-
"tanstack-router",
|
|
3516
|
-
"react-router",
|
|
3517
|
-
"tanstack-start",
|
|
3518
|
-
"next"
|
|
3519
|
-
].includes(f));
|
|
3520
|
-
if (reactFramework) {
|
|
3521
|
-
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
3522
|
-
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
3523
|
-
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start")) {
|
|
3524
|
-
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/${reactFramework}`);
|
|
3525
|
-
if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
|
|
3526
|
-
}
|
|
3527
|
-
}
|
|
3528
|
-
} else if (hasNuxtWeb) {
|
|
3529
|
-
const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
|
|
3530
|
-
if (await fs.pathExists(authWebNuxtSrc)) await processAndCopyFiles("**/*", authWebNuxtSrc, webAppDir, context);
|
|
3531
|
-
} else if (hasSvelteWeb) {
|
|
3532
|
-
const authWebSvelteSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/svelte`);
|
|
3533
|
-
if (await fs.pathExists(authWebSvelteSrc)) await processAndCopyFiles("**/*", authWebSvelteSrc, webAppDir, context);
|
|
3534
|
-
} else if (hasSolidWeb) {
|
|
3535
|
-
const authWebSolidSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/solid`);
|
|
3536
|
-
if (await fs.pathExists(authWebSolidSrc)) await processAndCopyFiles("**/*", authWebSolidSrc, webAppDir, context);
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
if (hasNative && nativeAppDirExists) {
|
|
3540
|
-
const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/base`);
|
|
3541
|
-
if (await fs.pathExists(authNativeBaseSrc)) await processAndCopyFiles("**/*", authNativeBaseSrc, nativeAppDir, context);
|
|
3542
|
-
let nativeFrameworkAuthPath = "";
|
|
3543
|
-
if (hasNativeBare) nativeFrameworkAuthPath = "bare";
|
|
3544
|
-
else if (hasUniwind) nativeFrameworkAuthPath = "uniwind";
|
|
3545
|
-
else if (hasUnistyles) nativeFrameworkAuthPath = "unistyles";
|
|
3546
|
-
if (nativeFrameworkAuthPath) {
|
|
3547
|
-
const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/${nativeFrameworkAuthPath}`);
|
|
3548
|
-
if (await fs.pathExists(authNativeFrameworkSrc)) await processAndCopyFiles("**/*", authNativeFrameworkSrc, nativeAppDir, context);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
}
|
|
3552
|
-
async function setupPaymentsTemplate(projectDir, context) {
|
|
3553
|
-
if (!context.payments || context.payments === "none") return;
|
|
3554
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3555
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3556
|
-
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
3557
|
-
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
3558
|
-
if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
|
|
3559
|
-
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
3560
|
-
await fs.ensureDir(authPackageDir);
|
|
3561
|
-
const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
|
|
3562
|
-
if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
|
|
3563
|
-
}
|
|
3564
|
-
const hasReactWeb = context.frontend.some((f) => [
|
|
3565
|
-
"tanstack-router",
|
|
3566
|
-
"react-router",
|
|
3567
|
-
"tanstack-start",
|
|
3568
|
-
"next"
|
|
3569
|
-
].includes(f));
|
|
3570
|
-
const hasNuxtWeb = context.frontend.includes("nuxt");
|
|
3571
|
-
const hasSvelteWeb = context.frontend.includes("svelte");
|
|
3572
|
-
const hasSolidWeb = context.frontend.includes("solid");
|
|
3573
|
-
if (webAppDirExists && (hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb)) {
|
|
3574
|
-
if (hasReactWeb) {
|
|
3575
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3576
|
-
"tanstack-router",
|
|
3577
|
-
"react-router",
|
|
3578
|
-
"tanstack-start",
|
|
3579
|
-
"next"
|
|
3580
|
-
].includes(f));
|
|
3581
|
-
if (reactFramework) {
|
|
3582
|
-
const paymentsWebSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/react/${reactFramework}`);
|
|
3583
|
-
if (await fs.pathExists(paymentsWebSrc)) await processAndCopyFiles("**/*", paymentsWebSrc, webAppDir, context);
|
|
3584
|
-
}
|
|
3585
|
-
} else if (hasNuxtWeb) {
|
|
3586
|
-
const paymentsWebNuxtSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/nuxt`);
|
|
3587
|
-
if (await fs.pathExists(paymentsWebNuxtSrc)) await processAndCopyFiles("**/*", paymentsWebNuxtSrc, webAppDir, context);
|
|
3588
|
-
} else if (hasSvelteWeb) {
|
|
3589
|
-
const paymentsWebSvelteSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/svelte`);
|
|
3590
|
-
if (await fs.pathExists(paymentsWebSvelteSrc)) await processAndCopyFiles("**/*", paymentsWebSvelteSrc, webAppDir, context);
|
|
3591
|
-
} else if (hasSolidWeb) {
|
|
3592
|
-
const paymentsWebSolidSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/web/solid`);
|
|
3593
|
-
if (await fs.pathExists(paymentsWebSolidSrc)) await processAndCopyFiles("**/*", paymentsWebSolidSrc, webAppDir, context);
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
async function setupAddonsTemplate(projectDir, context) {
|
|
3598
|
-
if (!context.addons || context.addons.length === 0) return;
|
|
3599
|
-
for (const addon of context.addons) {
|
|
3600
|
-
if (addon === "none") continue;
|
|
3601
|
-
let addonSrcDir = path.join(PKG_ROOT, `templates/addons/${addon}`);
|
|
3602
|
-
let addonDestDir = projectDir;
|
|
3603
|
-
if (addon === "pwa") {
|
|
3604
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3605
|
-
if (!await fs.pathExists(webAppDir)) continue;
|
|
3606
|
-
addonDestDir = webAppDir;
|
|
3607
|
-
if (context.frontend.includes("next")) addonSrcDir = path.join(PKG_ROOT, "templates/addons/pwa/apps/web/next");
|
|
3608
|
-
else if (context.frontend.some((f) => [
|
|
3609
|
-
"tanstack-router",
|
|
3610
|
-
"react-router",
|
|
3611
|
-
"solid"
|
|
3612
|
-
].includes(f))) addonSrcDir = path.join(PKG_ROOT, "templates/addons/pwa/apps/web/vite");
|
|
3613
|
-
else continue;
|
|
3614
|
-
}
|
|
3615
|
-
if (await fs.pathExists(addonSrcDir)) await processAndCopyFiles("**/*", addonSrcDir, addonDestDir, context);
|
|
3616
|
-
}
|
|
3617
|
-
}
|
|
3618
|
-
async function setupExamplesTemplate(projectDir, context) {
|
|
3619
|
-
if (!context.examples || context.examples.length === 0 || context.examples[0] === "none") return;
|
|
3620
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3621
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3622
|
-
const serverAppDirExists = await fs.pathExists(serverAppDir);
|
|
3623
|
-
const webAppDirExists = await fs.pathExists(webAppDir);
|
|
3624
|
-
const nativeAppDir = path.join(projectDir, "apps/native");
|
|
3625
|
-
const nativeAppDirExists = await fs.pathExists(nativeAppDir);
|
|
3626
|
-
const hasReactWeb = context.frontend.some((f) => [
|
|
3627
|
-
"tanstack-router",
|
|
3628
|
-
"react-router",
|
|
3629
|
-
"tanstack-start",
|
|
3630
|
-
"next"
|
|
3631
|
-
].includes(f));
|
|
3632
|
-
const hasNuxtWeb = context.frontend.includes("nuxt");
|
|
3633
|
-
const hasSvelteWeb = context.frontend.includes("svelte");
|
|
3634
|
-
const hasSolidWeb = context.frontend.includes("solid");
|
|
3635
|
-
for (const example of context.examples) {
|
|
3636
|
-
if (example === "none") continue;
|
|
3637
|
-
const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
|
|
3638
|
-
if (context.backend === "convex") {
|
|
3639
|
-
const convexBackendDestDir = path.join(projectDir, "packages/backend");
|
|
3640
|
-
const convexExampleSrc = path.join(exampleBaseDir, "convex/packages/backend");
|
|
3641
|
-
if (await fs.pathExists(convexExampleSrc)) await processAndCopyFiles("**/*", convexExampleSrc, convexBackendDestDir, context, false);
|
|
3642
|
-
} else if ((serverAppDirExists || context.backend === "self") && context.backend !== "none") {
|
|
3643
|
-
const exampleServerSrc = path.join(exampleBaseDir, "server");
|
|
3644
|
-
if (context.api !== "none") {
|
|
3645
|
-
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
3646
|
-
await fs.ensureDir(apiPackageDir);
|
|
3647
|
-
const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
|
|
3648
|
-
if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
|
|
3649
|
-
}
|
|
3650
|
-
if (context.orm !== "none" && context.database !== "none") {
|
|
3651
|
-
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3652
|
-
await fs.ensureDir(dbPackageDir);
|
|
3653
|
-
const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
|
|
3654
|
-
if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
if (webAppDirExists) {
|
|
3658
|
-
if (hasReactWeb) {
|
|
3659
|
-
const exampleWebSrc = path.join(exampleBaseDir, "web/react");
|
|
3660
|
-
if (await fs.pathExists(exampleWebSrc)) {
|
|
3661
|
-
const reactFramework = context.frontend.find((f) => [
|
|
3662
|
-
"next",
|
|
3663
|
-
"react-router",
|
|
3664
|
-
"tanstack-router",
|
|
3665
|
-
"tanstack-start"
|
|
3666
|
-
].includes(f));
|
|
3667
|
-
if (reactFramework) {
|
|
3668
|
-
const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
|
|
3669
|
-
if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
|
|
3670
|
-
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start")) {
|
|
3671
|
-
const exampleFullstackSrc = path.join(exampleBaseDir, `fullstack/${reactFramework}`);
|
|
3672
|
-
if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
|
|
3673
|
-
}
|
|
3674
|
-
}
|
|
3675
|
-
}
|
|
3676
|
-
} else if (hasNuxtWeb) {
|
|
3677
|
-
const exampleWebNuxtSrc = path.join(exampleBaseDir, "web/nuxt");
|
|
3678
|
-
if (await fs.pathExists(exampleWebNuxtSrc)) await processAndCopyFiles("**/*", exampleWebNuxtSrc, webAppDir, context, false);
|
|
3679
|
-
} else if (hasSvelteWeb) {
|
|
3680
|
-
const exampleWebSvelteSrc = path.join(exampleBaseDir, "web/svelte");
|
|
3681
|
-
if (await fs.pathExists(exampleWebSvelteSrc)) await processAndCopyFiles("**/*", exampleWebSvelteSrc, webAppDir, context, false);
|
|
3682
|
-
} else if (hasSolidWeb) {
|
|
3683
|
-
const exampleWebSolidSrc = path.join(exampleBaseDir, "web/solid");
|
|
3684
|
-
if (await fs.pathExists(exampleWebSolidSrc)) await processAndCopyFiles("**/*", exampleWebSolidSrc, webAppDir, context, false);
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
if (nativeAppDirExists) {
|
|
3688
|
-
const hasNativeBare = context.frontend.includes("native-bare");
|
|
3689
|
-
const hasUniwind = context.frontend.includes("native-uniwind");
|
|
3690
|
-
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
3691
|
-
if (hasNativeBare || hasUniwind || hasUnistyles) {
|
|
3692
|
-
let nativeFramework = "";
|
|
3693
|
-
if (hasNativeBare) nativeFramework = "bare";
|
|
3694
|
-
else if (hasUniwind) nativeFramework = "uniwind";
|
|
3695
|
-
else if (hasUnistyles) nativeFramework = "unistyles";
|
|
3696
|
-
const exampleNativeSrc = path.join(exampleBaseDir, `native/${nativeFramework}`);
|
|
3697
|
-
if (await fs.pathExists(exampleNativeSrc)) await processAndCopyFiles("**/*", exampleNativeSrc, nativeAppDir, context, false);
|
|
3698
|
-
}
|
|
3699
|
-
}
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
async function handleExtras(projectDir, context) {
|
|
3703
|
-
const extrasDir = path.join(PKG_ROOT, "templates/extras");
|
|
3704
|
-
const hasNativeBare = context.frontend.includes("native-bare");
|
|
3705
|
-
const hasUniwind = context.frontend.includes("native-uniwind");
|
|
3706
|
-
const hasUnistyles = context.frontend.includes("native-unistyles");
|
|
3707
|
-
const hasNative = hasNativeBare || hasUniwind || hasUnistyles;
|
|
3708
|
-
if (context.packageManager === "pnpm") {
|
|
3709
|
-
const pnpmWorkspaceSrc = path.join(extrasDir, "pnpm-workspace.yaml");
|
|
3710
|
-
const pnpmWorkspaceDest = path.join(projectDir, "pnpm-workspace.yaml");
|
|
3711
|
-
if (await fs.pathExists(pnpmWorkspaceSrc)) await processTemplate(pnpmWorkspaceSrc, pnpmWorkspaceDest, context);
|
|
3712
|
-
}
|
|
3713
|
-
if (context.packageManager === "bun") {
|
|
3714
|
-
const bunfigSrc = path.join(extrasDir, "bunfig.toml.hbs");
|
|
3715
|
-
if (await fs.pathExists(bunfigSrc)) await processAndCopyFiles("bunfig.toml.hbs", extrasDir, projectDir, context);
|
|
3716
|
-
}
|
|
3717
|
-
if (context.packageManager === "pnpm" && (hasNative || context.frontend.includes("nuxt"))) {
|
|
3718
|
-
const npmrcTemplateSrc = path.join(extrasDir, "_npmrc.hbs");
|
|
3719
|
-
if (await fs.pathExists(npmrcTemplateSrc)) await processAndCopyFiles("_npmrc.hbs", extrasDir, projectDir, context);
|
|
3720
|
-
}
|
|
3721
|
-
}
|
|
3722
|
-
async function setupDockerComposeTemplates(projectDir, context) {
|
|
3723
|
-
if (context.dbSetup !== "docker" || context.database === "none") return;
|
|
3724
|
-
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
3725
|
-
const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
|
|
3726
|
-
if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
|
|
3727
|
-
}
|
|
3728
|
-
async function setupDeploymentTemplates(projectDir, context) {
|
|
3729
|
-
const isBackendSelf = context.backend === "self";
|
|
3730
|
-
if (context.webDeploy === "cloudflare" || context.serverDeploy === "cloudflare") {
|
|
3731
|
-
const infraTemplateSrc = path.join(PKG_ROOT, "templates/packages/infra");
|
|
3732
|
-
const infraDir = path.join(projectDir, "packages/infra");
|
|
3733
|
-
if (await fs.pathExists(infraTemplateSrc)) {
|
|
3734
|
-
await fs.ensureDir(infraDir);
|
|
3735
|
-
await processAndCopyFiles("package.json.hbs", infraTemplateSrc, infraDir, context);
|
|
3736
|
-
await processAndCopyFiles("alchemy.run.ts.hbs", infraTemplateSrc, infraDir, context);
|
|
3737
|
-
}
|
|
3738
|
-
if (!isBackendSelf) {
|
|
3739
|
-
const envTemplateSrc = path.join(PKG_ROOT, "templates/packages/env");
|
|
3740
|
-
const envDir = path.join(projectDir, "packages/env");
|
|
3741
|
-
const envDtsTemplatePath = path.join(envTemplateSrc, "env.d.ts.hbs");
|
|
3742
|
-
if (await fs.pathExists(envDtsTemplatePath)) await processTemplate(envDtsTemplatePath, path.join(envDir, "env.d.ts"), context);
|
|
3743
|
-
}
|
|
3744
|
-
}
|
|
3745
|
-
if (context.webDeploy !== "none" && context.webDeploy !== "cloudflare") {
|
|
3746
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3747
|
-
if (await fs.pathExists(webAppDir)) {
|
|
3748
|
-
const frontends = context.frontend;
|
|
3749
|
-
const templateMap = {
|
|
3750
|
-
"tanstack-router": "react/tanstack-router",
|
|
3751
|
-
"tanstack-start": "react/tanstack-start",
|
|
3752
|
-
"react-router": "react/react-router",
|
|
3753
|
-
solid: "solid",
|
|
3754
|
-
next: "react/next",
|
|
3755
|
-
nuxt: "nuxt",
|
|
3756
|
-
svelte: "svelte"
|
|
3757
|
-
};
|
|
3758
|
-
for (const f of frontends) if (templateMap[f]) {
|
|
3759
|
-
const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.webDeploy}/web/${templateMap[f]}`);
|
|
3760
|
-
if (await fs.pathExists(deployTemplateSrc)) await processAndCopyFiles("**/*", deployTemplateSrc, webAppDir, context);
|
|
3761
|
-
}
|
|
3762
|
-
}
|
|
3763
|
-
}
|
|
3764
|
-
if (context.serverDeploy !== "none" && context.serverDeploy !== "cloudflare" && !isBackendSelf) {
|
|
3765
|
-
const serverAppDir = path.join(projectDir, "apps/server");
|
|
3766
|
-
if (await fs.pathExists(serverAppDir)) {
|
|
3767
|
-
const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
|
|
3768
|
-
if (await fs.pathExists(deployTemplateSrc)) await processAndCopyFiles("**/*", deployTemplateSrc, serverAppDir, context);
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
|
-
//#endregion
|
|
3774
|
-
//#region src/helpers/core/add-addons.ts
|
|
3775
|
-
async function addAddonsToProject(input) {
|
|
3776
|
-
try {
|
|
3777
|
-
const projectDir = input.projectDir || process.cwd();
|
|
3778
|
-
if (!await isBetterTStackProject(projectDir)) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
|
|
3779
|
-
const detectedConfig = await detectProjectConfig(projectDir);
|
|
3780
|
-
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
3781
|
-
const config = {
|
|
3782
|
-
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
3783
|
-
projectDir,
|
|
3784
|
-
relativePath: ".",
|
|
3785
|
-
database: detectedConfig.database || "none",
|
|
3786
|
-
orm: detectedConfig.orm || "none",
|
|
3787
|
-
backend: detectedConfig.backend || "none",
|
|
3788
|
-
runtime: detectedConfig.runtime || "none",
|
|
3789
|
-
frontend: detectedConfig.frontend || [],
|
|
3790
|
-
addons: input.addons,
|
|
3791
|
-
examples: detectedConfig.examples || [],
|
|
3792
|
-
auth: detectedConfig.auth || "none",
|
|
3793
|
-
payments: detectedConfig.payments || "none",
|
|
3794
|
-
git: false,
|
|
3795
|
-
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
3796
|
-
install: input.install || false,
|
|
3797
|
-
dbSetup: detectedConfig.dbSetup || "none",
|
|
3798
|
-
api: detectedConfig.api || "none",
|
|
3799
|
-
webDeploy: detectedConfig.webDeploy || "none",
|
|
3800
|
-
serverDeploy: detectedConfig.serverDeploy || "none"
|
|
3801
|
-
};
|
|
3802
|
-
for (const addon of input.addons) {
|
|
3803
|
-
const { isCompatible, reason } = validateAddonCompatibility(addon, config.frontend);
|
|
3804
|
-
if (!isCompatible) exitWithError(reason || `${addon} addon is not compatible with current frontend configuration`);
|
|
3805
|
-
}
|
|
3806
|
-
await setupAddonsTemplate(projectDir, config);
|
|
3807
|
-
await setupAddons(config, true);
|
|
3808
|
-
const currentAddons = detectedConfig.addons || [];
|
|
3809
|
-
await updateBtsConfig(projectDir, { addons: [...new Set([...currentAddons, ...input.addons])] });
|
|
3810
|
-
if (config.install) await installDependencies({
|
|
3811
|
-
projectDir,
|
|
3812
|
-
packageManager: config.packageManager
|
|
3813
|
-
});
|
|
3814
|
-
else if (!input.suppressInstallMessage) log.info(pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`));
|
|
3815
|
-
} catch (error) {
|
|
3816
|
-
exitWithError(`Error adding addons: ${error instanceof Error ? error.message : String(error)}`);
|
|
3817
|
-
}
|
|
3818
|
-
}
|
|
3819
|
-
|
|
3820
|
-
//#endregion
|
|
3821
|
-
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
3822
|
-
async function setupNextAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3823
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3824
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3825
|
-
await addPackageDependency({
|
|
3826
|
-
dependencies: ["@opennextjs/cloudflare"],
|
|
3827
|
-
devDependencies: [
|
|
3828
|
-
"alchemy",
|
|
3829
|
-
"wrangler",
|
|
3830
|
-
"@cloudflare/workers-types"
|
|
3831
|
-
],
|
|
3832
|
-
projectDir: webAppDir
|
|
3833
|
-
});
|
|
3834
|
-
const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
|
|
3835
|
-
await fs.writeFile(openNextConfigPath, `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
3836
|
-
|
|
3837
|
-
export default defineCloudflareConfig({});
|
|
3838
|
-
`);
|
|
3839
|
-
const gitignorePath = path.join(webAppDir, ".gitignore");
|
|
3840
|
-
if (await fs.pathExists(gitignorePath)) {
|
|
3841
|
-
if (!(await fs.readFile(gitignorePath, "utf-8")).includes("wrangler.jsonc")) await fs.appendFile(gitignorePath, "\nwrangler.jsonc\n");
|
|
3842
|
-
} else await fs.writeFile(gitignorePath, "wrangler.jsonc\n");
|
|
3843
|
-
}
|
|
3844
|
-
|
|
3845
|
-
//#endregion
|
|
3846
|
-
//#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
|
|
3847
|
-
async function setupNuxtAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3848
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3849
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3850
|
-
await addPackageDependency({
|
|
3851
|
-
devDependencies: [
|
|
3852
|
-
"alchemy",
|
|
3853
|
-
"nitro-cloudflare-dev",
|
|
3854
|
-
"wrangler"
|
|
3855
|
-
],
|
|
3856
|
-
projectDir: webAppDir
|
|
3857
|
-
});
|
|
3858
|
-
const nuxtConfigPath = path.join(webAppDir, "nuxt.config.ts");
|
|
3859
|
-
if (!await fs.pathExists(nuxtConfigPath)) return;
|
|
3860
|
-
try {
|
|
3861
|
-
const project = new Project({ manipulationSettings: {
|
|
3862
|
-
indentationText: IndentationText.TwoSpaces,
|
|
3863
|
-
quoteKind: QuoteKind.Double
|
|
3864
|
-
} });
|
|
3865
|
-
project.addSourceFileAtPath(nuxtConfigPath);
|
|
3866
|
-
const exportAssignment = project.getSourceFileOrThrow(nuxtConfigPath).getExportAssignment((d) => !d.isExportEquals());
|
|
3867
|
-
if (!exportAssignment) return;
|
|
3868
|
-
const defineConfigCall = exportAssignment.getExpression();
|
|
3869
|
-
if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineNuxtConfig") return;
|
|
3870
|
-
let configObject = defineConfigCall.getArguments()[0];
|
|
3871
|
-
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
3872
|
-
if (Node.isObjectLiteralExpression(configObject)) {
|
|
3873
|
-
if (!configObject.getProperty("nitro")) configObject.addPropertyAssignment({
|
|
3874
|
-
name: "nitro",
|
|
3875
|
-
initializer: `{
|
|
3876
|
-
preset: "cloudflare_module",
|
|
3877
|
-
cloudflare: {
|
|
3878
|
-
deployConfig: true,
|
|
3879
|
-
nodeCompat: true
|
|
3880
|
-
}
|
|
3881
|
-
}`
|
|
3882
|
-
});
|
|
3883
|
-
const modulesProperty = configObject.getProperty("modules");
|
|
3884
|
-
if (modulesProperty && Node.isPropertyAssignment(modulesProperty)) {
|
|
3885
|
-
const initializer = modulesProperty.getInitializer();
|
|
3886
|
-
if (Node.isArrayLiteralExpression(initializer)) {
|
|
3887
|
-
if (!initializer.getElements().some((el) => el.getText() === "\"nitro-cloudflare-dev\"" || el.getText() === "'nitro-cloudflare-dev'")) initializer.addElement("\"nitro-cloudflare-dev\"");
|
|
3888
|
-
}
|
|
3889
|
-
} else if (!modulesProperty) configObject.addPropertyAssignment({
|
|
3890
|
-
name: "modules",
|
|
3891
|
-
initializer: "[\"nitro-cloudflare-dev\"]"
|
|
3892
|
-
});
|
|
3893
|
-
}
|
|
3894
|
-
await project.save();
|
|
3895
|
-
} catch (error) {
|
|
3896
|
-
console.warn("Failed to update nuxt.config.ts:", error);
|
|
3897
|
-
}
|
|
3898
|
-
}
|
|
3899
|
-
|
|
3900
|
-
//#endregion
|
|
3901
|
-
//#region src/helpers/deployment/alchemy/alchemy-react-router-setup.ts
|
|
3902
|
-
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3903
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3904
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3905
|
-
await addPackageDependency({
|
|
3906
|
-
devDependencies: ["alchemy"],
|
|
3907
|
-
projectDir: webAppDir
|
|
3908
|
-
});
|
|
3909
|
-
}
|
|
3910
|
-
|
|
3911
|
-
//#endregion
|
|
3912
|
-
//#region src/helpers/deployment/alchemy/alchemy-solid-setup.ts
|
|
3913
|
-
async function setupSolidAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3914
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3915
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3916
|
-
await addPackageDependency({
|
|
3917
|
-
devDependencies: ["alchemy"],
|
|
3918
|
-
projectDir: webAppDir
|
|
3919
|
-
});
|
|
3920
|
-
}
|
|
3921
|
-
|
|
3922
|
-
//#endregion
|
|
3923
|
-
//#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
|
|
3924
|
-
async function setupSvelteAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3925
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3926
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3927
|
-
await addPackageDependency({
|
|
3928
|
-
devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
|
|
3929
|
-
projectDir: webAppDir
|
|
3930
|
-
});
|
|
3931
|
-
const svelteConfigPath = path.join(webAppDir, "svelte.config.js");
|
|
3932
|
-
if (!await fs.pathExists(svelteConfigPath)) return;
|
|
3933
|
-
try {
|
|
3934
|
-
const project = new Project({ manipulationSettings: {
|
|
3935
|
-
indentationText: IndentationText.TwoSpaces,
|
|
3936
|
-
quoteKind: QuoteKind.Single
|
|
3937
|
-
} });
|
|
3938
|
-
project.addSourceFileAtPath(svelteConfigPath);
|
|
3939
|
-
const sourceFile = project.getSourceFileOrThrow(svelteConfigPath);
|
|
3940
|
-
const adapterImport = sourceFile.getImportDeclarations().find((imp) => imp.getModuleSpecifierValue().includes("@sveltejs/adapter"));
|
|
3941
|
-
if (adapterImport) {
|
|
3942
|
-
adapterImport.setModuleSpecifier("alchemy/cloudflare/sveltekit");
|
|
3943
|
-
adapterImport.removeDefaultImport();
|
|
3944
|
-
adapterImport.setDefaultImport("alchemy");
|
|
3945
|
-
} else sourceFile.insertImportDeclaration(0, {
|
|
3946
|
-
moduleSpecifier: "alchemy/cloudflare/sveltekit",
|
|
3947
|
-
defaultImport: "alchemy"
|
|
3948
|
-
});
|
|
3949
|
-
const configVariable = sourceFile.getVariableDeclaration("config");
|
|
3950
|
-
if (configVariable) {
|
|
3951
|
-
const initializer = configVariable.getInitializer();
|
|
3952
|
-
if (Node.isObjectLiteralExpression(initializer)) updateAdapterInConfig(initializer);
|
|
3953
|
-
}
|
|
3954
|
-
await project.save();
|
|
3955
|
-
} catch (error) {
|
|
3956
|
-
console.warn("Failed to update svelte.config.js:", error);
|
|
3957
|
-
}
|
|
3958
|
-
}
|
|
3959
|
-
function updateAdapterInConfig(configObject) {
|
|
3960
|
-
if (!Node.isObjectLiteralExpression(configObject)) return;
|
|
3961
|
-
const kitProperty = configObject.getProperty("kit");
|
|
3962
|
-
if (kitProperty && Node.isPropertyAssignment(kitProperty)) {
|
|
3963
|
-
const kitInitializer = kitProperty.getInitializer();
|
|
3964
|
-
if (Node.isObjectLiteralExpression(kitInitializer)) {
|
|
3965
|
-
const adapterProperty = kitInitializer.getProperty("adapter");
|
|
3966
|
-
if (adapterProperty && Node.isPropertyAssignment(adapterProperty)) {
|
|
3967
|
-
const initializer = adapterProperty.getInitializer();
|
|
3968
|
-
if (Node.isCallExpression(initializer)) {
|
|
3969
|
-
const expression = initializer.getExpression();
|
|
3970
|
-
if (Node.isIdentifier(expression) && expression.getText() === "adapter") expression.replaceWithText("alchemy");
|
|
3971
|
-
}
|
|
3972
|
-
}
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
//#endregion
|
|
3978
|
-
//#region src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts
|
|
3979
|
-
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3980
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3981
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3982
|
-
await addPackageDependency({
|
|
3983
|
-
devDependencies: ["alchemy"],
|
|
3984
|
-
projectDir: webAppDir
|
|
3985
|
-
});
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
//#endregion
|
|
3989
|
-
//#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
|
|
3990
|
-
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
3991
|
-
const webAppDir = path.join(projectDir, "apps/web");
|
|
3992
|
-
if (!await fs.pathExists(webAppDir)) return;
|
|
3993
|
-
await addPackageDependency({
|
|
3994
|
-
devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
|
|
3995
|
-
projectDir: webAppDir
|
|
3996
|
-
});
|
|
3997
|
-
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
3998
|
-
if (await fs.pathExists(viteConfigPath)) try {
|
|
3999
|
-
const project = new Project({ manipulationSettings: {
|
|
4000
|
-
indentationText: IndentationText.TwoSpaces,
|
|
4001
|
-
quoteKind: QuoteKind.Double
|
|
4002
|
-
} });
|
|
4003
|
-
project.addSourceFileAtPath(viteConfigPath);
|
|
4004
|
-
const sourceFile = project.getSourceFileOrThrow(viteConfigPath);
|
|
4005
|
-
const alchemyImport = sourceFile.getImportDeclaration("alchemy/cloudflare/tanstack-start");
|
|
4006
|
-
if (!alchemyImport) sourceFile.addImportDeclaration({
|
|
4007
|
-
moduleSpecifier: "alchemy/cloudflare/tanstack-start",
|
|
4008
|
-
defaultImport: "alchemy"
|
|
4009
|
-
});
|
|
4010
|
-
else alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
|
|
4011
|
-
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
4012
|
-
if (!exportAssignment) return;
|
|
4013
|
-
const defineConfigCall = exportAssignment.getExpression();
|
|
4014
|
-
if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineConfig") return;
|
|
4015
|
-
let configObject = defineConfigCall.getArguments()[0];
|
|
4016
|
-
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
4017
|
-
if (Node.isObjectLiteralExpression(configObject)) {
|
|
4018
|
-
const pluginsProperty = configObject.getProperty("plugins");
|
|
4019
|
-
if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
|
|
4020
|
-
const initializer = pluginsProperty.getInitializer();
|
|
4021
|
-
if (Node.isArrayLiteralExpression(initializer)) {
|
|
4022
|
-
if (!initializer.getElements().some((el) => el.getText().includes("alchemy("))) initializer.addElement("alchemy()");
|
|
4023
|
-
}
|
|
4024
|
-
} else configObject.addPropertyAssignment({
|
|
4025
|
-
name: "plugins",
|
|
4026
|
-
initializer: "[alchemy()]"
|
|
4027
|
-
});
|
|
4028
|
-
}
|
|
4029
|
-
await project.save();
|
|
4030
|
-
} catch (error) {
|
|
4031
|
-
console.warn("Failed to update vite.config.ts:", error);
|
|
4032
|
-
}
|
|
4033
|
-
}
|
|
4034
|
-
|
|
4035
|
-
//#endregion
|
|
4036
|
-
//#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
|
|
4037
|
-
function getInfraFilter(packageManager, hasTurborepo, infraWorkspace) {
|
|
4038
|
-
if (hasTurborepo) return (script) => `turbo -F ${infraWorkspace} ${script}`;
|
|
4039
|
-
switch (packageManager) {
|
|
4040
|
-
case "pnpm": return (script) => `pnpm --filter ${infraWorkspace} ${script}`;
|
|
4041
|
-
case "npm": return (script) => `npm run ${script} --workspace ${infraWorkspace}`;
|
|
4042
|
-
case "bun": return (script) => `bun run --filter ${infraWorkspace} ${script}`;
|
|
4043
|
-
}
|
|
4044
|
-
}
|
|
4045
|
-
async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
4046
|
-
await setupInfraScripts(projectDir, packageManager, config);
|
|
4047
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4048
|
-
if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
4049
|
-
const frontend = config.frontend;
|
|
4050
|
-
const isNext = frontend.includes("next");
|
|
4051
|
-
const isNuxt = frontend.includes("nuxt");
|
|
4052
|
-
const isSvelte = frontend.includes("svelte");
|
|
4053
|
-
const isTanstackRouter = frontend.includes("tanstack-router");
|
|
4054
|
-
const isTanstackStart = frontend.includes("tanstack-start");
|
|
4055
|
-
const isReactRouter = frontend.includes("react-router");
|
|
4056
|
-
const isSolid = frontend.includes("solid");
|
|
4057
|
-
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4058
|
-
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4059
|
-
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4060
|
-
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4061
|
-
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4062
|
-
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4063
|
-
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4064
|
-
}
|
|
4065
|
-
async function setupInfraScripts(projectDir, packageManager, config) {
|
|
4066
|
-
const projectName = config.projectName;
|
|
4067
|
-
const hasTurborepo = config.addons.includes("turborepo");
|
|
4068
|
-
const infraWorkspace = `@${projectName}/infra`;
|
|
4069
|
-
const rootPkgPath = path.join(projectDir, "package.json");
|
|
4070
|
-
if (await fs.pathExists(rootPkgPath)) {
|
|
4071
|
-
const pkg = await fs.readJson(rootPkgPath);
|
|
4072
|
-
const filter = getInfraFilter(packageManager, hasTurborepo, infraWorkspace);
|
|
4073
|
-
pkg.scripts = {
|
|
4074
|
-
...pkg.scripts,
|
|
4075
|
-
deploy: filter("deploy"),
|
|
4076
|
-
destroy: filter("destroy")
|
|
4077
|
-
};
|
|
4078
|
-
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
4079
|
-
}
|
|
4080
|
-
if (config.serverDeploy === "cloudflare") {
|
|
4081
|
-
const serverPkgPath = path.join(projectDir, "apps/server/package.json");
|
|
4082
|
-
if (await fs.pathExists(serverPkgPath)) {
|
|
4083
|
-
const serverPkg = await fs.readJson(serverPkgPath);
|
|
4084
|
-
if (serverPkg.scripts?.dev) {
|
|
4085
|
-
serverPkg.scripts["dev:bare"] = serverPkg.scripts.dev;
|
|
4086
|
-
delete serverPkg.scripts.dev;
|
|
4087
|
-
await fs.writeJson(serverPkgPath, serverPkg, { spaces: 2 });
|
|
4088
|
-
}
|
|
4089
|
-
}
|
|
4090
|
-
}
|
|
4091
|
-
if (config.webDeploy === "cloudflare") {
|
|
4092
|
-
const webPkgPath = path.join(projectDir, "apps/web/package.json");
|
|
4093
|
-
if (await fs.pathExists(webPkgPath)) {
|
|
4094
|
-
const webPkg = await fs.readJson(webPkgPath);
|
|
4095
|
-
if (webPkg.scripts?.dev) {
|
|
4096
|
-
webPkg.scripts["dev:bare"] = webPkg.scripts.dev;
|
|
4097
|
-
delete webPkg.scripts.dev;
|
|
4098
|
-
await fs.writeJson(webPkgPath, webPkg, { spaces: 2 });
|
|
4099
|
-
}
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
}
|
|
4103
|
-
|
|
4104
|
-
//#endregion
|
|
4105
|
-
//#region src/helpers/deployment/server-deploy-setup.ts
|
|
4106
|
-
async function setupServerDeploy(config) {
|
|
4107
|
-
const { serverDeploy, webDeploy, projectDir, packageManager } = config;
|
|
4108
|
-
if (serverDeploy === "none") return;
|
|
4109
|
-
if (serverDeploy === "cloudflare" && webDeploy === "cloudflare") return;
|
|
4110
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4111
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
4112
|
-
if (serverDeploy === "cloudflare") {
|
|
4113
|
-
await setupInfraScripts(projectDir, packageManager, config);
|
|
4114
|
-
await setupAlchemyServerDeploy(serverDir, projectDir);
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
async function setupAlchemyServerDeploy(serverDir, projectDir) {
|
|
4118
|
-
if (!await fs.pathExists(serverDir)) return;
|
|
4119
|
-
await addPackageDependency({
|
|
4120
|
-
devDependencies: [
|
|
4121
|
-
"alchemy",
|
|
4122
|
-
"wrangler",
|
|
4123
|
-
"@types/node",
|
|
4124
|
-
"@cloudflare/workers-types"
|
|
4125
|
-
],
|
|
4126
|
-
projectDir: serverDir
|
|
4127
|
-
});
|
|
4128
|
-
if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
|
|
4129
|
-
}
|
|
4130
|
-
async function addAlchemyPackagesDependencies$1(projectDir) {
|
|
4131
|
-
await addPackageDependency({
|
|
4132
|
-
devDependencies: ["@cloudflare/workers-types"],
|
|
4133
|
-
projectDir
|
|
4134
|
-
});
|
|
4135
|
-
}
|
|
4136
|
-
|
|
4137
|
-
//#endregion
|
|
4138
|
-
//#region src/helpers/deployment/web-deploy-setup.ts
|
|
4139
|
-
async function setupWebDeploy(config) {
|
|
4140
|
-
const { webDeploy, serverDeploy, frontend, projectDir } = config;
|
|
4141
|
-
const { packageManager } = config;
|
|
4142
|
-
if (webDeploy === "none") return;
|
|
4143
|
-
if (webDeploy !== "cloudflare") return;
|
|
4144
|
-
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") {
|
|
4145
|
-
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
4146
|
-
await addAlchemyPackagesDependencies(projectDir);
|
|
4147
|
-
return;
|
|
4148
|
-
}
|
|
4149
|
-
await setupInfraScripts(projectDir, packageManager, config);
|
|
4150
|
-
const isNext = frontend.includes("next");
|
|
4151
|
-
const isNuxt = frontend.includes("nuxt");
|
|
4152
|
-
const isSvelte = frontend.includes("svelte");
|
|
4153
|
-
const isTanstackRouter = frontend.includes("tanstack-router");
|
|
4154
|
-
const isTanstackStart = frontend.includes("tanstack-start");
|
|
4155
|
-
const isReactRouter = frontend.includes("react-router");
|
|
4156
|
-
const isSolid = frontend.includes("solid");
|
|
4157
|
-
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager);
|
|
4158
|
-
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
|
|
4159
|
-
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
|
|
4160
|
-
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
|
|
4161
|
-
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
4162
|
-
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
4163
|
-
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
4164
|
-
await addAlchemyPackagesDependencies(projectDir);
|
|
4165
|
-
}
|
|
4166
|
-
async function addAlchemyPackagesDependencies(projectDir) {
|
|
4167
|
-
await addPackageDependency({
|
|
4168
|
-
devDependencies: ["@cloudflare/workers-types"],
|
|
4169
|
-
projectDir
|
|
4170
|
-
});
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4173
|
-
//#endregion
|
|
4174
|
-
//#region src/helpers/core/add-deployment.ts
|
|
4175
|
-
async function addDeploymentToProject(input) {
|
|
4176
|
-
try {
|
|
4177
|
-
const projectDir = input.projectDir || process.cwd();
|
|
4178
|
-
if (!await isBetterTStackProject(projectDir)) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
|
|
4179
|
-
const detectedConfig = await detectProjectConfig(projectDir);
|
|
4180
|
-
if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
4181
|
-
if (input.webDeploy && detectedConfig.webDeploy === input.webDeploy) exitWithError(`${input.webDeploy} web deployment is already configured for this project.`);
|
|
4182
|
-
if (input.serverDeploy && detectedConfig.serverDeploy === input.serverDeploy) exitWithError(`${input.serverDeploy} server deployment is already configured for this project.`);
|
|
4183
|
-
const config = {
|
|
4184
|
-
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
4185
|
-
projectDir,
|
|
4186
|
-
relativePath: ".",
|
|
4187
|
-
database: detectedConfig.database || "none",
|
|
4188
|
-
orm: detectedConfig.orm || "none",
|
|
4189
|
-
backend: detectedConfig.backend || "none",
|
|
4190
|
-
runtime: detectedConfig.runtime || "none",
|
|
4191
|
-
frontend: detectedConfig.frontend || [],
|
|
4192
|
-
addons: detectedConfig.addons || [],
|
|
4193
|
-
examples: detectedConfig.examples || [],
|
|
4194
|
-
auth: detectedConfig.auth || "none",
|
|
4195
|
-
payments: detectedConfig.payments || "none",
|
|
4196
|
-
git: false,
|
|
4197
|
-
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
4198
|
-
install: input.install || false,
|
|
4199
|
-
dbSetup: detectedConfig.dbSetup || "none",
|
|
4200
|
-
api: detectedConfig.api || "none",
|
|
4201
|
-
webDeploy: input.webDeploy || detectedConfig.webDeploy || "none",
|
|
4202
|
-
serverDeploy: input.serverDeploy || detectedConfig.serverDeploy || "none"
|
|
4203
|
-
};
|
|
4204
|
-
if (input.webDeploy && input.webDeploy !== "none") log.info(pc.green(`Adding ${input.webDeploy} web deployment to ${config.frontend.join("/")}`));
|
|
4205
|
-
if (input.serverDeploy && input.serverDeploy !== "none") log.info(pc.green(`Adding ${input.serverDeploy} server deployment`));
|
|
4206
|
-
await setupDeploymentTemplates(projectDir, config);
|
|
4207
|
-
await setupWebDeploy(config);
|
|
4208
|
-
await setupServerDeploy(config);
|
|
4209
|
-
await updateBtsConfig(projectDir, {
|
|
4210
|
-
webDeploy: input.webDeploy || config.webDeploy,
|
|
4211
|
-
serverDeploy: input.serverDeploy || config.serverDeploy
|
|
4212
|
-
});
|
|
4213
|
-
if (config.install) await installDependencies({
|
|
4214
|
-
projectDir,
|
|
4215
|
-
packageManager: config.packageManager
|
|
4216
|
-
});
|
|
4217
|
-
else if (!input.suppressInstallMessage) log.info(pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`));
|
|
4218
|
-
} catch (error) {
|
|
4219
|
-
exitWithError(`Error adding deployment: ${error instanceof Error ? error.message : String(error)}`);
|
|
4220
|
-
}
|
|
4221
|
-
}
|
|
4222
|
-
|
|
4223
|
-
//#endregion
|
|
4224
|
-
//#region src/utils/setup-catalogs.ts
|
|
4225
|
-
async function setupCatalogs(projectDir, options) {
|
|
4226
|
-
if (options.packageManager === "npm") return;
|
|
4227
|
-
const packagePaths = [
|
|
4228
|
-
".",
|
|
4229
|
-
"apps/server",
|
|
4230
|
-
"apps/web",
|
|
4231
|
-
"apps/native",
|
|
4232
|
-
"apps/fumadocs",
|
|
4233
|
-
"apps/docs",
|
|
4234
|
-
"packages/api",
|
|
4235
|
-
"packages/db",
|
|
4236
|
-
"packages/auth",
|
|
4237
|
-
"packages/backend",
|
|
4238
|
-
"packages/config",
|
|
4239
|
-
"packages/env",
|
|
4240
|
-
"packages/infra"
|
|
4241
|
-
];
|
|
4242
|
-
const packagesInfo = [];
|
|
4243
|
-
for (const pkgPath of packagePaths) {
|
|
4244
|
-
const fullPath = path.join(projectDir, pkgPath);
|
|
4245
|
-
const pkgJsonPath = path.join(fullPath, "package.json");
|
|
4246
|
-
if (await fs.pathExists(pkgJsonPath)) {
|
|
4247
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4248
|
-
packagesInfo.push({
|
|
4249
|
-
path: fullPath,
|
|
4250
|
-
dependencies: pkgJson.dependencies || {},
|
|
4251
|
-
devDependencies: pkgJson.devDependencies || {}
|
|
4252
|
-
});
|
|
4253
|
-
}
|
|
4254
|
-
}
|
|
4255
|
-
const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
|
|
4256
|
-
if (Object.keys(catalog).length === 0) return;
|
|
4257
|
-
if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
|
|
4258
|
-
else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
|
|
4259
|
-
await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
|
|
4260
|
-
}
|
|
4261
|
-
function findDuplicateDependencies(packagesInfo, projectName) {
|
|
4262
|
-
const depCount = /* @__PURE__ */ new Map();
|
|
4263
|
-
const projectScope = `@${projectName}/`;
|
|
4264
|
-
for (const pkg of packagesInfo) {
|
|
4265
|
-
const allDeps = {
|
|
4266
|
-
...pkg.dependencies,
|
|
4267
|
-
...pkg.devDependencies
|
|
4268
|
-
};
|
|
4269
|
-
for (const [depName, version] of Object.entries(allDeps)) {
|
|
4270
|
-
if (depName.startsWith(projectScope)) continue;
|
|
4271
|
-
if (version.startsWith("workspace:")) continue;
|
|
4272
|
-
const existing = depCount.get(depName);
|
|
4273
|
-
if (existing) {
|
|
4274
|
-
existing.versions.add(version);
|
|
4275
|
-
existing.packages.push(pkg.path);
|
|
4276
|
-
} else depCount.set(depName, {
|
|
4277
|
-
versions: new Set([version]),
|
|
4278
|
-
packages: [pkg.path]
|
|
4279
|
-
});
|
|
4280
|
-
}
|
|
4281
|
-
}
|
|
4282
|
-
const catalog = {};
|
|
4283
|
-
for (const [depName, info] of depCount.entries()) if (info.packages.length > 1 && info.versions.size === 1) catalog[depName] = Array.from(info.versions)[0];
|
|
4284
|
-
return catalog;
|
|
4285
|
-
}
|
|
4286
|
-
async function setupBunCatalogs(projectDir, catalog) {
|
|
4287
|
-
const rootPkgJsonPath = path.join(projectDir, "package.json");
|
|
4288
|
-
const rootPkgJson = await fs.readJson(rootPkgJsonPath);
|
|
4289
|
-
if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
|
|
4290
|
-
if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
|
|
4291
|
-
packages: rootPkgJson.workspaces,
|
|
4292
|
-
catalog
|
|
4293
|
-
};
|
|
4294
|
-
else if (typeof rootPkgJson.workspaces === "object") {
|
|
4295
|
-
if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
|
|
4296
|
-
rootPkgJson.workspaces.catalog = {
|
|
4297
|
-
...rootPkgJson.workspaces.catalog,
|
|
4298
|
-
...catalog
|
|
4299
|
-
};
|
|
4300
|
-
}
|
|
4301
|
-
await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
|
|
4302
|
-
}
|
|
4303
|
-
async function setupPnpmCatalogs(projectDir, catalog) {
|
|
4304
|
-
const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
|
|
4305
|
-
if (!await fs.pathExists(workspaceYamlPath)) return;
|
|
4306
|
-
const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
|
|
4307
|
-
const workspaceYaml = yaml.parse(workspaceContent);
|
|
4308
|
-
if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
|
|
4309
|
-
workspaceYaml.catalog = {
|
|
4310
|
-
...workspaceYaml.catalog,
|
|
4311
|
-
...catalog
|
|
4312
|
-
};
|
|
4313
|
-
await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
|
|
4314
|
-
}
|
|
4315
|
-
async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
|
|
4316
|
-
for (const pkg of packagesInfo) {
|
|
4317
|
-
const pkgJsonPath = path.join(pkg.path, "package.json");
|
|
4318
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4319
|
-
let updated = false;
|
|
4320
|
-
if (pkgJson.dependencies) {
|
|
4321
|
-
for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
|
|
4322
|
-
pkgJson.dependencies[depName] = "catalog:";
|
|
4323
|
-
updated = true;
|
|
4324
|
-
}
|
|
4325
|
-
}
|
|
4326
|
-
if (pkgJson.devDependencies) {
|
|
4327
|
-
for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
|
|
4328
|
-
pkgJson.devDependencies[depName] = "catalog:";
|
|
4329
|
-
updated = true;
|
|
4330
|
-
}
|
|
4331
|
-
}
|
|
4332
|
-
if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4333
|
-
}
|
|
4334
|
-
}
|
|
4335
|
-
|
|
4336
|
-
//#endregion
|
|
4337
|
-
//#region src/helpers/addons/examples-setup.ts
|
|
4338
|
-
async function setupExamples(config) {
|
|
4339
|
-
const { examples, backend } = config;
|
|
4340
|
-
if (!examples || examples.length === 0 || examples[0] === "none") return;
|
|
4341
|
-
if (examples.includes("todo") && backend !== "convex" && backend !== "none") await setupTodoDependencies(config);
|
|
4342
|
-
if (examples.includes("ai")) await setupAIDependencies(config);
|
|
4343
|
-
}
|
|
4344
|
-
async function setupTodoDependencies(config) {
|
|
4345
|
-
const { projectDir, orm, database, backend } = config;
|
|
4346
|
-
const apiDir = path.join(projectDir, "packages/api");
|
|
4347
|
-
if (!await fs.pathExists(apiDir) || backend === "none") return;
|
|
4348
|
-
if (orm === "drizzle") {
|
|
4349
|
-
const dependencies = ["drizzle-orm"];
|
|
4350
|
-
if (database === "postgres") dependencies.push("@types/pg");
|
|
4351
|
-
await addPackageDependency({
|
|
4352
|
-
dependencies,
|
|
4353
|
-
projectDir: apiDir
|
|
4354
|
-
});
|
|
4355
|
-
} else if (orm === "prisma") await addPackageDependency({
|
|
4356
|
-
dependencies: ["@prisma/client"],
|
|
4357
|
-
projectDir: apiDir
|
|
4358
|
-
});
|
|
4359
|
-
else if (orm === "mongoose") await addPackageDependency({
|
|
4360
|
-
dependencies: ["mongoose"],
|
|
4361
|
-
projectDir: apiDir
|
|
4362
|
-
});
|
|
4363
|
-
}
|
|
4364
|
-
async function setupAIDependencies(config) {
|
|
4365
|
-
const { frontend, backend, projectDir } = config;
|
|
4366
|
-
const webClientDir = path.join(projectDir, "apps/web");
|
|
4367
|
-
const nativeClientDir = path.join(projectDir, "apps/native");
|
|
4368
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4369
|
-
const convexBackendDir = path.join(projectDir, "packages/backend");
|
|
4370
|
-
const webClientDirExists = await fs.pathExists(webClientDir);
|
|
4371
|
-
const nativeClientDirExists = await fs.pathExists(nativeClientDir);
|
|
4372
|
-
const serverDirExists = await fs.pathExists(serverDir);
|
|
4373
|
-
const convexBackendDirExists = await fs.pathExists(convexBackendDir);
|
|
4374
|
-
const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
|
|
4375
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
4376
|
-
const hasSvelte = frontend.includes("svelte");
|
|
4377
|
-
const hasReactNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
4378
|
-
if (backend === "convex" && convexBackendDirExists) await addPackageDependency({
|
|
4379
|
-
dependencies: [
|
|
4380
|
-
"@convex-dev/agent",
|
|
4381
|
-
"ai",
|
|
4382
|
-
"@ai-sdk/google"
|
|
4383
|
-
],
|
|
4384
|
-
projectDir: convexBackendDir
|
|
4385
|
-
});
|
|
4386
|
-
else if (backend === "self" && webClientDirExists) await addPackageDependency({
|
|
4387
|
-
dependencies: [
|
|
4388
|
-
"ai",
|
|
4389
|
-
"@ai-sdk/google",
|
|
4390
|
-
"@ai-sdk/devtools"
|
|
4391
|
-
],
|
|
4392
|
-
projectDir: webClientDir
|
|
4393
|
-
});
|
|
4394
|
-
else if (serverDirExists && backend !== "none") await addPackageDependency({
|
|
4395
|
-
dependencies: [
|
|
4396
|
-
"ai",
|
|
4397
|
-
"@ai-sdk/google",
|
|
4398
|
-
"@ai-sdk/devtools"
|
|
4399
|
-
],
|
|
4400
|
-
projectDir: serverDir
|
|
4401
|
-
});
|
|
4402
|
-
if (webClientDirExists) {
|
|
4403
|
-
const dependencies = [];
|
|
4404
|
-
if (backend === "convex") {
|
|
4405
|
-
if (hasReactWeb) dependencies.push("@convex-dev/agent", "streamdown");
|
|
4406
|
-
} else {
|
|
4407
|
-
dependencies.push("ai");
|
|
4408
|
-
if (hasNuxt) dependencies.push("@ai-sdk/vue");
|
|
4409
|
-
else if (hasSvelte) dependencies.push("@ai-sdk/svelte");
|
|
4410
|
-
else if (hasReactWeb) dependencies.push("@ai-sdk/react", "streamdown");
|
|
4411
|
-
}
|
|
4412
|
-
if (dependencies.length > 0) await addPackageDependency({
|
|
4413
|
-
dependencies,
|
|
4414
|
-
projectDir: webClientDir
|
|
4415
|
-
});
|
|
4416
|
-
}
|
|
4417
|
-
if (nativeClientDirExists && hasReactNative) if (backend === "convex") await addPackageDependency({
|
|
4418
|
-
dependencies: ["@convex-dev/agent"],
|
|
4419
|
-
projectDir: nativeClientDir
|
|
4420
|
-
});
|
|
4421
|
-
else await addPackageDependency({
|
|
4422
|
-
dependencies: ["ai", "@ai-sdk/react"],
|
|
4423
|
-
projectDir: nativeClientDir
|
|
4424
|
-
});
|
|
4425
|
-
}
|
|
4426
|
-
|
|
4427
|
-
//#endregion
|
|
4428
|
-
//#region src/helpers/core/api-setup.ts
|
|
4429
|
-
function getFrontendType(frontend) {
|
|
4430
|
-
const reactBasedFrontends = [
|
|
4431
|
-
"tanstack-router",
|
|
4432
|
-
"react-router",
|
|
4433
|
-
"tanstack-start",
|
|
4434
|
-
"next"
|
|
4435
|
-
];
|
|
4436
|
-
const nativeFrontends = [
|
|
4437
|
-
"native-bare",
|
|
4438
|
-
"native-uniwind",
|
|
4439
|
-
"native-unistyles"
|
|
4440
|
-
];
|
|
4441
|
-
return {
|
|
4442
|
-
hasReactWeb: frontend.some((f) => reactBasedFrontends.includes(f)),
|
|
4443
|
-
hasNuxtWeb: frontend.includes("nuxt"),
|
|
4444
|
-
hasSvelteWeb: frontend.includes("svelte"),
|
|
4445
|
-
hasSolidWeb: frontend.includes("solid"),
|
|
4446
|
-
hasNative: frontend.some((f) => nativeFrontends.includes(f))
|
|
4447
|
-
};
|
|
4448
|
-
}
|
|
4449
|
-
function getApiDependencies(api, frontendType, backend) {
|
|
4450
|
-
const deps = {};
|
|
4451
|
-
if (api === "orpc") deps.server = { dependencies: [
|
|
4452
|
-
"@orpc/server",
|
|
4453
|
-
"@orpc/client",
|
|
4454
|
-
"@orpc/openapi",
|
|
4455
|
-
"@orpc/zod"
|
|
4456
|
-
] };
|
|
4457
|
-
else if (api === "trpc") deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
|
|
4458
|
-
if (backend !== "self" && backend !== "convex" && backend !== "none") {
|
|
4459
|
-
if (!deps.server) deps.server = { dependencies: [] };
|
|
4460
|
-
if (backend === "hono") deps.server.dependencies.push("hono");
|
|
4461
|
-
else if (backend === "elysia") deps.server.dependencies.push("elysia");
|
|
4462
|
-
}
|
|
4463
|
-
if (frontendType.hasReactWeb) {
|
|
4464
|
-
if (api === "orpc") deps.web = { dependencies: [
|
|
4465
|
-
"@orpc/tanstack-query",
|
|
4466
|
-
"@orpc/client",
|
|
4467
|
-
"@orpc/server"
|
|
4468
|
-
] };
|
|
4469
|
-
else if (api === "trpc") deps.web = { dependencies: [
|
|
4470
|
-
"@trpc/tanstack-react-query",
|
|
4471
|
-
"@trpc/client",
|
|
4472
|
-
"@trpc/server"
|
|
4473
|
-
] };
|
|
4474
|
-
} else if (frontendType.hasNuxtWeb && api === "orpc") deps.web = {
|
|
4475
|
-
dependencies: [
|
|
4476
|
-
"@tanstack/vue-query",
|
|
4477
|
-
"@orpc/tanstack-query",
|
|
4478
|
-
"@orpc/client",
|
|
4479
|
-
"@orpc/server"
|
|
4480
|
-
],
|
|
4481
|
-
devDependencies: ["@tanstack/vue-query-devtools"]
|
|
4482
|
-
};
|
|
4483
|
-
else if (frontendType.hasSvelteWeb && api === "orpc") deps.web = {
|
|
4484
|
-
dependencies: [
|
|
4485
|
-
"@orpc/tanstack-query",
|
|
4486
|
-
"@orpc/client",
|
|
4487
|
-
"@orpc/server",
|
|
4488
|
-
"@tanstack/svelte-query"
|
|
4489
|
-
],
|
|
4490
|
-
devDependencies: ["@tanstack/svelte-query-devtools"]
|
|
4491
|
-
};
|
|
4492
|
-
else if (frontendType.hasSolidWeb && api === "orpc") deps.web = {
|
|
4493
|
-
dependencies: [
|
|
4494
|
-
"@orpc/tanstack-query",
|
|
4495
|
-
"@orpc/client",
|
|
4496
|
-
"@orpc/server",
|
|
4497
|
-
"@tanstack/solid-query"
|
|
4498
|
-
],
|
|
4499
|
-
devDependencies: ["@tanstack/solid-query-devtools", "@tanstack/solid-router-devtools"]
|
|
4500
|
-
};
|
|
4501
|
-
if (api === "trpc") deps.native = { dependencies: [
|
|
4502
|
-
"@trpc/tanstack-react-query",
|
|
4503
|
-
"@trpc/client",
|
|
4504
|
-
"@trpc/server"
|
|
4505
|
-
] };
|
|
4506
|
-
else if (api === "orpc") deps.native = { dependencies: ["@orpc/tanstack-query", "@orpc/client"] };
|
|
4507
|
-
return deps;
|
|
4508
|
-
}
|
|
4509
|
-
function getQueryDependencies(frontend) {
|
|
4510
|
-
const reactBasedFrontends = [
|
|
4511
|
-
"react-router",
|
|
4512
|
-
"tanstack-router",
|
|
4513
|
-
"tanstack-start",
|
|
4514
|
-
"next",
|
|
4515
|
-
"native-bare",
|
|
4516
|
-
"native-uniwind",
|
|
4517
|
-
"native-unistyles"
|
|
4518
|
-
];
|
|
4519
|
-
const deps = {};
|
|
4520
|
-
if (frontend.some((f) => reactBasedFrontends.includes(f))) {
|
|
4521
|
-
const hasReactWeb = frontend.some((f) => f !== "native-bare" && f !== "native-uniwind" && f !== "native-unistyles" && reactBasedFrontends.includes(f));
|
|
4522
|
-
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
4523
|
-
if (hasReactWeb) deps.web = {
|
|
4524
|
-
dependencies: ["@tanstack/react-query"],
|
|
4525
|
-
devDependencies: ["@tanstack/react-query-devtools"]
|
|
4526
|
-
};
|
|
4527
|
-
if (hasNative) deps.native = { dependencies: ["@tanstack/react-query"] };
|
|
4528
|
-
}
|
|
4529
|
-
if (frontend.includes("solid")) deps.web = {
|
|
4530
|
-
dependencies: ["@tanstack/solid-query"],
|
|
4531
|
-
devDependencies: ["@tanstack/solid-query-devtools", "@tanstack/solid-router-devtools"]
|
|
4532
|
-
};
|
|
4533
|
-
return deps;
|
|
4534
|
-
}
|
|
4535
|
-
function getConvexDependencies(frontend) {
|
|
4536
|
-
const deps = {
|
|
4537
|
-
web: { dependencies: ["convex"] },
|
|
4538
|
-
native: { dependencies: ["convex"] }
|
|
4539
|
-
};
|
|
4540
|
-
if (frontend.includes("tanstack-start")) {
|
|
4541
|
-
deps.web.dependencies.push("@convex-dev/react-query");
|
|
4542
|
-
deps.web.dependencies.push("@tanstack/react-router-ssr-query");
|
|
4543
|
-
}
|
|
4544
|
-
if (frontend.includes("svelte")) deps.web.dependencies.push("convex-svelte");
|
|
4545
|
-
if (frontend.includes("nuxt")) deps.web.dependencies.push("convex-nuxt", "convex-vue");
|
|
4546
|
-
return deps;
|
|
4547
|
-
}
|
|
4548
|
-
async function setupApi(config) {
|
|
4549
|
-
const { api, frontend, backend, projectDir } = config;
|
|
4550
|
-
const isConvex = backend === "convex";
|
|
4551
|
-
const webDir = path.join(projectDir, "apps/web");
|
|
4552
|
-
const nativeDir = path.join(projectDir, "apps/native");
|
|
4553
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4554
|
-
const webDirExists = await fs.pathExists(webDir);
|
|
4555
|
-
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4556
|
-
await fs.pathExists(serverDir);
|
|
4557
|
-
const frontendType = getFrontendType(frontend);
|
|
4558
|
-
if (!isConvex && api !== "none") {
|
|
4559
|
-
const apiDeps = getApiDependencies(api, frontendType, backend);
|
|
4560
|
-
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
4561
|
-
if (apiDeps.server) {
|
|
4562
|
-
await addPackageDependency({
|
|
4563
|
-
dependencies: apiDeps.server.dependencies,
|
|
4564
|
-
projectDir: apiPackageDir
|
|
4565
|
-
});
|
|
4566
|
-
if (backend === "self" && webDirExists) await addPackageDependency({
|
|
4567
|
-
dependencies: apiDeps.server.dependencies,
|
|
4568
|
-
projectDir: webDir
|
|
4569
|
-
});
|
|
4570
|
-
if (backend === "self") {
|
|
4571
|
-
const frameworkDeps = [];
|
|
4572
|
-
if (frontend.includes("next")) frameworkDeps.push("next");
|
|
4573
|
-
if (frameworkDeps.length > 0) await addPackageDependency({
|
|
4574
|
-
dependencies: frameworkDeps,
|
|
4575
|
-
projectDir: apiPackageDir
|
|
4576
|
-
});
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
if (config.auth === "better-auth" && (backend === "express" || backend === "fastify")) await addPackageDependency({
|
|
4580
|
-
dependencies: ["better-auth"],
|
|
4581
|
-
projectDir: apiPackageDir
|
|
4582
|
-
});
|
|
4583
|
-
if (backend === "express") await addPackageDependency({
|
|
4584
|
-
devDependencies: ["@types/express"],
|
|
4585
|
-
projectDir: apiPackageDir
|
|
4586
|
-
});
|
|
4587
|
-
if (webDirExists && apiDeps.web) await addPackageDependency({
|
|
4588
|
-
dependencies: apiDeps.web.dependencies,
|
|
4589
|
-
devDependencies: apiDeps.web.devDependencies,
|
|
4590
|
-
projectDir: webDir
|
|
4591
|
-
});
|
|
4592
|
-
if (nativeDirExists && apiDeps.native) await addPackageDependency({
|
|
4593
|
-
dependencies: apiDeps.native.dependencies,
|
|
4594
|
-
projectDir: nativeDir
|
|
4595
|
-
});
|
|
4596
|
-
}
|
|
4597
|
-
if (!isConvex) {
|
|
4598
|
-
const queryDeps = getQueryDependencies(frontend);
|
|
4599
|
-
if (webDirExists && queryDeps.web) await addPackageDependency({
|
|
4600
|
-
dependencies: queryDeps.web.dependencies,
|
|
4601
|
-
devDependencies: queryDeps.web.devDependencies,
|
|
4602
|
-
projectDir: webDir
|
|
4603
|
-
});
|
|
4604
|
-
if (nativeDirExists && queryDeps.native) await addPackageDependency({
|
|
4605
|
-
dependencies: queryDeps.native.dependencies,
|
|
4606
|
-
projectDir: nativeDir
|
|
4607
|
-
});
|
|
4608
|
-
}
|
|
4609
|
-
if (isConvex) {
|
|
4610
|
-
const convexDeps = getConvexDependencies(frontend);
|
|
4611
|
-
if (webDirExists) await addPackageDependency({
|
|
4612
|
-
dependencies: convexDeps.web.dependencies,
|
|
4613
|
-
projectDir: webDir
|
|
4614
|
-
});
|
|
4615
|
-
if (nativeDirExists) await addPackageDependency({
|
|
4616
|
-
dependencies: convexDeps.native.dependencies,
|
|
4617
|
-
projectDir: nativeDir
|
|
4618
|
-
});
|
|
4619
|
-
}
|
|
4620
|
-
}
|
|
4621
|
-
|
|
4622
|
-
//#endregion
|
|
4623
|
-
//#region src/helpers/core/backend-setup.ts
|
|
4624
|
-
async function setupBackendDependencies(config) {
|
|
4625
|
-
const { backend, runtime, api, auth, projectDir } = config;
|
|
4626
|
-
if (backend === "convex") {
|
|
4627
|
-
await addPackageDependency({
|
|
4628
|
-
dependencies: ["convex"],
|
|
4629
|
-
projectDir: path.join(projectDir, "packages/backend")
|
|
4630
|
-
});
|
|
4631
|
-
return;
|
|
4632
|
-
}
|
|
4633
|
-
const framework = backend;
|
|
4634
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4635
|
-
const dependencies = [];
|
|
4636
|
-
const devDependencies = [];
|
|
4637
|
-
if (framework === "hono") {
|
|
4638
|
-
dependencies.push("hono");
|
|
4639
|
-
if (runtime === "node") dependencies.push("@hono/node-server");
|
|
4640
|
-
} else if (framework === "elysia") {
|
|
4641
|
-
dependencies.push("elysia", "@elysiajs/cors");
|
|
4642
|
-
if (runtime === "node") dependencies.push("@elysiajs/node");
|
|
4643
|
-
} else if (framework === "express") {
|
|
4644
|
-
dependencies.push("express", "cors");
|
|
4645
|
-
devDependencies.push("@types/express", "@types/cors");
|
|
4646
|
-
} else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
|
|
4647
|
-
if (api === "trpc") {
|
|
4648
|
-
dependencies.push("@trpc/server");
|
|
4649
|
-
if (framework === "hono") dependencies.push("@hono/trpc-server");
|
|
4650
|
-
else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
|
|
4651
|
-
} else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
|
|
4652
|
-
if (auth === "better-auth") dependencies.push("better-auth");
|
|
4653
|
-
if (runtime === "node") devDependencies.push("tsx", "@types/node");
|
|
4654
|
-
else if (runtime === "bun") devDependencies.push("@types/bun");
|
|
4655
|
-
if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
|
|
4656
|
-
dependencies,
|
|
4657
|
-
devDependencies,
|
|
4658
|
-
projectDir: serverDir
|
|
4659
|
-
});
|
|
4660
|
-
}
|
|
4661
|
-
|
|
4662
|
-
//#endregion
|
|
4663
|
-
//#region src/utils/better-auth-plugin-setup.ts
|
|
4664
|
-
async function setupBetterAuthPlugins(projectDir, config) {
|
|
4665
|
-
const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
|
|
4666
|
-
const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
|
|
4667
|
-
if (!authIndexFile) return;
|
|
4668
|
-
const pluginsToAdd = [];
|
|
4669
|
-
const importsToAdd = [];
|
|
4670
|
-
if (config.backend === "self" && config.frontend?.includes("tanstack-start")) {
|
|
4671
|
-
pluginsToAdd.push("tanstackStartCookies()");
|
|
4672
|
-
importsToAdd.push("import { tanstackStartCookies } from \"better-auth/tanstack-start\";");
|
|
4673
|
-
}
|
|
4674
|
-
if (config.backend === "self" && config.frontend?.includes("next")) {
|
|
4675
|
-
pluginsToAdd.push("nextCookies()");
|
|
4676
|
-
importsToAdd.push("import { nextCookies } from \"better-auth/next-js\";");
|
|
4677
|
-
}
|
|
4678
|
-
if (config.frontend?.includes("native-bare") || config.frontend?.includes("native-uniwind") || config.frontend?.includes("native-unistyles")) {
|
|
4679
|
-
pluginsToAdd.push("expo()");
|
|
4680
|
-
importsToAdd.push("import { expo } from \"@better-auth/expo\";");
|
|
4681
|
-
}
|
|
4682
|
-
if (pluginsToAdd.length === 0) return;
|
|
4683
|
-
importsToAdd.forEach((importStatement) => {
|
|
4684
|
-
if (!authIndexFile.getImportDeclaration((declaration) => declaration.getModuleSpecifierValue().includes(importStatement.split("\"")[1]))) authIndexFile.insertImportDeclaration(0, {
|
|
4685
|
-
moduleSpecifier: importStatement.split("\"")[1],
|
|
4686
|
-
namedImports: [importStatement.split("{")[1].split("}")[0].trim()]
|
|
4687
|
-
});
|
|
4688
|
-
});
|
|
4689
|
-
const betterAuthCall = authIndexFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((call) => call.getExpression().getText() === "betterAuth");
|
|
4690
|
-
if (betterAuthCall) {
|
|
4691
|
-
const configObject = betterAuthCall.getArguments()[0];
|
|
4692
|
-
if (configObject && configObject.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
|
4693
|
-
const pluginsArray = ensureArrayProperty(configObject.asKindOrThrow(SyntaxKind.ObjectLiteralExpression), "plugins");
|
|
4694
|
-
pluginsToAdd.forEach((plugin) => {
|
|
4695
|
-
pluginsArray.addElement(plugin);
|
|
4696
|
-
});
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
authIndexFile.save();
|
|
4700
|
-
}
|
|
4701
|
-
|
|
4702
|
-
//#endregion
|
|
4703
|
-
//#region src/helpers/core/auth-setup.ts
|
|
4704
|
-
async function setupAuth(config) {
|
|
4705
|
-
const { auth, frontend, backend, projectDir } = config;
|
|
4706
|
-
if (!auth || auth === "none") return;
|
|
4707
|
-
const serverDir = path.join(projectDir, "apps/server");
|
|
4708
|
-
const clientDir = path.join(projectDir, "apps/web");
|
|
4709
|
-
const nativeDir = path.join(projectDir, "apps/native");
|
|
4710
|
-
const clientDirExists = await fs.pathExists(clientDir);
|
|
4711
|
-
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
4712
|
-
await fs.pathExists(serverDir);
|
|
4713
|
-
try {
|
|
4714
|
-
if (backend === "convex") {
|
|
4715
|
-
if (auth === "clerk" && clientDirExists) {
|
|
4716
|
-
const hasNextJs = frontend.includes("next");
|
|
4717
|
-
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
4718
|
-
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4719
|
-
if (hasNextJs) await addPackageDependency({
|
|
4720
|
-
dependencies: ["@clerk/nextjs"],
|
|
4721
|
-
projectDir: clientDir
|
|
4722
|
-
});
|
|
4723
|
-
else if (hasTanStackStart) await addPackageDependency({
|
|
4724
|
-
dependencies: ["@clerk/tanstack-react-start", "srvx"],
|
|
4725
|
-
projectDir: clientDir
|
|
4726
|
-
});
|
|
4727
|
-
else if (hasViteReactOther) await addPackageDependency({
|
|
4728
|
-
dependencies: ["@clerk/clerk-react"],
|
|
4729
|
-
projectDir: clientDir
|
|
4730
|
-
});
|
|
4731
|
-
}
|
|
4732
|
-
if (auth === "better-auth") {
|
|
4733
|
-
const convexBackendDir = path.join(projectDir, "packages/backend");
|
|
4734
|
-
const convexBackendDirExists = await fs.pathExists(convexBackendDir);
|
|
4735
|
-
const hasNativeForBA = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
4736
|
-
if (convexBackendDirExists) {
|
|
4737
|
-
await addPackageDependency({
|
|
4738
|
-
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4739
|
-
customDependencies: { "better-auth": "1.4.9" },
|
|
4740
|
-
projectDir: convexBackendDir
|
|
4741
|
-
});
|
|
4742
|
-
if (hasNativeForBA) await addPackageDependency({
|
|
4743
|
-
dependencies: ["@better-auth/expo"],
|
|
4744
|
-
customDependencies: { "@better-auth/expo": "1.4.9" },
|
|
4745
|
-
projectDir: convexBackendDir
|
|
4746
|
-
});
|
|
4747
|
-
}
|
|
4748
|
-
if (clientDirExists) {
|
|
4749
|
-
const hasNextJs = frontend.includes("next");
|
|
4750
|
-
const hasTanStackStart = frontend.includes("tanstack-start");
|
|
4751
|
-
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4752
|
-
if (hasNextJs) await addPackageDependency({
|
|
4753
|
-
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4754
|
-
customDependencies: { "better-auth": "1.4.9" },
|
|
4755
|
-
projectDir: clientDir
|
|
4756
|
-
});
|
|
4757
|
-
else if (hasTanStackStart) await addPackageDependency({
|
|
4758
|
-
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4759
|
-
customDependencies: { "better-auth": "1.4.9" },
|
|
4760
|
-
projectDir: clientDir
|
|
4761
|
-
});
|
|
4762
|
-
else if (hasViteReactOther) await addPackageDependency({
|
|
4763
|
-
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4764
|
-
customDependencies: { "better-auth": "1.4.9" },
|
|
4765
|
-
projectDir: clientDir
|
|
4766
|
-
});
|
|
4767
|
-
}
|
|
4768
|
-
const hasNativeBare$1 = frontend.includes("native-bare");
|
|
4769
|
-
const hasNativeUniwind$1 = frontend.includes("native-uniwind");
|
|
4770
|
-
const hasUnistyles$1 = frontend.includes("native-unistyles");
|
|
4771
|
-
if (nativeDirExists && (hasNativeBare$1 || hasNativeUniwind$1 || hasUnistyles$1)) await addPackageDependency({
|
|
4772
|
-
dependencies: [
|
|
4773
|
-
"better-auth",
|
|
4774
|
-
"@better-auth/expo",
|
|
4775
|
-
"@convex-dev/better-auth"
|
|
4776
|
-
],
|
|
4777
|
-
customDependencies: {
|
|
4778
|
-
"better-auth": "1.4.9",
|
|
4779
|
-
"@better-auth/expo": "1.4.9"
|
|
4780
|
-
},
|
|
4781
|
-
projectDir: nativeDir
|
|
4782
|
-
});
|
|
4783
|
-
}
|
|
4784
|
-
const hasNativeBare = frontend.includes("native-bare");
|
|
4785
|
-
const hasNativeUniwind = frontend.includes("native-uniwind");
|
|
4786
|
-
const hasUnistyles = frontend.includes("native-unistyles");
|
|
4787
|
-
if (auth === "clerk" && nativeDirExists && (hasNativeBare || hasNativeUniwind || hasUnistyles)) await addPackageDependency({
|
|
4788
|
-
dependencies: ["@clerk/clerk-expo"],
|
|
4789
|
-
projectDir: nativeDir
|
|
4790
|
-
});
|
|
4791
|
-
return;
|
|
4792
|
-
}
|
|
4793
|
-
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
4794
|
-
const authPackageDirExists = await fs.pathExists(authPackageDir);
|
|
4795
|
-
if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
|
|
4796
|
-
dependencies: ["better-auth"],
|
|
4797
|
-
projectDir: authPackageDir
|
|
4798
|
-
});
|
|
4799
|
-
if (frontend.some((f) => [
|
|
4800
|
-
"react-router",
|
|
4801
|
-
"tanstack-router",
|
|
4802
|
-
"tanstack-start",
|
|
4803
|
-
"next",
|
|
4804
|
-
"nuxt",
|
|
4805
|
-
"svelte",
|
|
4806
|
-
"solid"
|
|
4807
|
-
].includes(f)) && clientDirExists) {
|
|
4808
|
-
if (auth === "better-auth") await addPackageDependency({
|
|
4809
|
-
dependencies: ["better-auth"],
|
|
4810
|
-
projectDir: clientDir
|
|
4811
|
-
});
|
|
4812
|
-
}
|
|
4813
|
-
if ((frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles")) && nativeDirExists) {
|
|
4814
|
-
if (auth === "better-auth") {
|
|
4815
|
-
await addPackageDependency({
|
|
4816
|
-
dependencies: ["better-auth", "@better-auth/expo"],
|
|
4817
|
-
projectDir: nativeDir
|
|
4818
|
-
});
|
|
4819
|
-
if (authPackageDirExists) await addPackageDependency({
|
|
4820
|
-
dependencies: ["@better-auth/expo"],
|
|
4821
|
-
projectDir: authPackageDir
|
|
4822
|
-
});
|
|
4823
|
-
}
|
|
4824
|
-
}
|
|
4825
|
-
if (authPackageDirExists && auth === "better-auth") await setupBetterAuthPlugins(projectDir, config);
|
|
4826
|
-
} catch (error) {
|
|
4827
|
-
consola.error(pc.red("Failed to configure authentication dependencies"));
|
|
4828
|
-
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
2744
|
+
//#region src/utils/add-package-deps.ts
|
|
2745
|
+
const addPackageDependency = async (opts) => {
|
|
2746
|
+
const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, projectDir } = opts;
|
|
2747
|
+
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
2748
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
2749
|
+
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
2750
|
+
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
2751
|
+
for (const pkgName of dependencies) {
|
|
2752
|
+
const version = dependencyVersionMap[pkgName];
|
|
2753
|
+
if (version) pkgJson.dependencies[pkgName] = version;
|
|
2754
|
+
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
4829
2755
|
}
|
|
4830
|
-
|
|
2756
|
+
for (const pkgName of devDependencies) {
|
|
2757
|
+
const version = dependencyVersionMap[pkgName];
|
|
2758
|
+
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
2759
|
+
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
2760
|
+
}
|
|
2761
|
+
for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
|
|
2762
|
+
for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
|
|
2763
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
2764
|
+
};
|
|
2765
|
+
|
|
2766
|
+
//#endregion
|
|
2767
|
+
//#region src/helpers/core/auth-setup.ts
|
|
4831
2768
|
function generateAuthSecret(length = 32) {
|
|
4832
2769
|
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
4833
2770
|
let result = "";
|
|
@@ -6045,106 +3982,23 @@ async function setupTurso(config, cliInput) {
|
|
|
6045
3982
|
|
|
6046
3983
|
//#endregion
|
|
6047
3984
|
//#region src/helpers/core/db-setup.ts
|
|
3985
|
+
/**
|
|
3986
|
+
* Database setup - CLI-only operations
|
|
3987
|
+
* Calls external database provider CLIs (turso, neon, prisma-postgres, etc.)
|
|
3988
|
+
* Dependencies are handled by the generator's db-deps processor
|
|
3989
|
+
*/
|
|
6048
3990
|
async function setupDatabase(config, cliInput) {
|
|
6049
|
-
const { database,
|
|
3991
|
+
const { database, dbSetup, backend, projectDir } = config;
|
|
6050
3992
|
if (backend === "convex" || database === "none") {
|
|
6051
3993
|
if (backend !== "convex") {
|
|
6052
|
-
const
|
|
6053
|
-
const serverDbDir = path.join(serverDir, "src/db");
|
|
3994
|
+
const serverDbDir = path.join(projectDir, "apps/server/src/db");
|
|
6054
3995
|
if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
|
|
6055
3996
|
}
|
|
6056
3997
|
return;
|
|
6057
3998
|
}
|
|
6058
3999
|
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
6059
|
-
const webDir = path.join(projectDir, "apps/web");
|
|
6060
|
-
const webDirExists = await fs.pathExists(webDir);
|
|
6061
4000
|
if (!await fs.pathExists(dbPackageDir)) return;
|
|
6062
4001
|
try {
|
|
6063
|
-
if (orm === "prisma") {
|
|
6064
|
-
if (database === "mongodb") await addPackageDependency({
|
|
6065
|
-
customDependencies: { "@prisma/client": "6.19.0" },
|
|
6066
|
-
customDevDependencies: { prisma: "6.19.0" },
|
|
6067
|
-
projectDir: dbPackageDir
|
|
6068
|
-
});
|
|
6069
|
-
else {
|
|
6070
|
-
const prismaDependencies = ["@prisma/client"];
|
|
6071
|
-
const prismaDevDependencies = ["prisma"];
|
|
6072
|
-
if (database === "mysql" && dbSetup === "planetscale") prismaDependencies.push("@prisma/adapter-planetscale", "@planetscale/database");
|
|
6073
|
-
else if (database === "mysql") prismaDependencies.push("@prisma/adapter-mariadb");
|
|
6074
|
-
else if (database === "sqlite") if (dbSetup === "d1") prismaDependencies.push("@prisma/adapter-d1");
|
|
6075
|
-
else prismaDependencies.push("@prisma/adapter-libsql");
|
|
6076
|
-
else if (database === "postgres") if (dbSetup === "neon") {
|
|
6077
|
-
prismaDependencies.push("@prisma/adapter-neon", "@neondatabase/serverless", "ws");
|
|
6078
|
-
prismaDevDependencies.push("@types/ws");
|
|
6079
|
-
} else if (dbSetup === "prisma-postgres") prismaDependencies.push("@prisma/adapter-pg");
|
|
6080
|
-
else {
|
|
6081
|
-
prismaDependencies.push("@prisma/adapter-pg");
|
|
6082
|
-
prismaDependencies.push("pg");
|
|
6083
|
-
prismaDevDependencies.push("@types/pg");
|
|
6084
|
-
}
|
|
6085
|
-
await addPackageDependency({
|
|
6086
|
-
dependencies: prismaDependencies,
|
|
6087
|
-
devDependencies: prismaDevDependencies,
|
|
6088
|
-
projectDir: dbPackageDir
|
|
6089
|
-
});
|
|
6090
|
-
}
|
|
6091
|
-
if (await fs.pathExists(webDir)) if (database === "mongodb") await addPackageDependency({
|
|
6092
|
-
customDependencies: { "@prisma/client": "6.19.0" },
|
|
6093
|
-
projectDir: webDir
|
|
6094
|
-
});
|
|
6095
|
-
else await addPackageDependency({
|
|
6096
|
-
dependencies: ["@prisma/client"],
|
|
6097
|
-
projectDir: webDir
|
|
6098
|
-
});
|
|
6099
|
-
} else if (orm === "drizzle") {
|
|
6100
|
-
if (database === "sqlite") {
|
|
6101
|
-
await addPackageDependency({
|
|
6102
|
-
dependencies: [
|
|
6103
|
-
"drizzle-orm",
|
|
6104
|
-
"@libsql/client",
|
|
6105
|
-
"libsql"
|
|
6106
|
-
],
|
|
6107
|
-
devDependencies: ["drizzle-kit"],
|
|
6108
|
-
projectDir: dbPackageDir
|
|
6109
|
-
});
|
|
6110
|
-
if (webDirExists) await addPackageDependency({
|
|
6111
|
-
dependencies: ["@libsql/client", "libsql"],
|
|
6112
|
-
projectDir: webDir
|
|
6113
|
-
});
|
|
6114
|
-
} else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
|
|
6115
|
-
dependencies: [
|
|
6116
|
-
"drizzle-orm",
|
|
6117
|
-
"@neondatabase/serverless",
|
|
6118
|
-
"ws"
|
|
6119
|
-
],
|
|
6120
|
-
devDependencies: ["drizzle-kit", "@types/ws"],
|
|
6121
|
-
projectDir: dbPackageDir
|
|
6122
|
-
});
|
|
6123
|
-
else if (dbSetup === "planetscale") await addPackageDependency({
|
|
6124
|
-
dependencies: ["drizzle-orm", "pg"],
|
|
6125
|
-
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
6126
|
-
projectDir: dbPackageDir
|
|
6127
|
-
});
|
|
6128
|
-
else await addPackageDependency({
|
|
6129
|
-
dependencies: ["drizzle-orm", "pg"],
|
|
6130
|
-
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
6131
|
-
projectDir: dbPackageDir
|
|
6132
|
-
});
|
|
6133
|
-
else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
|
|
6134
|
-
dependencies: ["drizzle-orm", "@planetscale/database"],
|
|
6135
|
-
devDependencies: ["drizzle-kit"],
|
|
6136
|
-
projectDir: dbPackageDir
|
|
6137
|
-
});
|
|
6138
|
-
else await addPackageDependency({
|
|
6139
|
-
dependencies: ["drizzle-orm", "mysql2"],
|
|
6140
|
-
devDependencies: ["drizzle-kit"],
|
|
6141
|
-
projectDir: dbPackageDir
|
|
6142
|
-
});
|
|
6143
|
-
} else if (orm === "mongoose") await addPackageDependency({
|
|
6144
|
-
dependencies: ["mongoose"],
|
|
6145
|
-
devDependencies: [],
|
|
6146
|
-
projectDir: dbPackageDir
|
|
6147
|
-
});
|
|
6148
4002
|
if (dbSetup === "docker") await setupDockerCompose(config);
|
|
6149
4003
|
else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
|
|
6150
4004
|
else if (database === "sqlite" && dbSetup === "d1") await setupCloudflareD1(config);
|
|
@@ -6153,389 +4007,319 @@ async function setupDatabase(config, cliInput) {
|
|
|
6153
4007
|
else if (dbSetup === "neon") await setupNeonPostgres(config, cliInput);
|
|
6154
4008
|
else if (dbSetup === "planetscale") await setupPlanetScale(config);
|
|
6155
4009
|
else if (dbSetup === "supabase") await setupSupabase(config, cliInput);
|
|
6156
|
-
} else if (database === "mysql")
|
|
6157
|
-
|
|
6158
|
-
} else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config, cliInput);
|
|
4010
|
+
} else if (database === "mysql" && dbSetup === "planetscale") await setupPlanetScale(config);
|
|
4011
|
+
else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config, cliInput);
|
|
6159
4012
|
} catch (error) {
|
|
6160
4013
|
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
6161
4014
|
}
|
|
6162
4015
|
}
|
|
6163
4016
|
|
|
6164
4017
|
//#endregion
|
|
6165
|
-
//#region src/helpers/
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
};
|
|
6183
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
6184
|
-
await addPackageDependency({
|
|
6185
|
-
devDependencies: ["@types/bun"],
|
|
6186
|
-
projectDir: serverDir
|
|
6187
|
-
});
|
|
6188
|
-
}
|
|
6189
|
-
async function setupNodeRuntime(serverDir, backend) {
|
|
6190
|
-
const packageJsonPath = path.join(serverDir, "package.json");
|
|
6191
|
-
if (!await fs.pathExists(packageJsonPath)) return;
|
|
6192
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
6193
|
-
packageJson.scripts = {
|
|
6194
|
-
...packageJson.scripts,
|
|
6195
|
-
dev: "tsx watch src/index.ts",
|
|
6196
|
-
start: "node dist/index.js"
|
|
6197
|
-
};
|
|
6198
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
6199
|
-
await addPackageDependency({
|
|
6200
|
-
devDependencies: ["tsx", "@types/node"],
|
|
6201
|
-
projectDir: serverDir
|
|
6202
|
-
});
|
|
6203
|
-
if (backend === "hono") await addPackageDependency({
|
|
6204
|
-
dependencies: ["@hono/node-server"],
|
|
6205
|
-
projectDir: serverDir
|
|
6206
|
-
});
|
|
6207
|
-
else if (backend === "elysia") await addPackageDependency({
|
|
6208
|
-
dependencies: ["@elysiajs/node"],
|
|
6209
|
-
projectDir: serverDir
|
|
6210
|
-
});
|
|
4018
|
+
//#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
|
|
4019
|
+
/**
|
|
4020
|
+
* Alchemy Next.js setup - CLI-only operations
|
|
4021
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts
|
|
4022
|
+
* This only modifies config files for "add deploy" command
|
|
4023
|
+
*/
|
|
4024
|
+
async function setupNextAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4025
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4026
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4027
|
+
const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
|
|
4028
|
+
await fs.writeFile(openNextConfigPath, `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
4029
|
+
|
|
4030
|
+
export default defineCloudflareConfig({});
|
|
4031
|
+
`);
|
|
4032
|
+
const gitignorePath = path.join(webAppDir, ".gitignore");
|
|
4033
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
4034
|
+
if (!(await fs.readFile(gitignorePath, "utf-8")).includes("wrangler.jsonc")) await fs.appendFile(gitignorePath, "\nwrangler.jsonc\n");
|
|
4035
|
+
} else await fs.writeFile(gitignorePath, "wrangler.jsonc\n");
|
|
6211
4036
|
}
|
|
6212
4037
|
|
|
6213
4038
|
//#endregion
|
|
6214
|
-
//#region src/helpers/
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
4039
|
+
//#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
|
|
4040
|
+
/**
|
|
4041
|
+
* Alchemy Nuxt setup - CLI-only operations
|
|
4042
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts
|
|
4043
|
+
* This only modifies config files for "add deploy" command
|
|
4044
|
+
*/
|
|
4045
|
+
async function setupNuxtAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4046
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4047
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4048
|
+
const nuxtConfigPath = path.join(webAppDir, "nuxt.config.ts");
|
|
4049
|
+
if (!await fs.pathExists(nuxtConfigPath)) return;
|
|
6218
4050
|
try {
|
|
6219
|
-
|
|
4051
|
+
const project = new Project({ manipulationSettings: {
|
|
4052
|
+
indentationText: IndentationText.TwoSpaces,
|
|
4053
|
+
quoteKind: QuoteKind.Double
|
|
4054
|
+
} });
|
|
4055
|
+
project.addSourceFileAtPath(nuxtConfigPath);
|
|
4056
|
+
const exportAssignment = project.getSourceFileOrThrow(nuxtConfigPath).getExportAssignment((d) => !d.isExportEquals());
|
|
4057
|
+
if (!exportAssignment) return;
|
|
4058
|
+
const defineConfigCall = exportAssignment.getExpression();
|
|
4059
|
+
if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineNuxtConfig") return;
|
|
4060
|
+
let configObject = defineConfigCall.getArguments()[0];
|
|
4061
|
+
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
4062
|
+
if (Node.isObjectLiteralExpression(configObject)) {
|
|
4063
|
+
if (!configObject.getProperty("nitro")) configObject.addPropertyAssignment({
|
|
4064
|
+
name: "nitro",
|
|
4065
|
+
initializer: `{
|
|
4066
|
+
preset: "cloudflare_module",
|
|
4067
|
+
cloudflare: {
|
|
4068
|
+
deployConfig: true,
|
|
4069
|
+
nodeCompat: true
|
|
4070
|
+
}
|
|
4071
|
+
}`
|
|
4072
|
+
});
|
|
4073
|
+
const modulesProperty = configObject.getProperty("modules");
|
|
4074
|
+
if (modulesProperty && Node.isPropertyAssignment(modulesProperty)) {
|
|
4075
|
+
const initializer = modulesProperty.getInitializer();
|
|
4076
|
+
if (Node.isArrayLiteralExpression(initializer)) {
|
|
4077
|
+
if (!initializer.getElements().some((el) => el.getText() === "\"nitro-cloudflare-dev\"" || el.getText() === "'nitro-cloudflare-dev'")) initializer.addElement("\"nitro-cloudflare-dev\"");
|
|
4078
|
+
}
|
|
4079
|
+
} else if (!modulesProperty) configObject.addPropertyAssignment({
|
|
4080
|
+
name: "modules",
|
|
4081
|
+
initializer: "[\"nitro-cloudflare-dev\"]"
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
await project.save();
|
|
6220
4085
|
} catch (error) {
|
|
6221
|
-
|
|
4086
|
+
console.warn("Failed to update nuxt.config.ts:", error);
|
|
6222
4087
|
}
|
|
6223
4088
|
}
|
|
6224
|
-
function generateReadmeContent(options) {
|
|
6225
|
-
const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc", webDeploy, serverDeploy } = options;
|
|
6226
|
-
const isConvex = backend === "convex";
|
|
6227
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
6228
|
-
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
6229
|
-
const hasSvelte = frontend.includes("svelte");
|
|
6230
|
-
const packageManagerRunCmd = `${packageManager} run`;
|
|
6231
|
-
let webPort = "3001";
|
|
6232
|
-
if (hasReactRouter || hasSvelte) webPort = "5173";
|
|
6233
|
-
const stackDescription = generateStackDescription(frontend, backend, api, isConvex);
|
|
6234
|
-
return `# ${projectName}
|
|
6235
|
-
|
|
6236
|
-
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack${stackDescription ? ` that combines ${stackDescription}` : ""}.
|
|
6237
|
-
|
|
6238
|
-
## Features
|
|
6239
|
-
|
|
6240
|
-
${generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api)}
|
|
6241
|
-
|
|
6242
|
-
## Getting Started
|
|
6243
|
-
|
|
6244
|
-
First, install the dependencies:
|
|
6245
4089
|
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
4090
|
+
//#endregion
|
|
4091
|
+
//#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
|
|
4092
|
+
/**
|
|
4093
|
+
* Alchemy Svelte setup - CLI-only operations
|
|
4094
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts
|
|
4095
|
+
* This only modifies config files for "add deploy" command
|
|
4096
|
+
*/
|
|
4097
|
+
async function setupSvelteAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4098
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4099
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4100
|
+
const svelteConfigPath = path.join(webAppDir, "svelte.config.js");
|
|
4101
|
+
if (!await fs.pathExists(svelteConfigPath)) return;
|
|
4102
|
+
try {
|
|
4103
|
+
const project = new Project({ manipulationSettings: {
|
|
4104
|
+
indentationText: IndentationText.TwoSpaces,
|
|
4105
|
+
quoteKind: QuoteKind.Single
|
|
4106
|
+
} });
|
|
4107
|
+
project.addSourceFileAtPath(svelteConfigPath);
|
|
4108
|
+
const sourceFile = project.getSourceFileOrThrow(svelteConfigPath);
|
|
4109
|
+
const adapterImport = sourceFile.getImportDeclarations().find((imp) => imp.getModuleSpecifierValue().includes("@sveltejs/adapter"));
|
|
4110
|
+
if (adapterImport) {
|
|
4111
|
+
adapterImport.setModuleSpecifier("alchemy/cloudflare/sveltekit");
|
|
4112
|
+
adapterImport.removeDefaultImport();
|
|
4113
|
+
adapterImport.setDefaultImport("alchemy");
|
|
4114
|
+
} else sourceFile.insertImportDeclaration(0, {
|
|
4115
|
+
moduleSpecifier: "alchemy/cloudflare/sveltekit",
|
|
4116
|
+
defaultImport: "alchemy"
|
|
4117
|
+
});
|
|
4118
|
+
const configVariable = sourceFile.getVariableDeclaration("config");
|
|
4119
|
+
if (configVariable) {
|
|
4120
|
+
const initializer = configVariable.getInitializer();
|
|
4121
|
+
if (Node.isObjectLiteralExpression(initializer)) updateAdapterInConfig(initializer);
|
|
4122
|
+
}
|
|
4123
|
+
await project.save();
|
|
4124
|
+
} catch (error) {
|
|
4125
|
+
console.warn("Failed to update svelte.config.js:", error);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
function updateAdapterInConfig(configObject) {
|
|
4129
|
+
if (!Node.isObjectLiteralExpression(configObject)) return;
|
|
4130
|
+
const kitProperty = configObject.getProperty("kit");
|
|
4131
|
+
if (kitProperty && Node.isPropertyAssignment(kitProperty)) {
|
|
4132
|
+
const kitInitializer = kitProperty.getInitializer();
|
|
4133
|
+
if (Node.isObjectLiteralExpression(kitInitializer)) {
|
|
4134
|
+
const adapterProperty = kitInitializer.getProperty("adapter");
|
|
4135
|
+
if (adapterProperty && Node.isPropertyAssignment(adapterProperty)) {
|
|
4136
|
+
const initializer = adapterProperty.getInitializer();
|
|
4137
|
+
if (Node.isCallExpression(initializer)) {
|
|
4138
|
+
const expression = initializer.getExpression();
|
|
4139
|
+
if (Node.isIdentifier(expression) && expression.getText() === "adapter") expression.replaceWithText("alchemy");
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
6271
4145
|
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
4146
|
+
//#endregion
|
|
4147
|
+
//#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
|
|
4148
|
+
/**
|
|
4149
|
+
* Alchemy TanStack Start setup - CLI-only operations
|
|
4150
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts
|
|
4151
|
+
* This only modifies vite.config.ts for "add deploy" command
|
|
4152
|
+
*/
|
|
4153
|
+
async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4154
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4155
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4156
|
+
const viteConfigPath = path.join(webAppDir, "vite.config.ts");
|
|
4157
|
+
if (await fs.pathExists(viteConfigPath)) try {
|
|
4158
|
+
const project = new Project({ manipulationSettings: {
|
|
4159
|
+
indentationText: IndentationText.TwoSpaces,
|
|
4160
|
+
quoteKind: QuoteKind.Double
|
|
4161
|
+
} });
|
|
4162
|
+
project.addSourceFileAtPath(viteConfigPath);
|
|
4163
|
+
const sourceFile = project.getSourceFileOrThrow(viteConfigPath);
|
|
4164
|
+
const alchemyImport = sourceFile.getImportDeclaration("alchemy/cloudflare/tanstack-start");
|
|
4165
|
+
if (!alchemyImport) sourceFile.addImportDeclaration({
|
|
4166
|
+
moduleSpecifier: "alchemy/cloudflare/tanstack-start",
|
|
4167
|
+
defaultImport: "alchemy"
|
|
4168
|
+
});
|
|
4169
|
+
else alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
|
|
4170
|
+
const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
|
|
4171
|
+
if (!exportAssignment) return;
|
|
4172
|
+
const defineConfigCall = exportAssignment.getExpression();
|
|
4173
|
+
if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineConfig") return;
|
|
4174
|
+
let configObject = defineConfigCall.getArguments()[0];
|
|
4175
|
+
if (!configObject) configObject = defineConfigCall.addArgument("{}");
|
|
4176
|
+
if (Node.isObjectLiteralExpression(configObject)) {
|
|
4177
|
+
const pluginsProperty = configObject.getProperty("plugins");
|
|
4178
|
+
if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
|
|
4179
|
+
const initializer = pluginsProperty.getInitializer();
|
|
4180
|
+
if (Node.isArrayLiteralExpression(initializer)) {
|
|
4181
|
+
if (!initializer.getElements().some((el) => el.getText().includes("alchemy("))) initializer.addElement("alchemy()");
|
|
4182
|
+
}
|
|
4183
|
+
} else configObject.addPropertyAssignment({
|
|
4184
|
+
name: "plugins",
|
|
4185
|
+
initializer: "[alchemy()]"
|
|
4186
|
+
});
|
|
4187
|
+
}
|
|
4188
|
+
await project.save();
|
|
4189
|
+
} catch (error) {
|
|
4190
|
+
console.warn("Failed to update vite.config.ts:", error);
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
6275
4193
|
|
|
6276
|
-
|
|
4194
|
+
//#endregion
|
|
4195
|
+
//#region src/helpers/deployment/alchemy/alchemy-vite-setup.ts
|
|
4196
|
+
/**
|
|
4197
|
+
* Alchemy setup for Vite-based frontends (React Router, Solid, TanStack Router)
|
|
4198
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts
|
|
4199
|
+
* These frontends don't need config modifications - templates already have correct setup
|
|
4200
|
+
*/
|
|
4201
|
+
async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4202
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4203
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4204
|
+
}
|
|
4205
|
+
async function setupSolidAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4206
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4207
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4208
|
+
}
|
|
4209
|
+
async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, _options) {
|
|
4210
|
+
const webAppDir = path.join(projectDir, "apps/web");
|
|
4211
|
+
if (!await fs.pathExists(webAppDir)) return;
|
|
4212
|
+
}
|
|
6277
4213
|
|
|
6278
|
-
|
|
6279
|
-
|
|
4214
|
+
//#endregion
|
|
4215
|
+
//#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
|
|
4216
|
+
function getInfraFilter(packageManager, hasTurborepo, infraWorkspace) {
|
|
4217
|
+
if (hasTurborepo) return (script) => `turbo -F ${infraWorkspace} ${script}`;
|
|
4218
|
+
switch (packageManager) {
|
|
4219
|
+
case "pnpm": return (script) => `pnpm --filter ${infraWorkspace} ${script}`;
|
|
4220
|
+
case "npm": return (script) => `npm run ${script} --workspace ${infraWorkspace}`;
|
|
4221
|
+
case "bun": return (script) => `bun run --filter ${infraWorkspace} ${script}`;
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
|
|
4225
|
+
await setupInfraScripts(projectDir, packageManager, config);
|
|
4226
|
+
const frontend = config.frontend;
|
|
4227
|
+
const isNext = frontend.includes("next");
|
|
4228
|
+
const isNuxt = frontend.includes("nuxt");
|
|
4229
|
+
const isSvelte = frontend.includes("svelte");
|
|
4230
|
+
const isTanstackRouter = frontend.includes("tanstack-router");
|
|
4231
|
+
const isTanstackStart = frontend.includes("tanstack-start");
|
|
4232
|
+
const isReactRouter = frontend.includes("react-router");
|
|
4233
|
+
const isSolid = frontend.includes("solid");
|
|
4234
|
+
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4235
|
+
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4236
|
+
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4237
|
+
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4238
|
+
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4239
|
+
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
4240
|
+
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
|
|
6280
4241
|
}
|
|
6281
|
-
function
|
|
6282
|
-
const
|
|
6283
|
-
const
|
|
6284
|
-
const
|
|
6285
|
-
const
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
else if (hasSvelte) parts.push("SvelteKit");
|
|
6296
|
-
else if (hasNuxt) parts.push("Nuxt");
|
|
6297
|
-
else if (hasSolid) parts.push("SolidJS");
|
|
6298
|
-
}
|
|
6299
|
-
if (backend !== "none") parts.push(backend[0].toUpperCase() + backend.slice(1));
|
|
6300
|
-
if (!isConvex && api !== "none") parts.push(api.toUpperCase());
|
|
6301
|
-
return parts.length > 0 ? `${parts.join(", ")}, and more` : "";
|
|
6302
|
-
}
|
|
6303
|
-
function generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex) {
|
|
6304
|
-
const instructions = [];
|
|
6305
|
-
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
6306
|
-
const isBackendNone = backend === "none";
|
|
6307
|
-
const isBackendSelf = backend === "self";
|
|
6308
|
-
if (!hasFrontendNone) {
|
|
6309
|
-
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
6310
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
6311
|
-
const hasNext = frontend.includes("next");
|
|
6312
|
-
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
6313
|
-
const hasSvelte = frontend.includes("svelte");
|
|
6314
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
6315
|
-
const hasSolid = frontend.includes("solid");
|
|
6316
|
-
if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) if (isBackendSelf) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see your fullstack application.`);
|
|
6317
|
-
else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
|
|
6318
|
-
}
|
|
6319
|
-
if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
|
|
6320
|
-
if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
|
|
6321
|
-
else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
|
|
6322
|
-
return instructions.join("\n");
|
|
6323
|
-
}
|
|
6324
|
-
function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
|
|
6325
|
-
const structure = [`${projectName}/`, "├── apps/"];
|
|
6326
|
-
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
6327
|
-
const isBackendNone = backend === "none";
|
|
6328
|
-
const isBackendSelf = backend === "self";
|
|
6329
|
-
if (!hasFrontendNone) {
|
|
6330
|
-
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
6331
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
6332
|
-
const hasNext = frontend.includes("next");
|
|
6333
|
-
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
6334
|
-
const hasSvelte = frontend.includes("svelte");
|
|
6335
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
6336
|
-
const hasSolid = frontend.includes("solid");
|
|
6337
|
-
if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) {
|
|
6338
|
-
let frontendType = "";
|
|
6339
|
-
if (hasTanstackRouter) frontendType = "React + TanStack Router";
|
|
6340
|
-
else if (hasReactRouter) frontendType = "React + React Router";
|
|
6341
|
-
else if (hasNext) frontendType = "Next.js";
|
|
6342
|
-
else if (hasTanstackStart) frontendType = "React + TanStack Start";
|
|
6343
|
-
else if (hasSvelte) frontendType = "SvelteKit";
|
|
6344
|
-
else if (hasNuxt) frontendType = "Nuxt";
|
|
6345
|
-
else if (hasSolid) frontendType = "SolidJS";
|
|
6346
|
-
if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
|
|
6347
|
-
else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
|
|
6348
|
-
}
|
|
4242
|
+
async function setupInfraScripts(projectDir, packageManager, config) {
|
|
4243
|
+
const projectName = config.projectName;
|
|
4244
|
+
const hasTurborepo = config.addons.includes("turborepo");
|
|
4245
|
+
const infraWorkspace = `@${projectName}/infra`;
|
|
4246
|
+
const rootPkgPath = path.join(projectDir, "package.json");
|
|
4247
|
+
if (await fs.pathExists(rootPkgPath)) {
|
|
4248
|
+
const pkg = await fs.readJson(rootPkgPath);
|
|
4249
|
+
const filter = getInfraFilter(packageManager, hasTurborepo, infraWorkspace);
|
|
4250
|
+
pkg.scripts = {
|
|
4251
|
+
...pkg.scripts,
|
|
4252
|
+
deploy: filter("deploy"),
|
|
4253
|
+
destroy: filter("destroy")
|
|
4254
|
+
};
|
|
4255
|
+
await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
|
|
6349
4256
|
}
|
|
6350
|
-
if (
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
}
|
|
6360
|
-
if (isConvex || !isBackendNone) {
|
|
6361
|
-
structure.push("├── packages/");
|
|
6362
|
-
if (isConvex) {
|
|
6363
|
-
structure.push("│ ├── backend/ # Convex backend functions and schema");
|
|
6364
|
-
if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
|
|
4257
|
+
if (config.serverDeploy === "cloudflare") {
|
|
4258
|
+
const serverPkgPath = path.join(projectDir, "apps/server/package.json");
|
|
4259
|
+
if (await fs.pathExists(serverPkgPath)) {
|
|
4260
|
+
const serverPkg = await fs.readJson(serverPkgPath);
|
|
4261
|
+
if (serverPkg.scripts?.dev) {
|
|
4262
|
+
serverPkg.scripts["dev:bare"] = serverPkg.scripts.dev;
|
|
4263
|
+
delete serverPkg.scripts.dev;
|
|
4264
|
+
await fs.writeJson(serverPkgPath, serverPkg, { spaces: 2 });
|
|
4265
|
+
}
|
|
6365
4266
|
}
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
4267
|
+
}
|
|
4268
|
+
if (config.webDeploy === "cloudflare") {
|
|
4269
|
+
const webPkgPath = path.join(projectDir, "apps/web/package.json");
|
|
4270
|
+
if (await fs.pathExists(webPkgPath)) {
|
|
4271
|
+
const webPkg = await fs.readJson(webPkgPath);
|
|
4272
|
+
if (webPkg.scripts?.dev) {
|
|
4273
|
+
webPkg.scripts["dev:bare"] = webPkg.scripts.dev;
|
|
4274
|
+
delete webPkg.scripts.dev;
|
|
4275
|
+
await fs.writeJson(webPkgPath, webPkg, { spaces: 2 });
|
|
4276
|
+
}
|
|
6370
4277
|
}
|
|
6371
4278
|
}
|
|
6372
|
-
return structure.join("\n");
|
|
6373
4279
|
}
|
|
6374
|
-
function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
|
|
6375
|
-
const isConvex = backend === "convex";
|
|
6376
|
-
const isBackendNone = backend === "none";
|
|
6377
|
-
const hasTanstackRouter = frontend.includes("tanstack-router");
|
|
6378
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
6379
|
-
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
6380
|
-
const hasNext = frontend.includes("next");
|
|
6381
|
-
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
6382
|
-
const hasSvelte = frontend.includes("svelte");
|
|
6383
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
6384
|
-
const hasSolid = frontend.includes("solid");
|
|
6385
|
-
const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
|
|
6386
|
-
const addonsList = ["- **TypeScript** - For type safety and improved developer experience"];
|
|
6387
|
-
if (!hasFrontendNone) {
|
|
6388
|
-
if (hasTanstackRouter) addonsList.push("- **TanStack Router** - File-based routing with full type safety");
|
|
6389
|
-
else if (hasReactRouter) addonsList.push("- **React Router** - Declarative routing for React");
|
|
6390
|
-
else if (hasNext) addonsList.push("- **Next.js** - Full-stack React framework");
|
|
6391
|
-
else if (hasTanstackStart) addonsList.push("- **TanStack Start** - SSR framework with TanStack Router");
|
|
6392
|
-
else if (hasSvelte) addonsList.push("- **SvelteKit** - Web framework for building Svelte apps");
|
|
6393
|
-
else if (hasNuxt) addonsList.push("- **Nuxt** - The Intuitive Vue Framework");
|
|
6394
|
-
else if (hasSolid) addonsList.push("- **SolidJS** - Simple and performant reactivity");
|
|
6395
|
-
}
|
|
6396
|
-
if (hasNative) {
|
|
6397
|
-
addonsList.push("- **React Native** - Build mobile apps using React");
|
|
6398
|
-
addonsList.push("- **Expo** - Tools for React Native development");
|
|
6399
|
-
}
|
|
6400
|
-
if (!hasFrontendNone) addonsList.push("- **TailwindCSS** - Utility-first CSS for rapid UI development", "- **shadcn/ui** - Reusable UI components");
|
|
6401
|
-
if (isConvex) addonsList.push("- **Convex** - Reactive backend-as-a-service platform");
|
|
6402
|
-
else if (!isBackendNone) {
|
|
6403
|
-
if (backend === "hono") addonsList.push("- **Hono** - Lightweight, performant server framework");
|
|
6404
|
-
else if (backend === "express") addonsList.push("- **Express** - Fast, unopinionated web framework");
|
|
6405
|
-
else if (backend === "fastify") addonsList.push("- **Fastify** - Fast, low-overhead web framework");
|
|
6406
|
-
else if (backend === "elysia") addonsList.push("- **Elysia** - Type-safe, high-performance framework");
|
|
6407
|
-
if (api === "trpc") addonsList.push("- **tRPC** - End-to-end type-safe APIs");
|
|
6408
|
-
else if (api === "orpc") addonsList.push("- **oRPC** - End-to-end type-safe APIs with OpenAPI integration");
|
|
6409
|
-
if (runtime !== "none") addonsList.push(`- **${runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime}** - Runtime environment`);
|
|
6410
|
-
}
|
|
6411
|
-
if (database !== "none" && !isConvex) {
|
|
6412
|
-
const ormName = orm === "drizzle" ? "Drizzle" : orm === "prisma" ? "Prisma" : orm === "mongoose" ? "Mongoose" : "ORM";
|
|
6413
|
-
const dbName = database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : database === "mongodb" ? "MongoDB" : "Database";
|
|
6414
|
-
addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
|
|
6415
|
-
}
|
|
6416
|
-
if (auth !== "none") {
|
|
6417
|
-
const authLabel = auth === "clerk" ? "Clerk" : "Better-Auth";
|
|
6418
|
-
addonsList.push(`- **Authentication** - ${authLabel}`);
|
|
6419
|
-
}
|
|
6420
|
-
for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
|
|
6421
|
-
else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
|
|
6422
|
-
else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
|
|
6423
|
-
else if (addon === "oxlint") addonsList.push("- **Oxlint** - Oxlint + Oxfmt (linting & formatting)");
|
|
6424
|
-
else if (addon === "husky") addonsList.push("- **Husky** - Git hooks for code quality");
|
|
6425
|
-
else if (addon === "starlight") addonsList.push("- **Starlight** - Documentation site with Astro");
|
|
6426
|
-
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
6427
|
-
return addonsList.join("\n");
|
|
6428
|
-
}
|
|
6429
|
-
function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, _serverDeploy, backend) {
|
|
6430
|
-
if (database === "none") return "";
|
|
6431
|
-
const isBackendSelf = backend === "self";
|
|
6432
|
-
const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
|
|
6433
|
-
let setup = "## Database Setup\n\n";
|
|
6434
|
-
if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6435
|
-
|
|
6436
|
-
1. Start the local SQLite database (optional):
|
|
6437
|
-
${dbSetup === "d1" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : `\`\`\`bash
|
|
6438
|
-
${packageManagerRunCmd} db:local
|
|
6439
|
-
\`\`\``}
|
|
6440
|
-
|
|
6441
|
-
2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
|
|
6442
|
-
`;
|
|
6443
|
-
else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6444
|
-
|
|
6445
|
-
1. Make sure you have a PostgreSQL database set up.
|
|
6446
|
-
2. Update your \`${envPath}\` file with your PostgreSQL connection details.
|
|
6447
|
-
`;
|
|
6448
|
-
else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
|
|
6449
4280
|
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
${packageManagerRunCmd} db:push
|
|
6465
|
-
\`\`\`` : `Apply the schema to your database:
|
|
6466
|
-
\`\`\`bash
|
|
6467
|
-
${packageManagerRunCmd} db:push
|
|
6468
|
-
\`\`\``}
|
|
6469
|
-
`;
|
|
6470
|
-
return setup;
|
|
6471
|
-
}
|
|
6472
|
-
function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend, dbSetup) {
|
|
6473
|
-
const isConvex = backend === "convex";
|
|
6474
|
-
const isBackendNone = backend === "none";
|
|
6475
|
-
const isBackendSelf = backend === "self";
|
|
6476
|
-
let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
|
|
6477
|
-
- \`${packageManagerRunCmd} build\`: Build all applications`;
|
|
6478
|
-
if (!isBackendSelf) scripts += `
|
|
6479
|
-
- \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
|
|
6480
|
-
if (isConvex) scripts += `
|
|
6481
|
-
- \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
|
|
6482
|
-
else if (!isBackendNone && !isBackendSelf) scripts += `
|
|
6483
|
-
- \`${packageManagerRunCmd} dev:server\`: Start only the server`;
|
|
6484
|
-
scripts += `
|
|
6485
|
-
- \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
|
|
6486
|
-
if (hasNative) scripts += `
|
|
6487
|
-
- \`${packageManagerRunCmd} dev:native\`: Start the React Native/Expo development server`;
|
|
6488
|
-
if (database !== "none" && !isConvex) {
|
|
6489
|
-
scripts += `
|
|
6490
|
-
- \`${packageManagerRunCmd} db:push\`: Push schema changes to database
|
|
6491
|
-
- \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
|
|
6492
|
-
if (database === "sqlite" && dbSetup !== "d1") scripts += `
|
|
6493
|
-
- \`${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
|
|
6494
|
-
}
|
|
6495
|
-
if (addons.includes("biome")) scripts += `
|
|
6496
|
-
- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
|
|
6497
|
-
if (addons.includes("oxlint")) scripts += `
|
|
6498
|
-
- \`${packageManagerRunCmd} check\`: Run Oxlint and Oxfmt`;
|
|
6499
|
-
if (addons.includes("pwa")) scripts += `
|
|
6500
|
-
- \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`;
|
|
6501
|
-
if (addons.includes("tauri")) scripts += `
|
|
6502
|
-
- \`cd apps/web && ${packageManagerRunCmd} desktop:dev\`: Start Tauri desktop app in development
|
|
6503
|
-
- \`cd apps/web && ${packageManagerRunCmd} desktop:build\`: Build Tauri desktop app`;
|
|
6504
|
-
if (addons.includes("starlight")) scripts += `
|
|
6505
|
-
- \`cd apps/docs && ${packageManagerRunCmd} dev\`: Start documentation site
|
|
6506
|
-
- \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
|
|
6507
|
-
return scripts;
|
|
6508
|
-
}
|
|
6509
|
-
function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
|
|
6510
|
-
const lines = [];
|
|
6511
|
-
if (webDeploy === "cloudflare" || serverDeploy === "cloudflare") {
|
|
6512
|
-
lines.push("## Deployment (Cloudflare via Alchemy)");
|
|
6513
|
-
if (webDeploy === "cloudflare" && serverDeploy !== "cloudflare") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
|
|
6514
|
-
if (serverDeploy === "cloudflare" && webDeploy !== "cloudflare") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
|
|
6515
|
-
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
|
|
6516
|
-
lines.push("", "For more details, see the guide on [Deploying to Cloudflare with Alchemy](https://www.better-t-stack.dev/docs/guides/cloudflare-alchemy).");
|
|
6517
|
-
}
|
|
6518
|
-
return lines.length ? `${lines.join("\n")}\n` : "";
|
|
4281
|
+
//#endregion
|
|
4282
|
+
//#region src/helpers/deployment/server-deploy-setup.ts
|
|
4283
|
+
/**
|
|
4284
|
+
* Server deploy setup - CLI-only operations
|
|
4285
|
+
* NOTE: Dependencies are handled by template-generator's deploy-deps.ts processor
|
|
4286
|
+
* This file only handles external CLI calls for "add deploy" command
|
|
4287
|
+
*/
|
|
4288
|
+
async function setupServerDeploy(config) {
|
|
4289
|
+
const { serverDeploy, webDeploy, projectDir, packageManager } = config;
|
|
4290
|
+
if (serverDeploy === "none") return;
|
|
4291
|
+
if (serverDeploy === "cloudflare" && webDeploy === "cloudflare") return;
|
|
4292
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
4293
|
+
if (!await fs.pathExists(serverDir)) return;
|
|
4294
|
+
if (serverDeploy === "cloudflare") await setupInfraScripts(projectDir, packageManager, config);
|
|
6519
4295
|
}
|
|
6520
4296
|
|
|
6521
4297
|
//#endregion
|
|
6522
|
-
//#region src/helpers/
|
|
6523
|
-
async function
|
|
6524
|
-
const
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
const
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
4298
|
+
//#region src/helpers/deployment/web-deploy-setup.ts
|
|
4299
|
+
async function setupWebDeploy(config) {
|
|
4300
|
+
const { webDeploy, serverDeploy, frontend, projectDir } = config;
|
|
4301
|
+
const { packageManager } = config;
|
|
4302
|
+
if (webDeploy === "none") return;
|
|
4303
|
+
if (webDeploy !== "cloudflare") return;
|
|
4304
|
+
if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") {
|
|
4305
|
+
await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
|
|
4306
|
+
return;
|
|
4307
|
+
}
|
|
4308
|
+
await setupInfraScripts(projectDir, packageManager, config);
|
|
4309
|
+
const isNext = frontend.includes("next");
|
|
4310
|
+
const isNuxt = frontend.includes("nuxt");
|
|
4311
|
+
const isSvelte = frontend.includes("svelte");
|
|
4312
|
+
const isTanstackRouter = frontend.includes("tanstack-router");
|
|
4313
|
+
const isTanstackStart = frontend.includes("tanstack-start");
|
|
4314
|
+
const isReactRouter = frontend.includes("react-router");
|
|
4315
|
+
const isSolid = frontend.includes("solid");
|
|
4316
|
+
if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager);
|
|
4317
|
+
else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
|
|
4318
|
+
else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
|
|
4319
|
+
else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
|
|
4320
|
+
else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
|
|
4321
|
+
else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
|
|
4322
|
+
else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
|
|
6539
4323
|
}
|
|
6540
4324
|
|
|
6541
4325
|
//#endregion
|
|
@@ -6561,44 +4345,19 @@ async function initializeGit(projectDir, useGit) {
|
|
|
6561
4345
|
}
|
|
6562
4346
|
|
|
6563
4347
|
//#endregion
|
|
6564
|
-
//#region src/helpers/core/
|
|
6565
|
-
async function
|
|
6566
|
-
const
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
}
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
const { payments, projectDir, frontend } = config;
|
|
6578
|
-
if (!payments || payments === "none") return;
|
|
6579
|
-
const clientDir = path.join(projectDir, "apps/web");
|
|
6580
|
-
const authDir = path.join(projectDir, "packages/auth");
|
|
6581
|
-
const clientDirExists = await fs.pathExists(clientDir);
|
|
6582
|
-
const authDirExists = await fs.pathExists(authDir);
|
|
6583
|
-
if (payments === "polar") {
|
|
6584
|
-
if (authDirExists) await addPackageDependency({
|
|
6585
|
-
dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
|
|
6586
|
-
projectDir: authDir
|
|
6587
|
-
});
|
|
6588
|
-
if (clientDirExists) {
|
|
6589
|
-
if (frontend.some((f) => [
|
|
6590
|
-
"react-router",
|
|
6591
|
-
"tanstack-router",
|
|
6592
|
-
"tanstack-start",
|
|
6593
|
-
"next",
|
|
6594
|
-
"nuxt",
|
|
6595
|
-
"svelte",
|
|
6596
|
-
"solid"
|
|
6597
|
-
].includes(f))) await addPackageDependency({
|
|
6598
|
-
dependencies: ["@polar-sh/better-auth"],
|
|
6599
|
-
projectDir: clientDir
|
|
6600
|
-
});
|
|
6601
|
-
}
|
|
4348
|
+
//#region src/helpers/core/install-dependencies.ts
|
|
4349
|
+
async function installDependencies({ projectDir, packageManager }) {
|
|
4350
|
+
const s = spinner();
|
|
4351
|
+
try {
|
|
4352
|
+
s.start(`Running ${packageManager} install...`);
|
|
4353
|
+
await $({
|
|
4354
|
+
cwd: projectDir,
|
|
4355
|
+
stderr: "inherit"
|
|
4356
|
+
})`${packageManager} install`;
|
|
4357
|
+
s.stop("Dependencies installed successfully");
|
|
4358
|
+
} catch (error) {
|
|
4359
|
+
s.stop(pc.red("Failed to install dependencies"));
|
|
4360
|
+
if (error instanceof Error) consola.error(pc.red(`Installation error: ${error.message}`));
|
|
6602
4361
|
}
|
|
6603
4362
|
}
|
|
6604
4363
|
|
|
@@ -6817,375 +4576,31 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend)
|
|
|
6817
4576
|
return instructions.length ? `\n${instructions.join("\n")}` : "";
|
|
6818
4577
|
}
|
|
6819
4578
|
|
|
6820
|
-
//#endregion
|
|
6821
|
-
//#region src/helpers/core/workspace-setup.ts
|
|
6822
|
-
async function setupWorkspaceDependencies(projectDir, options) {
|
|
6823
|
-
const { projectName, packageManager, runtime, backend } = options;
|
|
6824
|
-
const workspaceVersion = packageManager === "npm" ? "*" : "workspace:*";
|
|
6825
|
-
const packages = await detectPackages(projectDir);
|
|
6826
|
-
const configDep = packages.config.exists ? { [`@${projectName}/config`]: workspaceVersion } : {};
|
|
6827
|
-
const envDep = packages.env.exists ? { [`@${projectName}/env`]: workspaceVersion } : {};
|
|
6828
|
-
const ctx = {
|
|
6829
|
-
projectName,
|
|
6830
|
-
workspaceVersion,
|
|
6831
|
-
options,
|
|
6832
|
-
commonDeps: ["dotenv", "zod"],
|
|
6833
|
-
commonDevDeps: ["typescript"],
|
|
6834
|
-
configDep,
|
|
6835
|
-
envDep
|
|
6836
|
-
};
|
|
6837
|
-
await Promise.all([
|
|
6838
|
-
setupEnvPackage(packages.env, packages.infra, ctx),
|
|
6839
|
-
setupInfraPackage(packages.infra, ctx),
|
|
6840
|
-
setupDbPackage(packages.db, ctx),
|
|
6841
|
-
setupAuthPackage(packages.auth, packages.db, ctx),
|
|
6842
|
-
setupApiPackage(packages.api, packages.auth, packages.db, ctx),
|
|
6843
|
-
setupBackendPackage(packages.backend, ctx),
|
|
6844
|
-
setupServerPackage(packages.server, packages.api, packages.auth, packages.db, ctx),
|
|
6845
|
-
setupWebPackage(packages.web, packages.api, packages.auth, packages.backend, ctx),
|
|
6846
|
-
setupNativePackage(packages.native, packages.api, packages.backend, ctx)
|
|
6847
|
-
]);
|
|
6848
|
-
const runtimeDevDeps = getRuntimeDevDeps(runtime, backend);
|
|
6849
|
-
await addPackageDependency({
|
|
6850
|
-
dependencies: ctx.commonDeps,
|
|
6851
|
-
devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
|
|
6852
|
-
customDependencies: envDep,
|
|
6853
|
-
customDevDependencies: configDep,
|
|
6854
|
-
projectDir
|
|
6855
|
-
});
|
|
6856
|
-
}
|
|
6857
|
-
async function detectPackages(projectDir) {
|
|
6858
|
-
const entries = await Promise.all(Object.entries({
|
|
6859
|
-
config: "packages/config",
|
|
6860
|
-
env: "packages/env",
|
|
6861
|
-
infra: "packages/infra",
|
|
6862
|
-
db: "packages/db",
|
|
6863
|
-
auth: "packages/auth",
|
|
6864
|
-
api: "packages/api",
|
|
6865
|
-
backend: "packages/backend",
|
|
6866
|
-
server: "apps/server",
|
|
6867
|
-
web: "apps/web",
|
|
6868
|
-
native: "apps/native"
|
|
6869
|
-
}).map(async ([name, relativePath]) => {
|
|
6870
|
-
const dir = path.join(projectDir, relativePath);
|
|
6871
|
-
return [name, {
|
|
6872
|
-
dir,
|
|
6873
|
-
exists: await fs.pathExists(dir)
|
|
6874
|
-
}];
|
|
6875
|
-
}));
|
|
6876
|
-
return Object.fromEntries(entries);
|
|
6877
|
-
}
|
|
6878
|
-
async function setupEnvPackage(pkg, infraPkg, ctx) {
|
|
6879
|
-
if (!pkg.exists) return;
|
|
6880
|
-
const runtimeDevDeps = getRuntimeDevDeps(ctx.options.runtime, ctx.options.backend);
|
|
6881
|
-
const customDevDeps = { ...ctx.configDep };
|
|
6882
|
-
if ((ctx.options.serverDeploy === "cloudflare" || ctx.options.webDeploy === "cloudflare") && infraPkg.exists) customDevDeps[`@${ctx.projectName}/infra`] = ctx.workspaceVersion;
|
|
6883
|
-
await addPackageDependency({
|
|
6884
|
-
dependencies: ctx.commonDeps,
|
|
6885
|
-
devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
|
|
6886
|
-
customDevDependencies: customDevDeps,
|
|
6887
|
-
projectDir: pkg.dir
|
|
6888
|
-
});
|
|
6889
|
-
}
|
|
6890
|
-
async function setupInfraPackage(pkg, ctx) {
|
|
6891
|
-
if (!pkg.exists) return;
|
|
6892
|
-
await addPackageDependency({
|
|
6893
|
-
dependencies: ctx.commonDeps,
|
|
6894
|
-
devDependencies: ctx.commonDevDeps,
|
|
6895
|
-
customDevDependencies: ctx.configDep,
|
|
6896
|
-
projectDir: pkg.dir
|
|
6897
|
-
});
|
|
6898
|
-
}
|
|
6899
|
-
async function setupDbPackage(pkg, ctx) {
|
|
6900
|
-
if (!pkg.exists) return;
|
|
6901
|
-
await addPackageDependency({
|
|
6902
|
-
dependencies: ctx.commonDeps,
|
|
6903
|
-
devDependencies: ctx.commonDevDeps,
|
|
6904
|
-
customDependencies: ctx.envDep,
|
|
6905
|
-
customDevDependencies: ctx.configDep,
|
|
6906
|
-
projectDir: pkg.dir
|
|
6907
|
-
});
|
|
6908
|
-
}
|
|
6909
|
-
async function setupAuthPackage(pkg, dbPkg, ctx) {
|
|
6910
|
-
if (!pkg.exists) return;
|
|
6911
|
-
const deps = { ...ctx.envDep };
|
|
6912
|
-
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6913
|
-
await addPackageDependency({
|
|
6914
|
-
dependencies: ctx.commonDeps,
|
|
6915
|
-
devDependencies: ctx.commonDevDeps,
|
|
6916
|
-
customDependencies: deps,
|
|
6917
|
-
customDevDependencies: ctx.configDep,
|
|
6918
|
-
projectDir: pkg.dir
|
|
6919
|
-
});
|
|
6920
|
-
}
|
|
6921
|
-
async function setupApiPackage(pkg, authPkg, dbPkg, ctx) {
|
|
6922
|
-
if (!pkg.exists) return;
|
|
6923
|
-
const deps = { ...ctx.envDep };
|
|
6924
|
-
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6925
|
-
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6926
|
-
await addPackageDependency({
|
|
6927
|
-
dependencies: ctx.commonDeps,
|
|
6928
|
-
devDependencies: ctx.commonDevDeps,
|
|
6929
|
-
customDependencies: deps,
|
|
6930
|
-
customDevDependencies: ctx.configDep,
|
|
6931
|
-
projectDir: pkg.dir
|
|
6932
|
-
});
|
|
6933
|
-
}
|
|
6934
|
-
async function setupBackendPackage(pkg, ctx) {
|
|
6935
|
-
if (!pkg.exists) return;
|
|
6936
|
-
await addPackageDependency({
|
|
6937
|
-
dependencies: ctx.commonDeps,
|
|
6938
|
-
devDependencies: ctx.commonDevDeps,
|
|
6939
|
-
customDevDependencies: ctx.configDep,
|
|
6940
|
-
projectDir: pkg.dir
|
|
6941
|
-
});
|
|
6942
|
-
}
|
|
6943
|
-
async function setupServerPackage(pkg, apiPkg, authPkg, dbPkg, ctx) {
|
|
6944
|
-
if (!pkg.exists) return;
|
|
6945
|
-
const deps = { ...ctx.envDep };
|
|
6946
|
-
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6947
|
-
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6948
|
-
if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
|
|
6949
|
-
await addPackageDependency({
|
|
6950
|
-
dependencies: ctx.commonDeps,
|
|
6951
|
-
devDependencies: [...ctx.commonDevDeps, "tsdown"],
|
|
6952
|
-
customDependencies: deps,
|
|
6953
|
-
customDevDependencies: ctx.configDep,
|
|
6954
|
-
projectDir: pkg.dir
|
|
6955
|
-
});
|
|
6956
|
-
}
|
|
6957
|
-
async function setupWebPackage(pkg, apiPkg, authPkg, backendPkg, ctx) {
|
|
6958
|
-
if (!pkg.exists) return;
|
|
6959
|
-
const deps = { ...ctx.envDep };
|
|
6960
|
-
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6961
|
-
if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
|
|
6962
|
-
if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
|
|
6963
|
-
await addPackageDependency({
|
|
6964
|
-
dependencies: ctx.commonDeps,
|
|
6965
|
-
devDependencies: ctx.commonDevDeps,
|
|
6966
|
-
customDependencies: deps,
|
|
6967
|
-
customDevDependencies: ctx.configDep,
|
|
6968
|
-
projectDir: pkg.dir
|
|
6969
|
-
});
|
|
6970
|
-
}
|
|
6971
|
-
async function setupNativePackage(pkg, apiPkg, backendPkg, ctx) {
|
|
6972
|
-
if (!pkg.exists) return;
|
|
6973
|
-
const deps = { ...ctx.envDep };
|
|
6974
|
-
if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
|
|
6975
|
-
if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
|
|
6976
|
-
await addPackageDependency({
|
|
6977
|
-
dependencies: ctx.commonDeps,
|
|
6978
|
-
devDependencies: ctx.commonDevDeps,
|
|
6979
|
-
customDependencies: deps,
|
|
6980
|
-
customDevDependencies: ctx.configDep,
|
|
6981
|
-
projectDir: pkg.dir
|
|
6982
|
-
});
|
|
6983
|
-
}
|
|
6984
|
-
function getRuntimeDevDeps(runtime, backend) {
|
|
6985
|
-
if (runtime === "none" && backend === "self") return ["@types/node"];
|
|
6986
|
-
if (runtime === "node" || runtime === "workers") return ["@types/node"];
|
|
6987
|
-
if (runtime === "bun") return ["@types/bun"];
|
|
6988
|
-
return [];
|
|
6989
|
-
}
|
|
6990
|
-
|
|
6991
|
-
//#endregion
|
|
6992
|
-
//#region src/helpers/core/project-config.ts
|
|
6993
|
-
async function updatePackageConfigurations(projectDir, options) {
|
|
6994
|
-
await updateRootPackageJson(projectDir, options);
|
|
6995
|
-
if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
|
|
6996
|
-
else if (options.backend !== "none") {
|
|
6997
|
-
await updateDbPackageJson(projectDir, options);
|
|
6998
|
-
await updateAuthPackageJson(projectDir, options);
|
|
6999
|
-
await updateApiPackageJson(projectDir, options);
|
|
7000
|
-
if (options.backend !== "self") await updateServerPackageJson(projectDir, options);
|
|
7001
|
-
}
|
|
7002
|
-
await setupWorkspaceDependencies(projectDir, options);
|
|
7003
|
-
}
|
|
7004
|
-
async function updateRootPackageJson(projectDir, options) {
|
|
7005
|
-
const rootPackageJsonPath = path.join(projectDir, "package.json");
|
|
7006
|
-
if (!await fs.pathExists(rootPackageJsonPath)) return;
|
|
7007
|
-
const packageJson = await fs.readJson(rootPackageJsonPath);
|
|
7008
|
-
packageJson.name = options.projectName;
|
|
7009
|
-
packageJson.scripts = packageJson.scripts || {};
|
|
7010
|
-
packageJson.workspaces = packageJson.workspaces || [];
|
|
7011
|
-
const scripts = packageJson.scripts;
|
|
7012
|
-
const workspaces = packageJson.workspaces;
|
|
7013
|
-
const { projectName, packageManager, backend, database, orm, dbSetup, serverDeploy, addons } = options;
|
|
7014
|
-
const backendPackageName = backend === "convex" ? `@${projectName}/backend` : "server";
|
|
7015
|
-
const dbPackageName = `@${projectName}/db`;
|
|
7016
|
-
const hasTurborepo = addons.includes("turborepo");
|
|
7017
|
-
const needsDbScripts = backend !== "convex" && database !== "none" && orm !== "none" && orm !== "mongoose";
|
|
7018
|
-
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
|
|
7019
|
-
const pmConfig = getPackageManagerConfig(packageManager, hasTurborepo);
|
|
7020
|
-
scripts.dev = pmConfig.dev;
|
|
7021
|
-
scripts.build = pmConfig.build;
|
|
7022
|
-
scripts["check-types"] = pmConfig.checkTypes;
|
|
7023
|
-
scripts["dev:native"] = pmConfig.filter("native", "dev");
|
|
7024
|
-
scripts["dev:web"] = pmConfig.filter("web", "dev");
|
|
7025
|
-
if (backend !== "self" && backend !== "none") scripts["dev:server"] = pmConfig.filter(backendPackageName, "dev");
|
|
7026
|
-
if (backend === "convex") scripts["dev:setup"] = pmConfig.filter(backendPackageName, "dev:setup");
|
|
7027
|
-
if (needsDbScripts) {
|
|
7028
|
-
scripts["db:push"] = pmConfig.filter(dbPackageName, "db:push");
|
|
7029
|
-
if (!isD1Alchemy) scripts["db:studio"] = pmConfig.filter(dbPackageName, "db:studio");
|
|
7030
|
-
if (orm === "prisma") {
|
|
7031
|
-
scripts["db:generate"] = pmConfig.filter(dbPackageName, "db:generate");
|
|
7032
|
-
scripts["db:migrate"] = pmConfig.filter(dbPackageName, "db:migrate");
|
|
7033
|
-
} else if (orm === "drizzle") {
|
|
7034
|
-
scripts["db:generate"] = pmConfig.filter(dbPackageName, "db:generate");
|
|
7035
|
-
if (!isD1Alchemy) scripts["db:migrate"] = pmConfig.filter(dbPackageName, "db:migrate");
|
|
7036
|
-
}
|
|
7037
|
-
}
|
|
7038
|
-
if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = pmConfig.filter(dbPackageName, "db:local");
|
|
7039
|
-
if (dbSetup === "docker") {
|
|
7040
|
-
scripts["db:start"] = pmConfig.filter(dbPackageName, "db:start");
|
|
7041
|
-
scripts["db:watch"] = pmConfig.filter(dbPackageName, "db:watch");
|
|
7042
|
-
scripts["db:stop"] = pmConfig.filter(dbPackageName, "db:stop");
|
|
7043
|
-
scripts["db:down"] = pmConfig.filter(dbPackageName, "db:down");
|
|
7044
|
-
}
|
|
7045
|
-
try {
|
|
7046
|
-
const { stdout } = await $`${packageManager} -v`;
|
|
7047
|
-
packageJson.packageManager = `${packageManager}@${stdout.trim()}`;
|
|
7048
|
-
} catch {
|
|
7049
|
-
log.warn(`Could not determine ${packageManager} version.`);
|
|
7050
|
-
}
|
|
7051
|
-
if (backend === "convex") {
|
|
7052
|
-
if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
|
|
7053
|
-
if ((options.frontend.length > 0 || addons.includes("starlight")) && !workspaces.includes("apps/*")) workspaces.push("apps/*");
|
|
7054
|
-
} else {
|
|
7055
|
-
if (!workspaces.includes("apps/*")) workspaces.push("apps/*");
|
|
7056
|
-
if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
|
|
7057
|
-
}
|
|
7058
|
-
await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
|
|
7059
|
-
}
|
|
7060
|
-
function getPackageManagerConfig(packageManager, hasTurborepo) {
|
|
7061
|
-
if (hasTurborepo) return {
|
|
7062
|
-
dev: "turbo dev",
|
|
7063
|
-
build: "turbo build",
|
|
7064
|
-
checkTypes: "turbo check-types",
|
|
7065
|
-
filter: (workspace, script) => `turbo -F ${workspace} ${script}`
|
|
7066
|
-
};
|
|
7067
|
-
switch (packageManager) {
|
|
7068
|
-
case "pnpm": return {
|
|
7069
|
-
dev: "pnpm -r dev",
|
|
7070
|
-
build: "pnpm -r build",
|
|
7071
|
-
checkTypes: "pnpm -r check-types",
|
|
7072
|
-
filter: (workspace, script) => `pnpm --filter ${workspace} ${script}`
|
|
7073
|
-
};
|
|
7074
|
-
case "npm": return {
|
|
7075
|
-
dev: "npm run dev --workspaces",
|
|
7076
|
-
build: "npm run build --workspaces",
|
|
7077
|
-
checkTypes: "npm run check-types --workspaces",
|
|
7078
|
-
filter: (workspace, script) => `npm run ${script} --workspace ${workspace}`
|
|
7079
|
-
};
|
|
7080
|
-
case "bun": return {
|
|
7081
|
-
dev: "bun run --filter '*' dev",
|
|
7082
|
-
build: "bun run --filter '*' build",
|
|
7083
|
-
checkTypes: "bun run --filter '*' check-types",
|
|
7084
|
-
filter: (workspace, script) => `bun run --filter ${workspace} ${script}`
|
|
7085
|
-
};
|
|
7086
|
-
}
|
|
7087
|
-
}
|
|
7088
|
-
async function updateServerPackageJson(projectDir, _options) {
|
|
7089
|
-
const serverPackageJsonPath = path.join(projectDir, "apps/server/package.json");
|
|
7090
|
-
if (!await fs.pathExists(serverPackageJsonPath)) return;
|
|
7091
|
-
const serverPackageJson = await fs.readJson(serverPackageJsonPath);
|
|
7092
|
-
serverPackageJson.scripts = serverPackageJson.scripts || {};
|
|
7093
|
-
await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
|
|
7094
|
-
}
|
|
7095
|
-
async function updateDbPackageJson(projectDir, options) {
|
|
7096
|
-
const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
|
|
7097
|
-
if (!await fs.pathExists(dbPackageJsonPath)) return;
|
|
7098
|
-
const dbPackageJson = await fs.readJson(dbPackageJsonPath);
|
|
7099
|
-
dbPackageJson.name = `@${options.projectName}/db`;
|
|
7100
|
-
dbPackageJson.scripts = dbPackageJson.scripts || {};
|
|
7101
|
-
const scripts = dbPackageJson.scripts;
|
|
7102
|
-
const { database, orm, dbSetup, serverDeploy } = options;
|
|
7103
|
-
const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
|
|
7104
|
-
if (database !== "none") {
|
|
7105
|
-
if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
|
|
7106
|
-
if (orm === "prisma") {
|
|
7107
|
-
scripts["db:push"] = "prisma db push";
|
|
7108
|
-
scripts["db:generate"] = "prisma generate";
|
|
7109
|
-
scripts["db:migrate"] = "prisma migrate dev";
|
|
7110
|
-
if (!isD1Alchemy) scripts["db:studio"] = "prisma studio";
|
|
7111
|
-
} else if (orm === "drizzle") {
|
|
7112
|
-
scripts["db:push"] = "drizzle-kit push";
|
|
7113
|
-
scripts["db:generate"] = "drizzle-kit generate";
|
|
7114
|
-
if (!isD1Alchemy) {
|
|
7115
|
-
scripts["db:studio"] = "drizzle-kit studio";
|
|
7116
|
-
scripts["db:migrate"] = "drizzle-kit migrate";
|
|
7117
|
-
}
|
|
7118
|
-
}
|
|
7119
|
-
}
|
|
7120
|
-
if (dbSetup === "docker") {
|
|
7121
|
-
scripts["db:start"] = "docker compose up -d";
|
|
7122
|
-
scripts["db:watch"] = "docker compose up";
|
|
7123
|
-
scripts["db:stop"] = "docker compose stop";
|
|
7124
|
-
scripts["db:down"] = "docker compose down";
|
|
7125
|
-
}
|
|
7126
|
-
await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
|
|
7127
|
-
}
|
|
7128
|
-
async function updateAuthPackageJson(projectDir, options) {
|
|
7129
|
-
const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
|
|
7130
|
-
if (!await fs.pathExists(authPackageJsonPath)) return;
|
|
7131
|
-
const authPackageJson = await fs.readJson(authPackageJsonPath);
|
|
7132
|
-
authPackageJson.name = `@${options.projectName}/auth`;
|
|
7133
|
-
await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
|
|
7134
|
-
}
|
|
7135
|
-
async function updateApiPackageJson(projectDir, options) {
|
|
7136
|
-
const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
|
|
7137
|
-
if (!await fs.pathExists(apiPackageJsonPath)) return;
|
|
7138
|
-
const apiPackageJson = await fs.readJson(apiPackageJsonPath);
|
|
7139
|
-
apiPackageJson.name = `@${options.projectName}/api`;
|
|
7140
|
-
await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
|
|
7141
|
-
}
|
|
7142
|
-
async function updateConvexPackageJson(projectDir, options) {
|
|
7143
|
-
const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
|
|
7144
|
-
if (!await fs.pathExists(convexPackageJsonPath)) return;
|
|
7145
|
-
const convexPackageJson = await fs.readJson(convexPackageJsonPath);
|
|
7146
|
-
convexPackageJson.name = `@${options.projectName}/backend`;
|
|
7147
|
-
convexPackageJson.scripts = convexPackageJson.scripts || {};
|
|
7148
|
-
await fs.writeJson(convexPackageJsonPath, convexPackageJson, { spaces: 2 });
|
|
7149
|
-
}
|
|
7150
|
-
|
|
7151
4579
|
//#endregion
|
|
7152
4580
|
//#region src/helpers/core/create-project.ts
|
|
7153
4581
|
async function createProject(options, cliInput = {}) {
|
|
7154
4582
|
const projectDir = options.projectDir;
|
|
7155
4583
|
const isConvex = options.backend === "convex";
|
|
7156
|
-
const isSelfBackend = options.backend === "self";
|
|
7157
|
-
const needsServerSetup = !isConvex && !isSelfBackend;
|
|
7158
4584
|
try {
|
|
7159
4585
|
await fs.ensureDir(projectDir);
|
|
7160
|
-
await
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
await
|
|
7168
|
-
await setupDeploymentTemplates(projectDir, options);
|
|
7169
|
-
await setupEnvPackageDependencies(projectDir, options);
|
|
7170
|
-
if (options.serverDeploy === "cloudflare" || options.webDeploy === "cloudflare") await setupInfraPackageDependencies(projectDir, options);
|
|
7171
|
-
await setupApi(options);
|
|
7172
|
-
if (isConvex || needsServerSetup) await setupBackendDependencies(options);
|
|
7173
|
-
if (!isConvex) {
|
|
7174
|
-
if (needsServerSetup) await setupRuntime(options);
|
|
7175
|
-
await setupDatabase(options, cliInput);
|
|
7176
|
-
}
|
|
7177
|
-
if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
|
|
4586
|
+
const result = await generateVirtualProject({
|
|
4587
|
+
config: options,
|
|
4588
|
+
templates: EMBEDDED_TEMPLATES
|
|
4589
|
+
});
|
|
4590
|
+
if (!result.success || !result.tree) throw new Error(result.error || "Failed to generate project templates");
|
|
4591
|
+
await writeTreeToFilesystem(result.tree, projectDir);
|
|
4592
|
+
await setPackageManagerVersion(projectDir, options.packageManager);
|
|
4593
|
+
if (!isConvex && options.database !== "none") await setupDatabase(options, cliInput);
|
|
7178
4594
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
7179
|
-
if (options.auth
|
|
7180
|
-
|
|
7181
|
-
|
|
4595
|
+
if (options.auth === "better-auth" && !isConvex) {
|
|
4596
|
+
const authPackageDir = `${projectDir}/packages/auth`;
|
|
4597
|
+
if (await fs.pathExists(authPackageDir)) await setupBetterAuthPlugins(projectDir, options);
|
|
4598
|
+
}
|
|
7182
4599
|
await setupEnvironmentVariables(options);
|
|
7183
|
-
await updatePackageConfigurations(projectDir, options);
|
|
7184
4600
|
await setupWebDeploy(options);
|
|
7185
4601
|
await setupServerDeploy(options);
|
|
7186
|
-
await setupCatalogs(projectDir, options);
|
|
7187
|
-
await createReadme(projectDir, options);
|
|
7188
4602
|
await writeBtsConfig(options);
|
|
4603
|
+
await formatProject(projectDir);
|
|
7189
4604
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
7190
4605
|
if (options.install) await installDependencies({
|
|
7191
4606
|
projectDir,
|
|
@@ -7207,6 +4622,21 @@ async function createProject(options, cliInput = {}) {
|
|
|
7207
4622
|
}
|
|
7208
4623
|
}
|
|
7209
4624
|
}
|
|
4625
|
+
async function setPackageManagerVersion(projectDir, packageManager) {
|
|
4626
|
+
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
4627
|
+
if (!await fs.pathExists(pkgJsonPath)) return;
|
|
4628
|
+
try {
|
|
4629
|
+
const { stdout } = await $`${packageManager} -v`;
|
|
4630
|
+
const version = stdout.trim();
|
|
4631
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4632
|
+
pkgJson.packageManager = `${packageManager}@${version}`;
|
|
4633
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4634
|
+
} catch {
|
|
4635
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
4636
|
+
delete pkgJson.packageManager;
|
|
4637
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
7210
4640
|
|
|
7211
4641
|
//#endregion
|
|
7212
4642
|
//#region src/helpers/core/command-handlers.ts
|
|
@@ -7412,68 +4842,6 @@ async function handleDirectoryConflictProgrammatically(currentPathInput, strateg
|
|
|
7412
4842
|
default: throw new Error(`Unknown directory conflict strategy: ${strategy}`);
|
|
7413
4843
|
}
|
|
7414
4844
|
}
|
|
7415
|
-
async function addAddonsHandler(input) {
|
|
7416
|
-
try {
|
|
7417
|
-
const projectDir = input.projectDir || process.cwd();
|
|
7418
|
-
const detectedConfig = await detectProjectConfig(projectDir);
|
|
7419
|
-
if (!detectedConfig) exitWithError("Could not detect project configuration. Please ensure this is a valid Better-T-Stack project.");
|
|
7420
|
-
if (!input.addons || input.addons.length === 0) {
|
|
7421
|
-
const addonsPrompt = await getAddonsToAdd(detectedConfig.frontend || [], detectedConfig.addons || [], detectedConfig.auth);
|
|
7422
|
-
if (addonsPrompt.length > 0) input.addons = addonsPrompt;
|
|
7423
|
-
}
|
|
7424
|
-
if (!input.webDeploy) {
|
|
7425
|
-
const deploymentPrompt = await getDeploymentToAdd(detectedConfig.frontend || [], detectedConfig.webDeploy);
|
|
7426
|
-
if (deploymentPrompt !== "none") input.webDeploy = deploymentPrompt;
|
|
7427
|
-
}
|
|
7428
|
-
if (!input.serverDeploy) {
|
|
7429
|
-
const serverDeploymentPrompt = await getServerDeploymentToAdd(detectedConfig.runtime, detectedConfig.serverDeploy, detectedConfig.backend);
|
|
7430
|
-
if (serverDeploymentPrompt !== "none") input.serverDeploy = serverDeploymentPrompt;
|
|
7431
|
-
}
|
|
7432
|
-
const packageManager = input.packageManager || detectedConfig.packageManager || "npm";
|
|
7433
|
-
let somethingAdded = false;
|
|
7434
|
-
if (input.addons && input.addons.length > 0) {
|
|
7435
|
-
await addAddonsToProject({
|
|
7436
|
-
...input,
|
|
7437
|
-
install: false,
|
|
7438
|
-
suppressInstallMessage: true,
|
|
7439
|
-
addons: input.addons
|
|
7440
|
-
});
|
|
7441
|
-
somethingAdded = true;
|
|
7442
|
-
}
|
|
7443
|
-
if (input.webDeploy && input.webDeploy !== "none") {
|
|
7444
|
-
await addDeploymentToProject({
|
|
7445
|
-
...input,
|
|
7446
|
-
install: false,
|
|
7447
|
-
suppressInstallMessage: true,
|
|
7448
|
-
webDeploy: input.webDeploy
|
|
7449
|
-
});
|
|
7450
|
-
somethingAdded = true;
|
|
7451
|
-
}
|
|
7452
|
-
if (input.serverDeploy && input.serverDeploy !== "none") {
|
|
7453
|
-
await addDeploymentToProject({
|
|
7454
|
-
...input,
|
|
7455
|
-
install: false,
|
|
7456
|
-
suppressInstallMessage: true,
|
|
7457
|
-
serverDeploy: input.serverDeploy
|
|
7458
|
-
});
|
|
7459
|
-
somethingAdded = true;
|
|
7460
|
-
}
|
|
7461
|
-
if (!somethingAdded) {
|
|
7462
|
-
outro(pc.yellow("No addons or deployment configurations to add."));
|
|
7463
|
-
return;
|
|
7464
|
-
}
|
|
7465
|
-
if (input.install) await installDependencies({
|
|
7466
|
-
projectDir,
|
|
7467
|
-
packageManager
|
|
7468
|
-
});
|
|
7469
|
-
else log.info(`Run ${pc.bold(`${packageManager} install`)} to install dependencies`);
|
|
7470
|
-
outro("Add command completed successfully!");
|
|
7471
|
-
} catch (error) {
|
|
7472
|
-
if (error instanceof UserCancelledError) return;
|
|
7473
|
-
if (error instanceof CLIError) throw error;
|
|
7474
|
-
handleError(error, "Failed to add addons or deployment");
|
|
7475
|
-
}
|
|
7476
|
-
}
|
|
7477
4845
|
|
|
7478
4846
|
//#endregion
|
|
7479
4847
|
//#region src/utils/open-url.ts
|
|
@@ -7571,17 +4939,6 @@ const router = os.router({
|
|
|
7571
4939
|
});
|
|
7572
4940
|
if (options.verbose) return result;
|
|
7573
4941
|
}),
|
|
7574
|
-
add: os.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z.tuple([z.object({
|
|
7575
|
-
addons: z.array(types_exports.AddonsSchema).optional().default([]),
|
|
7576
|
-
webDeploy: types_exports.WebDeploySchema.optional(),
|
|
7577
|
-
serverDeploy: types_exports.ServerDeploySchema.optional(),
|
|
7578
|
-
projectDir: z.string().optional(),
|
|
7579
|
-
install: z.boolean().optional().default(false).describe("Install dependencies after adding addons or deployment"),
|
|
7580
|
-
packageManager: types_exports.PackageManagerSchema.optional()
|
|
7581
|
-
})])).handler(async ({ input }) => {
|
|
7582
|
-
const [options] = input;
|
|
7583
|
-
await addAddonsHandler(options);
|
|
7584
|
-
}),
|
|
7585
4942
|
sponsors: os.meta({ description: "Show Better-T-Stack sponsors" }).handler(async () => {
|
|
7586
4943
|
try {
|
|
7587
4944
|
renderTitle();
|
|
@@ -7672,6 +5029,69 @@ async function docs() {
|
|
|
7672
5029
|
async function builder() {
|
|
7673
5030
|
return caller.builder();
|
|
7674
5031
|
}
|
|
5032
|
+
/**
|
|
5033
|
+
* Programmatic API to generate a project in-memory (virtual filesystem).
|
|
5034
|
+
* Returns a VirtualFileTree without writing to disk.
|
|
5035
|
+
* Useful for web previews and testing.
|
|
5036
|
+
*
|
|
5037
|
+
* @example
|
|
5038
|
+
* ```typescript
|
|
5039
|
+
* import { createVirtual, EMBEDDED_TEMPLATES } from "create-better-t-stack";
|
|
5040
|
+
*
|
|
5041
|
+
* const result = await createVirtual({
|
|
5042
|
+
* frontend: ["tanstack-router"],
|
|
5043
|
+
* backend: "hono",
|
|
5044
|
+
* runtime: "bun",
|
|
5045
|
+
* database: "sqlite",
|
|
5046
|
+
* orm: "drizzle",
|
|
5047
|
+
* });
|
|
5048
|
+
*
|
|
5049
|
+
* if (result.success) {
|
|
5050
|
+
* console.log(`Generated ${result.tree.fileCount} files`);
|
|
5051
|
+
* }
|
|
5052
|
+
* ```
|
|
5053
|
+
*/
|
|
5054
|
+
async function createVirtual(options) {
|
|
5055
|
+
try {
|
|
5056
|
+
const result = await generateVirtualProject({
|
|
5057
|
+
config: {
|
|
5058
|
+
projectName: options.projectName || "my-project",
|
|
5059
|
+
projectDir: "/virtual",
|
|
5060
|
+
relativePath: "./virtual",
|
|
5061
|
+
database: options.database || "none",
|
|
5062
|
+
orm: options.orm || "none",
|
|
5063
|
+
backend: options.backend || "hono",
|
|
5064
|
+
runtime: options.runtime || "bun",
|
|
5065
|
+
frontend: options.frontend || ["tanstack-router"],
|
|
5066
|
+
addons: options.addons || [],
|
|
5067
|
+
examples: options.examples || [],
|
|
5068
|
+
auth: options.auth || "none",
|
|
5069
|
+
payments: options.payments || "none",
|
|
5070
|
+
git: options.git ?? false,
|
|
5071
|
+
packageManager: options.packageManager || "bun",
|
|
5072
|
+
install: false,
|
|
5073
|
+
dbSetup: options.dbSetup || "none",
|
|
5074
|
+
api: options.api || "trpc",
|
|
5075
|
+
webDeploy: options.webDeploy || "none",
|
|
5076
|
+
serverDeploy: options.serverDeploy || "none"
|
|
5077
|
+
},
|
|
5078
|
+
templates: EMBEDDED_TEMPLATES
|
|
5079
|
+
});
|
|
5080
|
+
if (result.success && result.tree) return {
|
|
5081
|
+
success: true,
|
|
5082
|
+
tree: result.tree
|
|
5083
|
+
};
|
|
5084
|
+
return {
|
|
5085
|
+
success: false,
|
|
5086
|
+
error: result.error || "Unknown error"
|
|
5087
|
+
};
|
|
5088
|
+
} catch (error) {
|
|
5089
|
+
return {
|
|
5090
|
+
success: false,
|
|
5091
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5092
|
+
};
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
7675
5095
|
|
|
7676
5096
|
//#endregion
|
|
7677
|
-
export {
|
|
5097
|
+
export { create as a, docs as c, sponsors as d, builder as i, generateVirtualProject$1 as l, TEMPLATE_COUNT as n, createBtsCli as o, VirtualFileSystem as r, createVirtual as s, EMBEDDED_TEMPLATES$1 as t, router as u };
|