Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .cursor/rules/better-t-stack-repo.mdc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
alwaysApply: true
---

- Always use functional programming; avoid object-oriented programming.
- Define functions using the standard function declaration syntax, not arrow functions.
- Do not include emojis.
- Use TypeScript type aliases instead of interface declarations.
- In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle.
- Do not use explicit return types
- Do not use explicit return types
- escape the '{{' in hbs templates like '\{{'
4 changes: 2 additions & 2 deletions apps/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Options:
--orm <type> ORM type (none, drizzle, prisma, mongoose)
--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)
--frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, native-bare, native-uniwind, native-unistyles, none)
--addons <types...> Additional addons (pwa, tauri, starlight, biome, husky, turborepo, fumadocs, ultracite, oxlint, none)
--examples <types...> Examples to include (todo, ai, none)
--git Initialize git repository
Expand Down Expand Up @@ -119,7 +119,7 @@ npx create-better-t-stack my-app --backend elysia --runtime node
Create a project with multiple frontend options (one web + one native):

```bash
npx create-better-t-stack my-app --frontend tanstack-router native-nativewind
npx create-better-t-stack my-app --frontend tanstack-router native-bare
```

Create a project with examples:
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const dependencyVersionMap = {
"@sveltejs/adapter-cloudflare": "^7.2.1",
"@cloudflare/workers-types": "^4.20250822.0",

alchemy: "^0.75.1",
alchemy: "^0.77.0",

dotenv: "^17.2.2",
tsdown: "^0.15.5",
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/helpers/addons/examples-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export async function setupExamples(config: ProjectConfig) {
frontend.includes("tanstack-start");
const hasNext = frontend.includes("next");
const hasReactNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");

if (webClientDirExists) {
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/helpers/addons/ultracite-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ function getFrameworksFromFrontend(frontend: string[]): string[] {
"tanstack-start": "react",
next: "next",
nuxt: "vue",
"native-nativewind": "react",
"native-bare": "react",
"native-uniwind": "react",
"native-unistyles": "react",
svelte: "svelte",
solid: "solid",
Expand Down
11 changes: 7 additions & 4 deletions apps/cli/src/helpers/core/api-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function getFrontendType(frontend: Frontend[]): {
"tanstack-start",
"next",
];
const nativeFrontends = ["native-nativewind", "native-unistyles"];
const nativeFrontends = ["native-bare", "native-uniwind", "native-unistyles"];

return {
hasReactWeb: frontend.some((f) => reactBasedFrontends.includes(f)),
Expand Down Expand Up @@ -149,7 +149,8 @@ function getQueryDependencies(frontend: Frontend[]) {
"tanstack-router",
"tanstack-start",
"next",
"native-nativewind",
"native-bare",
"native-uniwind",
"native-unistyles",
];

Expand All @@ -162,12 +163,14 @@ function getQueryDependencies(frontend: Frontend[]) {
if (needsReactQuery) {
const hasReactWeb = frontend.some(
(f) =>
f !== "native-nativewind" &&
f !== "native-bare" &&
f !== "native-uniwind" &&
f !== "native-unistyles" &&
reactBasedFrontends.includes(f),
);
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");

if (hasReactWeb) {
Expand Down
19 changes: 13 additions & 6 deletions apps/cli/src/helpers/core/auth-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export async function setupAuth(config: ProjectConfig) {
const convexBackendDirExists = await fs.pathExists(convexBackendDir);

const hasNativeForBA =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");

if (convexBackendDirExists) {
Expand Down Expand Up @@ -98,9 +99,13 @@ export async function setupAuth(config: ProjectConfig) {
}
}

const hasNativeWind = frontend.includes("native-nativewind");
const hasNativeBare = frontend.includes("native-bare");
const hasNativeUniwind = frontend.includes("native-uniwind");
const hasUnistyles = frontend.includes("native-unistyles");
if (nativeDirExists && (hasNativeWind || hasUnistyles)) {
if (
nativeDirExists &&
(hasNativeBare || hasNativeUniwind || hasUnistyles)
) {
await addPackageDependency({
dependencies: [
"better-auth",
Expand All @@ -116,12 +121,13 @@ export async function setupAuth(config: ProjectConfig) {
}
}

const hasNativeWind = frontend.includes("native-nativewind");
const hasNativeBare = frontend.includes("native-bare");
const hasNativeUniwind = frontend.includes("native-uniwind");
const hasUnistyles = frontend.includes("native-unistyles");
if (
auth === "clerk" &&
nativeDirExists &&
(hasNativeWind || hasUnistyles)
(hasNativeBare || hasNativeUniwind || hasUnistyles)
) {
await addPackageDependency({
dependencies: ["@clerk/clerk-expo"],
Expand Down Expand Up @@ -163,7 +169,8 @@ export async function setupAuth(config: ProjectConfig) {
}

if (
(frontend.includes("native-nativewind") ||
(frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles")) &&
nativeDirExists
) {
Expand Down
9 changes: 6 additions & 3 deletions apps/cli/src/helpers/core/create-readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ function generateReadmeContent(options: ProjectConfig) {
const isConvex = backend === "convex";
const hasReactRouter = frontend.includes("react-router");
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");
const hasSvelte = frontend.includes("svelte");

Expand Down Expand Up @@ -321,7 +322,8 @@ function generateProjectStructure(
}

const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");
if (hasNative) {
if (isBackendSelf) {
Expand Down Expand Up @@ -402,7 +404,8 @@ function generateFeaturesList(
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasReactRouter = frontend.includes("react-router");
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");
const hasNext = frontend.includes("next");
const hasTanstackStart = frontend.includes("tanstack-start");
Expand Down
6 changes: 4 additions & 2 deletions apps/cli/src/helpers/core/env-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ export async function setupEnvironmentVariables(config: ProjectConfig) {
}

if (
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles")
) {
const nativeDir = path.join(projectDir, "apps/native");
Expand Down Expand Up @@ -277,7 +278,8 @@ export async function setupEnvironmentVariables(config: ProjectConfig) {
const envLocalPath = path.join(convexBackendDir, ".env.local");

const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-bare") ||
frontend.includes("native-uniwind") ||
frontend.includes("native-unistyles");
const hasWeb = hasWebFrontend;

Expand Down
6 changes: 4 additions & 2 deletions apps/cli/src/helpers/core/post-installation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export async function displayPostInstallInstructions(
? getLintingInstructions(runCmd)
: "";
const nativeInstructions =
frontend?.includes("native-nativewind") ||
frontend?.includes("native-bare") ||
frontend?.includes("native-uniwind") ||
frontend?.includes("native-unistyles")
? getNativeInstructions(isConvex, isBackendSelf, frontend || [])
: "";
Expand Down Expand Up @@ -103,7 +104,8 @@ export async function displayPostInstallInstructions(
].includes(f),
);
const hasNative =
frontend?.includes("native-nativewind") ||
frontend?.includes("native-bare") ||
frontend?.includes("native-uniwind") ||
frontend?.includes("native-unistyles");

const bunWebNativeWarning =
Expand Down
48 changes: 29 additions & 19 deletions apps/cli/src/helpers/core/template-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ export async function setupFrontendTemplates(
const hasNuxtWeb = context.frontend.includes("nuxt");
const hasSvelteWeb = context.frontend.includes("svelte");
const hasSolidWeb = context.frontend.includes("solid");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasNativeBare = context.frontend.includes("native-bare");
const hasNativeUniwind = context.frontend.includes("native-uniwind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const _hasNative = hasNativeWind || hasUnistyles;
const _hasNative = hasNativeBare || hasNativeUniwind || hasUnistyles;
const isConvex = context.backend === "convex";

if (hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) {
Expand Down Expand Up @@ -201,7 +202,7 @@ export async function setupFrontendTemplates(
}
}

if (hasNativeWind || hasUnistyles) {
if (hasNativeBare || hasNativeUniwind || hasUnistyles) {
const nativeAppDir = path.join(projectDir, "apps/native");
await fs.ensureDir(nativeAppDir);

Expand All @@ -220,8 +221,10 @@ export async function setupFrontendTemplates(
}

let nativeFrameworkPath = "";
if (hasNativeWind) {
nativeFrameworkPath = "nativewind";
if (hasNativeBare) {
nativeFrameworkPath = "bare";
} else if (hasNativeUniwind) {
nativeFrameworkPath = "uniwind";
} else if (hasUnistyles) {
nativeFrameworkPath = "unistyles";
}
Expand Down Expand Up @@ -382,9 +385,10 @@ export async function setupAuthTemplate(
const hasNuxtWeb = context.frontend.includes("nuxt");
const hasSvelteWeb = context.frontend.includes("svelte");
const hasSolidWeb = context.frontend.includes("solid");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasNativeBare = context.frontend.includes("native-bare");
const hasUniwind = context.frontend.includes("native-uniwind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const hasNative = hasNativeWind || hasUnistyles;
const hasNative = hasNativeBare || hasUniwind || hasUnistyles;

const authProvider = context.auth;

Expand Down Expand Up @@ -440,10 +444,9 @@ export async function setupAuthTemplate(
);
}

const hasNativeWind = context.frontend.includes("native-nativewind");
const hasUnistyles = context.frontend.includes("native-unistyles");
let nativeFrameworkPath = "";
if (hasNativeWind) nativeFrameworkPath = "nativewind";
if (hasNativeBare) nativeFrameworkPath = "bare";
else if (hasUniwind) nativeFrameworkPath = "uniwind";
else if (hasUnistyles) nativeFrameworkPath = "unistyles";
if (nativeFrameworkPath) {
const convexClerkNativeFrameworkSrc = path.join(
Expand Down Expand Up @@ -529,7 +532,8 @@ export async function setupAuthTemplate(
}

let nativeFrameworkPath = "";
if (hasNativeWind) nativeFrameworkPath = "nativewind";
if (hasNativeBare) nativeFrameworkPath = "bare";
else if (hasUniwind) nativeFrameworkPath = "uniwind";
else if (hasUnistyles) nativeFrameworkPath = "unistyles";
if (nativeFrameworkPath) {
const convexBetterAuthNativeFrameworkSrc = path.join(
Expand Down Expand Up @@ -690,8 +694,10 @@ export async function setupAuthTemplate(
}

let nativeFrameworkAuthPath = "";
if (hasNativeWind) {
nativeFrameworkAuthPath = "nativewind";
if (hasNativeBare) {
nativeFrameworkAuthPath = "bare";
} else if (hasUniwind) {
nativeFrameworkAuthPath = "uniwind";
} else if (hasUnistyles) {
nativeFrameworkAuthPath = "unistyles";
}
Expand Down Expand Up @@ -1034,13 +1040,16 @@ export async function setupExamplesTemplate(
}

if (nativeAppDirExists) {
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasNativeBare = context.frontend.includes("native-bare");
const hasUniwind = context.frontend.includes("native-uniwind");
const hasUnistyles = context.frontend.includes("native-unistyles");

if (hasNativeWind || hasUnistyles) {
if (hasNativeBare || hasUniwind || hasUnistyles) {
let nativeFramework = "";
if (hasNativeWind) {
nativeFramework = "nativewind";
if (hasNativeBare) {
nativeFramework = "bare";
} else if (hasUniwind) {
nativeFramework = "uniwind";
} else if (hasUnistyles) {
nativeFramework = "unistyles";
}
Expand All @@ -1065,9 +1074,10 @@ export async function setupExamplesTemplate(

export async function handleExtras(projectDir: string, context: ProjectConfig) {
const extrasDir = path.join(PKG_ROOT, "templates/extras");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasNativeBare = context.frontend.includes("native-bare");
const hasUniwind = context.frontend.includes("native-uniwind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const hasNative = hasNativeWind || hasUnistyles;
const hasNative = hasNativeBare || hasUniwind || hasUnistyles;

if (context.packageManager === "pnpm") {
const pnpmWorkspaceSrc = path.join(extrasDir, "pnpm-workspace.yaml");
Expand Down
6 changes: 4 additions & 2 deletions apps/cli/src/prompts/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export async function getAuthChoice(
"tanstack-router",
"tanstack-start",
"next",
"native-nativewind",
"native-bare",
"native-uniwind",
"native-unistyles",
].includes(f),
);
Expand All @@ -29,7 +30,8 @@ export async function getAuthChoice(
"tanstack-router",
"tanstack-start",
"next",
"native-nativewind",
"native-bare",
"native-uniwind",
"native-unistyles",
].includes(f),
);
Expand Down
11 changes: 8 additions & 3 deletions apps/cli/src/prompts/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,13 @@ export async function getFrontendChoice(
message: "Choose native",
options: [
{
value: "native-nativewind" as const,
label: "NativeWind",
value: "native-bare" as const,
label: "Bare",
hint: "Bare Expo without styling library",
},
{
value: "native-uniwind" as const,
label: "UniWind",
hint: "Use Tailwind CSS for React Native",
},
{
Expand All @@ -102,7 +107,7 @@ export async function getFrontendChoice(
hint: "Consistent styling for React Native",
},
],
initialValue: "native-nativewind",
initialValue: "native-bare",
});

if (isCancel(nativeFramework)) return exitCancelled("Operation cancelled");
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const FrontendSchema = z
"tanstack-start",
"next",
"nuxt",
"native-nativewind",
"native-bare",
"native-uniwind",
"native-unistyles",
"svelte",
"solid",
Expand Down
Loading