A Hugo module with common utilities for your Hinode site
Hinode is a clean blog theme for Hugo, an open-source static site generator. Hinode is available as a template, and a main theme. This repository maintains a Hugo module that define common utilities compatible with your Hinode site. Visit the Hinode documentation site for installation instructions.
This module supports the following parameters (see the section params.modules in config.toml):
| Setting | Default | Description |
|---|---|---|
| utils.filter | [^0-9A-Za-zŽžÀ-ÿ ;.,\/'’"] |
Defines the regular expression for characters to remove from page descriptions. These page descriptions are used to define card content and metadata for search indexes. Adjust the filter to define which characters to support. You may need to adjust these settings to support specific diacritical letters. |
| utils.raw | false | Flag to indicate page descriptions should be returned as-is. In this setting, the filter is ignored. |
This module provides the argument and type validation system used by shortcodes, partials, and
Bookshop components across the Hinode ecosystem. As of v6 the system is split into a cached
schema compiler (ArgsSchema.html), a strict recursive validator (Args.html), and two
compatibility shims (InitArgs.html, InitTypes.html) that preserve the legacy contract for
existing call sites.
partial "utilities/Args.html" (dict
"structure" S | "bookshop" B # exactly one required
"child" C # optional
"args" .Params # map (named) or slice (positional)
"named" true|false # default true
"group" "shortcode"|"partial"|… # optional required-arg filter
"strict" true|false # default true
)
Returns a separated envelope — user values never share a namespace with bookkeeping keys:
{ args: { <camelKey> → value }, # camelCase-only canonical keys
err: bool, errmsg: []string, warnmsg: []string, defaulted: []string }
defaulted lists the dotted path (e.g. heading.align) of every value that was filled from a
default, at any nesting depth.
The legacy bookshop- prefix sugar (passing structure: "bookshop-hero" to redirect to the
hero Bookshop component) is not part of the clean API — it is preserved only inside the
InitArgs.html shim for backward compatibility. Callers using Args.html directly must pass
bookshop explicitly.
Args.html resolves its schema through ArgsSchema.html, invoked (and cached) per
(structure, bookshop, child) triple:
partial "utilities/ArgsSchema.html" (dict "structure" S "bookshop" B "child" C)
→ dict:
"schema" map: declared argument name → node
"positions" map: position as string → argument name
"err" bool
"errmsg" []string, each prefixed "schema:" for compile-time problems
node:
"name" string declared argument name
"camelKey" string canonical access key (kebab/snake → camelCase, leading _ kept)
"types" []string declared type names
"accepts" []string value-kind tokens: string|bool|int|float|map|slice|maplist
"kind" string scalar|dict|list
"children" map member name → node (single user-defined type only)
"udtType" string the user-defined type name (only when children present)
"default", "config", "options", "position", "group" ([]string),
"deprecated", "alternative", "release", "parent", "comment" — present only when defined
"optional" bool always present on every node
"reflects" []string optional; raw package-qualified Go reflect-type names (e.g.
"template.HTML") declared in the data files, matched verbatim against
printf "%T" of the value at validation time
Callers MUST invoke it through
partialCached "utilities/ArgsSchema.html" (dict …) (or $structure "") (or $bookshop "") (or $child "")
to benefit from caching; a plain partial call still works but recompiles the schema on every
call.
InitArgs.html and InitTypes.html remain supported as compatibility shims within v5. They
preserve the legacy flat-map return shape (argument values under their declared names plus
camelCase duplicates, merged with err/errmsg/warnmsg/default) and run Args.html in
non-strict mode. New code should call Args.html (and ArgsSchema.html, where the raw compiled
schema is needed) directly. There is no plan to remove the shims within v5; migrating existing
call sites across Hinode, mod-blocks, and other modules to the clean API is a separate, deferred
effort.
At every nesting level, null/absent is treated as "not provided": it is eligible for
defaulting (an isset-based config site-parameter lookup first — honoring an explicit false
or 0 site value — then the static default) and exempt from type, option, and range errors.
This is distinct from a value the caller explicitly provided as false, 0, or "", which
is validated against the declared type. This is what lets a CloudCannon-authored payload
(every blueprint key present, typically null, plus _bookshop_name/_ordinal) build clean
while a genuinely wrong explicit value is still caught.
A deprecated argument (e.g. a legacy snake_case twin of a kebab-case canonical, such as
show_preview next to show-preview) is compiled by ArgsSchema.html without a default or
config — even when the canonical argument it replaces carries one in the global
_arguments.yml definition. A deprecated alias exists only to accept an explicit legacy value
and emit a warning; defaults belong solely to the canonical replacement. This keeps an unset
deprecated twin out of the defaulted list entirely.
Because hyphens and underscores both camelize to the same key (show-preview and show_preview
both become showPreview), Args.html can visit the same output entry more than once while
walking the schema. The merge follows a fixed precedence lattice rather than relying on
iteration order: a caller-provided value on the canonical argument always wins, followed by a
caller-provided value on the deprecated twin, followed by a defaulted value. A defaulted value
never overwrites an existing entry, and a value provided through a deprecated argument never
overwrites a value already provided through its canonical replacement — regardless of which one
the schema loop happens to visit first.
The following newly detectable problem classes surface as warnings (warnmsg, err: false)
through the InitArgs.html/InitTypes.html shims, rather than errors, for the full v6 release
line:
- Falsy-value type/select mismatches at the top level (e.g.
0failing anintcheck,falsefailing aselectcheck). - Nested member type/select/range findings (anything below the top level).
- Unknown nested attributes.
- Excess positional arguments.
Calling Args.html directly with strict: true (the default) already treats every one of these
as an error today. The promotion of these warnings to errors in InitArgs.html/InitTypes.html
is planned as the next major release (v7). Watch your own build's warnmsg output (or switch
early to Args.html) to find call sites that need fixing before that promotion release.
v6 is a major release: the API of InitArgs.html/InitTypes.html is drop-in compatible, but
behavior changes in ways that can affect rendered output and build logs.
- Import path. Update
go.modand the[module.imports]path in your Hugo configuration fromgithub.com/gethinode/mod-utils/v5togithub.com/gethinode/mod-utils/v6, then re-vendor (npm run mod:vendororhugo mod vendor). - CloudCannon expose configuration. If your site uses
setup-cloudcannon-cms, update any expose globs that hardcode the vendored path (e.g._vendor/github.com/gethinode/mod-utils/v5/...→.../v6/...) and regeneratebookshop.config.cjs; otherwise the live editor silently loses access to the validation partials and structure data. - Behavior changes to expect.
- Nested defaults now apply correctly (e.g. an omitted
headingreceives its members' real defaults such asalign: start,width: 8) — previously they resolved to null. Rendered output can change without any content edit. - Defaults with falsy values (
false,0) are now applied; previously they were silently ignored. - Explicit
nullmembers inside nested arguments are dropped from the returned map instead of passed through. - All validation problems are reported per call instead of only the first one.
- Site parameters explicitly set to
false/0now take precedence over static defaults forconfig-driven arguments. - New warnings (see the rollout section above) may appear in your build logs; they are non-blocking in v6 and indicate call sites to clean up before v7.
- Nested defaults now apply correctly (e.g. an omitted
This module uses semantic-release to automate the release of new versions. The package uses husky and commitlint to ensure commit messages adhere to the Conventional Commits specification. You can run npx git-cz from the terminal to help prepare the commit message.
