Hello, I implemented a shortcode called glossary to provide modals that contain definitions for terms when user clicks on said term on a page. These definitions can contain calls to other glossary shortcodes of related terms.
Flow:
- The terms with definitions are present in frontmatter of
content/glossary.md
. - The shortcode is called from a content page with the arguments
term
anddisplayTerm
(optional outsideglossary.md
frontmatter). - ShortCode retrieves
glossary.md
, finds the description, callsmarkdownify
on it (may contain other glossary shortcode) and renders the modal.
Initially it worked well, I had even handled cyclic recursion. But now that the number of terms have increased to around 100 often with deep linking between the terms, the build time takes forever.
The issue is that there is repeated work being done for the same term. I tried to cache the result of markdownify
of term descriptions through Scratch
but looks like it does not support persistence/scope over cross shortcode calls.
Is there any way to implement the caching to improve build time? or is there a better method of implementing the feature? I feel its better to avoid all hassle by just using javascript to produce modals dynamically only when a user clicks them instead of statically rendering all modals during build.
Here is the glossary.html
shortcode (skipping styles and script):
{{- $glossary := site.GetPage "glossary.md" -}}
{{- $term := .Get "term" | lower -}} {{/* The term to be defined */}}
{{- $displayTerm := .Get "displayTerm" | default (.Get "term") -}} {{/* The term to be displayed */}}
{{- $ancestorTerms := .Get "ancestorTerms" | default "" -}} {{/* The recursive terms history */}}
{{- $definition := "" -}} {{/* The definition of the term */}}
{{- $renderedDefinition := "" -}} {{/* The rendered definition of the term */}}
{{- $cachedDefinition := "" -}} {{/* The cached definition of the term */}}
{{- warnf $ancestorTerms -}}
{{- /*
/* If the term is already in ancestorTerms, then prevent cyclic recursion by not rendering the term again.
/* If the rendered definition is already cached, then use the cached definition.
/* Else find the definition of the term in the glossary, render it and cache it.
/* If no definition is found, do not set the rendered definition.
/*
*/ -}}
{{- if not (strings.Contains $ancestorTerms (printf "{%s}" $term)) -}} {{/* Prevents cyclic recursion */}}
{{- $cachedDefinition = .Scratch.Get $term -}}
{{- if $cachedDefinition -}}
{{- $renderedDefinition = $cachedDefinition -}}
{{- else -}}
{{- range $key, $value := $glossary.Params.glossary -}}
{{- if eq (lower $key) $term -}}
{{- $definition = $value -}}
{{- end -}}
{{- end -}}
{{- if $definition -}}
{{- /*
* The glossary shortcode is of the pattern {{< glossary term="term" displayTerm="displayTerm" ancestorTerms="ancestorTerms" >}}
* ancestorTerms is used to prevent infinite recursion in the glossary shortcode where terms are delimited by {}
* Below matches the value of the ancestorTerms attribute of the glossary shortcode and appends the current term to the ancestorTerms.
*/ -}}
{{- $shortcodePattern := `\{\{[ ]*<[ ]*glossary[ ]+term="(?<d_term>[^"]+)"(?:[ ]+displayTerm="(?<d_displayTerm>[^"]+)")?(?:[ ]+ancestorTerms="(?<d_ancestorTerms>[^"]+)")?[ ]*>[ ]*\}\}` -}}
{{- $renderedDefinition = replaceRE $shortcodePattern (printf `{{< glossary term="$d_term" displayTerm="$d_displayTerm" ancestorTerms="%s{%s}" >}}` $ancestorTerms $term) $definition -}}
{{- .Scratch.Set $term ($renderedDefinition | markdownify) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /*
/* If the rendered definition is not empty, then render the glossary modal.
/* Else, display the term as is.
/*
*/ -}}
{{- if $renderedDefinition -}}
{{- /* Prevents multiple glossary terms on the same page from sharing the same modal ID */ -}}
{{- $unique_id := delimit (shuffle (seq 1 15)) "" -}}
{{- /* Render the glossary modal with cached or newly processed definition */ -}}
<span class="glossary-container" onclick="openModal('{{- $term -}}', '{{- $unique_id -}}')">
<span class="glossary-term"> {{- $displayTerm -}} </span>
<span id="modal-{{- $term -}}-{{- $unique_id -}}" class="glossary-modal">
<span class="modal-content">
<span> {{- $renderedDefinition -}} </span>
<span class="modal-close" onclick="closeModal('{{- $term }}', '{{- $unique_id }}')">×</span>
</span>
</span>
</span>
{{- else -}}
{{- $displayTerm -}}
{{- end -}}
my repo: GitHub - sandeshShahapur/ADeveloperHasNoName at glossary
use
hugo server
command without any flags as some of them like-D
will give errors. errors are related to the shortcode and i don’t know how to debug it (slice bounds out of range).
my env:
$ hugo env
hugo v0.135.0+extended windows/386 BuildDate=unknown
GOOS="windows"
GOARCH="386"
GOVERSION="go1.23.2"
github.com/sass/libsass="3.6.6"
github.com/webmproject/libwebp="v1.3.2"
any help in appreciated