From c497b0fc1dbe0094c3233ca1c5b1c21d6bff07dd Mon Sep 17 00:00:00 2001 From: jasmine Date: Fri, 14 Nov 2025 11:16:04 +0800 Subject: [PATCH] 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 --- .gitignore | 6 + README.md | 172 +++++++++++++++--- flake.nix | 2 +- nix/modules/devshell.nix | 1 + nix/modules/template.nix | 48 +++-- templates/nextjs/.eslintrc.json | 3 + templates/nextjs/.prettierignore | 3 + templates/nextjs/Justfile | 66 +++++++ templates/nextjs/app/globals.css | 64 +++++++ templates/nextjs/app/layout.tsx | 19 ++ templates/nextjs/app/page.tsx | 37 ++++ templates/nextjs/next.config.js | 17 ++ templates/nextjs/package.json | 25 +++ templates/nextjs/tsconfig.json | 27 +++ .../vanilla/.prettierignore | 0 .replrc.js => templates/vanilla/.replrc.js | 0 Justfile => templates/vanilla/Justfile | 0 bun.lockb => templates/vanilla/bun.lockb | Bin .../vanilla/eslint.config.js | 0 index.html => templates/vanilla/index.html | 0 .../vanilla/package.json | 0 {src => templates/vanilla/src}/main.js | 0 {src => templates/vanilla/src}/style.css | 0 23 files changed, 451 insertions(+), 39 deletions(-) create mode 100644 templates/nextjs/.eslintrc.json create mode 100644 templates/nextjs/.prettierignore create mode 100644 templates/nextjs/Justfile create mode 100644 templates/nextjs/app/globals.css create mode 100644 templates/nextjs/app/layout.tsx create mode 100644 templates/nextjs/app/page.tsx create mode 100644 templates/nextjs/next.config.js create mode 100644 templates/nextjs/package.json create mode 100644 templates/nextjs/tsconfig.json rename .prettierignore => templates/vanilla/.prettierignore (100%) rename .replrc.js => templates/vanilla/.replrc.js (100%) rename Justfile => templates/vanilla/Justfile (100%) rename bun.lockb => templates/vanilla/bun.lockb (100%) rename eslint.config.js => templates/vanilla/eslint.config.js (100%) rename index.html => templates/vanilla/index.html (100%) rename package.json => templates/vanilla/package.json (100%) rename {src => templates/vanilla/src}/main.js (100%) rename {src => templates/vanilla/src}/style.css (100%) diff --git a/.gitignore b/.gitignore index 248e5ac..350c6d5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ node_modules/ # Build output dist/ build/ +.next/ +out/ + +# TypeScript +*.tsbuildinfo +next-env.d.ts # Direnv .direnv/ diff --git a/README.md b/README.md index 9473058..f7d138e 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,82 @@ -# JavaScript Template +# JavaScript Templates -A minimal JavaScript development template using Nix. Key features: +A family of minimal JavaScript development templates using Nix for reproducible +environments. Choose the right foundation for your project while maintaining +consistent tooling and workflows. -- Nix + Flakes for reproducible environments -- Bun (fast JavaScript runtime & package manager) -- Vite (development server & build tool) -- Node.js (mature REPL with custom helpers) -- ESLint + Prettier (linting & formatting) -- Devshell commands via just +## Available Templates -## Prerequisites +### Vanilla + +Minimal JavaScript template for interactive sites and applications. + +**Best for:** + +- Interactive tools and utilities +- Client-side games +- Dashboards and data visualizations +- Projects that don't need a framework + +**Tech stack:** + +- Vite (development server & bundler) +- Vanilla JavaScript (no framework) +- Node.js REPL with custom helpers + +### Next.js + +TypeScript template with App Router for modern web applications. + +**Best for:** + +- Full-stack applications (e-commerce, SaaS) +- Static sites with growth potential +- Projects needing API routes +- Server-side rendering or static generation + +**Tech stack:** + +- Next.js 15 with App Router +- React 19 +- TypeScript +- Optimized for Vercel deployment + +## Shared Foundation + +All templates include: + +- **Nix + Flakes** - reproducible development environments +- **Bun** - fast JavaScript runtime & package manager +- **ESLint + Prettier** - code quality and formatting +- **just** - consistent command-line interface +- **FHS compatibility** - toggle for NixOS edge cases + +## Getting Started + +### Prerequisites - [Nix](https://nixos.org/download.html) with flakes enabled - [direnv](https://direnv.net/) (optional but recommended) -## Getting Started +### Initialize a Project -Initialize a new project using [omnix](https://omnix.page): +Using [omnix](https://omnix.page): + +**Vanilla template:** ```sh nix run nixpkgs#omnix -- \ - init github:sajenim/javascript-template -o ./my-project + init github:sajenim/javascript-templates -o ./my-project ``` -Then enter the development environment: +**Next.js template:** + +```sh +nix run nixpkgs#omnix -- \ + init github:sajenim/javascript-templates#nextjs -o ./my-nextjs-app +``` + +### Enter Development Environment ```sh cd my-project @@ -31,37 +84,106 @@ direnv allow # Or use: nix develop just dev ``` -**NixOS users:** If bun-installed binaries fail to find system libraries, enable FHS compatibility: +### NixOS FHS Compatibility + +If bun-installed binaries fail to find system libraries, enable FHS +compatibility: ```nix # nix/modules/devshell.nix fhs = true; ``` -## Available Commands +## Common Commands -Run `just` to see all available commands: +Both templates share the same command interface: ```sh -just dev # Start dev server -just repl # Node.js REPL with helpers -just lint # Lint with ESLint -just format # Format with Prettier +just # List all available commands +just dev # Start development server just build # Production build +just lint # Lint code +just format # Format code just clean # Clean build artifacts -just update # Update flake inputs -just upgrade # Upgrade bun packages +just update # Update Nix flake inputs +just upgrade # Upgrade package dependencies ``` +### Template-Specific Commands + +**Vanilla:** + +```sh +just repl # Node.js REPL with custom helpers +just preview # Preview production build +``` + +**Next.js:** + +```sh +just start # Start production server +just typecheck # TypeScript type checking +just check # Run format-check, lint, and typecheck +``` + +## Next.js Static Export + +For pure static sites (no server-side features), enable static export: + +```js +// templates/nextjs/next.config.js +const nextConfig = { + output: 'export', + // ... +}; +``` + +Note: This disables API routes and server-side rendering. + ## Customization -This template provides minimal, sensible defaults. Customize as needed: +All templates provide minimal, sensible defaults. Customize as needed: + +**Vanilla:** - Modify linting rules in `eslint.config.js` +- Add custom REPL helpers in `.replrc.js` +- Extend Vite config if needed + +**Next.js:** + +- Configure Next.js in `next.config.js` +- Customize ESLint rules in `.eslintrc.json` +- Adjust TypeScript settings in `tsconfig.json` +- Extend ESLint config for stricter rules + +**Both templates:** + - Add Prettier config via `.prettierrc` if needed - Extend `Justfile` with project-specific commands -- Add custom REPL helpers in `.replrc.js` +- Adjust Nix devshell in `nix/modules/devshell.nix` + +## Template Comparison + +| Feature | Vanilla | Next.js | +| ------------------ | ------- | ------- | +| Framework | None | Next.js | +| Language | JS | TS | +| Bundler | Vite | Next.js | +| Client-side only | ✓ | ✗ | +| Server-side API | ✗ | ✓ | +| Static generation | ✓ | ✓ | +| Type checking | ✗ | ✓ | +| React components | ✗ | ✓ | +| File-based routing | ✗ | ✓ | ## Acknowledgments -- [srid's haskell-template](https://github.com/srid/haskell-template) +- [srid's haskell-template](https://github.com/srid/haskell-template) - + inspiration for Nix flakes structure +- [Next.js](https://nextjs.org/) - React framework for production +- [Bun](https://bun.sh/) - fast JavaScript runtime + +## License + +MIT diff --git a/flake.nix b/flake.nix index 2fa174a..3dde900 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Nix template for JavaScript projects, powered by Bun"; + description = "Nix templates for JavaScript projects, powered by Bun"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; diff --git a/nix/modules/devshell.nix b/nix/modules/devshell.nix index 6b88beb..ca858e0 100644 --- a/nix/modules/devshell.nix +++ b/nix/modules/devshell.nix @@ -22,6 +22,7 @@ in { [ bun nodejs + typescript eslint just prettierd diff --git a/nix/modules/template.nix b/nix/modules/template.nix index c936a64..ea22107 100644 --- a/nix/modules/template.nix +++ b/nix/modules/template.nix @@ -1,19 +1,41 @@ -{inputs, ...}: { +{inputs, ...}: let + root = inputs.self; +in { flake = rec { - templates.default = { - description = "A minimal JavaScript project template with Bun and Nix"; - path = inputs.self; + templates = { + default = { + description = "Minimal JavaScript template with Vite and Bun"; + path = "${root}/templates/vanilla"; + }; + + nextjs = { + description = "Next.js TypeScript template with App Router for Vercel"; + path = "${root}/templates/nextjs"; + }; }; - om.templates.javascript-template = { - template = templates.default; - params = [ - { - name = "package-name"; - description = "Name of the JavaScript package"; - placeholder = "javascript-template"; - } - ]; + om.templates = { + javascript-vanilla = { + template = templates.default; + params = [ + { + name = "package-name"; + description = "Name of the JavaScript package"; + placeholder = "my-project"; + } + ]; + }; + + javascript-nextjs = { + template = templates.nextjs; + params = [ + { + name = "package-name"; + description = "Name of the Next.js project"; + placeholder = "my-nextjs-app"; + } + ]; + }; }; }; } diff --git a/templates/nextjs/.eslintrc.json b/templates/nextjs/.eslintrc.json new file mode 100644 index 0000000..3722418 --- /dev/null +++ b/templates/nextjs/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals", "next/typescript"] +} diff --git a/templates/nextjs/.prettierignore b/templates/nextjs/.prettierignore new file mode 100644 index 0000000..7fa5f54 --- /dev/null +++ b/templates/nextjs/.prettierignore @@ -0,0 +1,3 @@ +.next +node_modules +bun.lockb diff --git a/templates/nextjs/Justfile b/templates/nextjs/Justfile new file mode 100644 index 0000000..5f6022b --- /dev/null +++ b/templates/nextjs/Justfile @@ -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 diff --git a/templates/nextjs/app/globals.css b/templates/nextjs/app/globals.css new file mode 100644 index 0000000..362edd5 --- /dev/null +++ b/templates/nextjs/app/globals.css @@ -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; +} diff --git a/templates/nextjs/app/layout.tsx b/templates/nextjs/app/layout.tsx new file mode 100644 index 0000000..ec657b0 --- /dev/null +++ b/templates/nextjs/app/layout.tsx @@ -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 ( + + {children} + + ); +} diff --git a/templates/nextjs/app/page.tsx b/templates/nextjs/app/page.tsx new file mode 100644 index 0000000..77db059 --- /dev/null +++ b/templates/nextjs/app/page.tsx @@ -0,0 +1,37 @@ +export default function Home() { + return ( +
+
+

Welcome to {{package-name}}

+

+ A minimal Next.js TypeScript template with App Router, powered by + Bun and Nix. +

+ +
+ + Next.js Docs + + + React Docs + + + TypeScript Docs + +
+
+
+ ); +} diff --git a/templates/nextjs/next.config.js b/templates/nextjs/next.config.js new file mode 100644 index 0000000..604dfe0 --- /dev/null +++ b/templates/nextjs/next.config.js @@ -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; diff --git a/templates/nextjs/package.json b/templates/nextjs/package.json new file mode 100644 index 0000000..45be068 --- /dev/null +++ b/templates/nextjs/package.json @@ -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" + } +} diff --git a/templates/nextjs/tsconfig.json b/templates/nextjs/tsconfig.json new file mode 100644 index 0000000..5ddf5a5 --- /dev/null +++ b/templates/nextjs/tsconfig.json @@ -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"] +} diff --git a/.prettierignore b/templates/vanilla/.prettierignore similarity index 100% rename from .prettierignore rename to templates/vanilla/.prettierignore diff --git a/.replrc.js b/templates/vanilla/.replrc.js similarity index 100% rename from .replrc.js rename to templates/vanilla/.replrc.js diff --git a/Justfile b/templates/vanilla/Justfile similarity index 100% rename from Justfile rename to templates/vanilla/Justfile diff --git a/bun.lockb b/templates/vanilla/bun.lockb similarity index 100% rename from bun.lockb rename to templates/vanilla/bun.lockb diff --git a/eslint.config.js b/templates/vanilla/eslint.config.js similarity index 100% rename from eslint.config.js rename to templates/vanilla/eslint.config.js diff --git a/index.html b/templates/vanilla/index.html similarity index 100% rename from index.html rename to templates/vanilla/index.html diff --git a/package.json b/templates/vanilla/package.json similarity index 100% rename from package.json rename to templates/vanilla/package.json diff --git a/src/main.js b/templates/vanilla/src/main.js similarity index 100% rename from src/main.js rename to templates/vanilla/src/main.js diff --git a/src/style.css b/templates/vanilla/src/style.css similarity index 100% rename from src/style.css rename to templates/vanilla/src/style.css