add tanstack start

This commit is contained in:
Aman Varshney
2025-04-06 18:07:40 +05:30
parent 702eb2ecb5
commit d943bf0d80
44 changed files with 1559 additions and 70 deletions

View File

@@ -18,9 +18,6 @@ export async function setupAddons(
const hasWebFrontend =
frontends.includes("react-router") || frontends.includes("tanstack-router");
// if (addons.includes("docker")) {
// await setupDocker(projectDir);
// }
if (addons.includes("pwa") && hasWebFrontend) {
await setupPwa(projectDir, frontends);
}

View File

@@ -31,7 +31,8 @@ export async function setupAuth(
try {
if (
frontends.includes("react-router") ||
frontends.includes("tanstack-router")
frontends.includes("tanstack-router") ||
frontends.includes("tanstack-start")
) {
addPackageDependency({
dependencies: ["better-auth"],

View File

@@ -19,12 +19,13 @@ export async function setupEnvironmentVariables(
if (!envContent.includes("CORS_ORIGIN")) {
const hasReactRouter = options.frontend.includes("react-router");
const hasTanStackRouter = options.frontend.includes("tanstack-router");
const hasTanStackStart = options.frontend.includes("tanstack-start");
let corsOrigin = "http://localhost:3000";
if (hasReactRouter) {
corsOrigin = "http://localhost:5173";
} else if (hasTanStackRouter) {
} else if (hasTanStackRouter || hasTanStackStart) {
corsOrigin = "http://localhost:3001";
}
@@ -68,10 +69,12 @@ export async function setupEnvironmentVariables(
const hasReactRouter = options.frontend.includes("react-router");
const hasTanStackRouter = options.frontend.includes("tanstack-router");
const hasTanStackStart = options.frontend.includes("tanstack-start");
const hasWebFrontend =
hasReactRouter || hasTanStackRouter || hasTanStackStart;
if (hasReactRouter || hasTanStackRouter) {
if (hasWebFrontend) {
const clientDir = path.join(projectDir, "apps/web");
await setupClientEnvFile(clientDir);
}

View File

@@ -13,12 +13,19 @@ export async function setupExamples(
frontend: ProjectFrontend[] = ["tanstack-router"],
): Promise<void> {
const hasTanstackRouter = frontend.includes("tanstack-router");
const hasTanstackStart = frontend.includes("tanstack-start");
const hasReactRouter = frontend.includes("react-router");
const hasWebFrontend = hasTanstackRouter || hasReactRouter;
const hasWebFrontend =
hasTanstackRouter || hasReactRouter || hasTanstackStart;
const routerType = hasTanstackRouter
? "web-tanstack-router"
: "web-react-router";
let routerType: string;
if (hasTanstackRouter) {
routerType = "web-tanstack-router";
} else if (hasTanstackStart) {
routerType = "web-tanstack-start";
} else {
routerType = "web-react-router";
}
const webAppExists = await fs.pathExists(path.join(projectDir, "apps/web"));
@@ -87,27 +94,28 @@ async function updateServerIndexWithAIRoute(projectDir: string): Promise<void> {
const importSection = `import { streamText } from "ai";\nimport { google } from "@ai-sdk/google";\nimport { stream } from "hono/streaming";`;
const aiRouteHandler = `
app.post("/ai", async (c) => {
const body = await c.req.json();
const messages = body.messages || [];
// AI chat endpoint
app.post("/ai", async (c) => {
const body = await c.req.json();
const messages = body.messages || [];
const result = streamText({
model: google("gemini-2.0-flash-exp"),
messages,
});
const result = streamText({
model: google("gemini-1.5-flash"),
messages,
});
c.header("X-Vercel-AI-Data-Stream", "v1");
c.header("Content-Type", "text/plain; charset=utf-8");
c.header("X-Vercel-AI-Data-Stream", "v1");
c.header("Content-Type", "text/plain; charset=utf-8");
return stream(c, (stream) => stream.pipe(result.toDataStream()));
});`;
return stream(c, (stream) => stream.pipe(result.toDataStream()));
});`;
if (indexContent.includes("import {")) {
const lastImportIndex = indexContent.lastIndexOf("import");
const endOfLastImport = indexContent.indexOf("\n", lastImportIndex);
indexContent = `${indexContent.substring(0, endOfLastImport + 1)}
${importSection}
${indexContent.substring(endOfLastImport + 1)}`;
${importSection}
${indexContent.substring(endOfLastImport + 1)}`;
} else {
indexContent = `${importSection}
@@ -177,7 +185,7 @@ async function setupTodoExample(
const todoExampleDir = path.join(PKG_ROOT, "template/examples/todo");
if (await fs.pathExists(todoExampleDir)) {
const todoRouteSourceDir = path.join(
const todoRouteSourcePath = path.join(
todoExampleDir,
`apps/${routerType}/src/routes/todos.tsx`,
);
@@ -186,8 +194,8 @@ async function setupTodoExample(
"apps/web/src/routes/todos.tsx",
);
if (await fs.pathExists(todoRouteSourceDir)) {
await fs.copy(todoRouteSourceDir, todoRouteTargetPath, {
if (await fs.pathExists(todoRouteSourcePath)) {
await fs.copy(todoRouteSourcePath, todoRouteTargetPath, {
overwrite: true,
});
}

View File

@@ -43,11 +43,15 @@ export function displayPostInstallInstructions(
: "";
const hasTanstackRouter = frontends?.includes("tanstack-router");
const hasTanstackStart = frontends?.includes("tanstack-start");
const hasReactRouter = frontends?.includes("react-router");
const hasWebFrontend = hasTanstackRouter || hasReactRouter;
const hasWebFrontend =
hasTanstackRouter || hasReactRouter || hasTanstackStart;
const hasNativeFrontend = frontends?.includes("native");
const hasFrontend = hasWebFrontend || hasNativeFrontend;
const webPort = hasReactRouter ? "5173" : "3001";
note(
`${pc.cyan("1.")} ${cdCmd}
${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan(depsInstalled ? "2." : "3.")} ${runCmd} dev
@@ -55,7 +59,7 @@ ${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan
${pc.bold("Your project will be available at:")}
${
hasFrontend
? `${hasWebFrontend ? `${pc.cyan("•")} Frontend: http://localhost:${hasReactRouter ? "5173" : "3001"}\n` : ""}`
? `${hasWebFrontend ? `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n` : ""}`
: `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`
}${pc.cyan("•")} API: http://localhost:3000
${nativeInstructions ? `\n${nativeInstructions.trim()}` : ""}${databaseInstructions ? `\n${databaseInstructions.trim()}` : ""}${tauriInstructions ? `\n${tauriInstructions.trim()}` : ""}${lintingInstructions ? `\n${lintingInstructions.trim()}` : ""}${pwaInstructions ? `\n${pwaInstructions.trim()}` : ""}

View File

@@ -49,10 +49,11 @@ export async function setupFrontendTemplates(
frontends: ProjectFrontend[],
): Promise<void> {
const hasTanstackWeb = frontends.includes("tanstack-router");
const hasTanstackStart = frontends.includes("tanstack-start");
const hasReactRouterWeb = frontends.includes("react-router");
const hasNative = frontends.includes("native");
if (hasTanstackWeb || hasReactRouterWeb) {
if (hasTanstackWeb || hasReactRouterWeb || hasTanstackStart) {
const webDir = path.join(projectDir, "apps/web");
await fs.ensureDir(webDir);
@@ -61,9 +62,13 @@ export async function setupFrontendTemplates(
await fs.copy(webBaseDir, webDir);
}
const frameworkName = hasTanstackWeb
? "web-tanstack-router"
: "web-react-router";
let frameworkName = "web-react-router";
if (hasTanstackWeb) {
frameworkName = "web-tanstack-router";
} else if (hasTanstackStart) {
frameworkName = "web-tanstack-start";
}
const webFrameworkDir = path.join(
PKG_ROOT,
`template/base/apps/${frameworkName}`,
@@ -155,8 +160,9 @@ export async function setupAuthTemplate(
if (await fs.pathExists(authTemplateDir)) {
const hasReactRouter = frontends.includes("react-router");
const hasTanStackRouter = frontends.includes("tanstack-router");
const hasTanStackStart = frontends.includes("tanstack-start");
if (hasReactRouter || hasTanStackRouter) {
if (hasReactRouter || hasTanStackRouter || hasTanStackStart) {
const webDir = path.join(projectDir, "apps/web");
const webBaseAuthDir = path.join(authTemplateDir, "apps/web-base");
@@ -183,6 +189,16 @@ export async function setupAuthTemplate(
await fs.copy(tanstackAuthDir, webDir, { overwrite: true });
}
}
if (hasTanStackStart) {
const tanstackStartAuthDir = path.join(
authTemplateDir,
"apps/web-tanstack-start",
);
if (await fs.pathExists(tanstackStartAuthDir)) {
await fs.copy(tanstackStartAuthDir, webDir, { overwrite: true });
}
}
}
const serverAuthDir = path.join(authTemplateDir, "apps/server/src");