mirror of
https://github.com/sajenim/javascript-template.git
synced 2025-12-17 03:50:40 +08:00
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:
parent
506821f638
commit
c497b0fc1d
23 changed files with 451 additions and 39 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -4,6 +4,12 @@ node_modules/
|
||||||
# Build output
|
# Build output
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
# Direnv
|
# Direnv
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
|
||||||
172
README.md
172
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
|
## Available Templates
|
||||||
- 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
|
|
||||||
|
|
||||||
## 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
|
- [Nix](https://nixos.org/download.html) with flakes enabled
|
||||||
- [direnv](https://direnv.net/) (optional but recommended)
|
- [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
|
```sh
|
||||||
nix run nixpkgs#omnix -- \
|
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
|
```sh
|
||||||
cd my-project
|
cd my-project
|
||||||
|
|
@ -31,37 +84,106 @@ direnv allow # Or use: nix develop
|
||||||
just dev
|
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
|
||||||
# nix/modules/devshell.nix
|
# nix/modules/devshell.nix
|
||||||
fhs = true;
|
fhs = true;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Available Commands
|
## Common Commands
|
||||||
|
|
||||||
Run `just` to see all available commands:
|
Both templates share the same command interface:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
just dev # Start dev server
|
just # List all available commands
|
||||||
just repl # Node.js REPL with helpers
|
just dev # Start development server
|
||||||
just lint # Lint with ESLint
|
|
||||||
just format # Format with Prettier
|
|
||||||
just build # Production build
|
just build # Production build
|
||||||
|
just lint # Lint code
|
||||||
|
just format # Format code
|
||||||
just clean # Clean build artifacts
|
just clean # Clean build artifacts
|
||||||
just update # Update flake inputs
|
just update # Update Nix flake inputs
|
||||||
just upgrade # Upgrade bun packages
|
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
|
## 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`
|
- 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
|
- Add Prettier config via `.prettierrc` if needed
|
||||||
- Extend `Justfile` with project-specific commands
|
- 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
|
## 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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
description = "Nix template for JavaScript projects, powered by Bun";
|
description = "Nix templates for JavaScript projects, powered by Bun";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ in {
|
||||||
[
|
[
|
||||||
bun
|
bun
|
||||||
nodejs
|
nodejs
|
||||||
|
typescript
|
||||||
eslint
|
eslint
|
||||||
just
|
just
|
||||||
prettierd
|
prettierd
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,41 @@
|
||||||
{inputs, ...}: {
|
{inputs, ...}: let
|
||||||
|
root = inputs.self;
|
||||||
|
in {
|
||||||
flake = rec {
|
flake = rec {
|
||||||
templates.default = {
|
templates = {
|
||||||
description = "A minimal JavaScript project template with Bun and Nix";
|
default = {
|
||||||
path = inputs.self;
|
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 = {
|
om.templates = {
|
||||||
template = templates.default;
|
javascript-vanilla = {
|
||||||
params = [
|
template = templates.default;
|
||||||
{
|
params = [
|
||||||
name = "package-name";
|
{
|
||||||
description = "Name of the JavaScript package";
|
name = "package-name";
|
||||||
placeholder = "javascript-template";
|
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";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
templates/nextjs/.eslintrc.json
Normal file
3
templates/nextjs/.eslintrc.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||||
|
}
|
||||||
3
templates/nextjs/.prettierignore
Normal file
3
templates/nextjs/.prettierignore
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
bun.lockb
|
||||||
66
templates/nextjs/Justfile
Normal file
66
templates/nextjs/Justfile
Normal 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
|
||||||
64
templates/nextjs/app/globals.css
Normal file
64
templates/nextjs/app/globals.css
Normal 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;
|
||||||
|
}
|
||||||
19
templates/nextjs/app/layout.tsx
Normal file
19
templates/nextjs/app/layout.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
templates/nextjs/app/page.tsx
Normal file
37
templates/nextjs/app/page.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
templates/nextjs/next.config.js
Normal file
17
templates/nextjs/next.config.js
Normal 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;
|
||||||
25
templates/nextjs/package.json
Normal file
25
templates/nextjs/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
templates/nextjs/tsconfig.json
Normal file
27
templates/nextjs/tsconfig.json
Normal 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"]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue