feat: build the search index after page renders via templates.Defer (render-once B)#308
Merged
Merged
Conversation
The index used to be inlined into the core script bundle: flexsearch.index.js called GetSearchDocs.html, whose .Plain walk over every page executed inside the cached bundle partial while all page renders waited on its mutex. The index documents are now published as a per-language JSON asset (js/flexsearch-index.<lang>.json) from a templates.Defer block registered by the search-input.html partial and the ModalSearch shortcode. The deferred block runs after all pages have rendered, so .Plain reuses Hugo's cached content instead of forcing serial renders. The runtime bundle fetches the asset: immediately by default, or on the first search interaction when lazyLoad is set. The searchindex output format is deprecated and now emits an empty array; GetSearchIndex.html resolves the new asset URL. Requires CSP connect-src 'self' in both modes; the module already declares it in its csp block, so generated headers are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A page that demos a search UI inside its own content embeds the deferred search-index publisher in .Content, leaving a raw templates.Defer placeholder in the in-memory .Plain/.Summary read by GetSearchDocs.html — Hugo only substitutes placeholders in published page output. Strip the token from descriptions (before truncation can cut it in half) and from content, so the published index stays clean. Adds a regression fixture: a content page that embeds the search input via a shortcode, plus a check-search-index.mjs assertion that no field of any indexed document leaks a placeholder. The fixture pins an explicit frontmatter summary: without it the derived .Summary carries the raw placeholder into the RSS feed and Hugo panics with 'deferred execution with id ... not found' while substituting placeholders in RSS output — a hazard for sites whose feeds embed content demoing a search UI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
🎉 This PR is included in version 5.1.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Render-once program — chunk B (HELD: merge after hinode#2042's runbook completes)
The parked
perf/defer-index-buildbranch, rebased onto main (v5.0.3-era) and completed.The index publishes per language from a
templates.Deferblock after all page renders;the runtime always fetches (
lazyLoadnow only controls fetch timing).Why now
The original park (2026-07-15) cited an absent wall win and a gate polluted by build
nondeterminism. Both causes are resolved by the render-once flip (gethinode/hinode#2042):
(−33%) at +1.6 s user CPU — the serial index build dominated the post-flip critical path.
byte- and order-identical to stock; every candidate-vs-stock diff is the expected search-asset
class or a correction of a stock defect (4 pages' broken sprite refs).
Placeholder-token fix (second commit)
A page demoing
search="true"in content leaked an unsubstituted__hdeferred/…__d=tokeninto the published index (tokens substitute in HTML but not in
resources.FromStringassets).Fixed by stripping the token in
GetSearchDocs.html(before truncation); regression fixture +check-script assertion included, verified both ways.
Empirical Defer-token matrix: HTML → substituted;
resources.FromStringasset → raw leak (nowstripped); RSS/XML → Hugo panics (
deferred execution with id … not found). hinode's ownrss.xml is safe (frontmatter descriptions), but a downstream site embedding a derived
.Summaryof a search-demo page in a feed would crash — suggest a docs note and/or anupstream Hugo issue.
Sequencing
Gate-clean ONLY on the flip base — on current main the pre-flip UID race still pollutes the
candidate. Merge order: hinode#2042 runbook first, then this.
🤖 Generated with Claude Code