Skip to content

Broken documentation fails the build

Nacara turns markdown into a documentation site, and your site is an F# program. Front matter is decoded into types you declare, links are resolved against the route table, and anchors are checked against the headings they name.

Get started View on GitHub

Terminal
dotnet new install Nacara.Templates
dotnet new nacara-docs -o docs
cd docs && dotnet run -- watch

Your editor already knows it

The site is F#, so describing it comes with completion, go to definition and type errors. A plugin you spelled wrong is a squiggle under your cursor, not a surprise in CI.

docs/Site.fs
let site =
    Site.create "My library"
    |> Site.baseUrl "/"
    |> Markdown.register
    |> TreeSitter.register
    |> Search.register
    |> Theme.register theme
    |> Site.collection (Theme.docs theme "content")

[<EntryPoint>]
let main argv = Nacara.run site argv

Your documentation can be interactive

A code block marked live becomes an editor. Fable compiles it in the browser against your library, so a reader can change the example and run it, and it cannot drift from the code it documents.

open Browser.Dom
open Demo

// Point and distance come from this site's preset, so the type-checker answers for them.
let route =
    [
        { X = 10.0; Y = 60.0 }
        { X = 70.0; Y = 20.0 }
        { X = 130.0; Y = 75.0 }
        { X = 190.0; Y = 30.0 }
    ]
    
let travelled = route |> List.pairwise |> List.sumBy (fun (a, b) -> distance a b)

let line = route |> List.map (fun p -> $"%.0f{p.X},%.0f{p.Y}") |> String.concat " "

let dots =
    route
    |> List.map (fun p -> $"<circle cx='%.0f{p.X}' cy='%.0f{p.Y}' r='6' fill='#6669d7' />")
    |> String.concat ""

document.getElementById("app").innerHTML <-
    $"<h2>%.1f{travelled} units travelled</h2>"
    + "<svg viewBox='0 0 200 90' width='320' height='144'>"
    + $"<polyline points='%s{line}' fill='none' stroke='#6669d7' stroke-width='2' />"
    + dots
    + "</svg>"

What that buys you

Nothing to keep in sync

The engine is a library your project references, so the tool can never be a version behind the site it builds.

Front matter with types

A collection declares what it expects. A missing field is a build error with a file, a line and a column - not a blank heading in production.

Links that cannot rot

Write them the way they work on GitHub, ../guide/index.md. The engine resolves them, and a link or anchor pointing nowhere stops the build.

Fast, and it stays fast

Work is memoised on content hashes, only changed files are written, and orphaned output is pruned. Watching rebuilds what changed and nothing else.

One package per feature

Search, versions, changelogs, literate F#, an API reference read from your assemblies - each is a plugin on a small core, wired with the pipeline you already write.

Published from the same program

Build it, then dotnet run -- gh-pages puts it on the branch GitHub Pages serves, leaving the other versions where they are.

Start with a page and a menu

The template sets up a site you can deploy, with the plugins a published site needs.

Get started Browse the plugins