mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
55 lines
3.4 KiB
Markdown
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.
|