add unistyles

This commit is contained in:
Aman Varshney
2025-05-07 14:29:11 +05:30
parent d09a284ce7
commit 6c269a4c5b
74 changed files with 1762 additions and 208 deletions

View File

@@ -125,7 +125,8 @@ export async function setupApi(config: ProjectConfig): Promise<void> {
"tanstack-router",
"tanstack-start",
"next",
"native",
"native-nativewind",
"native-unistyles",
];
const needsSolidQuery = frontend.includes("solid");
const needsReactQuery = frontend.some((f) => reactBasedFrontends.includes(f));
@@ -137,9 +138,14 @@ export async function setupApi(config: ProjectConfig): Promise<void> {
];
const hasReactWeb = frontend.some(
(f) => f !== "native" && reactBasedFrontends.includes(f),
(f) =>
f !== "native-nativewind" &&
f !== "native-unistyles" &&
reactBasedFrontends.includes(f),
);
const hasNative = frontend.includes("native");
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-unistyles");
if (hasReactWeb && webDirExists) {
const webPkgJsonPath = path.join(webDir, "package.json");

View File

@@ -45,7 +45,11 @@ export async function setupAuth(config: ProjectConfig): Promise<void> {
});
}
if (frontend.includes("native") && nativeDirExists) {
if (
(frontend.includes("native-nativewind") ||
frontend.includes("native-unistyles")) &&
nativeDirExists
) {
await addPackageDependency({
dependencies: ["better-auth", "@better-auth/expo"],
projectDir: nativeDir,

View File

@@ -39,7 +39,9 @@ function generateReadmeContent(options: ProjectConfig): string {
const isConvex = backend === "convex";
const hasReactRouter = frontend.includes("react-router");
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasNative = frontend.includes("native");
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-unistyles");
const hasNext = frontend.includes("next");
const hasTanstackStart = frontend.includes("tanstack-start");
const hasSvelte = frontend.includes("svelte");
@@ -78,7 +80,16 @@ This project was created with [Better-T-Stack](https://github.com/AmanVarshney01
## Features
${generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api)}
${generateFeaturesList(
database,
auth,
addons,
orm,
runtime,
frontend,
backend,
api,
)}
## Getting Started
@@ -207,7 +218,9 @@ function generateFeaturesList(
const isConvex = backend === "convex";
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasReactRouter = frontend.includes("react-router");
const hasNative = frontend.includes("native");
const hasNative =
frontend.includes("native-nativewind") ||
frontend.includes("native-unistyles");
const hasNext = frontend.includes("next");
const hasTanstackStart = frontend.includes("tanstack-start");
const hasSvelte = frontend.includes("svelte");

View File

@@ -118,7 +118,10 @@ export async function setupEnvironmentVariables(
}
}
if (frontend.includes("native")) {
if (
frontend.includes("native-nativewind") ||
frontend.includes("native-unistyles")
) {
const nativeDir = path.join(projectDir, "apps/native");
if (await fs.pathExists(nativeDir)) {
let envVarName = "EXPO_PUBLIC_SERVER_URL";

View File

@@ -1,11 +1,6 @@
import { consola } from "consola";
import pc from "picocolors";
import type {
ProjectBackend,
ProjectDatabase,
ProjectOrm,
ProjectRuntime,
} from "../types";
import type { ProjectDatabase, ProjectOrm, ProjectRuntime } from "../types";
import { getPackageExecutionCommand } from "../utils/get-package-execution-command";
import type { ProjectConfig } from "../types";
@@ -43,9 +38,11 @@ export function displayPostInstallInstructions(
const lintingInstructions = hasHuskyOrBiome
? getLintingInstructions(runCmd)
: "";
const nativeInstructions = frontend?.includes("native")
? getNativeInstructions(isConvex)
: "";
const nativeInstructions =
frontend?.includes("native-nativewind") ||
frontend?.includes("native-unistyles")
? getNativeInstructions(isConvex)
: "";
const pwaInstructions =
addons?.includes("pwa") &&
(frontend?.includes("react-router") ||
@@ -67,7 +64,9 @@ export function displayPostInstallInstructions(
"solid",
].includes(f),
);
const hasNative = frontend?.includes("native");
const hasNative =
frontend?.includes("native-nativewind") ||
frontend?.includes("native-unistyles");
const bunWebNativeWarning =
packageManager === "bun" && hasNative && hasWeb
@@ -90,7 +89,9 @@ export function displayPostInstallInstructions(
}
if (isConvex) {
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim("(this will guide you through Convex project setup)")}\n`;
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim(
"(this will guide you through Convex project setup)",
)}\n`;
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
} else {
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
@@ -101,7 +102,9 @@ export function displayPostInstallInstructions(
if (hasWeb) {
output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
} else if (!hasNative && !addons?.includes("starlight")) {
output += `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`;
output += `${pc.yellow(
"NOTE:",
)} You are creating a backend-only app (no frontend selected)\n`;
}
if (!isConvex) {
@@ -122,8 +125,12 @@ export function displayPostInstallInstructions(
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
output += `${pc.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub:\n`;
output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(
tazeCommand,
)}\n\n`;
output += `${pc.bold(
"Like Better-T Stack?",
)} Please consider giving us a star on GitHub:\n`;
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
consola.box(output);
@@ -183,7 +190,9 @@ function getDatabaseInstructions(
instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
if (database === "sqlite") {
instructions.push(
`${pc.cyan("•")} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`,
`${pc.cyan(
"•",
)} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`,
);
}
} else if (orm === "none") {

View File

@@ -70,7 +70,9 @@ export async function setupFrontendTemplates(
const hasNuxtWeb = context.frontend.includes("nuxt");
const hasSvelteWeb = context.frontend.includes("svelte");
const hasSolidWeb = context.frontend.includes("solid");
const hasNative = context.frontend.includes("native");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const hasNative = hasNativeWind || hasUnistyles;
const isConvex = context.backend === "convex";
if (hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) {
@@ -181,16 +183,45 @@ export async function setupFrontendTemplates(
}
}
if (hasNative) {
if (hasNativeWind || hasUnistyles) {
const nativeAppDir = path.join(projectDir, "apps/native");
await fs.ensureDir(nativeAppDir);
const nativeBaseDir = path.join(PKG_ROOT, "templates/frontend/native");
if (await fs.pathExists(nativeBaseDir)) {
await processAndCopyFiles("**/*", nativeBaseDir, nativeAppDir, context);
const nativeBaseCommonDir = path.join(
PKG_ROOT,
"templates/frontend/native/native-base",
);
if (await fs.pathExists(nativeBaseCommonDir)) {
await processAndCopyFiles(
"**/*",
nativeBaseCommonDir,
nativeAppDir,
context,
);
} else {
}
let nativeFrameworkPath = "";
if (hasNativeWind) {
nativeFrameworkPath = "nativewind";
} else if (hasUnistyles) {
nativeFrameworkPath = "unistyles";
}
const nativeSpecificDir = path.join(
PKG_ROOT,
`templates/frontend/native/${nativeFrameworkPath}`,
);
if (await fs.pathExists(nativeSpecificDir)) {
await processAndCopyFiles(
"**/*",
nativeSpecificDir,
nativeAppDir,
context,
true,
);
}
if (!isConvex && (context.api === "trpc" || context.api === "orpc")) {
const apiNativeSrcDir = path.join(
PKG_ROOT,
@@ -203,7 +234,6 @@ export async function setupFrontendTemplates(
nativeAppDir,
context,
);
} else {
}
}
}
@@ -345,7 +375,9 @@ export async function setupAuthTemplate(
const hasNuxtWeb = context.frontend.includes("nuxt");
const hasSvelteWeb = context.frontend.includes("svelte");
const hasSolidWeb = context.frontend.includes("solid");
const hasNative = context.frontend.includes("native");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const hasNative = hasNativeWind || hasUnistyles;
if (serverAppDirExists) {
const authServerBaseSrc = path.join(PKG_ROOT, "templates/auth/server/base");
@@ -475,10 +507,39 @@ export async function setupAuthTemplate(
}
if (hasNative && nativeAppDirExists) {
const authNativeSrc = path.join(PKG_ROOT, "templates/auth/native");
if (await fs.pathExists(authNativeSrc)) {
await processAndCopyFiles("**/*", authNativeSrc, nativeAppDir, context);
} else {
const authNativeBaseSrc = path.join(
PKG_ROOT,
"templates/auth/native/native-base",
);
if (await fs.pathExists(authNativeBaseSrc)) {
await processAndCopyFiles(
"**/*",
authNativeBaseSrc,
nativeAppDir,
context,
);
}
let nativeFrameworkAuthPath = "";
if (hasNativeWind) {
nativeFrameworkAuthPath = "nativewind";
} else if (hasUnistyles) {
nativeFrameworkAuthPath = "unistyles";
}
if (nativeFrameworkAuthPath) {
const authNativeFrameworkSrc = path.join(
PKG_ROOT,
`templates/auth/native/${nativeFrameworkAuthPath}`,
);
if (await fs.pathExists(authNativeFrameworkSrc)) {
await processAndCopyFiles(
"**/*",
authNativeFrameworkSrc,
nativeAppDir,
context,
);
}
}
}
}
@@ -695,6 +756,9 @@ export async function handleExtras(
context: ProjectConfig,
): Promise<void> {
const extrasDir = path.join(PKG_ROOT, "templates/extras");
const hasNativeWind = context.frontend.includes("native-nativewind");
const hasUnistyles = context.frontend.includes("native-unistyles");
const hasNative = hasNativeWind || hasUnistyles;
if (context.packageManager === "pnpm") {
const pnpmWorkspaceSrc = path.join(extrasDir, "pnpm-workspace.yaml");
@@ -706,7 +770,7 @@ export async function handleExtras(
if (
context.packageManager === "pnpm" &&
(context.frontend.includes("native") || context.frontend.includes("nuxt"))
(hasNative || context.frontend.includes("nuxt"))
) {
const npmrcTemplateSrc = path.join(extrasDir, "_npmrc.hbs");
const npmrcDest = path.join(projectDir, ".npmrc");

View File

@@ -84,7 +84,8 @@ async function main() {
"tanstack-start",
"next",
"nuxt",
"native",
"native-nativewind",
"native-unistyles",
"svelte",
"solid",
"none",
@@ -303,6 +304,9 @@ async function main() {
config.runtime = "none";
config.dbSetup = "none";
config.examples = ["todo"];
log.info(
"Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo",
);
} else if (config.backend === "none") {
config.auth = false;
config.database = "none";
@@ -311,10 +315,24 @@ async function main() {
config.runtime = "none";
config.dbSetup = "none";
config.examples = [];
log.info(
"Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none",
);
} else if (config.database === "none") {
config.orm = "none";
log.info(
"Due to '--database none', '--orm' has been automatically set to 'none'.",
);
config.auth = false;
log.info(
"Due to '--database none', '--auth' has been automatically set to 'false'.",
);
config.dbSetup = "none";
log.info(
"Due to '--database none', '--db-setup' has been automatically set to 'none'.",
);
}
log.info(
@@ -380,13 +398,16 @@ function processAndValidateFlags(
if (options.api) {
config.api = options.api as ProjectApi;
if (options.api === "none") {
if (options.backend && options.backend !== "convex") {
if (
options.backend &&
options.backend !== "convex" &&
options.backend !== "none"
) {
consola.fatal(
`'--api none' is only supported with '--backend convex'. Please choose a different API setting or use '--backend convex'.`,
`'--api none' is only supported with '--backend convex' or '--backend none'. Please choose a different API setting or use '--backend convex' or '--backend none'.`,
);
process.exit(1);
}
config.backend = "convex";
}
}
@@ -468,12 +489,22 @@ function processAndValidateFlags(
f === "svelte" ||
f === "solid",
);
const nativeFrontends = validOptions.filter(
(f) => f === "native-nativewind" || f === "native-unistyles",
);
if (webFrontends.length > 1) {
consola.fatal(
"Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid",
);
process.exit(1);
}
if (nativeFrontends.length > 1) {
consola.fatal(
"Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles",
);
process.exit(1);
}
config.frontend = validOptions;
}
}
@@ -595,6 +626,9 @@ function processAndValidateFlags(
process.exit(1);
}
config.examples = [];
log.info(
"Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none",
);
} else {
const effectiveDatabase =
config.database ?? (options.yes ? DEFAULT_CONFIG.database : undefined);
@@ -621,6 +655,9 @@ function processAndValidateFlags(
process.exit(1);
}
config.orm = "none";
log.info(
"Due to '--database none', '--orm' has been automatically set to 'none'.",
);
if (providedFlags.has("auth") && options.auth === true) {
consola.fatal(
@@ -629,6 +666,9 @@ function processAndValidateFlags(
process.exit(1);
}
config.auth = false;
log.info(
"Due to '--database none', '--auth' has been automatically set to 'false'.",
);
if (providedFlags.has("dbSetup") && options.dbSetup !== "none") {
consola.fatal(
@@ -637,6 +677,9 @@ function processAndValidateFlags(
process.exit(1);
}
config.dbSetup = "none";
log.info(
"Due to '--database none', '--db-setup' has been automatically set to 'none'.",
);
}
if (config.orm === "mongoose" && !providedFlags.has("database")) {
@@ -753,6 +796,9 @@ function processAndValidateFlags(
) {
if (config.api !== "none") {
config.api = "orpc";
log.info(
`Due to frontend selection, API has been set to 'orpc'. tRPC is not compatible with Nuxt, Svelte, or Solid Framework`,
);
}
}
@@ -813,7 +859,8 @@ function processAndValidateFlags(
const onlyNativeFrontend =
effectiveFrontend &&
effectiveFrontend.length === 1 &&
effectiveFrontend[0] === "native";
(effectiveFrontend[0] === "native-nativewind" ||
effectiveFrontend[0] === "native-unistyles");
if (
onlyNativeFrontend &&
@@ -822,7 +869,7 @@ function processAndValidateFlags(
!config.examples.includes("none")
) {
consola.fatal(
"Examples are not supported when only the 'native' frontend is selected.",
"Examples are not supported when only a native frontend (NativeWind or Unistyles) is selected.",
);
process.exit(1);
}

View File

@@ -48,7 +48,6 @@ export async function getBackendFrameworkChoice(
});
}
// Add "None" option
backendOptions.push({
value: "none" as const,
label: "None",

View File

@@ -27,7 +27,10 @@ export async function getExamplesChoice(
if (database === "none") return [];
const onlyNative =
frontends && frontends.length === 1 && frontends[0] === "native";
frontends &&
frontends.length === 1 &&
(frontends[0] === "native-nativewind" ||
frontends[0] === "native-unistyles");
if (onlyNative) {
return [];
}

View File

@@ -95,7 +95,28 @@ export async function getFrontendChoice(
}
if (frontendTypes.includes("native")) {
result.push("native");
const nativeFramework = await select<ProjectFrontend>({
message: "Choose native framework",
options: [
{
value: "native-nativewind" as const,
label: "NativeWind",
hint: "Use Tailwind CSS for React Native",
},
{
value: "native-unistyles" as const,
label: "Unistyles",
hint: "Consistent styling for React Native",
},
],
initialValue: "native-nativewind",
});
if (isCancel(nativeFramework)) {
cancel(pc.red("Operation cancelled"));
process.exit(0);
}
result.push(nativeFramework);
}
return result;

View File

@@ -29,7 +29,8 @@ export type ProjectFrontend =
| "tanstack-start"
| "next"
| "nuxt"
| "native"
| "native-nativewind"
| "native-unistyles"
| "svelte"
| "solid"
| "none";