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

6
.gitignore vendored
View file

@ -4,6 +4,12 @@ node_modules/
# Build output
dist/
build/
.next/
out/
# TypeScript
*.tsbuildinfo
next-env.d.ts
# Direnv
.direnv/

172
README.md
View file

@ -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

View file

@ -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";

View file

@ -22,6 +22,7 @@ in {
[
bun
nodejs
typescript
eslint
just
prettierd

View file

@ -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";
}
];
};
};
};
}

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"]
}