Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ npx create-better-t-stack@latest
- Databases: SQLite, PostgreSQL, MySQL, MongoDB (or none)
- ORMs: Drizzle, Prisma, Mongoose (or none)
- Auth: Better-Auth (optional)
- Addons: Turborepo, PWA, Tauri, Biome, Husky, Starlight, Fumadocs, Ruler, Ultracite, Oxlint
- Addons: Turborepo, PWA, Tauri, Biome, Husky, Starlight, Fumadocs, Ruler, Ultracite, Oxlint, T3 Env
- Examples: Todo, AI
- DB Setup: Turso, Neon, Supabase, Prisma PostgreSQL, MongoDB Atlas, Cloudflare D1, Docker
- Web Deploy: Cloudflare Workers
Expand Down
6 changes: 3 additions & 3 deletions apps/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Follow the prompts to configure your project or use the `--yes` flag for default
| **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres (via Prisma Accelerate)<br>• MongoDB Atlas<br>• None (manual setup) |
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
| **Styling** | Tailwind CSS with shadcn/ui components |
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds) |
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds)<br>• T3 Env (Type-safe environment variables) |
| **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
| **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |

Expand All @@ -58,7 +58,7 @@ Options:
--auth Include authentication
--no-auth Exclude authentication
--frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, native-nativewind, native-unistyles, none)
--addons <types...> Additional addons (pwa, tauri, starlight, biome, husky, turborepo, fumadocs, ultracite, oxlint, none)
--addons <types...> Additional addons (pwa, tauri, starlight, biome, husky, turborepo, fumadocs, ultracite, oxlint, t3env, none)
--examples <types...> Examples to include (todo, ai, none)
--git Initialize git repository
--no-git Skip git initialization
Expand Down Expand Up @@ -192,7 +192,7 @@ npx create-better-t-stack my-app --frontend none --backend hono --api trpc --dat
- **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
- **Runtime 'none'**: Only available with Convex backend or when backend is 'none'.
- **Cloudflare Workers runtime**: Only compatible with Hono backend, Drizzle ORM (or no ORM), and SQLite database (with D1 setup). Not compatible with MongoDB.
- **Addons 'none'**: Skips all addons (PWA, Tauri, Starlight, Biome, Husky, Turborepo).
- **Addons 'none'**: Skips all addons (PWA, Tauri, Starlight, Biome, Husky, Turborepo, T3 Env).
- **Examples 'none'**: Skips all example implementations (todo, AI chat).
- **SvelteKit, Nuxt, and SolidJS** frontends are only compatible with oRPC API layer
- **PWA support** requires React with TanStack Router, React Router, or SolidJS
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"shadcn",
"pwa",
"tauri",
"biome"
"biome",
"t3env"
],
"repository": {
"type": "git",
Expand Down
8 changes: 7 additions & 1 deletion apps/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const DEFAULT_CONFIG_BASE = {
database: "sqlite",
orm: "drizzle",
auth: "better-auth",
addons: ["turborepo"],
addons: ["turborepo", "t3env"],
examples: [],
git: true,
install: true,
Expand Down Expand Up @@ -156,6 +156,11 @@ export const dependencyVersionMap = {
nitropack: "^2.12.4",

dotenv: "^17.2.1",

zod: "^4.0.14",
"@t3-oss/env-core": "^0.13.8",
"@t3-oss/env-nextjs": "^0.13.8",
"@t3-oss/env-nuxt": "^0.13.8",
} as const;

export type AvailableDependencies = keyof typeof dependencyVersionMap;
Expand All @@ -171,5 +176,6 @@ export const ADDON_COMPATIBILITY: Record<Addons, readonly Frontend[]> = {
ruler: [],
oxlint: [],
fumadocs: [],
t3env: [],
none: [],
} as const;
6 changes: 6 additions & 0 deletions apps/cli/src/helpers/addons/addons-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getPackageExecutionCommand } from "../../utils/package-runner";
import { setupFumadocs } from "./fumadocs-setup";
import { setupVibeRules } from "./ruler-setup";
import { setupStarlight } from "./starlight-setup";
import { setupT3Env } from "./t3env-setup";
import { setupTauri } from "./tauri-setup";
import { setupUltracite } from "./ultracite-setup";
import { addPwaToViteConfig } from "./vite-pwa-setup";
Expand Down Expand Up @@ -58,6 +59,11 @@ ${pc.cyan("Docs:")} ${pc.underline("https://turborepo.com/docs")}
) {
await setupTauri(config);
}

if (addons.includes("t3env")) {
await setupT3Env(config);
}

const hasUltracite = addons.includes("ultracite");
const hasBiome = addons.includes("biome");
const hasHusky = addons.includes("husky");
Expand Down
64 changes: 64 additions & 0 deletions apps/cli/src/helpers/addons/t3env-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import path from "node:path";
import { spinner } from "@clack/prompts";
import { consola } from "consola";
import fs from "fs-extra";
import pc from "picocolors";
import type { ProjectConfig } from "../../types";
import { addPackageDependency } from "../../utils/add-package-deps";

export async function setupT3Env(config: ProjectConfig) {
const { frontend, backend, projectDir } = config;
const s = spinner();

// T3 Env requires a backend to be present
if (backend === "none") {
s.stop(pc.yellow("T3 Env requires a backend to be configured"));
return;
}

try {
s.start("Setting up T3 Env for type-safe environment variables...");

// Add dependencies to server
const serverDir = path.join(projectDir, "apps/server");
if (await fs.pathExists(serverDir)) {
await addPackageDependency({
dependencies: ["@t3-oss/env-core", "zod"],
projectDir: serverDir,
});
}

// Add framework-specific dependencies to web app
const webDir = path.join(projectDir, "apps/web");
if (await fs.pathExists(webDir)) {
const hasNext = frontend.includes("next");
const hasNuxt = frontend.includes("nuxt");

if (hasNext) {
await addPackageDependency({
dependencies: ["@t3-oss/env-nextjs"],
projectDir: webDir,
});
} else if (hasNuxt) {
// For Nuxt, we'll use the core package
await addPackageDependency({
dependencies: ["@t3-oss/env-nuxt", "zod"],
projectDir: webDir,
});
} else {
// For other frameworks, use the core package
await addPackageDependency({
dependencies: ["@t3-oss/env-core", "zod"],
projectDir: webDir,
});
}
}

s.stop("T3 Env configured successfully!");
} catch (error) {
s.stop(pc.red("Failed to set up T3 Env"));
if (error instanceof Error) {
consola.error(pc.red(error.message));
}
}
}
2 changes: 2 additions & 0 deletions apps/cli/src/helpers/core/create-readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@ function generateFeaturesList(
addonsList.push("- **Starlight** - Documentation site with Astro");
} else if (addon === "turborepo") {
addonsList.push("- **Turborepo** - Optimized monorepo build system");
} else if (addon === "t3env") {
addonsList.push("- **T3 Env** - Type-safe environment variables");
}
}

Expand Down
6 changes: 5 additions & 1 deletion apps/cli/src/prompts/addons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } {
label = "Fumadocs";
hint = "Build excellent documentation site";
break;
case "t3env":
label = "T3 Env";
hint = "Type-safe environment variables";
break;
default:
label = addon;
hint = `Add ${addon}`;
Expand All @@ -69,7 +73,7 @@ function getAddonDisplay(addon: Addons): { label: string; hint: string } {
const ADDON_GROUPS = {
Documentation: ["starlight", "fumadocs"],
Linting: ["biome", "oxlint", "ultracite"],
Other: ["ruler", "turborepo", "pwa", "tauri", "husky"],
Other: ["ruler", "turborepo", "pwa", "tauri", "husky", "t3env"],
};

export async function getAddonsChoice(
Expand Down
1 change: 1 addition & 0 deletions apps/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const AddonsSchema = z
"fumadocs",
"ultracite",
"oxlint",
"t3env",
"none",
])
.describe("Additional addons");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@/env/server";
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
BETTER_AUTH_SECRET: z.string().min(1),
BETTER_AUTH_URL: z.string().url(),
CORS_ORIGIN: z.string().url(),
{{#if (includes examples 'ai')}}
GOOGLE_GENERATIVE_AI_API_KEY: z.string().min(1),
{{/if}}
},
experimental__runtimeEnv: process.env,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
client: {
NEXT_PUBLIC_SERVER_URL: z.string().url(),
{{#if (eq backend 'convex')}}
NEXT_PUBLIC_CONVEX_URL: z.string().url(),
{{/if}}
},
runtimeEnv: {
NEXT_PUBLIC_SERVER_URL: process.env.NEXT_PUBLIC_SERVER_URL,
{{#if (eq backend 'convex')}}
NEXT_PUBLIC_CONVEX_URL: process.env.NEXT_PUBLIC_CONVEX_URL,
{{/if}}
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@/env/client";
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createEnv } from "@t3-oss/env-nuxt";
import { z } from "zod";

export const env = createEnv({
client: {
NUXT_PUBLIC_SERVER_URL: z.string().url(),
{{#if (eq backend 'convex')}}
NUXT_PUBLIC_CONVEX_URL: z.string().url(),
{{/if}}
}
});
2 changes: 2 additions & 0 deletions apps/cli/templates/frontend/nuxt/nuxt.config.ts.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "./env";

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: 'latest',
Expand Down
1 change: 1 addition & 0 deletions apps/web/content/docs/cli/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ Additional features to include:
- `ultracite`: Ultracite configuration
- `oxlint`: Oxlint fast linting
- `ruler`: Centralize your AI rules with Ruler
- `t3env`: Type-safe environment variables

```bash
create-better-t-stack --addons pwa biome husky
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Skip prompts and use the default stack:
--database sqlite \
--orm drizzle \
--auth better-auth \
--addons turborepo
--addons turborepo t3env
```
</Tab>
</Tabs>
Expand Down
20 changes: 20 additions & 0 deletions apps/web/public/icon/t3env.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion apps/web/src/app/(home)/_components/npm-package.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ const NpmPackage = () => {
);
};

export default NpmPackage;
export default NpmPackage;
8 changes: 6 additions & 2 deletions apps/web/src/app/(home)/analytics/_components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,17 @@ export const addonsConfig = {
label: "Starlight",
color: "hsl(var(--chart-5))",
},
t3env: {
label: "T3 Env",
color: "hsl(var(--chart-6))",
},
turborepo: {
label: "Turborepo",
color: "hsl(var(--chart-6))",
color: "hsl(var(--chart-7))",
},
none: {
label: "No Addons",
color: "hsl(var(--chart-7))",
color: "hsl(var(--chart-8))",
},
} satisfies ChartConfig;

Expand Down
1 change: 1 addition & 0 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const metadata: Metadata = {
"hono",
"elysia",
"turborepo",
"t3env",
"trpc",
"orpc",
"turso",
Expand Down
Loading