{"id":16616925,"url":"https://github.com/sebastienrousseau/shokunin","last_synced_at":"2026-04-11T21:14:22.076Z","repository":{"id":142715998,"uuid":"613774615","full_name":"sebastienrousseau/shokunin","owner":"sebastienrousseau","description":"A Content-First Open Source Static Site Generator (SSG) written in Rust 🦀","archived":false,"fork":false,"pushed_at":"2024-04-30T03:11:29.000Z","size":1907,"stargazers_count":22,"open_issues_count":4,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-02T03:02:32.258Z","etag":null,"topics":["blog-engine","content-first","documentation-tool","markdown","markdown-to-html","rust","site-generator","ssg","static-site","static-site-generation","static-site-generator","website","website-template"],"latest_commit_sha":null,"homepage":"https://shokunin.one","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sebastienrousseau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/funding.yml","license":"LICENSE-APACHE","code_of_conduct":".github/CODE-OF-CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"sebastienrousseau","custom":"https://paypal.me/wwdseb"}},"created_at":"2023-03-14T08:46:48.000Z","updated_at":"2024-05-03T21:33:44.938Z","dependencies_parsed_at":"2023-11-28T08:25:34.487Z","dependency_job_id":"4af6c520-6ad1-4e0d-8716-1f0fd1f91be6","html_url":"https://github.com/sebastienrousseau/shokunin","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastienrousseau%2Fshokunin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastienrousseau%2Fshokunin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastienrousseau%2Fshokunin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebastienrousseau%2Fshokunin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sebastienrousseau","download_url":"https://codeload.github.com/sebastienrousseau/shokunin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243730841,"owners_count":20338724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog-engine","content-first","documentation-tool","markdown","markdown-to-html","rust","site-generator","ssg","static-site","static-site-generation","static-site-generator","website","website-template"],"created_at":"2024-10-12T02:14:41.837Z","updated_at":"2026-04-11T21:14:22.063Z","avatar_url":"https://github.com/sebastienrousseau.png","language":"Rust","funding_links":["https://github.com/sponsors/sebastienrousseau","https://paypal.me/wwdseb"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://cloudcdn.pro/shokunin/images/logos/shokunin.svg\" alt=\"SSG logo\" width=\"128\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eStatic Site Generator (SSG)\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eFast, memory-safe, and extensible — built in Rust.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/sebastienrousseau/shokunin/actions\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/sebastienrousseau/shokunin/test.yml?style=for-the-badge\u0026logo=github\" alt=\"Build\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://crates.io/crates/ssg\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/ssg.svg?style=for-the-badge\u0026color=fc8d62\u0026logo=rust\" alt=\"Crates.io\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://docs.rs/ssg\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs.rs-ssg-66c2a5?style=for-the-badge\u0026labelColor=555555\u0026logo=docs.rs\" alt=\"Docs.rs\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/sebastienrousseau/shokunin\"\u003e\u003cimg src=\"https://img.shields.io/codecov/c/github/sebastienrousseau/shokunin?style=for-the-badge\u0026logo=codecov\" alt=\"Coverage\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://lib.rs/crates/ssg\"\u003e\u003cimg src=\"https://img.shields.io/badge/lib.rs-v0.0.35-orange.svg?style=for-the-badge\" alt=\"lib.rs\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Contents\n\n- [Install](#install) — prerequisites, crate, library dependency\n- [Overview](#overview) — what SSG does\n- [Architecture](#architecture) — build pipeline diagram\n- [Features](#features) — capability matrix\n- [The CLI](#the-cli) — flags and usage\n- [First 5 Minutes](#first-5-minutes) — clone → tests passing\n- [Library Usage](#library-usage) — `ssg::run()`, plugins, incremental builds\n- [Benchmarks](#benchmarks) — binary size, test suite, coverage\n- [Development](#development) — make targets, contributing\n- [What's Included](#whats-included) — modules, security, tests\n- [License](#license)\n\n---\n\n## Install\n\n### Prerequisites\n\n| Platform | Setup |\n| :--- | :--- |\n| **macOS** | `brew install rustup-init \u0026\u0026 rustup-init -y`, or follow [rustup.rs](https://rustup.rs/) |\n| **Linux / WSL** | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \\| sh` |\n| **Windows (native)** | Download [`rustup-init.exe`](https://win.rustup.rs/). Native build is supported; the `make` targets below require Git Bash or WSL |\n\nSSG requires **Rust 1.88.0 or later** (pinned in `rust-toolchain.toml`). Verify with `rustc --version`.\n\n### Crate\n\n```bash\ncargo install ssg\n```\n\nOr add as a library dependency:\n\n```toml\n[dependencies]\nssg = \"0.0.35\"\n```\n\n---\n\n## Overview\n\nSSG 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.\n\n- **Zero-cost performance** through Rust's ownership model and parallel file operations\n- **Incremental builds** with content fingerprinting — only changed files are reprocessed\n- **File watching** with automatic rebuild on content changes\n- **Plugin architecture** with lifecycle hooks for custom processing\n- **WCAG 2.1 Level AA** accessibility compliance in generated output\n\n---\n\n## Architecture\n\n```mermaid\ngraph TD\n    A[Content: Markdown + YAML] --\u003e B{SSG CLI}\n    B --\u003e C[Incremental Cache]\n    C --\u003e D[Compile: staticdatagen]\n    D --\u003e E[Plugin Pipeline]\n    E --\u003e F[Minify / Optimize / Deploy]\n    D --\u003e G[Dev Server: http_handle]\n    B --\u003e H[File Watcher]\n    H --\u003e|changed files| C\n```\n\n---\n\n## Features\n\n| | |\n| :--- | :--- |\n| **Performance** | Parallel file operations with Rayon, iterative traversal with depth bounds, incremental builds |\n| **Content** | Markdown, YAML frontmatter, JSON, TOML. Atom and RSS feed generation |\n| **SEO** | Meta tags, Open Graph, sitemaps, structured data, canonical URLs |\n| **Accessibility** | Automatic WCAG 2.1 Level AA compliance |\n| **Theming** | Custom HTML templates with variable substitution |\n| **Plugins** | Lifecycle hooks: `before_compile`, `after_compile`, `on_serve`. Built-in minify, image-opti, deploy |\n| **Watch mode** | Polling-based file watcher with configurable interval |\n| **Caching** | Content fingerprinting via `.ssg-cache.json` for fast rebuilds |\n| **Config** | TOML config files with JSON Schema for IDE autocomplete (`ssg.schema.json`) |\n| **Security** | `#![forbid(unsafe_code)]`, path traversal prevention, symlink rejection, file size limits |\n| **CI** | Shared `rust-ci.yml` pipeline on `stable` toolchain, plus `cargo audit`, `cargo deny`, dependency review and SBOM generation |\n\n---\n\n## The CLI\n\n| Command | What it does |\n| :--- | :--- |\n| `ssg -n mysite -c content -o build -t templates` | Generate a site from source directories |\n| `ssg --config config.toml` | Load configuration from a TOML file |\n| `ssg --serve public` | Serve from a specific directory |\n| `ssg --watch` | Watch content for changes and rebuild |\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFull CLI reference\u003c/b\u003e\u003c/summary\u003e\n\n```text\nUsage: ssg [OPTIONS]\n\nOptions:\n  -f, --config \u003cFILE\u003e    Configuration file path\n  -n, --new \u003cNAME\u003e       Create new project\n  -c, --content \u003cDIR\u003e    Content directory\n  -o, --output \u003cDIR\u003e     Output directory\n  -t, --template \u003cDIR\u003e   Template directory\n  -s, --serve \u003cDIR\u003e      Development server directory\n  -w, --watch            Watch for changes and rebuild\n      --drafts           Include draft pages in the build\n      --deploy \u003cTARGET\u003e  Generate deployment config (netlify, vercel, cloudflare, github)\n  -q, --quiet            Suppress non-error output\n      --verbose          Show detailed build information\n  -h, --help             Print help\n  -V, --version          Print version\n```\n\nWhen no flags are provided, sensible defaults are used (`content/`, `public/`, `templates/`).\n\n\u003c/details\u003e\n\n---\n\n## First 5 Minutes\n\n```bash\n# 1 — Install\ncargo install ssg                       # macOS / Linux / Windows\n\n# 2 — Scaffold + build a brand-new site\nssg -n mysite -c content -o build -t templates\n\n# 3 — Or build from source and run the bundled examples\ngit clone https://github.com/sebastienrousseau/shokunin.git\ncd shokunin\ncargo build                             # ~2 min cold, \u003c 10 s incremental\ncargo test --lib                        # 741 tests, ~8 s on M-series Mac\ncargo run --example basic               # minimal site\ncargo run --example quickstart          # opinionated defaults\ncargo run --example plugins             # plugin pipeline walk-through\ncargo run --example multilingual        # 28 locales + localized search\n```\n\n\u003e On **WSL/Linux** the same commands work verbatim. On **native Windows**, replace `make` targets with their `cargo` equivalents (see [*Development*](#development)).\n\n### One-command bootstrap\n\n```bash\nmake init       # detects platform, installs rustfmt + clippy + cargo-deny,\n                # wires up the signed-commit git hook, and runs cargo build\n```\n\n### WSL2 troubleshooting\n\nThe bundled dev server binds to `127.0.0.1:3000` (or `:8000` for the CLI). On WSL2, that loopback is reachable from your Windows host as long as `localhostForwarding` is enabled (the default since the Microsoft Store version of WSL). If your browser can't reach the site, override the bind address with environment variables — no code changes needed:\n\n```bash\nSSG_HOST=0.0.0.0 SSG_PORT=8080 cargo run --example multilingual\n```\n\nThe same vars work for `ssg --serve`. Use `0.0.0.0` for Codespaces, dev-containers, and any remote-dev setup where the listener needs to be reachable from outside its network namespace.\n\n---\n\n## Library Usage\n\n```rust,no_run\n// The simplest path: delegate to ssg's own pipeline.\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    ssg::run().await\n}\n```\n\nIf you only want the compile primitive (no plugin pipeline, no dev server), depend on [`staticdatagen`](https://crates.io/crates/staticdatagen) directly:\n\n```rust,no_run\nuse staticdatagen::compiler::service::compile;\nuse std::path::Path;\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n    compile(\n        Path::new(\"build\"),\n        Path::new(\"content\"),\n        Path::new(\"public\"),\n        Path::new(\"templates\"),\n    )?;\n    Ok(())\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ePlugin example\u003c/b\u003e\u003c/summary\u003e\n\n```rust,no_run\nuse ssg::plugin::{Plugin, PluginContext, PluginManager};\nuse anyhow::Result;\nuse std::path::Path;\n\n#[derive(Debug)]\nstruct LogPlugin;\n\nimpl Plugin for LogPlugin {\n    fn name(\u0026self) -\u003e \u0026str { \"logger\" }\n    fn after_compile(\u0026self, ctx: \u0026PluginContext) -\u003e Result\u003c()\u003e {\n        println!(\"Site compiled to {:?}\", ctx.site_dir);\n        Ok(())\n    }\n}\n\nfn main() -\u003e Result\u003c()\u003e {\n    let mut pm = PluginManager::new();\n    pm.register(LogPlugin);\n    pm.register(ssg::plugins::MinifyPlugin);\n\n    let ctx = PluginContext::new(\n        Path::new(\"content\"),\n        Path::new(\"build\"),\n        Path::new(\"public\"),\n        Path::new(\"templates\"),\n    );\n    pm.run_after_compile(\u0026ctx)?;\n    Ok(())\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eIncremental build example\u003c/b\u003e\u003c/summary\u003e\n\n```rust,no_run\nuse ssg::cache::BuildCache;\nuse std::path::Path;\n\nlet cache_path = Path::new(\".ssg-cache.json\");\nlet content_dir = Path::new(\"content\");\n\nlet mut cache = BuildCache::load(cache_path).unwrap();\nlet changed = cache.changed_files(content_dir).unwrap();\n\nif changed.is_empty() {\n    println!(\"No changes detected, skipping build.\");\n} else {\n    println!(\"Rebuilding {} changed files\", changed.len());\n    // ... run build ...\n    cache.update(content_dir).unwrap();\n    cache.save().unwrap();\n}\n```\n\n\u003c/details\u003e\n\n---\n\n## Benchmarks\n\n| Metric | Value |\n| :--- | :--- |\n| **Release binary** | ~23 MB (unstripped), ~5 MB stripped with LTO |\n| **Unsafe code** | 0 blocks — `#![forbid(unsafe_code)]` enforced |\n| **Test suite** | **741 lib** + 33 doc + integration tests. Run `make bench` for Criterion site-generation benchmarks (10/50/100 pages) |\n| **Dependencies** | 24 direct, audited via `cargo audit` and `cargo deny check` (run `make deny` to reproduce). All CI refs pinned to SHA. |\n| **Coverage** | ~98 % line coverage, measured with `cargo llvm-cov` |\n| **Plugin pipeline** | Rayon-parallelised: search, SEO, canonical, and JSON-LD injection all use `par_iter` |\n\n---\n\n## Development\n\n```bash\nmake init         # One-command bootstrap (rustfmt + clippy + cargo-deny + hooks + build)\nmake build        # Build the project\nmake test         # Run all tests\nmake bench        # Run performance benchmarks (Criterion)\nmake lint         # Lint with Clippy\nmake format       # Format with rustfmt\nmake deny         # Check licenses and advisories\nmake doc          # Generate API docs and open in browser\nmake clean        # Remove build artifacts and stray logs\nmake hooks        # Install the signed-commit git hook\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for setup, signed commits, and PR guidelines.\n\n---\n\n## What's Included\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eCore modules\u003c/b\u003e\u003c/summary\u003e\n\n- **cmd** — CLI argument parsing, `SsgConfig`, input validation\n- **process** — Argument-driven site processing and directory creation\n- **lib** — Orchestrator: `run()` → plugin pipeline → compile → serve\n- **plugin** — `Plugin` trait with `before_compile`, `after_compile`, `on_serve` hooks\n- **plugins** — Built-in `MinifyPlugin`, `ImageOptiPlugin`, `DeployPlugin`\n- **cache** — Content fingerprinting for incremental builds\n- **watch** — Polling-based file watcher for live rebuild\n- **schema** — JSON Schema generator for configuration\n- **scaffold** — Project scaffolding (`ssg --new`)\n- **frontmatter** — Frontmatter extraction and `.meta.json` sidecar support\n- **tera_engine** — Tera templating engine integration\n- **tera_plugin** — Tera template rendering plugin\n- **seo** — `SeoPlugin`, `JsonLdPlugin`, `CanonicalPlugin`, `RobotsPlugin`\n- **search** — Client-side search index generation + localized `SearchLabels`\n- **accessibility** — Automated WCAG checker and ARIA validation\n- **ai** — AI-readiness content hooks (alt-text validation, `llms.txt`)\n- **deploy** — Deployment adapters (Netlify, Vercel, Cloudflare, GitHub Pages)\n- **assets** — Asset fingerprinting and SRI hash generation\n- **highlight** — Syntax highlighting plugin for code blocks\n- **shortcodes** — Shortcode expansion (youtube, gist, figure, admonitions)\n- **markdown_ext** — GFM extensions (tables, strikethrough, task lists)\n- **image_plugin** — Image optimization with WebP output and responsive `srcset`\n- **livereload** — WebSocket live-reload injection\n- **pagination** — Pagination plugin for listing pages\n- **taxonomy** — Taxonomy generation (tags, categories)\n- **drafts** — Draft content filtering plugin\n- **stream** — High-performance streaming file processor\n- **walk** — Shared bounded directory walkers\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eSecurity and compliance\u003c/b\u003e\u003c/summary\u003e\n\n- **`#![forbid(unsafe_code)]`** across the entire codebase\n- **Path traversal prevention** with `..` detection and symlink rejection\n- **File size limits** (10 MB per file) and directory depth bounds (128 levels)\n- **`cargo audit`** with zero warnings — all advisories tracked in `.cargo/audit.toml`\n- **`cargo deny`** — license, advisory, ban, and source checks all pass\n- **SBOM** generated as a release artifact\n- **Signed commits** enforced via SSH ED25519\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eTest coverage\u003c/b\u003e\u003c/summary\u003e\n\n- **741 lib tests** plus integration + fault-injection suites\n- **~98 % library line coverage** measured with cargo-llvm-cov\n- CI runs on the shared `rust-ci.yml` pipeline (`stable` toolchain)\n\u003c/details\u003e\n\n---\n\n**THE ARCHITECT** ᛫ [Sebastien Rousseau](https://sebastienrousseau.com)\n**THE ENGINE** ᛞ [EUXIS](https://euxis.co) ᛫ Enterprise Unified Execution Intelligence System\n\n---\n\n## Code of Conduct\n\nPlease read our [Code of Conduct](.github/CODE-OF-CONDUCT.md) before participating in the project.\n\n## License\n\nDual-licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT](https://opensource.org/licenses/MIT), at your option.\n\nSee [CHANGELOG.md](CHANGELOG.md) for release history.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#contents\"\u003eBack to Top\u003c/a\u003e\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastienrousseau%2Fshokunin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebastienrousseau%2Fshokunin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastienrousseau%2Fshokunin/lists"}