Refactor into multi-template monorepo with Next.js support

Restructure project to support multiple templates via Nix flakes and omnix.
Users can now choose between vanilla JavaScript or Next.js TypeScript
templates during initialization.

- Move existing template to templates/vanilla/
- Add templates/nextjs/ with App Router, TypeScript, and React 19
- Update flake.nix for multi-template outputs
- Add TypeScript support to shared devshell
- Preserve FHS compatibility for both templates
- Update README with comprehensive template comparison and usage
This commit is contained in:
♥ Minnie ♥ 2025-11-14 11:16:04 +08:00
parent 506821f638
commit c497b0fc1d
Signed by: jasmine
GPG key ID: 8563E358D4E8040E
23 changed files with 451 additions and 39 deletions

View file

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

View file

@ -0,0 +1,3 @@
.next
node_modules
bun.lockb

66
templates/nextjs/Justfile Normal file
View file

@ -0,0 +1,66 @@
# List available commands
default:
@just --list
# Start development server with hot reload
dev:
bun run dev
# Add a package dependency
add package:
bun add {{package}}
# Add a dev dependency
add-dev package:
bun add -d {{package}}
# Remove a package dependency
remove package:
bun remove {{package}}
# Build for production
build:
bun run build
# Start production server
start:
bun run start
# Install dependencies
install:
bun install
# Update flake inputs
update:
nix flake update
# Upgrade package dependencies
upgrade:
bun update
# Clean build artifacts and dependencies
clean:
rm -rf .next node_modules bun.lockb
# Format code with prettierd
format:
prettierd --write .
# Check formatting without making changes
format-check:
prettierd --check .
# Lint code with Next.js ESLint
lint:
bun run lint
# Lint and auto-fix issues
lint-fix:
bun run lint -- --fix
# Type check with TypeScript
typecheck:
tsc --noEmit
# Check formatting, linting, and types
check: format-check lint typecheck

View file

@ -0,0 +1,64 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
padding: 2rem;
background: #f5f5f5;
color: #333;
}
main {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h1 {
margin-bottom: 1rem;
color: #2c3e50;
}
p {
margin-bottom: 1.5rem;
}
code {
padding: 0.2em 0.4em;
background: #f0f0f0;
border-radius: 3px;
font-family: 'Monaco', 'Courier New', monospace;
font-size: 0.9em;
}
.container {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.links {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.links a {
padding: 0.5rem 1rem;
background: #2c3e50;
color: white;
text-decoration: none;
border-radius: 4px;
transition: background 0.2s;
}
.links a:hover {
background: #34495e;
}

View file

@ -0,0 +1,19 @@
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: '{{package-name}}',
description: 'A Next.js application built with Bun and Nix',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

View file

@ -0,0 +1,37 @@
export default function Home() {
return (
<main>
<div className="container">
<h1>Welcome to {{package-name}}</h1>
<p>
A minimal Next.js TypeScript template with App Router, powered by
Bun and Nix.
</p>
<div className="links">
<a
href="https://nextjs.org/docs"
target="_blank"
rel="noopener noreferrer"
>
Next.js Docs
</a>
<a
href="https://react.dev"
target="_blank"
rel="noopener noreferrer"
>
React Docs
</a>
<a
href="https://www.typescriptlang.org/docs"
target="_blank"
rel="noopener noreferrer"
>
TypeScript Docs
</a>
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,17 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// Uncomment for pure static export (Vercel will auto-detect this)
// output: 'export',
// Recommended for static sites
reactStrictMode: true,
// Optional: Disable server-side logging in production
// logging: {
// fetches: {
// fullUrl: true,
// },
// },
};
export default nextConfig;

View file

@ -0,0 +1,25 @@
{
"name": "{{package-name}}",
"type": "module",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"eslint": "^9.0.0",
"eslint-config-next": "^15.0.0",
"typescript": "^5.6.0"
}
}

View file

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

View file

@ -0,0 +1,6 @@
# Dependencies
node_modules
# Build output
dist
build

View file

@ -0,0 +1,5 @@
// REPL helper functions
// Loaded automatically by `just repl`
globalThis.println = (...args) => console.log(...args);
globalThis.dir = (obj) => console.dir(obj, { depth: null, colors: true });

View file

@ -0,0 +1,66 @@
# List available commands
default:
@just --list
# Start development server with hot reload
dev:
bun run dev
# Start interactive REPL with auto-loaded helpers
repl:
node --eval "await import('./.replrc.js')" -i
# Add a package dependency
add package:
bun add {{package}}
# Add a dev dependency
add-dev package:
bun add -d {{package}}
# Remove a package dependency
remove package:
bun remove {{package}}
# Build for production
build:
bun run build
# Preview production build
preview:
bun run preview
# Install dependencies
install:
bun install
# Update flake inputs
update:
nix flake update
# Upgrade package dependencies
upgrade:
bun update
# Clean build artifacts and dependencies
clean:
rm -rf dist node_modules bun.lockb
# Format code with prettierd
format:
prettierd --write .
# Check formatting without making changes
format-check:
prettierd --check .
# Lint code with eslint
lint:
eslint .
# Lint and auto-fix issues
lint-fix:
eslint --fix .
# Check both formatting and linting
check: format-check lint

BIN
templates/vanilla/bun.lockb Executable file

Binary file not shown.

View file

@ -0,0 +1,13 @@
export default [
{
ignores: ["dist", "build", "node_modules"],
},
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
rules: {},
},
];

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript App</title>
<link rel="stylesheet" href="src/style.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="src/main.js"></script>
</body>
</html>

View file

@ -0,0 +1,13 @@
{
"name": "javascript-template",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^6.0.0"
}
}

View file

@ -0,0 +1,8 @@
const app = document.getElementById("app");
app.innerHTML = `
<h1>JavaScript Template</h1>
<p>Edit <code>src/main.js</code> to get started.</p>
`;
console.log("Development server running");

View file

@ -0,0 +1,35 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
padding: 2rem;
background: #f5f5f5;
color: #333;
}
#app {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h1 {
margin-bottom: 1rem;
color: #2c3e50;
}
code {
padding: 0.2em 0.4em;
background: #f0f0f0;
border-radius: 3px;
font-family: "Monaco", "Courier New", monospace;
font-size: 0.9em;
}