ssg 0.0.36

A Content-First Open Source Static Site Generator (SSG) crafted in Rust.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->

<p align="center">
  <img src="https://cloudcdn.pro/static-site-generator/images/logos/static-site-generator.svg" alt="SSG logo" width="128" />
</p>

<h1 align="center">Static Site Generator (SSG)</h1>

<p align="center">
  <strong>Fast, memory-safe, and extensible — built in Rust.</strong>
</p>

<p align="center">
  <a href="https://github.com/sebastienrousseau/static-site-generator/actions"><img src="https://img.shields.io/github/actions/workflow/status/sebastienrousseau/static-site-generator/ci.yml?style=for-the-badge&logo=github" alt="Build" /></a>
  <a href="https://crates.io/crates/ssg"><img src="https://img.shields.io/crates/v/ssg.svg?style=for-the-badge&color=fc8d62&logo=rust" alt="Crates.io" /></a>
  <a href="https://docs.rs/ssg"><img src="https://img.shields.io/badge/docs.rs-ssg-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" alt="Docs.rs" /></a>
  <a href="https://codecov.io/gh/sebastienrousseau/static-site-generator"><img src="https://img.shields.io/codecov/c/github/sebastienrousseau/static-site-generator?style=for-the-badge&logo=codecov" alt="Coverage" /></a>
  <a href="https://lib.rs/crates/ssg"><img src="https://img.shields.io/badge/lib.rs-v0.0.36-orange.svg?style=for-the-badge" alt="lib.rs" /></a>
</p>

---

## Contents

- [Install]#install — one-liner, Homebrew, Cargo, apt, AUR, Scoop, winget
- [Quick Start]#quick-start — scaffold a site in 30 seconds
- [Overview]#overview — what SSG does
- [Architecture]#architecture — build pipeline diagram
- [Features]#features — v0.0.36 capability matrix
- [The CLI]#the-cli — flags and usage
- [Library Usage]#library-usage`ssg::run()`, plugins, schemas
- [Benchmarks]#benchmarks — binary size, test suite, coverage
- [Development]#development — make targets, contributing
- [What's Included]#whats-included — all 36 modules
- [License]#license

---

## Install

### Quick install (prebuilt binary)

**macOS / Linux — one command:**

```sh
curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/static-site-generator/main/scripts/install.sh | sh
```

Auto-detects your OS and architecture, downloads the correct binary from GitHub Releases, verifies the SHA256 checksum, and installs to `~/.local/bin`.

**Homebrew (macOS / Linux):**

```sh
brew install --formula https://raw.githubusercontent.com/sebastienrousseau/static-site-generator/main/packaging/homebrew/ssg.rb
```

**Cargo (any platform with Rust):**

```sh
cargo install ssg
```

**Debian / Ubuntu (apt):**

```sh
# Download the .deb from the latest release, then:
sudo dpkg -i ssg_0.0.36_amd64.deb
```

Or build it yourself with `packaging/deb/build.sh`.

**Arch Linux (AUR):**

```sh
# Using an AUR helper (e.g. yay):
yay -S ssg
```

Or build manually with the PKGBUILD in `packaging/arch/`.

**Windows (Scoop):**

```powershell
scoop bucket add ssg https://github.com/sebastienrousseau/static-site-generator
scoop install ssg
```

**Windows (winget):**

```powershell
winget install sebastienrousseau.ssg
```

**Windows (manual):** download the `.zip` from the [latest release](https://github.com/sebastienrousseau/static-site-generator/releases/latest), extract `ssg.exe`, and add it to your `PATH`.

### Use as a library

```toml
[dependencies]
ssg = "0.0.36"
```

### Build from source

```bash
git clone https://github.com/sebastienrousseau/static-site-generator.git
cd static-site-generator
make init    # installs toolchain, hooks, builds
cargo test --lib
```

Requires **Rust 1.88.0+** (pinned in `rust-toolchain.toml`).

---

## Quick Start

```bash
# 1 — Install
cargo install ssg

# 2 — Scaffold a new site
ssg -n mysite -c content -o build -t templates

# 3 — Build with custom content
ssg -c content -o public -t templates

# 4 — Validate content schemas (no build)
ssg --validate -c content

# 5 — Development server
ssg -c content -o public -t templates -s public
```

---

## Overview

SSG generates static websites from Markdown content, YAML frontmatter, and HTML templates. It compiles everything into production-ready HTML with built-in SEO metadata, accessibility compliance, and feed generation. The plugin system handles the rest.

- **Zero-cost performance** — Rust ownership model, Rayon-parallelised plugins
- **Incremental builds** — content fingerprinting via `.ssg-cache.json`
- **File watching** — automatic rebuild on content changes
- **22-plugin pipeline** — SEO, a11y, i18n, search, images, JSON-LD, RSS, sitemaps
- **WCAG 2.1 Level AA** — accessibility compliance validated on every build
- **Lighthouse 100** — SEO and accessibility scores on generated output

---

## Architecture

```mermaid
graph TD
    A[Content: Markdown + YAML] --> B{SSG CLI}
    B --> V[Content Schema Validation]
    V --> C[Incremental Cache]
    C --> D[Compile: staticdatagen]
    D --> E[Post-Processing Fixes]
    E --> F[Plugin Pipeline: 22 plugins]
    F --> G[Output: HTML + RSS + Sitemap + JSON-LD]
    B --> H[File Watcher]
    H -->|changed files| C
    B -->|--serve| S[Dev Server + Live Reload]
```

---

## Features

| | |
| :--- | :--- |
| **Performance** | Parallel file operations with Rayon `par_iter`, iterative traversal with depth bounds, incremental builds, `--jobs N` thread control |
| **Content** | Markdown with GFM extensions (tables, strikethrough, task lists), YAML/TOML/JSON frontmatter, typed content schemas with compile-time validation |
| **SEO** | Meta description, Open Graph (title, description, type, url, image, image:width/height, locale), Twitter Cards (`summary_large_image` for articles), canonical URLs, robots.txt, sitemaps with per-page lastmod |
| **Structured Data** | JSON-LD Article/WebPage with datePublished, dateModified, author (Person entity), image (`ImageObject`), inLanguage, `BreadcrumbList` |
| **Syndication** | RSS 2.0 with enclosures, categories, language, lastBuildDate, copyright. Google News sitemap with keywords |
| **Accessibility** | Automatic WCAG 2.1 AA validation on every build. Decorative image detection (`role="presentation"`). Heading hierarchy, link text, ARIA landmarks |
| **i18n** | Hreflang injection for multi-locale sites, `x-default` support, per-locale sitemaps with `xhtml:link` alternates, language switcher HTML helper |
| **Images** | Responsive `<picture>` with AVIF/WebP sources, `srcset` at 320/640/1024/1440, lazy loading, CLS prevention via width/height from source metadata |
| **Templates** | Tera engine with inheritance, loops, conditionals, custom filters. 7 bundled templates + 3 themes (minimal, docs, full) |
| **Search** | Client-side full-text search with modal UI, 28 locale translations, `Ctrl+K` / `⌘K` shortcut |
| **Plugins** | Lifecycle hooks: `before_compile`, `after_compile`, `on_serve`. 22 built-in plugins |
| **Deployment** | One-command config for Netlify, Vercel, Cloudflare Pages, GitHub Pages. CSP + HSTS security headers |
| **Security** | `#![forbid(unsafe_code)]`, path traversal prevention, symlink rejection, file size limits, `CycloneDX` SBOM, Sigstore attestation |
| **CI** | Automated multi-platform releases (Linux glibc/musl, macOS ARM64/Intel, Windows), pa11y accessibility audits, cargo audit/deny |

---

## The CLI

```text
Usage: ssg [OPTIONS]

Options:
  -f, --config <FILE>    Configuration file path
  -n, --new <NAME>       Create new project
  -c, --content <DIR>    Content directory
  -o, --output <DIR>     Output directory
  -t, --template <DIR>   Template directory
  -s, --serve <DIR>      Start development server
  -w, --watch            Watch for changes and rebuild
  -j, --jobs <N>         Rayon thread count (default: num_cpus)
      --validate         Validate content schemas and exit
      --drafts           Include draft pages in the build
      --deploy <TARGET>  Generate deployment config (netlify, vercel, cloudflare, github)
  -q, --quiet            Suppress non-error output
      --verbose          Show detailed build information
  -h, --help             Print help
  -V, --version          Print version
```

### Environment variables

| Variable | Default | Purpose |
| :--- | :--- | :--- |
| `SSG_HOST` | `127.0.0.1` | Dev server bind address (use `0.0.0.0` for WSL2/Codespaces) |
| `SSG_PORT` | `3000` | Dev server port |

---

## Library Usage

```rust,no_run
// The simplest path: delegate to ssg's own pipeline.
fn main() -> anyhow::Result<()> {
    ssg::run()
}
```

<details>
<summary><b>Plugin example</b></summary>

```rust,no_run
use ssg::plugin::{Plugin, PluginContext, PluginManager};
use anyhow::Result;
use std::path::Path;

#[derive(Debug)]
struct LogPlugin;

impl Plugin for LogPlugin {
    fn name(&self) -> &str { "logger" }
    fn after_compile(&self, ctx: &PluginContext) -> Result<()> {
        println!("Site compiled to {:?}", ctx.site_dir);
        Ok(())
    }
}

fn main() -> Result<()> {
    let mut pm = PluginManager::new();
    pm.register(LogPlugin);
    pm.register(ssg::plugins::MinifyPlugin);

    let ctx = PluginContext::new(
        Path::new("content"),
        Path::new("build"),
        Path::new("public"),
        Path::new("templates"),
    );
    pm.run_after_compile(&ctx)?;
    Ok(())
}
```

</details>

<details>
<summary><b>Content schema validation</b></summary>

Create `content/content.schema.toml`:

```toml
[[schemas]]
name = "post"

[[schemas.fields]]
name = "title"
type = "string"
required = true

[[schemas.fields]]
name = "date"
type = "date"
required = true

[[schemas.fields]]
name = "draft"
type = "bool"
default = "false"
```

Pages with `schema = "post"` in their frontmatter are validated at compile time. Run `ssg --validate` for schema-only checks.

</details>

<details>
<summary><b>Incremental build example</b></summary>

```rust,no_run
use ssg::cache::BuildCache;
use std::path::Path;

let cache_path = Path::new(".ssg-cache.json");
let content_dir = Path::new("content");

let mut cache = BuildCache::load(cache_path).unwrap();
let changed = cache.changed_files(content_dir).unwrap();

if changed.is_empty() {
    println!("No changes detected, skipping build.");
} else {
    println!("Rebuilding {} changed files", changed.len());
    cache.update(content_dir).unwrap();
    cache.save().unwrap();
}
```

</details>

---

## Benchmarks

| Metric | Value |
| :--- | :--- |
| **Release binary** | ~23 MB stripped with LTO |
| **Unsafe code** | 0 blocks — `#![forbid(unsafe_code)]` enforced |
| **Test suite** | **848 lib** + 34 doc tests |
| **Dependencies** | 21 direct (down from 25 in v0.0.35) |
| **Coverage** | ~98 % line coverage |
| **Plugin pipeline** | 22 plugins, Rayon-parallelised |
| **Build** | `cargo build`: ~2 min cold, <10 s incremental |
| **MSRV** | Rust 1.88.0 |

---

## Development

```bash
make init         # Bootstrap (rustfmt + clippy + cargo-deny + hooks + build)
make build        # Build the project
make test         # Run all tests
make bench        # Run Criterion benchmarks
make lint         # Lint with Clippy
make format       # Format with rustfmt
make deny         # Check licenses and advisories
make doc          # Generate API docs and open in browser
make a11y         # Run pa11y accessibility audit on example site
make clean        # Remove build artifacts and stray logs
make hooks        # Install the signed-commit git hook
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for signed commits and PR guidelines.

---

## What's Included

<details>
<summary><b>All 36 modules</b></summary>

| Module | Purpose |
| :--- | :--- |
| **cmd** | CLI argument parsing, `SsgConfig`, input validation |
| **process** | Directory creation and site processing |
| **lib** | Orchestrator: `run()` → pipeline → compile → serve |
| **plugin** | `Plugin` trait with lifecycle hooks |
| **plugins** | `MinifyPlugin`, `ImageOptiPlugin`, `DeployPlugin` |
| **postprocess** | `SitemapFixPlugin`, `NewsSitemapFixPlugin`, `RssAggregatePlugin`, `ManifestFixPlugin`, `HtmlFixPlugin` |
| **seo** | `SeoPlugin`, `JsonLdPlugin`, `CanonicalPlugin`, `RobotsPlugin` |
| **content** | `ContentSchema`, `ContentValidationPlugin`, `--validate` |
| **i18n** | `I18nPlugin`, hreflang injection, per-locale sitemaps |
| **search** | Full-text index, search UI, 28 locale translations |
| **accessibility** | WCAG checker, ARIA validation, decorative image detection |
| **`image_plugin`** | `<picture>` with AVIF/WebP, responsive srcset |
| **ai** | AI-readiness hooks, alt-text validation, `llms.txt` |
| **deploy** | Netlify, Vercel, Cloudflare Pages, GitHub Pages adapters |
| **assets** | Asset fingerprinting and SRI hash generation |
| **highlight** | Syntax highlighting for code blocks |
| **shortcodes** | youtube, gist, figure, admonition expansion |
| **`markdown_ext`** | GFM tables, strikethrough, task lists |
| **livereload** | WebSocket live-reload injection (dev only) |
| **pagination** | Pagination plugin for listing pages |
| **taxonomy** | Tag and category index generation |
| **drafts** | Draft content filtering |
| **frontmatter** | Frontmatter extraction and `.meta.json` sidecars |
| **`tera_engine`** | Tera templating engine integration |
| **`tera_plugin`** | Tera template rendering plugin |
| **cache** | Content fingerprinting for incremental builds |
| **watch** | Polling-based file watcher |
| **schema** | JSON Schema generator for configuration |
| **scaffold** | Project scaffolding (`ssg --new`) |
| **stream** | High-performance streaming file processor |
| **walk** | Shared bounded directory walkers |

</details>

<details>
<summary><b>Security and compliance</b></summary>

- `#![forbid(unsafe_code)]` across the entire codebase
- Path traversal prevention with `..` detection and symlink rejection
- File size limits (10 MB per file) and directory depth bounds (128 levels)
- `cargo audit` with zero warnings
- `cargo deny` — license, advisory, ban, and source checks
- `CycloneDX` SBOM generated as release artifact with Sigstore attestation
- SPDX license headers on all source files
- Signed commits enforced via SSH ED25519

</details>

---

## License

Dual-licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT](https://opensource.org/licenses/MIT), at your option.

See [CHANGELOG.md](CHANGELOG.md) for release history.

<p align="right"><a href="#contents">Back to Top</a></p>