Subsystems
- Script loading: registry contract, shape guards, and build details of the plugin loop
Patches and workarounds
- ScrollSpy patch for Bootstrap: runtime patch to fix Bootstrap ScrollSpy handling of invalid CSS selector IDs
This is the multi-page printable view of this section. Click here to print.
Code-level notes for _partials/scripts/plugins.html, the loop
behind params.docsy.plugins. For the architecture and ordering decisions, see
the design notes.
params.docsy.plugins is a list of plugin entries:
params:
docsy:
plugins:
- name: NAME # resolves assets/js/plugins/NAME.js
enable: true # optional; false skips the entry
defer: true # optional; adds `defer` to the script tag
pageGate: FLAG # optional; a .Page.Store flag name
options: {} # optional; reaches the module as @params
- NAME # bare-name shorthand for { name: NAME }
printf "%v"): YAML auto-types entries like
name: 2048, and the loop must resolve the same asset path either way.^[A-Za-z0-9_-]+$: they address assets and partials
by path, so anything that could traverse outside the plugin namespaces is
refused with a warning (docsy-plugin-name).pageGate set, the plugin is emitted only on pages carrying the named
.Page.Store flag.The loop appends theme-default entries after the site-declared registry. A
site-declared entry of the same name supersedes its auto-registration but
inherits the default fields it leaves unset (pageGate, defer); an
explicit value, pageGate: '' included, always wins:
tabpane-persist, page-gated on hasTabs (set by the tabpane shortcode).click-to-copy, deferred:params.prism_syntax_highlighting or
params.disable_click2copy_chroma is set (the pre-plugin gates keep their
semantics for the default entry).docsy-c2c-prism).markmap:hasMarkmap (set by the markmap
code-block render hook).params.markmap.enable alias registers it ungated (exact
pre-0.18 site-wide behavior: custom render hooks and raw markmap HTML never
set the flag) and warns (docsy-markmap-legacy) for its deprecation cycle.An entry with enable: false is skipped; the string "false" counts as false
(YAML strings are truthy in Go templates).
The registry read must not break sites that already carry a params.docsy
value:
params.docsy is left untouched (the read is gated on
reflect.IsMap) and the site registry is treated as empty; theme-default
entries still emit.params.docsy.plugins, falsy scalars included, warns
(docsy-plugins-config) and is ignored.docsy-plugin-unnamed) and is skipped.warnf): plugins load once, and a
second registration is a misconfiguration.assets/js/plugins/NAME.js warns
(docsy-plugin-missing), regardless of pageGate, so a typo can’t hide
behind a gate.Except the duplicate-name warnf, warnings are issued with warnidf, so each
is suppressible through Hugo’s ignoreLogs.
js.Build; the entry’s options travel
as build params, so the module reads them with
import * as params from '@params'. Production builds add minify.integrity, crossorigin), and
defer when the entry sets it.Optional companions resolve by naming convention and emit before the script tag (why):
_partials/scripts/plugins/NAME.html: a partial for vendored libraries,
component markup, and config-provider patterns; invoked with
(dict "Page" PAGE "Plugin" ENTRY).assets/scss/plugins/NAME.scss: a stylesheet compiled through the SCSS
pipeline, minified in production, fingerprinted, and emitted with SRI.Plugin.options through safeHTML, safeJS, or safeURL in
a companion partial: options are site-config-controlled strings, and Hugo’s
contextual autoescaping is the defense; a safe* cast disables it.options (and any value reaching a module as @params) ship
world-readable in the built JS: never route secrets or tokens through
them.latest, and no loader
that pulls unpinned secondary code (SRI on a loader is worthless if the loader
fetches unpinned dependencies).pageGate so their code ships only where used.As of Docsy 0.13.0
Docsy 0.13.0 includes a runtime patch for Bootstrap ScrollSpy that fixes a bug affecting pages with heading IDs that aren’t valid CSS selectors. The patch ensures that active TOC entry tracking works reliably for all pages.
As of Bootstrap 5.3.8 (the version used by Docsy 0.13.0), ScrollSpy fails if
a page contains a heading ID that is not also a valid CSS # selector. This can
happen, for example, if a heading ID starts with a digit. For technical details
about this bug, see #2329.
Docsy 0.13.0 implements a runtime patch for ScrollSpy that intercepts ScrollSpy’s initialization to properly handle heading IDs starting with digits or containing other characters that form invalid CSS selectors. This allows active TOC entry tracking to work correctly without altering the original heading IDs, so links to headings continue to work as expected.
The patch is automatically applied when ScrollSpy is enabled (which is the default). For implementation details, see #2382, #2383.
CI/CD automatically keeps the patch up-to-date when Bootstrap is updated. The
_test:full:pre script extracts the method from Bootstrap, applies the patch,
and updates the runtime patch file. If the Bootstrap method code has changed to
a degree that the patch no longer works, CI will fail, indicating that the patch
file needs manual review and updates.
Until the upstream ScrollSpy fix is released in a future Bootstrap version, this patch ensures that active TOC entry tracking works reliably for all pages.
scrollspy-patch details