feat: Demoted author to a param.

This commit is contained in:
Spencer Brower
2026-07-21 10:22:30 -04:00
parent bed7effd4b
commit cc42e694fd
6 changed files with 17 additions and 17 deletions
+2 -3
View File
@@ -5,7 +5,7 @@ Thor is a static site generator written in [Odin](https://odin-lang.org), replac
## Architecture ## Architecture
``` ```
thor.json ← site config (title, base_url, author, params, modules) thor.json ← site config (title, base_url, params, modules)
content/ ← markdown and HTML content files content/ ← markdown and HTML content files
layouts/ ← Mustache templates + partials (user overrides) layouts/ ← Mustache templates + partials (user overrides)
assets/ ← CSS (Tufte-based), JS, fonts, images assets/ ← CSS (Tufte-based), JS, fonts, images
@@ -129,7 +129,6 @@ Config precedence: `CLI flags > thor.json values > hardcoded defaults`.
{ {
"title": "...", "title": "...",
"base_url": "...", "base_url": "...",
"author": "...",
"modules": ["../path/to/module"], "modules": ["../path/to/module"],
"markdown_extensions": { "markdown_extensions": {
"emoji": true, "emoji": true,
@@ -139,6 +138,7 @@ Config precedence: `CLI flags > thor.json values > hardcoded defaults`.
"sections": true "sections": true
}, },
"params": { "params": {
"author": "...",
"social": [ "social": [
{ "name": "github", "url": "...", "icon": "icons/github" } { "name": "github", "url": "...", "icon": "icons/github" }
] ]
@@ -201,7 +201,6 @@ Data is passed as **typed structs** (not `map[string]any`). Mustache resolves st
```odin ```odin
Base_Data :: struct { Base_Data :: struct {
now: datetime.DateTime, now: datetime.DateTime,
author: string,
params: json.Value, params: json.Value,
body: string, body: string,
title: string, title: string,
-1
View File
@@ -45,7 +45,6 @@
- [x] Block-override source-template tracking: warnings inside overrides point at the override's source file, not the parent template - [x] Block-override source-template tracking: warnings inside overrides point at the override's source file, not the parent template
- [ ] Partial invocation stack in diagnostics: when an error fires inside a partial, show "invoked from" chain through `{{> name}}` calls. Currently warnings inside partials point at the partial (correct file) but don't show the invocation site. - [ ] Partial invocation stack in diagnostics: when an error fires inside a partial, show "invoked from" chain through `{{> name}}` calls. Currently warnings inside partials point at the partial (correct file) but don't show the invocation site.
- [ ] Could be better error message when missing a closing (or opening) brace - [ ] Could be better error message when missing a closing (or opening) brace
- [ ] Author should be a struct adhering to https://schema.org/author
- [ ] Block attributes on code fences (`{ #ex-1 }`) — hello-world.md - [ ] Block attributes on code fences (`{ #ex-1 }`) — hello-world.md
- [ ] include-code shortcode (`{{< include-code ... >}}`) — i-ported-fd-to-odin - [ ] include-code shortcode (`{{< include-code ... >}}`) — i-ported-fd-to-odin
- [ ] follow symlinks in `scan_content`? - [ ] follow symlinks in `scan_content`?
+1 -1
View File
@@ -1,3 +1,3 @@
<footer> <footer>
<p>&copy; {{now.year}} {{author}}</p> <p>&copy; {{now.year}} {{params.author.name}}</p>
</footer> </footer>
-2
View File
@@ -21,7 +21,6 @@ Page_Context :: struct {
Base_Data :: struct { Base_Data :: struct {
now: datetime.DateTime, now: datetime.DateTime,
author: string,
params: json.Value, params: json.Value,
body: string, body: string,
title: string, title: string,
@@ -149,7 +148,6 @@ render_site :: proc(site: ^Site) {
// Build base data once // Build base data once
base := Base_Data { base := Base_Data {
now = now, now = now,
author = site.author,
params = site.params, params = site.params,
description = site.description, description = site.description,
og = site.og, og = site.og,
-3
View File
@@ -19,7 +19,6 @@ Site :: struct {
vfs: VFS, vfs: VFS,
title: string, title: string,
description: string, description: string,
author: string,
base_url: string, base_url: string,
config_path: string, config_path: string,
content_dir: string, content_dir: string,
@@ -44,7 +43,6 @@ Config_File :: struct {
title: string, title: string,
description: string, description: string,
base_url: string, base_url: string,
author: string,
content_dir: string, content_dir: string,
assets_dir: string, assets_dir: string,
output_dir: string, output_dir: string,
@@ -136,7 +134,6 @@ load_config_file :: proc(
site_apply_config :: proc(site: ^Site, config: Config_File, config_dir: string) { site_apply_config :: proc(site: ^Site, config: Config_File, config_dir: string) {
if config.title != "" do site.title = config.title if config.title != "" do site.title = config.title
if config.description != "" do site.description = config.description if config.description != "" do site.description = config.description
if config.author != "" do site.author = config.author
if config.base_url != "" do site.base_url = config.base_url if config.base_url != "" do site.base_url = config.base_url
site.content_dir = site.content_dir =
+13 -6
View File
@@ -25,8 +25,8 @@ test_load_config_file :: proc(t: ^testing.T) {
"title":"Test Site", "title":"Test Site",
"description":"Test desc", "description":"Test desc",
"base_url":"https://example.com", "base_url":"https://example.com",
"author":"Tester",
"params":{ "params":{
"author":"Tester",
"social":[ "social":[
{"name":"github","url":"https://github.com/test"}, {"name":"github","url":"https://github.com/test"},
{"name":"rss","url":"/index.xml"}] {"name":"rss","url":"/index.xml"}]
@@ -42,11 +42,15 @@ test_load_config_file :: proc(t: ^testing.T) {
testing.expect_value(t, cfg.title, "Test Site") testing.expect_value(t, cfg.title, "Test Site")
testing.expect_value(t, cfg.description, "Test desc") testing.expect_value(t, cfg.description, "Test desc")
testing.expect_value(t, cfg.base_url, "https://example.com") testing.expect_value(t, cfg.base_url, "https://example.com")
testing.expect_value(t, cfg.author, "Tester")
params, has_params := cfg.params.(json.Object) params, has_params := cfg.params.(json.Object)
testing.expect(t, has_params) testing.expect(t, has_params)
author_val := params["author"]
author, has_author := author_val.(json.String)
testing.expect(t, has_author)
testing.expect_value(t, author, "Tester")
social_val := params["social"] social_val := params["social"]
social, has_social := social_val.(json.Array) social, has_social := social_val.(json.Array)
testing.expect(t, has_social) testing.expect(t, has_social)
@@ -94,7 +98,6 @@ test_load_config_file_partial :: proc(t: ^testing.T) {
testing.expect(t, ok) testing.expect(t, ok)
testing.expect_value(t, cfg.title, "Partial") testing.expect_value(t, cfg.title, "Partial")
testing.expect_value(t, cfg.description, "") testing.expect_value(t, cfg.description, "")
testing.expect_value(t, cfg.author, "")
testing.expect(t, cfg.params == nil) testing.expect(t, cfg.params == nil)
} }
@@ -143,7 +146,7 @@ test_init_site_flag_overrides_default :: proc(t: ^testing.T) {
test_init_site_full_pipeline :: proc(t: ^testing.T) { test_init_site_full_pipeline :: proc(t: ^testing.T) {
path := write_temp_config( path := write_temp_config(
"pipeline", "pipeline",
`{"title":"Pipeline Test","description":"Full","base_url":"https://config.com","author":"Author"}`, `{"title":"Pipeline Test","description":"Full","base_url":"https://config.com"}`,
) )
defer os.remove(path) defer os.remove(path)
@@ -154,7 +157,6 @@ test_init_site_full_pipeline :: proc(t: ^testing.T) {
testing.expect_value(t, site.title, "Pipeline Test") testing.expect_value(t, site.title, "Pipeline Test")
testing.expect_value(t, site.description, "Full") testing.expect_value(t, site.description, "Full")
testing.expect_value(t, site.author, "Author")
testing.expect(t, .Drafts in site.features) testing.expect(t, .Drafts in site.features)
testing.expect_value(t, site.base_url, "https://config.com") testing.expect_value(t, site.base_url, "https://config.com")
} }
@@ -162,7 +164,12 @@ test_init_site_full_pipeline :: proc(t: ^testing.T) {
@(test) @(test)
test_init_site_md_enable_disable :: proc(t: ^testing.T) { test_init_site_md_enable_disable :: proc(t: ^testing.T) {
site: Site site: Site
args := []string{"thor", "-config:./nonexistent.json", "-ext:highlight,sections", "-no-ext:emoji"} args := []string {
"thor",
"-config:./nonexistent.json",
"-ext:highlight,sections",
"-no-ext:emoji",
}
init_site(&site, args) init_site(&site, args)
defer destroy_site(&site) defer destroy_site(&site)