Replace the partial return template rewriting with a sentinel error
trapped in the template executor:
* return now works in any template, not just partials.
* return can be used anywhere in the template, e.g. inside if/range;
it stops execution of the current template, so a bare return in a
block or template include ends just that template.
* {{ return <value> }} sets the return value of the enclosing partial;
using it outside a partial is now an error (it was silently ignored).
The fork changes are limited to hugo_template.go plus one mechanical
rename (walkTemplate -> walkTemplateOld) mirrored in the fork script.
Closes#15212
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The contentNodes cases in Delete/DeleteFunc spliced the slice inside a
forward range loop, panicking when a second deletion hit the last index,
and the shrunken slice was never written back to the tree. Let the
Shifter return the updated node and re-insert it on partial deletes.
Fixes#15207
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This is a follow up the bug fix in #15189. With the example given in that issue, the `css.Build` would be performed twice, which was unfortunate.
This commit fixes that by resuming the transformation from the last executed transformation.
See #15189
When a transformed resource had been initialized (e.g. via .Data, .Content
or .RelPermalink) before chaining another transformation, the new chain
would run on the transformed output instead of the original source,
re-running all transformations on their own output.
Fixes#15189
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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>
Chroma drops token rules whose color equals the style's default foreground
(e.g. .nx in github-dark). In a paired light/dark setup the other sheet's
explicit rule then leaks in, since an explicit declaration beats inheritance
from .chroma. Apply the chromaCSSOverrides custom CSS unconditionally so
these rules survive in modeSelector sheets too.
Updates #15161
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>
Move the stylesheet generation from the gen chromastyles command into
markup/highlight and share it with the new template function.
Fixes#15112
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An atomic save (write temp file, rename into place) unlinks the inode the
watcher holds, so kqueue reports Remove for a file that's still on disk.
That took the delete branch and wiped the entire taxonomy subtree; the
following assemble then panicked in createMissingTaxonomies, where the
shifting tree.Get hit a not yet assembled *pageMetaSource.
Treat Remove of a path that still exists as an update, and use the
non-shifting GetRaw when checking for the auto created taxonomy node.
Fixes#15130
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On case-insensitive filesystems, entries created before we started lowercasing
the content paths in v0.123 are the same file as today's lowercased cache key,
and were removed on every hugo --gc.
Fixes#15101
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Lstat only refrains from following the last element of a path, so a symlink
in an intermediate directory was still resolved, and reads could escape the
mount via e.g. resources.Get "symlinkdir/secret.txt".
Walk the directories up to the mount root and reject any that is a symlink.
Follow-up to cf9c8f93c and f8b5fa09a.
E.g. {{ .Render "foo/mylayout" }} matches mylayout templates in
<dir>/foo for every dir from the page's layout path up to the layouts root.
Closes#15056
Co-authored-by: Joe Mooring <joe.mooring@veriphor.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The warning logged when calling IsSet with an unsupported type did not
identify which key triggered it, making it hard to locate the offending
template. Include the key in the message.
Fixes#11794
Alias generation hardcoded "index.html" and only recognized a ".html"
suffix, so an alias with an explicit ".htm" extension (or any other
suffix configured for the html media type) was written to the wrong
path. For example "d.htm" produced the directory "d.htm/index.html"
instead of the file "d.htm".
Build the alias file name from the output format's BaseName and the
media type's first suffix, and detect an explicit file by matching the
alias extension against the configured suffixes. The same check now
drives the uglyURLs case in Aliases.
Fixes#15066
The old implementation copied the incoming bytes into a fixed-size buffer
one byte at a time, and for every byte ran bytes.Contains for every
pattern over the whole buffer. That's O(n·patterns) scans of the buffer,
which showed up badly on large output (a full rendered page).
Scan each not-yet-matched pattern once per Write with bytes.Contains over
the chunk itself, in place, rather than per byte. The only carried state
is a small boundary window (the last maxPatternLen-1 bytes) joined with
the head of the next chunk, so a pattern straddling a Write boundary is
still detected. The chunk is never copied, so the extra allocation is
bounded by the longest pattern and independent of the output size. Once
all patterns have matched we mark done and drop the buffer.
patternLen summed the pattern lengths (to size the old buffer); the
boundary window only needs the longest pattern, so it's renamed
maxPatternLen and returns the max.
```bash
│ benchcmp.bench │ fix-hasbytewriter.bench │
│ sec/op │ sec/op vs base │
HasBytesWriter-10 2473.095µ ± ∞ ¹ 6.114µ ± ∞ ¹ -99.75% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ benchcmp.bench │ fix-hasbytewriter.bench │
│ B/op │ B/op vs base │
HasBytesWriter-10 48.00 ± ∞ ¹ 128.00 ± ∞ ¹ +166.67% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ benchcmp.bench │ fix-hasbytewriter.bench │
│ allocs/op │ allocs/op vs base │
```
Add a generic crypto.Hash template function returning the hex-encoded
checksum of a string using one of md5, sha1, sha256 (default), sha384 or
sha512. The supported algorithms match those used for the SRI hash in
.Data.Integrity on fingerprinted resources, so an SRI hash can be built
by composing with encoding.HexDecode and encoding.Base64Encode.
Fixes#15072
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make it insted consume just ASCII whitespace, which preserves e.g. ideographic space (U+3000) after the summary divider, which is important for e.g. Chinese and Japanese content, and possibly other Unicode whitespace characters with meaning.
Doing this is possibly breaking, but not likely, and obviously the correct thing to do.
Fixes#15046
When a regular page (e.g. content/s1.md) and a section (content/s1/)
share the same tree key, the assembler must not overwrite the real page
with a synthetic section. Restore the existence guard that was present
in v0.152.2.
The fix checks if a node already exists at the section key before
inserting a synthetic section during root section creation.
Co-authored-by: Claude Sonnet 4.6 <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
Canonicalize integer/hex/octal IPv4 hosts to dotted-decimal before
applying the security.http.urls policy so all encodings of an address
are treated alike.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Before this commit, lossy compression with 100 quality meant lossless compression, leading to a big gap between 99 and 100.
This commit caps the lossy quality at 99, ensuring that lossy compression is always lossy.
Fixes#14981
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
With renderSegments only a subset of pages is rendered, so the resulting
hugo_stats.json would no longer contain elements from the excluded pages,
causing tools like Tailwind to strip classes that are actually in use.
Fixes#14939
Treat type and code as highlighting options in both transform.Highlight
and transform.HighlightCodeBlock. The type option overrides the language
and code overrides the code, so the two functions now share the same
options handling.
transform.Highlight's LANG argument is now optional:
transform.Highlight CODE [LANG] [OPTIONS]
Fixes#11872
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a module provides a config key whose value is a slice (e.g.
cascade or permalinks), and the site config declares the same key as a
map with only a merge strategy marker (_merge = 'deep'), the types do
not match and Params.merge silently dropped the module's value, leaving
the site with no effective cascade or permalink config from the module.
Fix Params.merge so that when the destination value is an empty Params
(IsZero — only the _merge key is present) and the source value is a
non-Params type, the source value is used provided the user-declared
merge strategy is not 'none'. This honours the explicit _merge
directive regardless of the surrounding shallow-merge context.
Closes#13869
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
A partial's rendered output (placeholder included) is cached by
partialCached across rebuilds, but BuildState.DeferredExecutions
is reset every stage. On a fast-render rebuild the cached string
replays the placeholder while doDefer is not called this build,
leaving executeDeferredTemplates to panic with "deferred execution
with id ... not found".
Mark the ctx inside IncludeCached's body execution and have Defer
return a clear error if it sees the flag. Catches transitive cases
(partialCached -> partial -> Defer) via ctx propagation. Defer in
baseof.html and in a plain partial is unaffected.
Fixes#13492
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
A stale or removed resource used by js.Batch could panic while the esbuild import loader read its content during a rebuild. Return the read error through the loader so esbuild reports a normal build error and a later rebuild can recover when the file returns.
Closes#13737
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>
* Note for themes, this is only an issue for themes stored locally, e.g. below `themes/...`. Themes mounted as modules from GitHub gets symlinks stripped away.
* Thas was also not an issue for file reading walking one or more directories.
* This is an regression introduced in `v0.123.0`.
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
These keys are reserved at the top level of front matter, but are
legitimate user params under cascade.params. Only fire the deprecation
when the key was actually set at the top level of the original front
matter.
Fixes#14848
Fall back to global resources via resources.Get when page resources
don't match for named images in the images front matter parameter.
This aligns get-page-images.html with the existing behavior in
render-image.html, render-link.html, and figure.html.
Fixes#14062
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
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
And be specific about which commit we use.
Hugo treat this as an upstream dependency, so we would appreciate that any bugs will be reported and fixed upstream.
See #14823
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
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>
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
The where function previously fell through to a no-op when comparing
two values whose kinds were not handled by the primitive type switches
(e.g. two Page interface values). This made `where pages "Parent" $page`
return an empty list, while the equivalent `range pages` + `if eq` worked.
Use compare.Eqer for equality operators when either side implements it,
matching the behavior of the eq/ne template funcs.
Fixes#14777
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Go 1.24 tool directive uses single-token entries inside a
tool ( ... ) block. The previous splitter treated any tab-indented
line as a require entry, causing an index out of range panic when
running hugo mod tidy on a module with a tool block.
Track the require block state explicitly so other blocks (tool,
replace, exclude, retract) are left untouched.
Fixes#14783
The contentInclusionFilter used strings.Contains to match filenames
against the target path. Because strings.Contains is a substring check,
a directory entry like "content/about" matches "content/about.md",
causing unrelated files to be pulled into the mini-build. Whether the
conflict was then detected depended on whether the filesystem walker
delivered a directory entry or a full file path.
Also adds an upfront check for the directory-conflict case, since
a corrected filter alone would allow about.md to be created alongside
an existing about/ directory.
Closes#12602Closes#12786Closes#14112Closes#14769
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Filename identifiers for roles and versions were parsed but never applied
to the SitesMatrix. Now they replace the mount's configuration for that
dimension, matching how language identifiers already worked.
Fixes#14756
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
Strip leading whitespace from Hugo context marker lines before
Goldmark parsing to prevent them from being treated as indented
code blocks.
Fixes#12457
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: fix broken links in top README.md
* point at the docs site instead of github
* fix typo
Co-authored-by: Joe Mooring <joe@mooring.com>
---------
Co-authored-by: Joe Mooring <joe@mooring.com>
The XSS fix in 479fe6c65 accidentally called util.EscapeHTML twice
on link destinations, causing `&` to render as `&amp;` instead
of `&`.
Fixes#14715
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use template.JS for the falsy return value of _PopPartialDecorator
so Go's html/template JS escaper doesn't wrap the empty string in
quotes inside <script> tags.
Fixes#14711
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Goldmark v1.8 reports source positions in render hooks.
Use this to build a source map that translates Goldmark positions back to
the original content source.
Now all render hooks' context object implenent the `BaseContext` interface:
```go
type BaseContext interface {
Position() Position
Page() any
PageInner() any
Ordinal() int
}
```
Closes#14663
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* This issue in question was fixed upstream in https://github.com/yuin/goldmark/releases/tag/v1.8.2
* We do, however, have a copy of the affected functions, used as fallbacks when no render hook are defined for e.g. links and images, so we need to port these fixes to our copy of the affected functions.
Update tdewolff/minify to v2.24.11 and add "" and "x-bind" to KeepNamespaces
to prevent Alpine.js directives from being stripped in SVGs.
This is a new option in tdewolff/minify v2.24.11, and it is needed to prevent breakage of Alpine.js, and possilby others, directives in SVGs.
Fixes#14669
When `hugo new` creates a content file on a case-insensitive filesystem
where a file with the same name but different case already exists (e.g.
Floop.md vs floop.md), the internal page lookup fails because it uses
exact string comparison. This previously caused an unhelpful panic.
Replace the panic with a descriptive error message that tells the user
what went wrong and suggests checking for case-variant filenames.
Fixes#14112
Co-authored-by: easonysliu <easonysliu@tencent.com>
Co-authored-by: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
When running `hugo convert` with `--output`, copy the content tree first so non-content bundle resources are kept in the destination, then overwrite converted content files.
Also avoid recursive self-copy when the output path points inside the content tree by skipping output directories during copy.
Fixes#4621
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 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
Before this commit, we did dynamic loader resolution for CSS bundling for resources resolved by Hugo while any fallback to ESBuild would fall back to a (potentially) empty loaders config.
This revises the logic to always use a static list (see below) if `loaders` is not set. This should be easier do document and less confusing for the end user.
````
".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".avif",
".woff", ".woff2", ".ttf", ".eot", ".otf"
````
Fixes#14619
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>
* I had all kinds of issues upgrading CLDR on the old library, so I decided to start fresh in github.com/bep/golocales
* It shaves off about 3 MB of the binary, and package init cost is zero compared to the old localescompressed (5000x faster).
* We will probably move to the `text/**` packages at this point, but this will have to do for now.
* Main reason is that it slows down my own `gh` workflow (I also frequently accidentally select that template).
* We also don't have a large volume of PRs from new contributors, so the template isn't as necessary.
The nested loop had dirs as the outer loop and others as the inner loop
with a single counter, causing n to exceed len(others) when multiple
dirs existed. Swap the loop order so each file in others is checked
against all dirs exactly once.
Fixes#14573
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add AVIF, HEIF and HEIC partial support
* Add them as media types.
* Support reading metadata (Width, Height, Exif, etc.) from these formats.
* Add a new template function IsImageResourceMeta to check if a resource supports image metadata operations, which will return true for AVIF, HEIF and HEIC resources even if they don't support full image operations yet.
Fixes#14549
Use blobless git clone (--filter=blob:none --no-checkout) to fetch
commit history for content from Git modules, then map it using the
existing gitmap library. Cloned repos are cached in the modulegitinfo file cache.
Closes#14431Fixes#5533
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>
When a home page or section page in language A is backed by a file and
language B has no content file for that section, the automatic page for
language B was not replaced by the complement from language A. This
happened because:
1. The content node shifter returned auto pages (not backed by files) as
exact matches without trying complement fallback.
2. findContentNodeForSiteVector immediately returned auto pages as exact
matches without considering file-backed complements.
3. getPagesInSection used Get (no fallback) for IncludeSelf, preventing
complement resolution for home/section pages.
Fix by preferring file-backed complement pages over auto pages in the
shift and lookup mechanisms, and using fallback for self-inclusion.
Fixes#14540
Co-Authored-By: Joe Mooring <joe.mooring@veriphor.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This moves Site.Data to the hugo namespace as hugo.Data, following the
same pattern used for hugo.Sites. Also deprecate Site.AllPages,
Site.BuildDrafts, and Site.Languages.
Closes#14521
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* This break the circular dependency between hugolib and resources/page, and allow HugoInfo to use the proper Page ans Site interfaces.
* It's a little misplaced, but it makes everything easier to maintain and test, so that's well worth it.
The --panicOnWarning flag was not wired into the root command's logger,
so warnings emitted during config loading (such as module version
incompatibility warnings) would not trigger a panic.
Bind the panicOnWarning flag to the rootCommand struct and set the
PanicOnWarningHook on the logger created in createLogger.
Fixes#14524
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changes:
- Add hugo.Sites to return all sites for all dimensions
- Modify .Site.Sites to return all sites for all dimensions
- Deprecate .Site.Sites in favor of hugo.Sites
- Add .Site.IsDefault() to report whether the current site is the
default site across all dimensions
- Consolidate tests
Closes#14479Closes#14481
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The pluralTreeKey is normalized (spaces become hyphens), but the
unnormalized path preserves spaces. Use the plural name directly
for prefix trimming on the unnormalized path.
Fixes#13422
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace parser.ParseDir (deprecated since Go 1.25) with manual
file iteration using parser.ParseFile, and replace doc.New with
doc.NewFromFiles.
Fixes#14513
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a custom output format has isHTML=true but no Path, renderDefaultSiteRedirect
would derive intermediate redirect paths from homeLink (e.g. /en/foo.html),
creating a spurious redirect at /en/ that overwrites the actual HTML content.
Only render the default site redirect for the canonical "html" format
or for formats with their own Path prefix (e.g. AMP).
Fixes#14482
Co-Authored-By: Joe Mooring <joe.mooring@veriphor.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When templates were edited in a multi-version/role setup, only pages
from the current site slice (h.Sites) were checked for dependencies,
not pages from all sites. Fix by changing withPage to iterate over
all sites using allSites.
Fixes#14461
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Running `mage check` takes too long time for user (and agent) iterations. That will eventualy run on CI.
This is a faster version that:
* Checks gofmt.
* Runs go vet.
* Runs staticcheck.
* Runs unit tests without the race flag.
It assumes that Go is installed, but will check if staticcheck is available and install it if not with go install.
It takes an optional argument to specify packages to check, otherwise it checks all packages.
This commit also enable local runs of the image golden tests.
In v0.141.0 we deprecated:
- privacy.twitter.simple in favor of privacy.x.simple
- privacy.twitter.disable in favor of privacy.x.disable
Use of those settings is now causing the integration test to fail.
Since the meta config is new (and exif config is deprecated), remove
these options from MetaConfig. The fields filter controls which metadata
is extracted, including Date and GPS fields.
The existing exif config options are preserved for backward compatibility.
Closes#14437
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When {{ with partial "foo" }} returns a falsy value (false, nil, ""),
the with block is skipped but _popPartialDecorator was only called
inside the with block. This left an orphan entry on the decorator
stack, causing "partial decorator ID mismatch" panic on subsequent
partial calls.
Add _popPartialDecorator call to the else branch to ensure the
decorator stack is always balanced regardless of the partial's
return value.
Fixes#14419
The fetchResourceForSpec function was reusing a single ReadSeekCloser
across multiple goroutines, causing a race condition when
TestImageTransformConcurrent runs with imageProcWorkers > 1.
This was exposed by commit e569dd59a ("Misc webp performance work")
which increased imageProcWorkers from 1 to 2.
Fix by creating a new reader on each call to the open function.
Fixes race condition causing segfault on Apple Silicon.
Co-authored-by: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.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
There are 2 fundamental changes related to alias handling in this PR:
1. the `Page.Aliases`` method returns a ready-to-use list of aliases, not the raw input data. This means that it's prefixed with correct dimension values (e.g. language code) and output format paths, and ready to use in e.g. Netlify's _redirects file.
2. We only render aliases for output formats that is both defined as `IsHTML` and `Permalinkable`.
See #14402
*- Dynamic pool sizing for WebP and Katex based on CPU count
- WebP decode: use RGB (3 bytes) for opaque images instead of RGBA (4 bytes)
- Add new imaging.config with new encoder method option; this is default 4 (what we had before), if have tested with value 2 for a 30% speedup on my MacBook, but that does sacrifice quality in some cases (quality/speed trade-off 0-6)
- Track hasAlpha in decode response to enable RGB optimization
- Scale image processing workers (1->2) on high-core machines
- Add WebP benchmark test
On my MacBook Pro M1 Pro, I now get these numbers compared to Hugo v0.152.0:
```
goos: darwin
goarch: arm64
pkg: github.com/gohugoio/hugo/internal/warpc
cpu: Apple M1 Pro
│ cmp152.bench │ fix-webpbench.bench │
│ sec/op │ sec/op vs base │
Webp-10 162.6m ± 32% 145.3m ± 21% -10.60% (p=0.026 n=6)
│ cmp152.bench │ fix-webpbench.bench │
│ B/op │ B/op vs base │
Webp-10 41.69Mi ± 0% 123.79Mi ± 0% +196.92% (p=0.002 n=6)
│ cmp152.bench │ fix-webpbench.bench │
│ allocs/op │ allocs/op vs base │
Webp-10 9.963k ± 3% 18.797k ± 2% +88.67% (p=0.002 n=6)
```
For general image resize benchmarks compared to Hugo v0.152.0 (note that that's not the result of this branch):
```bash
goos: darwin
goarch: arm64
pkg: github.com/gohugoio/hugo/resources/images
cpu: Apple M1 Pro
│ cmp152.bench │ fix-webpbench.bench │
│ sec/op │ sec/op vs base │
ImageResize-10 84.12m ± ∞ ¹ 40.70m ± ∞ ¹ -51.61% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ cmp152.bench │ fix-webpbench.bench │
│ B/op │ B/op vs base │
ImageResize-10 32.37Mi ± ∞ ¹ 29.25Mi ± ∞ ¹ -9.64% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ cmp152.bench │ fix-webpbench.bench │
│ allocs/op │ allocs/op vs base │
ImageResize-10 11.34k ± ∞ ¹ 12.03k ± ∞ ¹ +6.07% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
```
Closes#14370
Rename resources/images/exif to resources/images/meta and extend
metadata support to include XMP and IPTC fields. Deprecate .Exif
in favor of .Meta. Update imagemeta dependency and add configuration
options for metadata extraction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Closes#13146
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.
This has 2 main benefits:
1. When you only need width/height, we don't need to spin up a WASM runtime.
2. When all processed images are cached in the file cache, we don't need to spin up a WASM runtime.
The above is only really visible for smaller sites, but it's annoying to potentially have a site building in 15ms take e.g. 200ms because of WASM startup time.
Fixes#14371
* 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
When a new shortcode file was added during `hugo server`, the shortcode
was detected but not properly registered in the template cache. This caused
subsequent content edits using the shortcode to fail with "template for
shortcode not found" until the server was restarted.
The issue was that when a shortcode was added, only a glob identity for
dependent content was added to the changes list, but not the shortcode
file's own pathInfo. This meant RefreshFiles filtered it out and never
inserted it into the shortcodesByName cache.
The fix adds the shortcode file's pathInfo to changes (in addition to the
glob for dependent content), and corrects the glob pattern from
`shortcodes/...` to `/_shortcodes/...` to match the actual path format.
Fixes#14207
* 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
Limits the number of root groups shown in 'Watching for changes' output
to 10 maximum, with a summary message for remaining paths. This prevents
the message from becoming excessively long when watching sites with many
mount points.
Fixes#14277
Signed-off-by: majiayu000 <1835304752@qq.com>
The original idea of setting quality to 0 for lossless WebP was didn't allow a setting that made sense for both JPEG and WebP.
Note that the above didn't really work at all, so this should not break anything.
It was accidentally introduced because we compile it to file mounts, and `lang` on file mounts is deprecated in favor of `sites.matrix`.
But contentDir per language is something we have had for a long time, and is to useful too to deprecate.
Fixes#14287
Which is it behaved before we added the extended WebP support.
Testing on some sites shows that it's not uncommon to store JPEGs with PNG extensions and vice versa.
This an error situation that needs to be reported, but let us push that to a future Hugo version to reduce the noise in this one.
As in content trees with 10 thousand of directories and more.
A benchmark with the bottle neck code in `helpers.ExtractAndGroupRootPaths`:
```
│ cmp20251125.bench │ fix-extractandgrouproot-14211.bench │
│ sec/op │ sec/op vs base │
ExtractAndGroupRootPaths-10 1282818.8µ ± 8% 493.8µ ± 38% -99.96% (p=0.002 n=6)
│ cmp20251125.bench │ fix-extractandgrouproot-14211.bench │
│ B/op │ B/op vs base │
ExtractAndGroupRootPaths-10 3343.8Ki ± 0% 146.3Ki ± 0% -95.63% (p=0.002 n=6)
│ cmp20251125.bench │ fix-extractandgrouproot-14211.bench │
│ allocs/op │ allocs/op vs base │
ExtractAndGroupRootPaths-10 20.043k ± 0% 2.979k ± 0% -85.14% (p=0.002 n=6)
```
For test project that started this (a 60k directory conent tree), the server startup with no rendering, wen from 1.5 minutes to less than 4 seconds:
```
hugop server --renderSegments none main ✚ ✖ ✱ ◼
Watching for changes in /Users/bep/dev/sites/hugotestsites/60k/content/{section0,section1,section10,section100,section101,section102,section103,section104,section105,section106,...}
Watching for changes in /Users/bep/dev/sites/hugotestsites/60k/layouts/_default
Watching for config changes in /Users/bep/dev/sites/hugotestsites/60k/config.toml
Start building sites …
hugo v0.153.0-DEV-7e27c303904ed8b221d6a5a4fc9a764bb7b2935b darwin/arm64 BuildDate=2025-11-25T15:02:58Z
│ EN
──────────────────┼────
Pages │ 0
Paginator pages │ 0
Non-page files │ 0
Static files │ 0
Processed images │ 0
Aliases │ 0
Cleaned │ 0
Built in 3884 ms
Environment: "development"
Serving pages from disk
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at //localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
```
Note that the output may be a little different and a little more verbose than before., but the information is correct and this implementation is significantly faster and simpler.
Fixes#14211
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
Changes:
- If n > hi, return the full, sorted range [0, hi) of size hi.
- Throw errors when seed, n, or hi are < 0
- Improve error messages
Closes#14143
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)
````
Hugo's build process is roughly divided into three steps:
1. Process content (walk file system and insert source nodes into content tree)
2. Assemble content (assemble pages and resources according to sites matrix)
3. Render content
In #13679 we consolidated the page creation logic into one place (the assemble step). This made it much simpler to reason about, but it lost us some performance esp. in big content trees.
This commit re-introduces parallelization in the first step in the assemble step by handling each top level section in its own goroutine. This gives significant performance improvements for content trees with many sections.
Compared to master:
```
AssembleDeepSiteWithManySections/depth=1/sectionsPerLevel=6/pagesPerSection=100-10 19.26m ± ∞ ¹ 14.54m ± ∞ ¹ -24.52% (p=0.029 n=4)
AssembleDeepSiteWithManySections/depth=2/sectionsPerLevel=2/pagesPerSection=100-10 19.74m ± ∞ ¹ 16.45m ± ∞ ¹ -16.71% (p=0.029 n=4)
AssembleDeepSiteWithManySections/depth=2/sectionsPerLevel=6/pagesPerSection=100-10 106.18m ± ∞ ¹ 71.23m ± ∞ ¹ -32.91% (p=0.029 n=4)
AssembleDeepSiteWithManySections/depth=3/sectionsPerLevel=2/pagesPerSection=100-10 38.85m ± ∞ ¹ 30.47m ± ∞ ¹ -21.59% (p=0.029 n=4)
```
In 2c80dee358 we enabled rendering of
PlantUML diagrams within integration tests. This commit removes PlantUML
support from the tests in favor of rendering Ditaa diagrams, which take
less time to generate.
The slugorcontentbasename permalink token was creating an extra
subdirectory for section pages (_index.md files). This was because
pageToPermalinkContentBaseName did not have the same special handling
for _index.md files as pageToPermalinkFilename.
This commit adds the special handling to return an empty string for
section pages, ensuring backward compatibility with slugorfilename.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes#14104
Previously the JSON -> map roundtrip produced float64 values which caused the YAML encoder to emit integers like 404.0 instead of 404. Use the YAML encoder option yaml.AutoInt() so numbers that are integer-valued are encoded as integers.
This change affects the
ewDocsHelper command (commands/gen.go) where the docs data is written to docs/data/docs.yaml. After this change generated YAML will contain integer values (e.g. status: 404) instead of floats (e.g. status: 404.0).
Fixes#14122
We have had several had several integration test setups over the years. What we have now, backed by txtar files, has been working very well for a long time.
The tests deleted here may lose us some coverage, but they are painful to maintain and it's unlikely that we will port them to the new setup.
We already cached the method index on the struct, but caching the resolved `reflect.Method` itself saves us from having to do another lookup on each call, which is escpeciall import in the hot path used by collections.Where and otherrs:
```bash
│ master.bench │ fix-reflectmethodcache.bench │
│ sec/op │ sec/op vs base │
WhereSliceOfStructPointersWithMethod-10 592.2µ ± ∞ ¹ 390.1µ ± ∞ ¹ -34.14% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ master.bench │ fix-reflectmethodcache.bench │
│ B/op │ B/op vs base │
WhereSliceOfStructPointersWithMethod-10 205.14Ki ± ∞ ¹ 64.52Ki ± ∞ ¹ -68.55% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ master.bench │ fix-reflectmethodcache.bench │
│ allocs/op │ allocs/op vs base │
WhereSliceOfStructPointersWithMethod-10 9.003k ± ∞ ¹ 4.503k ± ∞ ¹ -49.98% (p=0.029 n=4)
````
* Use helper funcs in hreflect package when possible.
* Use hreflect.ConvertIfPossible to handle conversions when possible.
* Move scratch.go from common/maps to common/hstore to clear cyclic import in the next step.
* Move Indirect to hreflect and reimplementing it and adusting the behavior to preserve struct pointers.
* Adjust evaluateSubElem used by where and others making the struct pointer method case slightly faster.
For this and similar mounts in a theme:
```toml
[[module.mounts]]
source = 'node_modules/bootstrap'
target = 'assets/vendor/bootstrap'
```
We first check the theme itself, then the project root.
For backwards compatibility, we also make any `../../node_modules/...` `source` paths into `node_modules/...` paths when defined in themes/modules.
Fixes#14089
E.g. in date.AddDate.
In Hugo v0.152.0 we moved to a new YAML library (github.com/goccy/go-yaml) which produces uint64 for unsigned integers.
This unfortunately breaks common constructs like:
.Date.AddDate 0 0 7
when .Date is a time.Time and the integers are unmarshaled from YAML front matter.
This commit adds code to handle conversion from uint64 (and other int types) to the required int types where possible.
Fixes#14079
This change is mainly motivated to support sites built by HTML fragments with e.g. a JS framework.
Now we don't inject the script if we don't find any of `doctype` (the only one required by the HTML 5 spec), `html` or `head`.
Co-authored-by: bep <bjorn.erik.pedersen@gmail.com>
On s390x, math.Atan(1) and math.Tan(1) give values that slightly differ
from other architectures, causing TestTemplateFuncsExamples to fail.
The golang math package states in the overview:
"This package does not guarantee bit-identical results across architectures."
This cache is used by `resources.GetRemote`.
Default values are:
* respectCacheControlNoStoreInResponse: false
* respectCacheControlNoStoreInRequest: true
This is a slightly breaking change, but the current behaviour is confusing, as:
* Many servers set the `no-store` header without much consideration, see https://developer.chrome.com/docs/web-platform/bfcache-ccns for more context
* We almost always want to cache the `resources.GetRemote` to disk.
Fixes#13990
This commit introduces a new option, backlinkHTML, to the Goldmark
footnote extension. This allows users to the set custom text for
footnote backlink anchors.
Closes#11434
This commit introduces a new option, enableAutoIDPrefix, to the Goldmark
footnote extension. When enabled, it prepends a unique prefix to
footnote IDs, preventing clashes when multiple documents are rendered
together. This prefix is unique to each logical path, which means that
the prefix is not unique across content dimensions such as language.
This change also refactors the extension's configuration from a boolean
to a struct.
Closes#8045
Google does not behave as expected when an alias is used. It does not index the client-side alias page, but on top of that it also does not index the original page.
Google does not require noindex to be present and since alias pages do not have any content, it makes little sense to provide it. Therefore, this patch removes the line completely.
https://developers.google.com/search/docs/crawling-indexing/301-redirects
stale-pr-message:This PR has been automatically marked as stale because it has not had
recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
Please check https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md#code-contribution and verify that this code contribution fits with the description. If yes, tell is in a comment.
Please check https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md#code-contribution and verify that this code contribution fits with the description. If yes, tell us in a comment.
This PR will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
* Assume that the maintainers and readers of the code you write are Go experts:
* Don't use comments to explain the obvious.
* Use self-explanatory variable and function names.
* Use short variable names when the context is clear.
* If you need to add temporary debug printing, use `hdebug.Printf`.[^1]
* Never export symbols that's not needed outside of the package.
* Avoid global state at (almost) all cost.
* This is a project with a long history; assume that a similiar problem has been solved before, look hard for helper functions before creating new ones.
* In tests, almost always write end-to-end integration tests using `hugolib.Test` or one of its siblings. Write unit tests only for isolated utilities.
* In tests, use `qt` matchers (e.g. `b.Assert(err, qt.ErrorMatches, ...)`) instead of raw `if`/`t.Fatal` checks.
* In tests, always use the latest Hugo specification, e.g. for layouts, it's `layouts/page.html` and not `layouts/_default/single.html`, `layouts/list.html` and not `layouts/_default/list.html`
* Never name tests `TestIssue1234`; always give the test function a descriptive name, e.g. `TestDisablePathToLower`, and add any issue reference as a Go doc function comment, e.g. `// See issue 1234.`.
* If you borrow a test case (e.g. from the issue), that test's author must be added as co-author in the commit.
* If you're a security researcher, read @SECURITY.md carefully.
* Brevity is good. This applies to code, comments and commit messages. Don't write a novel.
* Use `./check.sh ./somepackage/...` when iterating.
* Use `./check.sh` when you're done.
[^1]: CI build fail if you forget to remove the debug printing.
>**Note:** We would appreciate if you hold on with any big refactoring (like renaming deprecated Go packages), mainly because of potential for extra merge work for future coming in in the near future.
# Contributing to Hugo
We welcome contributions to Hugo of any kind including documentation, themes,
@@ -19,6 +17,7 @@ The Hugo community and maintainers are [very active](https://github.com/gohugoio
* [Fetching the Sources From GitHub](#fetching-the-sources-from-github)
* [Building Hugo with Your Changes](#building-hugo-with-your-changes)
@@ -54,7 +53,7 @@ If it is of some complexity, the contributor is expected to maintain and support
Any non-trivial code change needs to update an open [issue](https://github.com/gohugoio/hugo/issues). A non-trivial code change without an issue reference with one of the labels `bug` or `enhancement` will not be merged.
Note that we do not accept new features that require [CGO](https://github.com/golang/go/wiki/cgo).
Note that we do not accept new features that require [CGO](https://go.dev/wiki/cgo).
We have one exception to this rule which is LibSASS.
**Bug fixes are, of course, always welcome.**
@@ -73,12 +72,26 @@ To make the contribution process as seamless as possible, we ask for the followi
* When you’re ready to create a pull request, be sure to:
* Sign the [CLA](https://cla-assistant.io/gohugoio/hugo).
* Have test cases for the new code. If you have questions about how to do this, please ask in your pull request.
* If you borrow a test case (e.g. from the issue), that test's author must be added as [co-author](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors) in the commit.
* Run `go fmt`.
* Add documentation if you are adding new features or changing functionality. The docs site lives in `/docs`.
* Squash your commits into a single commit. `git rebase -i`. It’s okay to force update your pull request with `git push -f`.
* Ensure that `mage check` succeeds. [Travis CI](https://travis-ci.org/gohugoio/hugo) (Windows, Linux and macOS) will fail the build if `mage check` fails.
* Ensure that `./check.sh` succeeds. Note that some tests are skipped when running locally, some because they are slow. To run these locally, do `CI_LOCAL=true ./check.sh ./somepackage/...`.
* Follow the **Git Commit Message Guidelines** below.
## AI Assistance Notice
If a substantial part of your contribution is autogenerated with AI, **this must be disclosed in the pull request**, along with the extent to which AI assistance was used. AI contributions from non-maintainers needs to have a fairly narrow scope (e.g. a bug fix), as we have limited review capacity.
An example disclosure:
> This PR was written primarily by Claude Code.
Also, When using AI assistance:
* We expect contributors to manually verify that the state of the pull request is OK (e.g. that the CLI is signed).
* We expect contributors to understand the code that is produced and be able to answer critical questions about it
### Git Commit Message Guidelines
This [blog article](https://cbea.ms/git-commit/) is a good resource for learning how to write good commit messages,
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
Hugo is a [static site generator] written in [Go], optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
Hugo is a [static site generator][] written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create:
@@ -52,116 +57,131 @@ Use Hugo's embedded web server during development to instantly see changes to co
Hugo's fast asset pipelines include:
-Image processing – Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data
-CSS Processing – Bundle, transform, minify, create source maps, perform SRI hashing, and integrate with PostCSS.
- Image processing – Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract metadata
- JavaScript bundling – Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing.
- Sass processing – Transpile Sass to CSS, bundle, tree shake, minify, create source maps, perform SRI hashing, and integrate with PostCSS
- Tailwind CSS processing – Compile Tailwind CSS utility classes into standard CSS, bundle, tree shake, optimize, minify, perform SRI hashing, and integrate with PostCSS
And with [Hugo Modules], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
And with [Hugo Modules][], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
See the [features] section of the documentation for a comprehensive summary of Hugo's capabilities.
See the [features][] section of the documentation for a comprehensive summary of Hugo's capabilities.
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
Hugo is available in three editions: standard, extended, and extended/deploy. While the standard edition provides core functionality, the extended and extended/deploy editions offer advanced features.
Hugo is available in several editions. Use the standard edition unless you need additional features.
Feature|extended edition|extended/deploy edition
:--|:-:|:-:
Encode to the WebP format when [processing images]. You can decode WebP images with any edition.|:heavy_check_mark:|:heavy_check_mark:
[Transpile Sass to CSS] using the embedded LibSass transpiler. You can use the [Dart Sass] transpiler with any edition.|:heavy_check_mark:|:heavy_check_mark:
Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details].|:x:|:heavy_check_mark:
(1) Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details][].
Unless your specific deployment needs require the extended/deploy edition, we recommend the extended edition.
(2) [Transpile Sass to CSS][] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass][] transpiler instead, which is compatible with any edition.
## Installation
Install Hugo from a [prebuilt binary], package manager, or package repository. Please see the installation instructions for your operating system:
Install Hugo from a [prebuilt binary][], package manager, or package repository. Please see the installation instructions for your operating system:
- [macOS]
- [Linux]
- [Windows]
- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD]
- [macOS][]
- [Linux][]
- [Windows][]
- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD][]
## Build from source
Prerequisites to build Hugo from source:
To build Hugo from source you must install:
- Standard edition: Go 1.23.0 or later
- Extended edition: Go 1.23.0 or later, and GCC
- Extended/deploy edition: Go 1.23.0 or later, and GCC
1. [Git][]
1. [Go][] version 1.26.0 or later
Build the standard edition:
### Standard edition
```text
go install github.com/gohugoio/hugo@latest
To build and install the standard edition:
```sh
CGO_ENABLED=0 go install github.com/gohugoio/hugo@latest
```
Build the extended edition:
### Deploy edition
```text
To build and install the deploy edition:
```sh
CGO_ENABLED=0 go install -tags withdeploy github.com/gohugoio/hugo@latest
```
### Extended edition
To build and install the extended edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command.
```sh
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
```
Build the extended/deploy edition:
### Extended/deploy edition
```text
To build and install the extended/deploy edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command.
```sh
CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest
```
## Star History
[](https://star-history.com/#gohugoio/hugo&Timeline)
## Documentation
Hugo's [documentation] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
Hugo's [documentation][] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
Please submit documentation issues and pull requests to the [documentation repository].
Please submit documentation issues and pull requests to the [documentation repository][].
## Support
Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum].
Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum][].
Hugo’s [forum] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question.
Hugo's [forum][] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help][] before asking your first question.
## Contributing
You can contribute to the Hugo project by:
- Answering questions on the [forum]
- Improving the [documentation]
- Monitoring the [issue queue]
- Creating or improving [themes]
- Squashing [bugs]
- Answering questions on the [forum][]
- Improving the [documentation][]
- Monitoring the [issue queue][]
- Creating or improving [themes][]
- Squashing [bugs][]
Please submit documentation issues and pull requests to the [documentation repository].
Please submit documentation issues and pull requests to the [documentation repository][].
If you have an idea for an enhancement or new feature, create a new topic on the [forum] in the "Feature" category. This will help you to:
If you have an idea for an enhancement or new feature, create a new topic on the [forum][] in the "Feature" category. This will help you to:
- Determine if the capability already exists
- Measure interest
- Refine the concept
If there is sufficient interest, [create a proposal]. Do not submit a pull request until the project lead accepts the proposal.
If there is sufficient interest, [create a proposal][]. Do not submit a pull request until the project lead accepts the proposal.
For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
## License
For the Hugo source code, see [LICENSE](/LICENSE).
We also bundle some libraries in binary/WASM form:
Please read [Hugo's Security Model](https://gohugo.io/about/security/) first. If the issue reproduces in an upstream project, please report it there — we cannot triage or patch on their behalf.
### Reporting a Vulnerability
Please report (suspected) security vulnerabilities to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. You will receive a response from us within 48 hours. If we can confirm the issue, we will release a patch as soon as possible depending on the complexity of the issue but historically within days.
If, after the above, you believe you have found a vulnerability in Hugo itself with a concrete, reproducible impact, report it privately to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. Include a minimal reproducer, the Hugo version, and the observed vs. expected behavior.
Also see [Hugo's Security Model](https://gohugo.io/about/security/).
You should receive an initial response within a few days. Confirmed issues are typically patched within days, depending on complexity.
cmd.Flags().BoolP("ignoreCache","",false,"ignores the cache directory")
cmd.Flags().BoolP("ignoreCache","",false,"ignore the configured file caches")
cmd.Flags().Bool("enableGitInfo",false,"add Git revision, date, author, and CODEOWNERS info to the pages")
cmd.Flags().StringP("layoutDir","l","","filesystem path to layout directory")
_=cmd.MarkFlagDirname("layoutDir")
cmd.Flags().BoolVar(&r.gc,"gc",false,"enable to run some cleanup tasks (remove unused cache files) after the build")
cmd.Flags().StringVar(&r.poll,"poll","","set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
short:"Generate CSS stylesheet for the Chroma code highlighter",
long:`Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if markup.highlight.noClasses is disabled in config.
See https://xyproto.github.io/splash/docs/all.html for a preview of the available styles`,
See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles.`,
cmd.PersistentFlags().StringVar(&highlightStyle,"highlightStyle","",`foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`)
cmd.PersistentFlags().StringVar(&lineNumbersInlineStyle,"lineNumbersInlineStyle","",`foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"`)
cmd.PersistentFlags().StringVar(&lineNumbersTableStyle,"lineNumbersTableStyle","",`foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"`)
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.