Artifacts are the additional output files published as part of the build:
source maps and files emitted by ESBuild's file loader (e.g. fonts). Each
artifact provides Permalink, RelPermalink and MediaType, so e.g. font
preload links can be constructed in templates.
Also add media type definitions for source maps (application/source-map),
font/woff and font/woff2.
Fixes#15173
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This allows @import statements to be resolved in a set of user provided resources (e.g. from resources.FromString or css.ChromaStyles) before the assets filesystem.
The option also applies to css.PostCSS via the shared import inlining, and css.Sass requires the dartsass transpiler. The import context is part of the transformation cache key.
Fixes#15103
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
And then rethrow the ERR_ACCESS_DENIED if we cannot recover.
There's more details in #15041, but this error has been seen on Netlify with the CJS because of how Netlify has their node_modules cache folder set up.
Fixes#15041
IsNode's meaning was murky. Add IsBranch, defined as the set of branch
node kinds (home, section, taxonomy, term), and make IsNode a deprecated
alias for it.
Fixes#11574
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hint setting controls WebP encoding (preset) and AVIF encoding
(chroma subsampling), but only lived on imaging.webp. Add imaging.avif.hint
so it shows up under the AVIF section in the docs, with the same root-level
backwards compatibility as imaging.webp.hint. Per-image hint now resolves
from the target format.
Fixes#14992
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Encode photo/picture hints (and the default) as YUV420 instead of YUV444,
keeping 444 for text/icon/drawing. Lossless stays 444.
This roughly halves the encoder's peak memory (42 -> 27 MiB/MP) and the
output size, while 444 remains available for sharp-edged content. A
3000x3000 image now needs ~239 MiB to encode, down from ~381 MiB (which
sat right at the 384 MiB WASM cap).
Closes#14987
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AVIF's quality scale is not perceptually comparable to JPEG/WebP:
libavif anchors its "good" default at 60, where Hugo's universal 75
produced visibly higher quality and larger files. AVIF now defaults to
60 while JPEG and WebP keep 75. An explicit imaging.quality,
imaging.avif.quality or a per-image qNN still wins.
Fixes#14979
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Allow setting quality per output format via imaging.jpeg.quality,
imaging.webp.quality and imaging.avif.quality. Each falls back to the
global imaging.quality when unset, and a per-image qNN still wins.
Fixes#14957
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The encode/decode is implemented in a WebAssembly module built from a
small C wrapper around libavif. Bundled libraries (statically linked,
compiled with the WASI SDK):
* libavif v1.4.1 (container + codec glue)
* libaom v3.14.1 (AV1 encoder + decoder)
* dav1d 1.5.3 (AV1 decoder)
* libyuv (Chromium pin) for color conversion
* parson for JSON message passing across the wasm boundary
HDR handling on the encoder:
* SDR images are written as BT.709 / sRGB / BT.601 (8-bit).
* 10-bit and up are written as BT.2020 primaries with PQ (SMPTE
ST 2084) transfer and BT.2020-NCL matrix coefficients, signalled
via CICP.
* Adobe-style SDR+gainmap inputs (e.g. Lightroom HDR exports) are
baked into a single true-HDR image in BT.2020/PQ at 10-bit, with
the CLLI (Content Light Level Information) box carried through so
HDR-capable clients can tone-map correctly.
Limitations:
* Animated input (animated WebP/GIF) is collapsed to its first frame
when re-encoded as AVIF; animated AVIF output is not yet supported.
Fixes#7837
Node's ESM resolver does not consult NODE_PATH (unlike CJS require), so
an ESM postcss.config.js shipped by a Hugo theme fails when loaded from
the module cache: bare imports like `import x from "postcss-import"`
have no node_modules to walk up to.
Install a synchronous resolver hook (module.registerHooks) via
--import=data:... on every Node invocation. On ERR_MODULE_NOT_FOUND for
a bare specifier it resolves the package from each NODE_PATH entry via
createRequire().resolve(). No-op for relative, absolute, URL-scheme and
non-MODULE_NOT_FOUND failures. Synchronous hooks run on the main thread,
so no --allow-worker is needed under the Node permission model.
Fixes#13987
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smartcrop can return a crop rectangle that is smaller than the requested dimensions after prescaling and rounding. Expand that rectangle within the source image bounds before applying Hugo's crop/fill pipeline, so smart crops keep the requested size without stretching the image.
Bump the smart crop cache version for crop/fill only.
Fixes#13688
Co-Authored-By: Joe Mooring <joe.mooring@veriphor.com>
A server allowed by security.http.urls could redirect resources.GetRemote
to a host that is not. Re-run the check on each hop via CheckRedirect.
Fixes#14871
When CSS imports assets via the file loader (fonts, images), the emitted
URLs were relative to the CSS output directory. That broke when the CSS
was inlined into HTML, since browsers then resolved the URLs against the
page rather than the CSS file.
Set esbuild's PublicPath to the CSS output directory joined with the
site base path so URLs work whether the CSS is published as a file or
inlined.
Fixes#14849
When the server returns a temporary HTTP error (e.g. 429 or 503)
together with a Retry-After header, use that value as the next sleep
duration instead of the default exponential backoff. The Retry-After
value is also surfaced in the retry-timeout error message.
Fixes#14828
Allow CSS variables to be grouped under sub-paths and imported via
@import "hugo:vars/mobile" (or @use for Dart Sass), so callers can pass
nested dicts like:
{{ dict "primary-color" "blue" "mobile" (dict "primary-color" "red") }}
Top-level "hugo:vars" now skips nested map entries instead of emitting
garbage for them.
Fixes#14705
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add security.node.permissions config to run Node tools (PostCSS, Babel,
TailwindCSS) under Node's permission model, restricting file system access
to the working directory by default.
The binary resolution is simplified to node_modules/.bin → PATH (npx removed).
For both locations, the actual JS entry point is resolved via symlinks (macOS/Linux)
or by parsing npm wrapper scripts (Windows .cmd), then executed as
"node --permission --allow-fs-read=<path> --allow-fs-write=<path> <script>".
Users can opt out by removing "node" from security.exec.allow.
Closes#7287
This is some logic that's left behind from when we had only one dimension (language) where the common case would be to have one resource set (e.g. an image) and many content translation.
After this commit:
* For sites matrix defined in the content filename (e.g. data.en.js) or in its mount definition, we may use that as a fallback for e.g. German languages if we don't find a better match.
* For content adapters, this is not relevant: Here you must be explicit about this.
* We never auto-fallback on resources from a role/version to another.
* When a page bundle spans multiple roles (e.g. via roles = "*"), we clone its resources to all roles so each gets role-specific paths.
Fixes#14749Fixes#14752
When a resource transformation result was served from cache (same
options as a previous build), the output file was not re-written to
disk. This caused incorrect output when toggling transformation
options (e.g. minify) back to a previously seen value in server mode.
Fixes#14629
The per-request timeout context was cancelled via defer in the getRes
closure, before io.ReadAll(res.Body) in the outer scope could read
the response body. Fix this by returning the cancel function from
getRes so each caller manages its own cancel lifecycle.
Fixes#14611
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>