setup turborepo and web app with fumadocs

This commit is contained in:
Aman Varshney
2025-02-12 00:45:40 +05:30
parent 8a77febd7e
commit 8b730b9adc
38 changed files with 1689 additions and 140 deletions

39
.gitignore vendored
View File

@@ -1,2 +1,37 @@
/node_modules # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/dist
# Dependencies
node_modules
.pnp
.pnp.js
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Outputs
out/
build
dist
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Misc
.DS_Store
*.pem

View File

@@ -1,3 +1,3 @@
{ {
"language_servers": ["biome", "..."] "language_servers": ["biome", "..."]
} }

2
apps/cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/node_modules
/dist

35
apps/cli/package.json Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "create-better-t-stack",
"version": "1.0.0",
"description": "CLI tool to scaffold Better-T Stack projects",
"type": "module",
"bin": {
"create-better-t-stack": "./dist/index.js"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"check-types": "tsc --noEmit",
"check": "biome check --write .",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"files": ["dist", "templates"],
"dependencies": {
"@inquirer/prompts": "^7.3.1",
"chalk": "^5.3.0",
"commander": "^13.1.0",
"execa": "^8.0.1",
"fs-extra": "^11.2.0",
"gradient-string": "^3.0.0",
"ora": "^7.0.1"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7",
"@types/node": "^20.10.5",
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vitest": "^1.1.0"
}
}

View File

@@ -0,0 +1,47 @@
import path from "node:path";
import { execa } from "execa";
import fs from "fs-extra";
import ora from "ora";
import { setupTurso } from "./helpers/db-setup";
import type { ProjectOptions } from "./types";
export async function createProject(options: ProjectOptions) {
const spinner = ora("Creating project directory...").start();
const projectDir = path.resolve(process.cwd(), options.projectName);
try {
await fs.ensureDir(projectDir);
spinner.succeed();
spinner.start("Cloning template repository...");
await execa("git", [
"degit",
"https://github.com/AmanVarshney01/Better-T-Stack.git",
projectDir,
]);
spinner.succeed();
if (options.git) {
spinner.start("Initializing git repository...");
await execa("git", ["init"], { cwd: projectDir });
spinner.succeed();
}
spinner.start("Installing dependencies...");
await execa("bun", ["install"], { cwd: projectDir });
spinner.succeed();
if (options.database === "libsql") {
await setupTurso(projectDir);
}
console.log("\n✨ Project created successfully!\n");
console.log("Next steps:");
console.log(` cd ${options.projectName}`);
console.log(" bun dev");
} catch (error) {
spinner.fail("Failed to create project");
console.error(error);
process.exit(1);
}
}

14
apps/cli/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

3
apps/web/.eslintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

28
apps/web/.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# deps
/node_modules
# generated content
.contentlayer
.content-collections
.source
# test & build
/coverage
/.next/
/out/
/build
*.tsbuildinfo
# misc
.DS_Store
*.pem
/.pnp
.pnp.js
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# others
.env*.local
.vercel
next-env.d.ts

26
apps/web/README.md Normal file
View File

@@ -0,0 +1,26 @@
# web
This is a Next.js application generated with
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
Run development server:
```bash
npm run dev
# or
pnpm dev
# or
yarn dev
```
Open http://localhost:3000 with your browser to see the result.
## Learn More
To learn more about Next.js and Fumadocs, take a look at the following
resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs

View File

@@ -0,0 +1,13 @@
---
title: Hello World
description: Your first document
---
Welcome to the docs! You can start writing documents in `/content/docs`.
## What is Next?
<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
</Cards>

View File

@@ -0,0 +1,17 @@
---
title: Components
description: Components
---
## Code Block
```js
console.log('Hello World');
```
## Cards
<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
</Cards>

10
apps/web/next.config.mjs Normal file
View File

@@ -0,0 +1,10 @@
import { createMDX } from "fumadocs-mdx/next";
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);

31
apps/web/package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "web",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"start": "next start",
"postinstall": "fumadocs-mdx"
},
"dependencies": {
"next": "15.1.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"fumadocs-ui": "15.0.6",
"fumadocs-core": "15.0.6",
"fumadocs-mdx": "11.5.3"
},
"devDependencies": {
"@types/node": "22.13.1",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"typescript": "^5.7.3",
"@types/mdx": "^2.0.13",
"@tailwindcss/postcss": "^4.0.5",
"tailwindcss": "^4.0.5",
"postcss": "^8.5.1",
"eslint": "^8",
"eslint-config-next": "15.1.6"
}
}

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};

11
apps/web/source.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
export const docs = defineDocs({
dir: "content/docs",
});
export default defineConfig({
mdxOptions: {
// MDX options
},
});

View File

@@ -0,0 +1,7 @@
import { baseOptions } from "@/app/layout.config";
import { HomeLayout } from "fumadocs-ui/layouts/home";
import type { ReactNode } from "react";
export default function Layout({ children }: { children: ReactNode }) {
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
}

View File

@@ -0,0 +1,19 @@
import Link from "next/link";
export default function HomePage() {
return (
<main className="flex flex-1 flex-col justify-center text-center">
<h1 className="mb-4 text-2xl font-bold">Hello World</h1>
<p className="text-fd-muted-foreground">
You can open{" "}
<Link
href="/docs"
className="text-fd-foreground font-semibold underline"
>
/docs
</Link>{" "}
and see the documentation.
</p>
</main>
);
}

View File

@@ -0,0 +1,4 @@
import { source } from "@/lib/source";
import { createFromSource } from "fumadocs-core/search/server";
export const { GET } = createFromSource(source);

View File

@@ -0,0 +1,46 @@
import { source } from "@/lib/source";
import defaultMdxComponents from "fumadocs-ui/mdx";
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from "fumadocs-ui/page";
import { notFound } from "next/navigation";
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
const MDX = page.data.body;
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
};
}

View File

@@ -0,0 +1,12 @@
import { baseOptions } from "@/app/layout.config";
import { source } from "@/lib/source";
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import type { ReactNode } from "react";
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={source.pageTree} {...baseOptions}>
{children}
</DocsLayout>
);
}

View File

@@ -0,0 +1,5 @@
@import "tailwindcss";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";
@source '../../../../node_modules/fumadocs-ui/dist/**/*.js';

View File

@@ -0,0 +1,22 @@
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
/**
* Shared layout configurations
*
* you can configure layouts individually from:
* Home Layout: app/(home)/layout.tsx
* Docs Layout: app/docs/layout.tsx
*/
export const baseOptions: BaseLayoutProps = {
nav: {
// can be JSX too!
title: "Better-T-Stack",
},
links: [
{
text: "Documentation",
url: "/docs",
active: "nested-url",
},
],
};

View File

@@ -0,0 +1,18 @@
import "./global.css";
import { RootProvider } from "fumadocs-ui/provider";
import { Inter } from "next/font/google";
import type { ReactNode } from "react";
const inter = Inter({
subsets: ["latin"],
});
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body className="flex flex-col min-h-screen">
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}

View File

@@ -0,0 +1,7 @@
import { docs } from "@/.source";
import { loader } from "fumadocs-core/source";
export const source = loader({
baseUrl: "/docs",
source: docs.toFumadocsSource(),
});

30
apps/web/tsconfig.json Normal file
View File

@@ -0,0 +1,30 @@
{
"compilerOptions": {
"baseUrl": ".",
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"@/.source": ["./.source/index.ts"],
"@/*": ["./src/*"]
},
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -7,8 +7,7 @@
}, },
"files": { "files": {
"ignoreUnknown": false, "ignoreUnknown": false,
"ignore": [], "ignore": [".next", "dist", ".source"]
"include": ["**/*.ts"]
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,

1224
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,25 @@
{ {
"name": "create-better-t-stack", "name": "better-t-stack",
"version": "1.0.0", "private": true,
"description": "CLI tool to scaffold Better-T Stack projects", "scripts": {
"type": "module", "build": "turbo build",
"bin": { "dev": "turbo dev",
"create-better-t-stack": "./dist/index.js" "lint": "turbo lint",
}, "prepare": "husky"
"scripts": { },
"build": "tsup", "devDependencies": {
"dev": "tsup --watch", "turbo": "^2.4.1",
"typecheck": "tsc --noEmit", "typescript": "5.7.3",
"check": "biome check --write .", "@biomejs/biome": "1.9.4",
"test": "vitest run", "husky": "^9.1.7",
"prepublishOnly": "npm run build", "lint-staged": "^15.4.3"
"prepare": "husky" },
}, "lint-staged": {
"files": [ "*": ["biome check --write ."]
"dist", },
"templates" "engines": {
], "node": ">=18"
"dependencies": { },
"@inquirer/prompts": "^7.3.1", "packageManager": "bun@1.2.2",
"chalk": "^5.3.0", "workspaces": ["apps/*"]
"commander": "^13.1.0",
"execa": "^8.0.1",
"fs-extra": "^11.2.0",
"gradient-string": "^3.0.0",
"lint-staged": "^15.4.3",
"ora": "^7.0.1"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/inquirer": "^9.0.7",
"@types/node": "^20.10.5",
"husky": "^9.1.7",
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vitest": "^1.1.0"
},
"lint-staged": {
"*": [
"biome check --write ."
]
}
} }

View File

@@ -1,47 +0,0 @@
import path from "node:path";
import { execa } from "execa";
import fs from "fs-extra";
import ora from "ora";
import { setupTurso } from "./helpers/db-setup";
import type { ProjectOptions } from "./types";
export async function createProject(options: ProjectOptions) {
const spinner = ora("Creating project directory...").start();
const projectDir = path.resolve(process.cwd(), options.projectName);
try {
await fs.ensureDir(projectDir);
spinner.succeed();
spinner.start("Cloning template repository...");
await execa("git", [
"degit",
"https://github.com/AmanVarshney01/Better-T-Stack.git",
projectDir,
]);
spinner.succeed();
if (options.git) {
spinner.start("Initializing git repository...");
await execa("git", ["init"], { cwd: projectDir });
spinner.succeed();
}
spinner.start("Installing dependencies...");
await execa("bun", ["install"], { cwd: projectDir });
spinner.succeed();
if (options.database === "libsql") {
await setupTurso(projectDir);
}
console.log("\n✨ Project created successfully!\n");
console.log("Next steps:");
console.log(` cd ${options.projectName}`);
console.log(" bun dev");
} catch (error) {
spinner.fail("Failed to create project");
console.error(error);
process.exit(1);
}
}

View File

@@ -1,14 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

21
turbo.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui",
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"check-types": {
"dependsOn": ["^check-types"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}