A markdown docs site
This site - the one you are reading - is a Punk application, and these docs are one keyword of it:
markdown '/docs' => 'docs',
title => 'Punk',
template_dir => 'root/docs-templates',
sort => 'order',
reload => $ENV{PUNK_DEV} ? 1 : 0,
footer => '<a href="/">Punk</a> - the compiled Perl web framework';
Point markdown at a nested directory of .md files and you get a
documentation site: navigation reflecting the tree, a per-page table of
contents from the headings, syntax highlighting, ranked search, and any
images sitting beside the markdown served as static files.
The whole site is rendered at boot - the tree is walked, every page is rendered through Markdown::Simple and wrapped by Template::Stencil, the search index is filled, and the finished bytes are frozen. A request is then a hash lookup and a triplet: no markdown parse, no template render, no Perl frame.
The tree
docs/
├── index.md # /docs
├── guide/
│ ├── getting-started.md # /docs/guide/getting-started
│ └── routing.md
└── recipes/
└── markdown-site.md # this page
Top-level directories become collapsible sections in the navigation,
named after the directory. index.md is the landing page of its
directory.
Front matter
A restricted --- block controls each page:
---
title: Routing
order: 2
---
title names the page (falling back to the first # heading, then the
filename), order sorts it within its section under sort => 'order',
nav overrides the navigation label, and draft: true excludes the
page entirely.
Links and images
Internal links are absolute, prefix-qualified paths -
[Routing](/docs/guide/routing) - and a request for the .md spelling
or a trailing slash 301s to the canonical URL. An image beside the page
() is served from the same tree,
with .. segments refused.
Custom chrome
The default templates are fine; this site replaces them to share its
header with the landing page. template_dir must hold all three files -
page.tmpl, wrapper.tmpl, app.css - and nothing is inherited from
the shipped set:
wrapper.tmplis the HTML shell; this site's carries the same site header as the landing page, brand link pointing home to/.page.tmplrenders the page body (three lines, usually verbatim).app.cssstyles the document, sidebar, contents and search.
The wrapper sees title, site_title, nav, content, toc, url,
prefix, search, search_path, assets and footer.
Search
On by default, server-rendered, no JavaScript: a Search::Trigram
index filled at boot, a GET form at /docs/search, ranked results with
highlighted snippets. search => 0 turns it off and drops the
dependency.
Writing mode
reload => 1 re-checks mtimes and re-renders changed pages per request
- this site enables it under
PUNK_DEV=1, so editing a guide is save-and-refresh. In production the flag is off and the bytes are frozen at boot; a deploy restarts the app anyway.