diff --git a/src/cli/init/README-tanstack.default.md b/src/cli/init/README-tanstack.default.md new file mode 100644 index 0000000000..2473ab1844 --- /dev/null +++ b/src/cli/init/README-tanstack.default.md @@ -0,0 +1,35 @@ +# {[name]s} + +A TanStack Start project powered by Bun. + +To install dependencies: + +```bash +bun install +``` + +To start a development server: + +```bash +bun dev +``` + +To build for production: + +```bash +bun run build +``` + +## About TanStack Start + +[TanStack Start](https://tanstack.com/start/latest) is a full-stack framework powered by TanStack Router for React and Solid that provides: + +- File-based routing +- Server-side rendering (SSR) +- Server functions with `createServerFn` +- Built-in data loading with route loaders +- Hot module replacement (HMR) + +This project was created using `bun init --react=tanstack` in bun v{[bunVersion]s}. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. + +For more information, check out Bun's [TanStack Start guide](https://bun.com/guides/ecosystem/tanstack-start). diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index d46d261f0b..9a79490f20 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -228,6 +228,7 @@ pub const InitCommand = struct { const @"tsconfig.json" = @embedFile("init/tsconfig.default.json"); const @"README.md" = @embedFile("init/README.default.md"); const @"README2.md" = @embedFile("init/README2.default.md"); + const @"README-tanstack.md" = @embedFile("init/README-tanstack.default.md"); /// Create a new asset file, overriding anything that already exists. Known /// assets will have their contents pre-populated; otherwise the file will be empty. @@ -382,6 +383,10 @@ pub const InitCommand = struct { template = .react_tailwind_shadcn; prev_flag_was_react = false; auto_yes = true; + } else if ((template == .react_blank and prev_flag_was_react and strings.eqlComptime(arg, "tanstack") or strings.eqlComptime(arg, "--react=tanstack")) or strings.eqlComptime(arg, "r=tanstack")) { + template = .react_tanstack; + prev_flag_was_react = false; + auto_yes = true; } else { prev_flag_was_react = false; } @@ -585,12 +590,14 @@ pub const InitCommand = struct { default, tailwind, shadcn_tailwind, + tanstack, pub fn fmt(self: @This()) []const u8 { return switch (self) { .default => "Default (blank)", .tailwind => "TailwindCSS", .shadcn_tailwind => "Shadcn + TailwindCSS", + .tanstack => "TanStack Start", }; } }); @@ -599,6 +606,7 @@ pub const InitCommand = struct { .default => .react_blank, .tailwind => .react_tailwind, .shadcn_tailwind => .react_tailwind_shadcn, + .tanstack => .react_tanstack, }; }, .blank => template = .blank, @@ -613,7 +621,7 @@ pub const InitCommand = struct { } switch (template) { - inline .react_blank, .react_tailwind, .react_tailwind_shadcn => |t| { + inline .react_blank, .react_tailwind, .react_tailwind_shadcn, .react_tanstack => |t| { try t.@"write files and run `bun dev`"(alloc); return; }, @@ -791,7 +799,7 @@ pub const InitCommand = struct { switch (template) { .blank, .typescript_library => { - Template.createAgentRule(); + Template.createAgentRule(template); if (package_json_file != null and !did_load_package_json) { Output.prettyln(" + package.json", .{}); @@ -910,6 +918,24 @@ const DependencyGroup = struct { } ++ tailwind.dependencies[0..tailwind.dependencies.len].*, .devDependencies = &[_]DependencyNeeded{} ++ tailwind.devDependencies[0..tailwind.devDependencies.len].*, }; + + pub const tanstack = DependencyGroup{ + .dependencies = &[_]DependencyNeeded{ + .{ .name = "@tailwindcss/vite", .version = "^4.1.17" }, + .{ .name = "@tanstack/react-router", .version = "^1.135.2" }, + .{ .name = "@tanstack/react-start", .version = "^1.135.2" }, + .{ .name = "react", .version = "^19.2.0" }, + .{ .name = "react-dom", .version = "^19.2.0" }, + .{ .name = "tailwindcss", .version = "^4.1.17" }, + }, + .devDependencies = &[_]DependencyNeeded{ + .{ .name = "@types/react", .version = "^19.2.3" }, + .{ .name = "@types/react-dom", .version = "^19.2.3" }, + .{ .name = "@vitejs/plugin-react", .version = "^5.1.0" }, + .{ .name = "vite", .version = "^7.2.2" }, + .{ .name = "vite-tsconfig-paths", .version = "^5.1.4" }, + } ++ blank.devDependencies[0..1].*, + }; }; const Template = enum { @@ -917,6 +943,7 @@ const Template = enum { react_blank, react_tailwind, react_tailwind_shadcn, + react_tanstack, typescript_library, const TemplateFile = struct { path: [:0]const u8, @@ -931,7 +958,7 @@ const Template = enum { } pub fn isReact(this: Template) bool { return switch (this) { - .react_blank, .react_tailwind, .react_tailwind_shadcn => true, + .react_blank, .react_tailwind, .react_tailwind_shadcn, .react_tanstack => true, else => false, }; } @@ -959,6 +986,7 @@ const Template = enum { .react_blank => DependencyGroup.react, .react_tailwind => DependencyGroup.tailwind, .react_tailwind_shadcn => DependencyGroup.shadcn, + .react_tanstack => DependencyGroup.tanstack, .typescript_library => DependencyGroup.blank, }; } @@ -969,6 +997,7 @@ const Template = enum { .react_blank => "bun-react-template", .react_tailwind => "bun-react-tailwind-template", .react_tailwind_shadcn => "bun-react-tailwind-shadcn-template", + .react_tanstack => "bun-tanstack-start-template", }; } pub fn scripts(this: Template) []const []const u8 { @@ -986,13 +1015,16 @@ const Template = enum { "build", "NODE_ENV=production bun .", }, + .react_tanstack => &.{ "dev", "bun --bun vite dev", "build", "bun --bun vite build", "serve", "bun --bun vite preview" }, }; return s; } const agent_rule = @embedFile("../init/rule.md"); + const agent_rule_tanstack = @embedFile("../init/rule-tanstack.md"); const cursor_rule = TemplateFile{ .path = ".cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc", .contents = agent_rule }; + const cursor_rule_tanstack = TemplateFile{ .path = ".cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc", .contents = agent_rule_tanstack }; const cursor_rule_path_to_claude_md = "../../CLAUDE.md"; fn isClaudeCodeInstalled() bool { @@ -1012,12 +1044,12 @@ const Template = enum { return bun.which(pathbuffer, bun.env_var.PATH.get() orelse return false, bun.fs.FileSystem.instance.top_level_dir, "claude") != null; } - pub fn createAgentRule() void { + pub fn createAgentRule(this: Template) void { var @"create CLAUDE.md" = Template.isClaudeCodeInstalled() and // Never overwrite CLAUDE.md !bun.sys.exists("CLAUDE.md"); - if (Template.getCursorRule()) |template_file| { + if (this.getCursorRule()) |template_file| { var did_create_agent_rule = false; // If both Cursor & Claude is installed, make the cursor rule a @@ -1053,9 +1085,13 @@ const Template = enum { // If cursor is not installed but claude code is installed, then create the CLAUDE.md. if (@"create CLAUDE.md") { // In this case, the frontmatter from the cursor rule is not helpful so let's trim it out. - const end_of_frontmatter = if (bun.strings.lastIndexOf(agent_rule, "---\n")) |start| start + "---\n".len else 0; + const rule_to_use = switch (this) { + .react_tanstack => agent_rule_tanstack, + else => agent_rule, + }; + const end_of_frontmatter = if (bun.strings.lastIndexOf(rule_to_use, "---\n")) |start| start + "---\n".len else 0; - InitCommand.Assets.createNew("CLAUDE.md", agent_rule[end_of_frontmatter..]) catch {}; + InitCommand.Assets.createNew("CLAUDE.md", rule_to_use[end_of_frontmatter..]) catch {}; } } @@ -1092,9 +1128,12 @@ const Template = enum { return false; } - fn getCursorRule() ?*const TemplateFile { + fn getCursorRule(this: Template) ?*const TemplateFile { if (isCursorInstalled()) { - return &cursor_rule; + return switch (this) { + .react_tanstack => &cursor_rule_tanstack, + else => &cursor_rule, + }; } return null; @@ -1168,17 +1207,36 @@ const Template = enum { }; }; + const ReactTanstack = struct { + const files: []const TemplateFile = &.{ + .{ .path = "package.json", .contents = @embedFile("../init/react-tanstack/package.json") }, + .{ .path = "tsconfig.json", .contents = @embedFile("../init/react-tanstack/tsconfig.json") }, + .{ .path = "vite.config.ts", .contents = @embedFile("../init/react-tanstack/vite.config.ts") }, + .{ .path = "styles.css", .contents = @embedFile("../init/react-tanstack/styles.css") }, + .{ .path = "README.md", .contents = InitCommand.Assets.@"README-tanstack.md" }, + .{ .path = ".gitignore", .contents = InitCommand.Assets.@".gitignore", .can_skip_if_exists = true }, + .{ .path = "src/router.tsx", .contents = @embedFile("../init/react-tanstack/src/router.tsx") }, + .{ .path = "src/routes/__root.tsx", .contents = @embedFile("../init/react-tanstack/src/routes/__root.tsx") }, + .{ .path = "src/routes/index.tsx", .contents = @embedFile("../init/react-tanstack/src/routes/index.tsx") }, + .{ .path = "src/routes/stats.tsx", .contents = @embedFile("../init/react-tanstack/src/routes/stats.tsx") }, + .{ .path = "src/routeTree.gen.ts", .contents = @embedFile("../init/react-tanstack/src/routeTree.gen.ts") }, + .{ .path = "public/header.webp", .contents = @embedFile("../init/react-tanstack/public/header.webp") }, + .{ .path = "public/favicon.ico", .contents = @embedFile("../init/react-tanstack/public/favicon.ico") }, + }; + }; + pub fn files(this: Template) []const TemplateFile { return switch (this) { .react_blank => ReactBlank.files, .react_tailwind => ReactTailwind.files, .react_tailwind_shadcn => ReactShadcn.files, + .react_tanstack => ReactTanstack.files, else => &.{.{ &.{}, &.{} }}, }; } pub fn @"write files and run `bun dev`"(comptime this: Template, allocator: std.mem.Allocator) !void { - Template.createAgentRule(); + this.createAgentRule(); inline for (comptime this.files()) |file| { const path = file.path; @@ -1218,10 +1276,22 @@ const Template = enum { _ = try install.spawnAndWait(); + var cwd_buf: bun.PathBuffer = undefined; + const cwd_path = switch (bun.sys.getcwd(&cwd_buf)) { + .result => |p| p, + .err => |e| { + Output.err(e, "failed to get current working directory", .{}); + Global.exit(1); + }, + }; + const dir_name = std.fs.path.basename(cwd_path); + Output.prettyln( \\ \\✨ New project configured! \\ + \\cd {s} + \\ \\Development - full-stack dev server with hot reload \\ \\ bun dev @@ -1236,7 +1306,7 @@ const Template = enum { \\ \\Happy bunning! 🐇 \\ - , .{}); + , .{dir_name}); Output.flush(); } diff --git a/src/init/react-tanstack/package.json b/src/init/react-tanstack/package.json new file mode 100644 index 0000000000..dabea66dff --- /dev/null +++ b/src/init/react-tanstack/package.json @@ -0,0 +1,28 @@ +{ + "name": "bun-tanstack-start-template", + "type": "module", + "scripts": { + "dev": "bun --bun vite dev", + "build": "bun --bun vite build", + "start": "bun --bun vite preview" + }, + "devDependencies": { + "@types/bun": "^1.3.2", + "@types/react": "^19.2.3", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.0", + "vite": "^7.2.2", + "vite-tsconfig-paths": "^5.1.4" + }, + "peerDependencies": { + "typescript": "^5.9.3" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.17", + "@tanstack/react-router": "^1.135.2", + "@tanstack/react-start": "^1.135.2", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwindcss": "^4.1.17" + } +} diff --git a/src/init/react-tanstack/public/favicon.ico b/src/init/react-tanstack/public/favicon.ico new file mode 100644 index 0000000000..5b9f938748 Binary files /dev/null and b/src/init/react-tanstack/public/favicon.ico differ diff --git a/src/init/react-tanstack/public/header.webp b/src/init/react-tanstack/public/header.webp new file mode 100644 index 0000000000..4ddbf30c45 Binary files /dev/null and b/src/init/react-tanstack/public/header.webp differ diff --git a/src/init/react-tanstack/src/routeTree.gen.ts b/src/init/react-tanstack/src/routeTree.gen.ts new file mode 100644 index 0000000000..a841db7ff3 --- /dev/null +++ b/src/init/react-tanstack/src/routeTree.gen.ts @@ -0,0 +1,84 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from "./routes/__root"; +import { Route as IndexRouteImport } from "./routes/index"; +import { Route as StatsRouteImport } from "./routes/stats"; + +const StatsRoute = StatsRouteImport.update({ + id: "/stats", + path: "/stats", + getParentRoute: () => rootRouteImport, +} as any); +const IndexRoute = IndexRouteImport.update({ + id: "/", + path: "/", + getParentRoute: () => rootRouteImport, +} as any); + +export interface FileRoutesByFullPath { + "/": typeof IndexRoute; + "/stats": typeof StatsRoute; +} +export interface FileRoutesByTo { + "/": typeof IndexRoute; + "/stats": typeof StatsRoute; +} +export interface FileRoutesById { + __root__: typeof rootRouteImport; + "/": typeof IndexRoute; + "/stats": typeof StatsRoute; +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath; + fullPaths: "/" | "/stats"; + fileRoutesByTo: FileRoutesByTo; + to: "/" | "/stats"; + id: "__root__" | "/" | "/stats"; + fileRoutesById: FileRoutesById; +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute; + StatsRoute: typeof StatsRoute; +} + +declare module "@tanstack/react-router" { + interface FileRoutesByPath { + "/stats": { + id: "/stats"; + path: "/stats"; + fullPath: "/stats"; + preLoaderRoute: typeof StatsRouteImport; + parentRoute: typeof rootRouteImport; + }; + "/": { + id: "/"; + path: "/"; + fullPath: "/"; + preLoaderRoute: typeof IndexRouteImport; + parentRoute: typeof rootRouteImport; + }; + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + StatsRoute: StatsRoute, +}; +export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)._addFileTypes(); + +import type { createStart } from "@tanstack/react-start"; +import type { getRouter } from "./router.tsx"; +declare module "@tanstack/react-start" { + interface Register { + ssr: true; + router: Awaited>; + } +} diff --git a/src/init/react-tanstack/src/router.tsx b/src/init/react-tanstack/src/router.tsx new file mode 100644 index 0000000000..04ab70bfd4 --- /dev/null +++ b/src/init/react-tanstack/src/router.tsx @@ -0,0 +1,11 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + const router = createRouter({ + routeTree, + scrollRestoration: true, + }); + + return router; +} diff --git a/src/init/react-tanstack/src/routes/__root.tsx b/src/init/react-tanstack/src/routes/__root.tsx new file mode 100644 index 0000000000..0f533adacb --- /dev/null +++ b/src/init/react-tanstack/src/routes/__root.tsx @@ -0,0 +1,87 @@ +// src/routes/__root.tsx +/// +import { createRootRoute, HeadContent, Link, Outlet, Scripts } from "@tanstack/react-router"; +import type { ReactNode } from "react"; + +import appCss from "../../styles.css?url"; + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: "utf-8", + }, + { + name: "viewport", + content: "width=device-width, initial-scale=1", + }, + { + title: "Bun + TanStack Start Starter", + }, + ], + links: [ + { rel: "stylesheet", href: appCss }, + { rel: "icon", href: "/favicon.ico" }, + ], + }), + component: RootComponent, + notFoundComponent: NotFoundComponent, +}); + +function NotFoundComponent() { + return ( +
+
+
+
+
+

404

+

Page Not Found

+
+
+ +
+
+

+ The page you're looking for doesn't exist or has been moved. +

+
+
+ +
+
+ + ← Back to Home + +
+
+
+
+
+ ); +} + +function RootComponent() { + return ( + + + + ); +} + +function RootDocument({ children }: Readonly<{ children: ReactNode }>) { + return ( + + + + + + {children} + + + + ); +} diff --git a/src/init/react-tanstack/src/routes/index.tsx b/src/init/react-tanstack/src/routes/index.tsx new file mode 100644 index 0000000000..bc0f479825 --- /dev/null +++ b/src/init/react-tanstack/src/routes/index.tsx @@ -0,0 +1,118 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; + +const getBunInfo = createServerFn({ + method: "GET", +}).handler(async () => { + return { + version: Bun.version, + revision: Bun.revision, + }; +}); + +export const Route = createFileRoute("/")({ + component: Home, + loader: async () => { + const bunInfo = await getBunInfo(); + return { bunInfo }; + }, +}); + +function Home() { + const { bunInfo } = Route.useLoaderData(); + + return ( +
+
+
+
+ TanStack Logo +
+
+
+ Bun {bunInfo.version} +
+ {bunInfo.revision && ( + + {bunInfo.revision.slice(0, 8)} + + )} +
+
+ +
+
+
+
+

+ Welcome to TanStack Start +

+

+ Powered by Bun {"\u2764\uFE0F"} +

+
+
+

+ Edit{" "} + + src/routes/index.tsx + {" "} + to see HMR in action. +
+ Visit{" "} + + /stats + {" "} + for server-side info, or explore{" "} + + Bun's APIs + + .
+
+ Ready to deploy? Check out the{" "} + + TanStack guide + + . +

+
+
+
+
+ +
+
+ + View Server Stats → + +
+
+
+
+
+ ); +} diff --git a/src/init/react-tanstack/src/routes/stats.tsx b/src/init/react-tanstack/src/routes/stats.tsx new file mode 100644 index 0000000000..971c61a49e --- /dev/null +++ b/src/init/react-tanstack/src/routes/stats.tsx @@ -0,0 +1,157 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; +import { cpus, totalmem } from "os"; + +const getServerStats = createServerFn({ + method: "GET", +}).handler(async () => { + const bunVersion = Bun.version; + const bunRevision = Bun.revision; + const cpuUsage = process.cpuUsage(); + const processUptime = process.uptime(); + + // Calculate CPU usage percentage to avoid showing falsy cumulative values + // CPU percentage = (total CPU time / (uptime * number of cores)) * 100 + const numCores = cpus().length; + const totalCpuTime = (cpuUsage.user + cpuUsage.system) / 1000000; // Convert microseconds to seconds + const cpuPercentage = processUptime > 0 ? Math.min(100, (totalCpuTime / (processUptime * numCores)) * 100) : 0; + + const cpuInfo = cpus()[0]; + + return { + bunVersion, + bunRevision, + platform: process.platform, + arch: process.arch, + pid: process.pid, + uptime: Math.floor(processUptime), + cpu: { + percentage: Math.round(cpuPercentage * 100) / 100, + cores: numCores, + }, + environment: { + cpuModel: cpuInfo?.model || "Unknown", + totalMemory: totalmem(), + }, + }; +}); + +export const Route = createFileRoute("/stats")({ + component: Stats, + loader: async () => { + const stats = await getServerStats(); + return { stats }; + }, +}); + +function Stats() { + const { stats } = Route.useLoaderData(); + + const formatUptime = (seconds: number) => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + if (hours > 0) { + return `${hours}h ${minutes}m ${secs}s`; + } + if (minutes > 0) { + return `${minutes}m ${secs}s`; + } + return `${secs}s`; + }; + + const formatBytes = (bytes: number) => { + const formatter = new Intl.NumberFormat("en-US", { + maximumFractionDigits: 1, + minimumFractionDigits: 0, + }); + + const gb = bytes / (1024 * 1024 * 1024); + if (gb >= 1) { + return `${formatter.format(gb)} GB`; + } + const mb = bytes / (1024 * 1024); + if (mb >= 1) { + return `${formatter.format(mb)} MB`; + } + const kb = bytes / 1024; + return `${formatter.format(kb)} KB`; + }; + + return ( +
+
+
+
+
+

Server Stats

+

Runtime information

+
+
+ +
+
+
+
+

Bun Version

+

{stats.bunVersion}

+
+
+

Revision

+

{stats.bunRevision?.slice(0, 8)}

+
+
+

Platform

+

{stats.platform}

+
+
+

Architecture

+

{stats.arch}

+
+
+

Process ID

+

{stats.pid}

+
+
+

Uptime

+

{formatUptime(stats.uptime)}

+
+
+

CPU Cores

+

{stats.cpu.cores}

+
+
+

CPU Usage

+

{stats.cpu.percentage}%

+
+
+
+
+
+

CPU Model

+

{stats.environment.cpuModel}

+
+
+

Total Memory

+

{formatBytes(stats.environment.totalMemory)}

+
+
+
+
+
+ +
+
+ + ← Back to Home + +
+
+
+
+
+ ); +} diff --git a/src/init/react-tanstack/styles.css b/src/init/react-tanstack/styles.css new file mode 100644 index 0000000000..42b0422af0 --- /dev/null +++ b/src/init/react-tanstack/styles.css @@ -0,0 +1,57 @@ +@import "tailwindcss"; + +:root { + --background: oklch(0.985 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --muted-foreground: oklch(0.556 0 0); + --border: oklch(0.9 0 0); + --ring: oklch(0.556 0 0); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --muted-foreground: oklch(0.708 0 0); + --border: oklch(1 0 0 / 10%); + --ring: oklch(0.556 0 0); + } +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-muted-foreground: var(--muted-foreground); + --color-border: var(--border); + --color-ring: var(--ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + html { + overflow-x: hidden; + overflow-y: auto; + max-width: 100vw; + width: 100%; + } + body { + @apply bg-background text-foreground; + font-family: + system-ui, + -apple-system, + sans-serif; + overflow-x: hidden; + max-width: 100vw; + width: 100%; + margin: 0; + padding: 0; + } +} diff --git a/src/init/react-tanstack/tsconfig.json b/src/init/react-tanstack/tsconfig.json new file mode 100644 index 0000000000..edfdf0e833 --- /dev/null +++ b/src/init/react-tanstack/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ES2022", + "skipLibCheck": true, + "strictNullChecks": true + } +} diff --git a/src/init/react-tanstack/vite.config.ts b/src/init/react-tanstack/vite.config.ts new file mode 100644 index 0000000000..847f6342ee --- /dev/null +++ b/src/init/react-tanstack/vite.config.ts @@ -0,0 +1,18 @@ +import tailwindcss from "@tailwindcss/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import viteReact from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import tsConfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [ + tsConfigPaths(), + tanstackStart(), + // react's vite plugin must come after start's vite plugin + viteReact(), + tailwindcss(), + ], +}); diff --git a/src/init/rule-tanstack.md b/src/init/rule-tanstack.md new file mode 100644 index 0000000000..b494b213cf --- /dev/null +++ b/src/init/rule-tanstack.md @@ -0,0 +1,45 @@ +--- +description: Use Bun with TanStack Start instead of Node.js, npm, pnpm, or vite. +globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json" +alwaysApply: false +--- + +Default to using Bun instead of Node.js. + +- Use `bun ` instead of `node ` or `ts-node ` +- Use `bun test` instead of `jest` or `vitest` +- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` +- Use `bun run