mirror of
https://github.com/sbrow/thor.git
synced 2026-10-25 10:11:55 -04:00
fix: Auto mode is no longer disabled by explictly adding a menu.
A can of worms has been opened.
This commit is contained in:
@@ -20,6 +20,9 @@
|
|||||||
- [ ] instead of `warnings: [dynamic]Error` we should use `warnings: [dynamic; 8]Error`
|
- [ ] instead of `warnings: [dynamic]Error` we should use `warnings: [dynamic; 8]Error`
|
||||||
- when limit reached, the template fails and stops rendering.
|
- when limit reached, the template fails and stops rendering.
|
||||||
- [ ] Brainstorm plugin/tool/(postccs/less/tailwind/sass) support.
|
- [ ] Brainstorm plugin/tool/(postccs/less/tailwind/sass) support.
|
||||||
|
- [ ] Reconsider `"menus": {}` opt-out: it currently discards all page frontmatter
|
||||||
|
menus (with only a warning). Decide whether empty config should instead just
|
||||||
|
suppress the auto-generated menu while still honoring frontmatter.
|
||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
|
|||||||
+24
-26
@@ -4,7 +4,6 @@ import "core:encoding/json"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
DEFAULT_WEIGHT :: 10
|
DEFAULT_WEIGHT :: 10
|
||||||
@@ -112,42 +111,32 @@ parse_page_menus :: proc(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// build_menus populates site.menus:
|
// build_menus populates site.menus by merging every source into one menu set:
|
||||||
// 1. Config menus (thor.json "menus" key present) — exclusive, preserves array order
|
// 1. Auto-menus (sections + root-level pages)
|
||||||
// 2. Auto-menus (sections + root-level pages) + page frontmatter menus — merged, sorted
|
// 2. Config menus (thor.json "menus" key), already parsed into site.menus
|
||||||
|
// 3. Page frontmatter menus
|
||||||
|
// All three are combined per menu name and sorted by weight, then name.
|
||||||
//
|
//
|
||||||
// If "menus" is present but empty ({}) it means explicit opt-out: no menus.
|
// If "menus" is present but empty ({}) it means explicit opt-out: no menus at
|
||||||
// Config menus cannot be mixed with page frontmatter menus (error).
|
// all, and page frontmatter menus are ignored.
|
||||||
build_menus :: proc(site: ^Site) {
|
build_menus :: proc(site: ^Site) {
|
||||||
if site.menus != nil {
|
// Explicit opt-out: "menus": {} present but empty → no menus; page
|
||||||
has_menus := false
|
// frontmatter menus are ignored (with a warning).
|
||||||
|
// TODO(TODOS.md): silently discarding frontmatter here is harsh; revisit.
|
||||||
|
if site.menus != nil && len(site.menus) == 0 {
|
||||||
for page in site.pages {
|
for page in site.pages {
|
||||||
if len(page.menus) > 0 {
|
if len(page.menus) > 0 {
|
||||||
has_menus = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already populated from config in site_apply_config
|
|
||||||
if len(site.menus) == 0 {
|
|
||||||
// Explicit opt-out ("menus": {})
|
|
||||||
if has_menus {
|
|
||||||
log.warnf(
|
log.warnf(
|
||||||
"menus: config has empty menus but pages have frontmatter menu entries; ignoring page menus",
|
"menus: config has empty menus but pages have frontmatter menu entries; ignoring page menus",
|
||||||
)
|
)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// Config menus active
|
|
||||||
if has_menus {
|
|
||||||
log.fatalf("menus: cannot mix config menus with frontmatter menus")
|
|
||||||
os.exit(1)
|
|
||||||
}
|
|
||||||
warn_all_duplicate_weights(site)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// No config menus — auto-generate, then merge page menus on top
|
// Merge all sources: auto section/root menus, config menus (already in
|
||||||
|
// site.menus if present), then page frontmatter menus.
|
||||||
collect_auto_menus(site)
|
collect_auto_menus(site)
|
||||||
merge_page_menus(site)
|
merge_page_menus(site)
|
||||||
warn_all_duplicate_weights(site)
|
warn_all_duplicate_weights(site)
|
||||||
@@ -253,8 +242,17 @@ collect_auto_menus :: proc(site: ^Site) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_menu_entries(entries[:])
|
if site.menus == nil {
|
||||||
site.menus = make(map[string][]Menu_Entry, alloc)
|
site.menus = make(map[string][]Menu_Entry, alloc)
|
||||||
|
}
|
||||||
|
// Merge into any existing "main" (e.g. config menus) rather than clobbering.
|
||||||
|
if existing, ok := site.menus["main"]; ok {
|
||||||
|
merged := make([dynamic]Menu_Entry, 0, len(existing) + len(entries), alloc)
|
||||||
|
append(&merged, ..existing)
|
||||||
|
append(&merged, ..entries[:])
|
||||||
|
entries = merged
|
||||||
|
}
|
||||||
|
sort_menu_entries(entries[:])
|
||||||
site.menus["main"] = entries[:]
|
site.menus["main"] = entries[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -421,6 +421,71 @@ test_auto_menus_no_duplicate_with_frontmatter :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, main[0].name, "Ideas")
|
testing.expect_value(t, main[0].name, "Ideas")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- config + frontmatter + auto merge tests ---
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_build_menus_config_merges_frontmatter :: proc(t: ^testing.T) {
|
||||||
|
site: Site
|
||||||
|
mem.dynamic_arena_init(&site.arena)
|
||||||
|
defer mem.dynamic_arena_destroy(&site.arena)
|
||||||
|
context.allocator = site_allocator(&site)
|
||||||
|
|
||||||
|
// Simulate config menus as site_apply_config would populate them.
|
||||||
|
site.menus = make(map[string][]Menu_Entry, site_allocator(&site))
|
||||||
|
cfg := make([dynamic]Menu_Entry, 0, 1, site_allocator(&site))
|
||||||
|
append(&cfg, Menu_Entry{name = "GitHub", url = "https://example.com", weight = 20})
|
||||||
|
site.menus["main"] = cfg[:]
|
||||||
|
|
||||||
|
// A root-level page opting into "main" via frontmatter.
|
||||||
|
page := make_page("Ideas", "/ideas/")
|
||||||
|
page.menus = parse_page_menus(parse_raw(`"main"`), page, site_allocator(&site))
|
||||||
|
|
||||||
|
site.pages = make(#soa[dynamic]Page, 0, 1, site_allocator(&site))
|
||||||
|
append(&site.pages, page)
|
||||||
|
|
||||||
|
build_menus(&site)
|
||||||
|
|
||||||
|
main, ok := site.menus["main"]
|
||||||
|
testing.expect(t, ok)
|
||||||
|
// GitHub (config, weight 20) + Ideas (frontmatter, nil → DEFAULT_WEIGHT 10).
|
||||||
|
testing.expect(t, len(main) == 2, "expected config + frontmatter merged")
|
||||||
|
testing.expect_value(t, main[0].name, "Ideas")
|
||||||
|
testing.expect_value(t, main[1].name, "GitHub")
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_build_menus_config_merges_auto :: proc(t: ^testing.T) {
|
||||||
|
site: Site
|
||||||
|
mem.dynamic_arena_init(&site.arena)
|
||||||
|
defer mem.dynamic_arena_destroy(&site.arena)
|
||||||
|
context.allocator = site_allocator(&site)
|
||||||
|
|
||||||
|
// Config menu in a different bucket ("footer"); auto-gen owns "main".
|
||||||
|
site.menus = make(map[string][]Menu_Entry, site_allocator(&site))
|
||||||
|
cfg := make([dynamic]Menu_Entry, 0, 1, site_allocator(&site))
|
||||||
|
append(&cfg, Menu_Entry{name = "GitHub", url = "https://example.com", weight = 20})
|
||||||
|
site.menus["footer"] = cfg[:]
|
||||||
|
|
||||||
|
// A section (non-index) page triggers an auto "main" section entry.
|
||||||
|
page := make_page("Post", "/blog/post/")
|
||||||
|
page.section = "blog"
|
||||||
|
|
||||||
|
site.pages = make(#soa[dynamic]Page, 0, 1, site_allocator(&site))
|
||||||
|
append(&site.pages, page)
|
||||||
|
|
||||||
|
build_menus(&site)
|
||||||
|
|
||||||
|
footer, ok_f := site.menus["footer"]
|
||||||
|
testing.expect(t, ok_f, "config footer menu should be preserved")
|
||||||
|
testing.expect(t, len(footer) == 1)
|
||||||
|
testing.expect_value(t, footer[0].name, "GitHub")
|
||||||
|
|
||||||
|
main, ok_m := site.menus["main"]
|
||||||
|
testing.expect(t, ok_m, "auto-generated main menu should exist alongside config")
|
||||||
|
testing.expect(t, len(main) == 1)
|
||||||
|
testing.expect_value(t, main[0].name, "Blog")
|
||||||
|
}
|
||||||
|
|
||||||
// --- warn_duplicate_weights tests ---
|
// --- warn_duplicate_weights tests ---
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
|
|||||||
+13
-1
@@ -6,9 +6,21 @@
|
|||||||
"markdown_extensions": {
|
"markdown_extensions": {
|
||||||
"footnotes": true
|
"footnotes": true
|
||||||
},
|
},
|
||||||
|
"menus": {
|
||||||
|
"main": [
|
||||||
|
{
|
||||||
|
"name": "GitHub",
|
||||||
|
"url": "https://github.com/sbrow/thor",
|
||||||
|
"weight": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Spencer Brower"
|
"name": "Spencer Brower"
|
||||||
}
|
},
|
||||||
|
"stylesheets": [
|
||||||
|
"https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.blue.min.css"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user