Gohyde's asset pipeline (internal/assets) walks the
configured source directories, processes each file (Liquid, Sass, fingerprint),
writes the result to the output directory, and records a manifest.json so
Liquid tags and filters resolve to the final URL.
The pipeline runs before the page render pass, so asset_path / asset_url
and the {% asset %} family always see the current digest in the same build.
assets:
digest: false # content-hash fingerprinting (true for production)
destination: assets # output subdir under the site root
sources: # directories scanned, in order
- _assets
- _assets/css
- _assets/js
- _assets/images
- assets
cdn:
url: "https://cdn.example.com" # absolute CDN base; overrides baseurl
Asset URLs include the site baseurl so sub-path deploys resolve correctly:
baseurl: "/routeway"
{{ "logo.svg" | asset_path }} → /routeway/assets/logo.svg
{% asset "app.css" @path %} → /routeway/assets/app.css
Precedence:
assets.cdn.url is set, the URL is <cdn.url>/assets/<file> (baseurl is<baseurl>/<destination>/<file>.Asset URLs carry only the baseurl, never site.url. For an absolute URL,
prepend {{ site.url }} in the template:
<meta property="og:image"
content="{{ site.url }}{% asset {{ site.mainlogo }} @path %}" />
<!-- → https://example.com/routeway/assets/logo.svg -->
digest (default true) appends a content hash to asset filenames
(style.css → style-a1b2c3d4.css) for cache-busting. It is forced off
under gohyde serve regardless of config, so dev URLs stay stable across live
reloads — no churn while you edit. gohyde build fingerprints as configured.
Treat digests as a production concern.
{{ "css/style.css" | asset_path }} → /assets/css/style-a1b2c3d4.css (when digest on)
{{ "css/style.css" | asset_url }} → same (alias)
{{ "css/style.css" | asset_digest }} → a1b2c3d4
The manifest is written to <destination>/assets/manifest.json.
Four tags emit the right HTML for the asset type:
{% asset "app.css" %}
→ <link rel="stylesheet" href="/assets/app-a1b2c3d4.css">
{% asset "app.js" defer %}
→ <script src="/assets/app-a1b2c3d4.js" defer></script>
{% asset "logo.png" alt="Logo" class="brand" %}
→ <img src="/assets/logo-a1b2c3d4.png" width="200" height="80" alt="Logo" class="brand">
{% javascript "main" async %} → <script src="/assets/js/main-….js" async></script>
{% stylesheet "main" media="print" %} → <link rel="stylesheet" href="/assets/css/main-….css" media="print">
{% image "hero.jpg" alt="Hero" %} → <img src="/assets/images/hero-….jpg" …>
Append any of @uri, @src, @href, @path, @url to get just the resolved
URL instead of a full HTML tag:
<meta property="og:image" content="{{ site.url }}{% asset site.mainlogo @path %}" />
<div style="background:url({% asset 'bg.svg' @url %})"></div>
The first argument can be quoted, a variable, or a {{ … }} expression:
{% asset "logo.svg" %} ← string literal
{% asset item.cover %} ← resolved against the loop/page variable
{% asset {{ site.mainlogo }} @path %} ← config value via {{ … }}
A missing asset logs a warning to stderr and renders an empty string. It
never injects an HTML comment like <!-- asset error --> into the output, and it
never hard-fails the build:
Warning: asset "missing.svg" not found
The processor is keyed by the file's source extension:
| Source file | Output | Processing |
|---|---|---|
main.js.liquid |
main.js |
Liquid-rendered (with site in scope), .liquid stripped |
style.scss |
style.css |
Compiled by Dart Sass |
style.scss.liquid |
style.css |
Liquid first, then Sass |
app.css |
app.css |
Copied (fingerprinted if digest: true) |
Files prefixed with _ (Sass partials) are never emitted standalone.
CMS media library: the CMS serves its image library from the first
assets.sourcesentry ending inimages(e.g._assets/images), falling
back toassets/images. Picking an image from a pipeline source inserts the
bare asset key (for{% asset %}), not an absolute path.
Stale-output gotcha: raw
*.liquid/*.scssfiles appearing in your
output dir are leftovers from an earlier dirty build, not a processing leak.
Clean the destination (rm -rf _site/_builds) and rebuild.
Gohyde compiles .scss/.sass via Dart Sass when a real sass/dart-sass
binary is on PATH. It verifies the binary with --version and rejects the
legacy Ruby sass gem (which can't accept --no-source-map). With no real
compiler available, sources pass through unchanged and a warning is printed —
the build still succeeds.
sass:
sass_dir: _sass
style: compressed
tailwind:
enabled: true
input: assets/css/input.css
output: assets/css/tailwind.css
bundles: # optional: extra css compilations
- input: _assets/css/admin.css
output: assets/admin.min.css
Each bundles entry gets its own CLI run — and its own --watch process under
gohyde serve. A failing bundle doesn't stop the others.
gohyde serve starts tailwindcss --watch only when enabled: true or the
input file exists — so sites that disable Tailwind don't get spammed with
input.css does not exist.