Problem
fireContentTransformedHooks (build.go:1813) passes page.FrontMatter directly into the hook payload at line 1820. When json.Marshal serializes this payload for the QuickJS/WASM bridge, any *ordered.Map values in FrontMatter trigger ordered.Map.MarshalJSON — which is 13.7% of total CPU (2.28s) due to byte-by-byte appendCompact copying.
*ordered.Map values enter FrontMatter when the cascade merges JSON data file values into the page context.
Profiling evidence (from plans/CONTENT_TRANSFORMHOOK_PERF.md)
ordered.Map.MarshalJSON 2.28s 13.7% of CPU
json.Marshal (total) 2.75s 16.6% of CPU
Inter-pass hooks total 11.9s 61.9% of build
Fix
Before building the hook payload, convert *ordered.Map values in page.FrontMatter to plain map[string]interface{} via ToGoMap(). Regular map marshaling is dramatically faster — no custom MarshalJSON, no appendCompact.
// build.go:1815 — before payload construction
fm := convertOrderedMaps(page.FrontMatter)
payload := map[string]interface{}{
"html": string(page.RenderedBody),
"toc": serializeTOC(page.TOC),
"path": page.RelPath,
"url": page.URL,
"frontMatter": fm, // plain maps, fast marshal
}
convertOrderedMaps recursively walks the map and calls ToGoMap() on any *ordered.Map value. This is a read-only conversion at the hook boundary — page.FrontMatter is untouched. Templates still see the original *ordered.Map values for ordered iteration.
Scope
- Only affects
fireContentTransformedHooks in build.go
- Only the JSON payload sent to the QuickJS/WASM VM is converted
- Template engines (Liquid, Go templates) are not involved — they receive
page.FrontMatter directly after hooks complete
page.FrontMatter is NOT mutated — only the serialization copy is converted
- Plugins don't need key ordering in front matter — they're processing HTML and metadata
Expected impact
ordered.Map.MarshalJSON drops from 13.7% to 0%. json.Marshal total drops from ~2.75s to ~0.47s. Inter-pass hooks drop from ~12s to ~9s (remaining cost is WASM memory overhead, addressed separately).
Related
Problem
fireContentTransformedHooks(build.go:1813) passespage.FrontMatterdirectly into the hook payload at line 1820. Whenjson.Marshalserializes this payload for the QuickJS/WASM bridge, any*ordered.Mapvalues inFrontMattertriggerordered.Map.MarshalJSON— which is 13.7% of total CPU (2.28s) due to byte-by-byteappendCompactcopying.*ordered.Mapvalues enterFrontMatterwhen the cascade merges JSON data file values into the page context.Profiling evidence (from plans/CONTENT_TRANSFORMHOOK_PERF.md)
Fix
Before building the hook payload, convert
*ordered.Mapvalues inpage.FrontMatterto plainmap[string]interface{}viaToGoMap(). Regular map marshaling is dramatically faster — no customMarshalJSON, noappendCompact.convertOrderedMapsrecursively walks the map and callsToGoMap()on any*ordered.Mapvalue. This is a read-only conversion at the hook boundary —page.FrontMatteris untouched. Templates still see the original*ordered.Mapvalues for ordered iteration.Scope
fireContentTransformedHooksinbuild.gopage.FrontMatterdirectly after hooks completepage.FrontMatteris NOT mutated — only the serialization copy is convertedExpected impact
ordered.Map.MarshalJSONdrops from 13.7% to 0%.json.Marshaltotal drops from ~2.75s to ~0.47s. Inter-pass hooks drop from ~12s to ~9s (remaining cost is WASM memory overhead, addressed separately).Related