Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ A static site generator that lets you build websites using Python and [nitro-ui]
- **Live Reload** - Development server with automatic browser refresh
- **Incremental Builds** - Only rebuild changed pages
- **Dynamic Routes** - Generate pages from data with `[slug].py` pattern
- **Draft Pages** - Mark pages as drafts to exclude from production builds
- **Environment Variables** - Auto-load `.env` files with `from nitro import env`
- **Image Optimization** - Responsive images with WebP/AVIF conversion
- **Islands Architecture** - Partial hydration for interactive components
- **Plugin System** - Extend the build lifecycle with nitro-dispatch hooks
Expand Down Expand Up @@ -83,15 +85,19 @@ def render(slug, title):

## Commands

| Command | Description |
|--------------------|-----------------------------------|
| `nitro new <name>` | Create new project |
| `nitro dev` | Start dev server with live reload |
| `nitro build` | Build for production |
| `nitro preview` | Preview production build |
| `nitro clean` | Remove build artifacts |
| `nitro deploy` | Deploy to hosting platform |
| `nitro info` | Show project and environment info |
| Command | Description |
|--------------------|------------------------------------|
| `nitro new <name>` | Create new project |
| `nitro init` | Initialize Nitro in current dir |
| `nitro dev` | Start dev server with live reload |
| `nitro build` | Build for production |
| `nitro preview` | Preview production build |
| `nitro routes` | List all routes |
| `nitro check` | Validate site without building |
| `nitro export` | Export site as zip archive |
| `nitro clean` | Remove build artifacts |
| `nitro deploy` | Deploy to hosting platform |
| `nitro info` | Show project and environment info |

Run `nitro <command> --help` for options.

Expand Down
112 changes: 104 additions & 8 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ All public classes are importable from the top-level `nitro` package:
from nitro import (
Config, # Project configuration
Page, # Page object returned by render()
env, # Environment variable accessor (auto-loads .env)
ImageConfig, # Image optimization settings
ImageOptimizer, # Image optimization engine
OptimizedImage, # Result of optimizing an image
Expand All @@ -39,14 +40,22 @@ from nitro import (
All commands support `--verbose` / `-v` for detailed output and `--debug` for full tracebacks.

### `nitro new <name>`
Create a new project.
Create a new project with full scaffolding.

```bash
nitro new my-site
nitro new my-site --no-git # Skip git initialization
nitro new my-site --no-install # Skip dependency installation
```

### `nitro init`
Initialize Nitro in an existing directory (minimal scaffolding).

```bash
nitro init # Create config, directories, starter page
nitro init --force # Overwrite existing files
```

### `nitro dev` / `nitro serve`
Start development server with live reload.

Expand Down Expand Up @@ -119,6 +128,32 @@ nitro info
nitro info --json # Output as JSON
```

### `nitro routes`
List all routes the site will generate.

```bash
nitro routes # Table output with URL, source, type, status
nitro routes --json # JSON output
```

### `nitro check`
Validate site without building (render check + internal link check).

```bash
nitro check # Check all pages and links
nitro check --verbose # Show detailed output
nitro check --no-links # Skip link checking
```

### `nitro export`
Export built site as a zip archive.

```bash
nitro export # Export build/ to <project>-<date>.zip
nitro export -o site.zip # Custom output path
nitro export --build-first # Build before exporting
```

## Project Structure

```
Expand Down Expand Up @@ -207,9 +242,27 @@ Page(
content=html_element, # Required: nitro-ui element
meta={"key": "value"}, # Optional: meta tags dict (arbitrary keys)
template="layout", # Optional: template name
draft=False, # Optional: exclude from production builds
)
```

### Draft Pages

Pages with `draft=True` are:
- Rendered during development (`nitro dev`)
- Excluded from production builds (`nitro build`)
- Excluded from sitemap generation
- Shown with "draft" status in `nitro routes`

```python
def render():
return Page(
title="Work in Progress",
content=html_element,
draft=True, # Won't be included in production build
)
```

### File Path to URL Mapping

| File Path | Output URL |
Expand Down Expand Up @@ -805,7 +858,7 @@ Production builds (`nitro build`) include:
4. **Responsive Images** - Generates multi-size AVIF/WebP variants (`--no-responsive` to skip)
5. **Island Processing** - Injects hydration runtime for islands (`--no-islands` to skip)
6. **Asset Fingerprinting** - Adds content hashes to CSS/JS filenames for cache busting
7. **Sitemap Generation** - Creates `sitemap.xml` with all pages
7. **Sitemap Generation** - Creates `sitemap.xml` with all pages (respects page meta)
8. **Robots.txt** - Creates `robots.txt` pointing to sitemap
9. **Asset Manifest** - Creates `manifest.json` with file hashes and sizes
10. **Incremental Builds** - Only rebuilds changed pages (use `--force` to bypass)
Expand Down Expand Up @@ -917,13 +970,36 @@ Picture(
# (standard <img> tags are automatically replaced with <picture> during build)
```

### Environment Variables

Use `env` to access environment variables with automatic `.env` file loading:

```python
from nitro import env

# Access variables as attributes
api_key = env.API_KEY
debug_mode = env.DEBUG

# Check environment
if env.is_production():
# Production-only code
pass

if env.is_development():
# Dev-only code
pass
```

Requires `python-dotenv` for `.env` file support: `pip install nitro-cli[dotenv]`

### Conditional Content

```python
def render():
is_production = os.getenv("NODE_ENV") == "production"
from nitro import env

analytics = Script(src="/analytics.js") if is_production else None
def render():
analytics = Script(src="/analytics.js") if env.is_production() else None

return HTML(
Head(Title("Page"), analytics),
Expand Down Expand Up @@ -955,17 +1031,37 @@ nitro build --debug # Full tracebacks
nitro dev --verbose # Detailed logging
```

## Sitemap Customization

Control sitemap generation via page `meta`:

```python
Page(
title="My Page",
content=html,
meta={
"sitemap": False, # Exclude from sitemap
"lastmod": "2024-01-15", # Custom last modified date
"sitemap_priority": 0.9, # Priority (0.0-1.0, default: 0.8)
"sitemap_changefreq": "daily", # Change frequency
},
)
```

Draft pages are automatically excluded from the sitemap.

## Dependencies

- **nitro-ui** >= 1.0.5 - HTML element builder
- **nitro-ui** >= 1.0.6 - HTML element builder
- **nitro-datastore** >= 1.0.2 - Data loading with dot notation
- **nitro-dispatch** >= 1.0.0 - Plugin system hooks

**Optional (for build optimizations):**
**Optional:**
- **python-dotenv** - `.env` file support (`pip install nitro-cli[dotenv]`)
- **Pillow** - Image optimization and responsive image generation
- **csscompressor** - CSS minification
- **htmlmin** - HTML minification

## Version

Current: nitro-cli 1.0.7
Current: nitro-cli 1.0.8
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "nitro-cli"
version = "1.0.7"
version = "1.0.8"
description = "Build static sites with Python code instead of template engines"
authors = [
{name = "Sean Nieuwoudt", email = "sean@nitro.sh"}
Expand Down Expand Up @@ -46,7 +46,7 @@ dependencies = [
"csscompressor>=0.9.5",
"minify-html>=0.15.0",
"pillow>=10.0.0",
"nitro-ui>=1.0.5",
"nitro-ui>=1.0.6",
"nitro-datastore>=1.0.2",
"nitro-dispatch>=1.0.0",
"beautifulsoup4>=4.12.0",
Expand All @@ -66,6 +66,9 @@ markdown = [
images = [
"pillow-avif-plugin>=1.3.0",
]
dotenv = [
"python-dotenv>=1.0.0",
]

[project.scripts]
nitro = "nitro.cli:main"
Expand Down
2 changes: 2 additions & 0 deletions src/nitro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .core.config import Config
from .core.page import Page
from .core.env import env
from .core.images import (
ImageConfig,
ImageOptimizer,
Expand All @@ -19,6 +20,7 @@
__all__ = [
"Config",
"Page",
"env",
# Images
"ImageConfig",
"ImageOptimizer",
Expand Down
24 changes: 22 additions & 2 deletions src/nitro/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@
from rich.text import Text
from rich.table import Table
from . import __version__
from .commands import new, serve, dev, build, preview, clean, info, deploy
from .commands import (
new,
serve,
dev,
build,
preview,
clean,
info,
deploy,
init,
export_cmd,
routes,
check,
)
from .core.page import get_project_root

console = Console()
Expand Down Expand Up @@ -50,11 +63,14 @@ def show_welcome():
table.add_column("description", style="dim")

table.add_row(" nitro new [dim]<name>[/dim]", "Create a new project")
table.add_row(" nitro init", "Initialize Nitro in current directory")
table.add_row(" nitro dev", "Start dev server with hot reload")
table.add_row(" nitro build", "Build for production")
table.add_row(" nitro preview", "Preview production build")
table.add_row(" nitro routes", "List all routes")
table.add_row(" nitro check", "Validate site without building")
table.add_row(" nitro export", "Export site as zip")
table.add_row(" nitro clean", "Clean build artifacts")
table.add_row(" nitro info", "Show project info")

console.print(" [bold]Commands:[/bold]\n")
console.print(table)
Expand Down Expand Up @@ -88,6 +104,10 @@ def main(ctx):
main.add_command(clean) # type: ignore[arg-type]
main.add_command(info) # type: ignore[arg-type]
main.add_command(deploy) # type: ignore[arg-type]
main.add_command(init) # type: ignore[arg-type]
main.add_command(export_cmd, name="export") # type: ignore[arg-type]
main.add_command(routes) # type: ignore[arg-type]
main.add_command(check) # type: ignore[arg-type]


if __name__ == "__main__":
Expand Down
8 changes: 8 additions & 0 deletions src/nitro/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
from .clean import clean
from .info import info
from .deploy import deploy
from .init import init
from .export import export_cmd
from .routes import routes
from .check import check

__all__ = [
"new",
Expand All @@ -18,4 +22,8 @@
"clean",
"info",
"deploy",
"init",
"export_cmd",
"routes",
"check",
]
9 changes: 8 additions & 1 deletion src/nitro/commands/build.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Build command for production optimization."""

import os
import sys
from datetime import datetime

Expand Down Expand Up @@ -76,6 +77,9 @@ def build(
set_level(LogLevel.QUIET)

try:
# Set production environment variable
os.environ["NITRO_ENV"] = "production"

header("Building for production...")
start_time = datetime.now()

Expand Down Expand Up @@ -108,7 +112,7 @@ def build(

update("Generating pages...")
success_result = generator.generate(
verbose=verbose_flag, force=force or clean
verbose=verbose_flag, force=force or clean, production=True
)

if not success_result:
Expand Down Expand Up @@ -186,10 +190,13 @@ def build(
update("Generating metadata...")
html_files = list(generator.build_dir.rglob("*.html"))
sitemap_path = generator.build_dir / "sitemap.xml"
# Pass page metadata for enhanced sitemap generation
page_metadata = getattr(generator, "page_metadata", None)
bundler.generate_sitemap(
base_url=config.base_url,
html_files=html_files,
output_path=sitemap_path,
page_metadata=page_metadata,
)
verbose(f"Created sitemap.xml with {len(html_files)} URLs")

Expand Down
Loading