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
If a non-default-name file in the config folder parses to a map with a
single top-level key matching the file's basename, unwrap it. This lets
TOML/YAML express slice-typed roots (cascade, permalinks), which can't
have a headless top-level array, and also lets users copy-paste docs
examples that include the root container (e.g. params.yaml with a
top-level params: block).
Fixes#12899Fixes#14882
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Domains like 1password.com and 37signals.com were blocked by the default
allow rule '^https?://[a-z]'. Allow [a-z0-9] for the first hostname char
and add an explicit deny for hosts whose first label is all-digit (IP
literals like 127.0.0.1) to retain the prior SSRF protections.
Fixes#14837
Some Linux setups trigger detect-libc's spawnSync('getconf') fallback
when process.report does not expose glibcVersionRuntime, breaking
tailwindcss under the Node permission model. Add AllowChildProcess
mirroring AllowAddons/AllowWorker, default to ["tailwindcss"], and
emit --allow-child-process accordingly.
Fixes#14824
Whitelist now treats any pattern prefixed with "! " (the same negation
prefix used by hglob/predicate) as a deny rule. Deny matches take
precedence over allow, and a whitelist made up exclusively of deny
rules implicitly allows everything it does not deny.
The default security.http.urls now reads:
urls = ['(?i)^https?://[a-z]', '! (?i)localhost', '! @']
i.e. allow URLs whose host starts with a letter (the common
"https://example.com" shape), deny anything that looks like localhost,
and deny URLs with userinfo to foil "http://user@127.0.0.1/" bypasses.
Public IP literals are collateral blocks; users who need them (or their
own private hosts) override security.http.urls as before, mixing allow
and deny rules with the same "! " prefix, e.g.
[security.http]
urls = ['.*', '! ^https?://evil\.example\.com']
Fixes#14792
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
Rewrite `hugo mod npm pack` to use npm workspaces. Module deps are now
written to packages/hugoautogen/package.json and the root package.json
gets a "workspaces" reference. A hugo_packagemeta.json sidecar stores a
hash of all input package files so regular commands can warn when npm
deps are out of sync.
Other changes:
- Workspace glob patterns (*, **, {a,b}) are resolved via gobwas/glob.
- Workspaces defined in package.hugo.json are supported.
- package.hugo.json is only recognised at module roots, not in workspaces.
- When package.hugo.json exists, package.json is not mounted or vendored.
- packages/hugoautogen is not mounted or vendored from dependencies.
- Add usePackageJSON import option (auto/always/never) to control whether
a module's npm deps are included. "auto" checks for Hugo config files
or package.hugo.json.
- The staleness check is skipped when running `hugo mod npm pack` itself.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When non-taxonomy keys (e.g. disableKinds = []) are placed after
[taxonomies] in TOML, they become part of the taxonomies table.
An empty-valued entry creates a phantom taxonomy with an empty
pluralTreeKey, causing .Ancestors to loop indefinitely.
Fixes#14550
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This adds a new file cache named 'modulequeries' with a 24h maxAge
that caches JSON responses from 'go mod download -json' when querying
module versions with constraints (e.g., version = "<v3.0.0").
On subsequent builds, if the cached JSON exists and the module
directory it references is non-empty, the cached result is used
directly, skipping the costly VCS interaction entirely.
Fixes#14417
Go's stdlid now has a maps package, which is very useful. We have been using imports on the form `xmaps "maps"`, but auto import tools doesn't handle this, which is annoying.
I have been thinking about this for some time, but have been holding back because of all the import changes. However, now is a good time to do this, with very little unmerged code.
* The fix for #14357 yesterday sadly had the assumption that default language/version/role always was the first site in the sites matrix. This is common, but not always true.
* Also, that fix forgot to add a redirect the other way when `defaultContentLanguageInSubdir` was disabled, e.g. from `/en/` to `/`.
* This commit also renames config option `DisableDefaultDimensionRedirect` to `DisableDefaultSiteRedirect`. This is stricly a breaking change, but it's only been out for a day, and the old name didn't make much sense.
Fixes#14361
* The alias handling was left as is when we added new dimensions in v0.152.0, which meant that if you had `defaultContentVersionInSubDir=true` set, the alias page in the root would not be correctly created.
* Also, if you had multihost setup with multiple dimensions, the publish would be incorrect: The language key was added last instead of first.
* The home page alias handling is reworked and made more robust, which also fixes some subtle issues:
* It now supports redircects for multiple HTML output formats for the home page.
* We skip creating aliases for disabled home pages.
* With potentially multiple output formats with one of them canonical, we added a new `Canonical` method to the `Page.OutputFormats`, which allows you to do this in a template:
```handlebars
{{ with .OutputFormats.Canonical }}<link rel="{{ .Rel }}" href="{{ .Permalink }}">{{ end }}
```
Fixes#14354Fixes#14356
Fixes an issue where improper attribute derivation from the page's
relative permalink caused failures with `outdir`, `imagesoutdir`, and
`imagesdir` when `markup.asciidocext.workingFolderCurrent` is enabled.
The updated logic now correctly handles:
- Multi-byte characters
- Multilingual multi-host sites
- Site builds from a subdirectory
- Pages using ugly URLs
Supports diagram caching as implemented in v3.1.0 of the asciidoctor-diagram
extension:
- Enables caching by default
- Sets default cache location to the compiled value of caches.misc.dir
Reduces duration of integration tests by:
- Generating GoAT diagrams instead of Ditaa diagrams
- Taking advantage of asciidoctor-diagram caching
Closes#9202Closes#10183Closes#10473Closes#14160
By
* Caching common sites matrix setups (e.g. the single site in single site setups).
* Adding a fast path to IntSets.HasAnyVector for the common case of single vector input.
```
AssembleDeepSiteWithManySections/depth=3/sectionsPerLevel=2/pagesPerSection=100-10 31.62m ± 46% 30.68m ± 42% ~ (p=0.310 n=6)
│ master.bench │ perfcommon.bench │
│ B/op │ B/op vs base │
AssembleDeepSiteWithManySections/depth=3/sectionsPerLevel=2/pagesPerSection=100-10 31.98Mi ± 0% 31.24Mi ± 0% -2.30% (p=0.002 n=6)
│ master.bench │ perfcommon.bench │
│ allocs/op │ allocs/op vs base │
AssembleDeepSiteWithManySections/depth=3/sectionsPerLevel=2/pagesPerSection=100-10 460.9k ± 0% 419.9k ± 0% -8.90% (p=0.002 n=6)
````