Files
thor/HUGO.md
T
2026-07-14 11:59:21 -04:00

55 lines
3.4 KiB
Markdown

# Hugo vs Thor: Why Thor Doesn't Need Shortcode Context Isolation
## Hugo's Shortcode Isolation
Hugo strictly separates shortcode context from layout template context. Shortcodes run during markdown rendering, before layouts. They get a limited context (`.Page`, `.Site`, `.Params`), not the full layout rendering state.
### Why Hugo does this
1. **Circular dependencies** — Hugo shortcodes output into `.Content`. Layouts read `.Content` and compute derived properties (`.TableOfContents`, `.WordCount`, `.ReadingTime`). If shortcodes could access these computed properties, you'd get circular dependencies (shortcode output → computed property → shortcode reads it).
2. **Multiple layouts/themes** — Hugo supports user-selectable themes and multiple layouts per section type. A shortcode must produce identical output regardless of which layout renders it. Isolating context guarantees portability.
3. **Shared mutable page object** — Hugo builds a rich `Page` object that both shortcodes and layouts access. Context isolation prevents shortcodes from mutating layout-visible state.
4. **Security** — Shortcodes live in content files (potentially user-authored). Full template access would let content authors inject arbitrary logic, access sensitive config, or break site structure.
5. **Caching** — Hugo caches shortcode output independently. Predictable context = predictable cache behavior.
## Why this doesn't apply to thor
- **No computed page properties** — The content Mustache pass runs on raw markdown (before cmark). The layout Mustache pass runs on finished HTML (after cmark). There's no shared `.Content` object or derived properties. The two passes are at different pipeline stages with no shared mutable state.
- **Single layout set** — Thor has one set of templates (`base.html` + page templates). No theme switching, no multiple layout variants per section. Portability across layouts isn't a concern.
- **Explicit data passing** — Each `mustache.render` call receives its own data struct. Content pass gets frontmatter + params. Layout pass gets page data + site config. No shared mutable object between them.
- **Single author** — Content is authored by the site owner. No untrusted user-generated content.
- **No caching** — Thor rebuilds from scratch every time. No cache invalidation concerns.
## Thor's approach
Thor runs Mustache on content **before** cmark as a pre-processing step, then runs Mustache on layout templates **after** cmark as a post-processing step. These are two independent `mustache.render` calls:
```
content (markdown)
→ mustache.render(content, content_data, partials) ← content pass
→ cmark markdown_to_html
→ inject_sidenotes / inject_alerts / highlight_code
→ mustache.render_in_layout(template, page_data, layout, partials) ← layout pass
→ final HTML
```
The content pass can use Mustache variables, partials (`{{> ./file}}`), sections, and conditionals freely. The layout pass wraps the rendered content in the page chrome. They share nothing except the data we explicitly choose to pass.
## When this WOULD matter for thor
If thor ever adds:
- Computed page properties (table of contents, reading time, word count)
- Multiple selectable themes/layouts
- User-generated content / multi-author support
- Partial template caching
...then context isolation between the content and layout passes would become relevant. Until then, it's unnecessary complexity.