Compare commits

..

1 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen bd1657c360 Build without the deploy feature by default
Build tags setup changed to:

* !nodeploy => withdeploy
* nodeploy => !withdeploy

Also move the deploy feature out into its own release archives.

See #12994 for the primary motivation for this change. But this also greatly reduces the number of dependencies in Hugo when you don't need this feature and cuts the binary size greatly.

Fixes #12994
2024-10-30 10:45:40 +01:00
943 changed files with 8487 additions and 16999 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ parameters:
defaults: &defaults
resource_class: large
docker:
- image: bepsays/ci-hugoreleaser:1.22301.20401
- image: bepsays/ci-hugoreleaser:1.22300.20200
environment: &buildenv
GOMODCACHE: /root/project/gomodcache
version: 2
@@ -58,7 +58,7 @@ jobs:
environment:
<<: [*buildenv]
docker:
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22301.20401
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20200
steps:
- *restore-cache
- &attach-workspace
+4 -4
View File
@@ -6,10 +6,10 @@ name: Test
env:
GOPROXY: https://proxy.golang.org
GO111MODULE: on
SASS_VERSION: 1.80.3
DART_SASS_SHA_LINUX: 7c933edbad0a7d389192c5b79393485c088bd2c4398e32f5754c32af006a9ffd
DART_SASS_SHA_MACOS: 79e060b0e131c3bb3c16926bafc371dc33feab122bfa8c01aa337a072097967b
DART_SASS_SHA_WINDOWS: 0bc4708b37cd1bac4740e83ac5e3176e66b774f77fd5dd364da5b5cfc9bfb469
SASS_VERSION: 1.63.2
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
permissions:
contents: read
jobs:
-1
View File
@@ -3,4 +3,3 @@
imports.*
dist/
public/
.DS_Store
+93 -43
View File
@@ -33,7 +33,8 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
[![Tests on Linux, MacOS and Windows](https://github.com/gohugoio/hugo/workflows/Test/badge.svg)](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/gohugoio/hugo)](https://goreportcard.com/report/github.com/gohugoio/hugo)
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a> | <a rel="me" href="https://x.com/gohugoiov2">X</a>
## Overview
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.
@@ -66,7 +67,7 @@ See the [features] section of the documentation for a comprehensive summary of H
<p float="left">
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
&nbsp;&nbsp;&nbsp;
<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/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
<a href="https://route4me.com/" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg" width="200" alt="Route Planning & Route Optimization Software"></a>
</p>
## Installation
@@ -101,7 +102,6 @@ Build the extended edition:
```text
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
```
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline)
@@ -148,103 +148,153 @@ Hugo stands on the shoulders of great open source libraries. Run `hugo env --log
<summary>See current dependencies</summary>
```text
cloud.google.com/go/compute/metadata="v0.2.3"
cloud.google.com/go/iam="v1.1.5"
cloud.google.com/go/storage="v1.35.1"
cloud.google.com/go="v0.110.10"
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.9.0"
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.4.0"
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.5.0"
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.2.0"
github.com/Azure/go-autorest/autorest/to="v0.4.0"
github.com/AzureAD/microsoft-authentication-library-for-go="v1.2.0"
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
github.com/alecthomas/chroma/v2="v2.14.0"
github.com/armon/go-radix="v1.0.1-0.20221118154546-54df44f2176c"
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.5.4"
github.com/aws/aws-sdk-go-v2/config="v1.26.1"
github.com/aws/aws-sdk-go-v2/credentials="v1.16.12"
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.14.10"
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.15.7"
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.3.5"
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.6.5"
github.com/aws/aws-sdk-go-v2/internal/ini="v1.7.2"
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.2.9"
github.com/aws/aws-sdk-go-v2/service/cloudfront="v1.35.4"
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.10.4"
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.2.9"
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.10.9"
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.16.9"
github.com/aws/aws-sdk-go-v2/service/s3="v1.47.5"
github.com/aws/aws-sdk-go-v2/service/sso="v1.18.5"
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.21.5"
github.com/aws/aws-sdk-go-v2/service/sts="v1.26.5"
github.com/aws/aws-sdk-go-v2="v1.26.1"
github.com/aws/aws-sdk-go="v1.50.7"
github.com/aws/smithy-go="v1.20.2"
github.com/bep/clocks="v0.5.0"
github.com/bep/debounce="v1.2.0"
github.com/bep/gitmap="v1.6.0"
github.com/bep/gitmap="v1.1.2"
github.com/bep/goat="v0.5.0"
github.com/bep/godartsass/v2="v2.3.2"
github.com/bep/golibsass="v1.2.0"
github.com/bep/godartsass/v2="v2.0.0"
github.com/bep/godartsass="v1.2.0"
github.com/bep/golibsass="v1.1.1"
github.com/bep/gowebp="v0.3.0"
github.com/bep/imagemeta="v0.8.3"
github.com/bep/lazycache="v0.7.0"
github.com/bep/lazycache="v0.4.0"
github.com/bep/logg="v0.4.0"
github.com/bep/mclib="v1.20400.20402"
github.com/bep/overlayfs="v0.9.2"
github.com/bep/simplecobra="v0.4.0"
github.com/bep/tmc="v0.5.1"
github.com/cespare/xxhash/v2="v2.3.0"
github.com/clbanning/mxj/v2="v2.7.0"
github.com/cli/safeexec="v1.0.1"
github.com/cpuguy83/go-md2man/v2="v2.0.4"
github.com/cpuguy83/go-md2man/v2="v2.0.3"
github.com/disintegration/gift="v1.2.1"
github.com/dlclark/regexp2="v1.11.0"
github.com/evanw/esbuild="v0.24.0"
github.com/fatih/color="v1.18.0"
github.com/dustin/go-humanize="v1.0.1"
github.com/evanw/esbuild="v0.21.4"
github.com/fatih/color="v1.16.0"
github.com/frankban/quicktest="v1.14.6"
github.com/fsnotify/fsnotify="v1.8.0"
github.com/fsnotify/fsnotify="v1.7.0"
github.com/getkin/kin-openapi="v0.123.0"
github.com/ghodss/yaml="v1.0.0"
github.com/go-openapi/jsonpointer="v0.20.2"
github.com/go-openapi/swag="v0.22.8"
github.com/gobuffalo/flect="v1.0.3"
github.com/gobuffalo/flect="v1.0.2"
github.com/gobwas/glob="v0.2.3"
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20230805085216-e63c13218d0e"
github.com/gohugoio/hashstructure="v0.1.0"
github.com/gohugoio/httpcache="v0.7.0"
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.2.0"
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.3.0"
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.2.0"
github.com/gohugoio/locales="v0.14.0"
github.com/gohugoio/localescompressed="v1.0.1"
github.com/golang-jwt/jwt/v5="v5.1.0"
github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da"
github.com/golang/protobuf="v1.5.3"
github.com/google/go-cmp="v0.6.0"
github.com/gorilla/websocket="v1.5.3"
github.com/hairyhenderson/go-codeowners="v0.7.0"
github.com/google/s2a-go="v0.1.7"
github.com/google/uuid="v1.4.0"
github.com/google/wire="v0.5.0"
github.com/googleapis/enterprise-certificate-proxy="v0.3.2"
github.com/googleapis/gax-go/v2="v2.12.0"
github.com/gorilla/websocket="v1.5.1"
github.com/hairyhenderson/go-codeowners="v0.4.0"
github.com/hashicorp/golang-lru/v2="v2.0.7"
github.com/invopop/yaml="v0.2.0"
github.com/jdkato/prose="v1.2.1"
github.com/jmespath/go-jmespath="v0.4.0"
github.com/josharian/intern="v1.0.0"
github.com/kr/pretty="v0.3.1"
github.com/kr/text="v0.2.0"
github.com/kyokomi/emoji/v2="v2.2.13"
github.com/kylelemons/godebug="v1.1.0"
github.com/kyokomi/emoji/v2="v2.2.12"
github.com/mailru/easyjson="v0.7.7"
github.com/makeworld-the-better-one/dither/v2="v2.4.0"
github.com/marekm4/color-extractor="v1.2.1"
github.com/mattn/go-colorable="v0.1.13"
github.com/mattn/go-isatty="v0.0.20"
github.com/mattn/go-runewidth="v0.0.9"
github.com/mitchellh/hashstructure="v1.1.0"
github.com/mitchellh/mapstructure="v1.5.1-0.20231216201459-8508981c8b6c"
github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826"
github.com/muesli/smartcrop="v0.3.0"
github.com/niklasfasching/go-org="v1.7.0"
github.com/olekukonko/tablewriter="v0.0.5"
github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58"
github.com/pelletier/go-toml/v2="v2.2.3"
github.com/pelletier/go-toml/v2="v2.2.2"
github.com/perimeterx/marshmallow="v1.1.5"
github.com/pkg/browser="v0.0.0-20240102092130-5ac0b6a4141c"
github.com/pkg/browser="v0.0.0-20210911075715-681adbf594b8"
github.com/pkg/errors="v0.9.1"
github.com/rogpeppe/go-internal="v1.13.1"
github.com/rogpeppe/go-internal="v1.12.0"
github.com/russross/blackfriday/v2="v2.1.0"
github.com/sass/dart-sass/compiler="1.81.0"
github.com/sass/dart-sass/implementation="1.81.0"
github.com/sass/dart-sass/protocol="3.1.0"
github.com/sass/libsass="3.6.6"
github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd"
github.com/sass/dart-sass/compiler="1.77.5"
github.com/sass/dart-sass/implementation="1.77.5"
github.com/sass/dart-sass/protocol="2.7.1"
github.com/sass/libsass="3.6.5"
github.com/spf13/afero="v1.11.0"
github.com/spf13/cast="v1.7.1"
github.com/spf13/cobra="v1.8.1"
github.com/spf13/cast="v1.6.0"
github.com/spf13/cobra="v1.8.0"
github.com/spf13/fsync="v0.10.1"
github.com/spf13/pflag="v1.0.5"
github.com/tdewolff/minify/v2="v2.20.37"
github.com/tdewolff/parse/v2="v2.7.15"
github.com/tetratelabs/wazero="v1.8.2"
github.com/tdewolff/minify/v2="v2.20.20"
github.com/tdewolff/parse/v2="v2.7.13"
github.com/webmproject/libwebp="v1.3.2"
github.com/yuin/goldmark-emoji="v1.0.4"
github.com/yuin/goldmark="v1.7.8"
github.com/yuin/goldmark-emoji="v1.0.3"
github.com/yuin/goldmark="v1.7.4"
go.opencensus.io="v0.24.0"
go.uber.org/automaxprocs="v1.5.3"
golang.org/x/crypto="v0.31.0"
gocloud.dev="v0.36.0"
golang.org/x/crypto="v0.23.0"
golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326"
golang.org/x/image="v0.22.0"
golang.org/x/mod="v0.22.0"
golang.org/x/net="v0.33.0"
golang.org/x/sync="v0.10.0"
golang.org/x/sys="v0.28.0"
golang.org/x/text="v0.21.0"
golang.org/x/tools="v0.28.0"
google.golang.org/protobuf="v1.35.2"
golang.org/x/image="v0.16.0"
golang.org/x/mod="v0.17.0"
golang.org/x/net="v0.25.0"
golang.org/x/oauth2="v0.15.0"
golang.org/x/sync="v0.7.0"
golang.org/x/sys="v0.20.0"
golang.org/x/text="v0.15.0"
golang.org/x/time="v0.5.0"
golang.org/x/tools="v0.20.0"
golang.org/x/xerrors="v0.0.0-20231012003039-104605ab7028"
google.golang.org/api="v0.152.0"
google.golang.org/genproto/googleapis/api="v0.0.0-20231120223509-83a465c0220f"
google.golang.org/genproto/googleapis/rpc="v0.0.0-20231120223509-83a465c0220f"
google.golang.org/genproto="v0.0.0-20231120223509-83a465c0220f"
google.golang.org/grpc="v1.59.0"
google.golang.org/protobuf="v1.33.0"
gopkg.in/yaml.v2="v2.4.0"
gopkg.in/yaml.v3="v3.0.1"
rsc.io/qr="v0.2.0"
software.sslmate.com/src/go-pkcs12="v0.2.0"
```
</details>
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# allow user to override go executable by running as GOEXE=xxx make ...
GOEXE="${GOEXE-go}"
# Convenience script to
# - For a given branch
# - Run benchmark tests for a given package
# - Do the same for master
# - then compare the two runs with benchcmp
benchFilter=".*"
if (( $# < 2 ));
then
echo "USAGE: ./bench.sh <git-branch> <package-to-bench> (and <benchmark filter> (regexp, optional))"
exit 1
fi
if [ $# -eq 3 ]; then
benchFilter=$3
fi
BRANCH=$1
PACKAGE=$2
git checkout $BRANCH
"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-$BRANCH.txt
git checkout master
"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-master.txt
benchcmp /tmp/bench-$PACKAGE-master.txt /tmp/bench-$PACKAGE-$BRANCH.txt
Executable
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# allow user to override go executable by running as GOEXE=xxx make ...
GOEXE="${GOEXE-go}"
# Send in a regexp matching the benchmarks you want to run, i.e. './benchSite.sh "YAML"'.
# Note the quotes, which will be needed for more complex expressions.
# The above will run all variations, but only for front matter YAML.
echo "Running with BenchmarkSiteBuilding/${1}"
"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -count 3 -cpuprofile cpu.prof
Executable
+1
View File
@@ -0,0 +1 @@
gobench -package=./hugolib -bench="BenchmarkSiteNew/Deep_content_tree"
Executable
+1
View File
@@ -0,0 +1 @@
docker run --rm --mount type=bind,source="$(pwd)",target=/hugo -w /hugo -i -t bepsays/ci-goreleaser:1.11-2 /bin/bash
+5 -11
View File
@@ -176,12 +176,11 @@ func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManag
}
// ClearOnRebuild prepares the cache for a new rebuild taking the given changeset into account.
// predicate is optional and will clear any entry for which it returns true.
func (c *Cache) ClearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) {
func (c *Cache) ClearOnRebuild(changeset ...identity.Identity) {
g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{
NumWorkers: len(c.partitions),
Handle: func(ctx context.Context, partition PartitionManager) error {
partition.clearOnRebuild(predicate, changeset...)
partition.clearOnRebuild(changeset...)
return nil
},
})
@@ -480,12 +479,7 @@ func (p *Partition[K, V]) clearMatching(predicate func(k, v any) bool) {
})
}
func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) {
if predicate == nil {
predicate = func(k, v any) bool {
return false
}
}
func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) {
opts := p.getOptions()
if opts.ClearWhen == ClearNever {
return
@@ -531,7 +525,7 @@ func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changese
// Second pass needs to be done in a separate loop to catch any
// elements marked as stale in the other partitions.
p.c.DeleteFunc(func(key K, v V) bool {
if predicate(key, v) || shouldDelete(key, v) {
if shouldDelete(key, v) {
p.trace.Log(
logg.StringFunc(
func() string {
@@ -607,7 +601,7 @@ type PartitionManager interface {
adjustMaxSize(addend int) int
getMaxSize() int
getOptions() OptionsPartition
clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity)
clearOnRebuild(changeset ...identity.Identity)
clearMatching(predicate func(k, v any) bool)
clearStale()
}
+2 -2
View File
@@ -147,13 +147,13 @@ func TestClear(t *testing.T) {
c.Assert(cache.Keys(predicateAll), qt.HasLen, 4)
cache.ClearOnRebuild(nil)
cache.ClearOnRebuild()
// Stale items are always cleared.
c.Assert(cache.Keys(predicateAll), qt.HasLen, 2)
cache = newTestCache(t)
cache.ClearOnRebuild(nil, identity.StringIdentity("changed"))
cache.ClearOnRebuild(identity.StringIdentity("changed"))
c.Assert(cache.Keys(nil), qt.HasLen, 1)
+26 -15
View File
@@ -39,6 +39,7 @@ import (
"github.com/gohugoio/hugo/common/hstrings"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/types"
@@ -103,8 +104,7 @@ type configKey struct {
type rootCommand struct {
Printf func(format string, v ...interface{})
Println func(a ...interface{})
StdOut io.Writer
StdErr io.Writer
Out io.Writer
logger loggers.Logger
@@ -141,6 +141,8 @@ type rootCommand struct {
logLevel string
verbose bool
debug bool
quiet bool
devMode bool // Hidden flag.
@@ -357,7 +359,7 @@ func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotEx
}
func (r *rootCommand) newDepsConfig(conf *commonConfig) deps.DepsCfg {
return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, StdOut: r.logger.StdOut(), StdErr: r.logger.StdErr(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild}
return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild}
}
func (r *rootCommand) Name() string {
@@ -422,23 +424,21 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args
}
func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
r.StdOut = os.Stdout
r.StdErr = os.Stderr
r.Out = os.Stdout
if r.quiet {
r.StdOut = io.Discard
r.StdErr = io.Discard
r.Out = io.Discard
}
// Used by mkcert (server).
log.SetOutput(r.StdOut)
log.SetOutput(r.Out)
r.Printf = func(format string, v ...interface{}) {
if !r.quiet {
fmt.Fprintf(r.StdOut, format, v...)
fmt.Fprintf(r.Out, format, v...)
}
}
r.Println = func(a ...interface{}) {
if !r.quiet {
fmt.Fprintln(r.StdOut, a...)
fmt.Fprintln(r.Out, a...)
}
}
_, running := runner.Command.(*serverCommand)
@@ -447,8 +447,6 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
if err != nil {
return err
}
// Set up the global logger early to allow info deprecations during config load.
loggers.InitGlobalLogger(r.logger.Level(), false)
r.changesFromBuild = make(chan []identity.Identity, 10)
@@ -484,14 +482,25 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
default:
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
}
} else {
if r.verbose {
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
level = logg.LevelInfo
}
if r.debug {
hugo.Deprecate("--debug", "use --logLevel debug", "v0.114.0")
level = logg.LevelDebug
}
}
}
optsLogger := loggers.Options{
DistinctLevel: logg.LevelWarn,
Level: level,
StdOut: r.StdOut,
StdErr: r.StdErr,
Stdout: r.Out,
Stderr: r.Out,
StoreErrors: running,
}
@@ -540,7 +549,6 @@ Complete documentation is available at https://gohugo.io/.`
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
_ = cmd.MarkFlagDirname("themesDir")
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
cmd.PersistentFlags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
_ = cmd.RegisterFlagCompletionFunc("ignoreVendorPaths", cobra.NoFileCompletions)
cmd.PersistentFlags().String("clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
_ = cmd.RegisterFlagCompletionFunc("clock", cobra.NoFileCompletions)
@@ -552,6 +560,8 @@ Complete documentation is available at https://gohugo.io/.`
cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode")
cmd.PersistentFlags().BoolVarP(&r.renderToMemory, "renderToMemory", "M", false, "render to memory (mostly useful when running the server)")
cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output")
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
cmd.PersistentFlags().BoolVarP(&r.devMode, "devMode", "", false, "only used for internal testing, flag hidden.")
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
_ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp))
@@ -596,6 +606,7 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.")
cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files")
cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations")
cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.")
cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.")
+3 -5
View File
@@ -43,9 +43,8 @@ func newConfigCommand() *configCommand {
type configCommand struct {
r *rootCommand
format string
lang string
printZero bool
format string
lang string
commands []simplecobra.Commander
}
@@ -79,7 +78,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
dec.SetIndent("", " ")
dec.SetEscapeHTML(false)
if err := dec.Encode(parser.ReplacingJSONMarshaller{Value: config, KeysToLower: true, OmitEmpty: !c.printZero}); err != nil {
if err := dec.Encode(parser.ReplacingJSONMarshaller{Value: config, KeysToLower: true, OmitEmpty: true}); err != nil {
return err
}
@@ -116,7 +115,6 @@ func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)")
_ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp))
cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.")
cmd.Flags().BoolVar(&c.printZero, "printZero", false, `include config options with zero values (e.g. false, 0, "") in the output`)
_ = cmd.RegisterFlagCompletionFunc("lang", cobra.NoFileCompletions)
applyLocalFlagsBuildConfig(cmd, c.r)
+12 -1
View File
@@ -20,6 +20,7 @@ import (
"context"
"github.com/gohugoio/hugo/deploy"
"github.com/gohugoio/hugo/deploy/deployconfig"
"github.com/bep/simplecobra"
"github.com/spf13/cobra"
@@ -46,7 +47,17 @@ documentation.
return deployer.Deploy(ctx)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
applyDeployFlags(cmd, r)
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
cmd.Flags().Bool("dryRun", false, "dry run")
cmd.Flags().Bool("force", false, "force upload of all files")
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
},
}
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package commands
import (
"github.com/gohugoio/hugo/deploy/deployconfig"
"github.com/spf13/cobra"
)
func applyDeployFlags(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
cmd.Flags().Bool("dryRun", false, "dry run")
cmd.Flags().Bool("force", false, "force upload of all files")
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
}
-1
View File
@@ -44,7 +44,6 @@ func newDeployCommand() simplecobra.Commander {
return errors.New("deploy not supported in this version of Hugo; install a release with 'withdeploy' in the archive filename or build yourself with the 'withdeploy' build tag. Also see https://github.com/gohugoio/hugo/pull/12995")
},
withc: func(cmd *cobra.Command, r *rootCommand) {
applyDeployFlags(cmd, r)
cmd.Hidden = true
},
}
+1 -3
View File
@@ -75,9 +75,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
return err
}
formatter := html.New(html.WithAllClasses(true))
w := os.Stdout
fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
formatter.WriteCSS(w, style)
formatter.WriteCSS(os.Stdout, style)
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
+5 -9
View File
@@ -62,7 +62,7 @@ type hugoBuilder struct {
// Currently only set when in "fast render mode".
changeDetector *fileChangeDetector
visitedURLs *types.EvictingQueue[string]
visitedURLs *types.EvictingStringQueue
fullRebuildSem *semaphore.Weighted
debounce func(f func())
@@ -920,11 +920,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
changed := c.changeDetector.changed()
if c.changeDetector != nil {
if len(changed) >= 10 {
lrl.Logf("build changed %d files", len(changed))
} else {
lrl.Logf("build changed %d files: %q", len(changed), changed)
}
lrl.Logf("build changed %d files", len(changed))
if len(changed) == 0 {
// Nothing has changed.
return
@@ -970,7 +966,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(otherChanges[0]), false)
lrl.Logf("refreshing %q", pathToRefresh)
livereload.RefreshPath(pathToRefresh)
} else if len(cssChanges) == 0 || len(otherChanges) > 1 {
} else if len(cssChanges) == 0 {
lrl.Logf("force refresh")
livereload.ForceRefresh()
}
@@ -1103,7 +1099,7 @@ func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) {
if err != nil {
return
}
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
return
}
@@ -1119,7 +1115,7 @@ func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error
}
whatChanged := &hugolib.WhatChanged{}
whatChanged.Add(ids...)
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
return
}
+1 -1
View File
@@ -57,7 +57,7 @@ func newListCommand() *listCommand {
return err
}
writer := csv.NewWriter(r.StdOut)
writer := csv.NewWriter(r.Out)
defer writer.Flush()
writer.Write([]string{
+7 -22
View File
@@ -32,7 +32,6 @@ import (
"path"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
@@ -41,7 +40,6 @@ import (
"time"
"github.com/bep/mclib"
"github.com/pkg/browser"
"github.com/bep/debounce"
"github.com/bep/simplecobra"
@@ -85,9 +83,9 @@ const (
)
func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder {
var visitedURLs *types.EvictingQueue[string]
var visitedURLs *types.EvictingStringQueue
if s != nil && !s.disableFastRender {
visitedURLs = types.NewEvictingQueue[string](20)
visitedURLs = types.NewEvictingStringQueue(20)
}
return &hugoBuilder{
r: r,
@@ -211,17 +209,16 @@ func (f *fileChangeDetector) changed() []string {
}
}
return f.filterIrrelevantAndSort(c)
return f.filterIrrelevant(c)
}
func (f *fileChangeDetector) filterIrrelevantAndSort(in []string) []string {
func (f *fileChangeDetector) filterIrrelevant(in []string) []string {
var filtered []string
for _, v := range in {
if !f.irrelevantRe.MatchString(v) {
filtered = append(filtered, v)
}
}
sort.Strings(filtered)
return filtered
}
@@ -364,10 +361,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
}
if f.c.fastRenderMode && f.c.errState.buildErr() == nil {
// Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate
// Fall back to the file extension if not set.
// The main take here is that we don't want to have CSS/JS files etc. partake in this logic.
if r.Header.Get("Sec-Fetch-Mode") == "navigate" || strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") {
if strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") {
if !f.c.visitedURLs.Contains(requestURI) {
// If not already on stack, re-render that single page.
if err := f.c.partialReRender(requestURI); err != nil {
@@ -454,7 +448,6 @@ type serverCommand struct {
// Flags.
renderStaticToDisk bool
navigateToChanged bool
openBrowser bool
serverAppend bool
serverInterface string
tlsCertFile string
@@ -546,7 +539,6 @@ of a second, you will be able to save and see your changes nearly instantly.`
cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL")
cmd.Flags().BoolVar(&c.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
cmd.Flags().BoolVarP(&c.navigateToChanged, "navigateToChanged", "N", false, "navigate to changed content file on live browser reload")
cmd.Flags().BoolVarP(&c.openBrowser, "openBrowser", "O", false, "open the site in a browser after server startup")
cmd.Flags().BoolVar(&c.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory")
cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
@@ -841,7 +833,7 @@ func (c *serverCommand) partialReRender(urls ...string) (err error) {
defer func() {
c.errState.setWasErr(false)
}()
visited := types.NewEvictingQueue[string](len(urls))
visited := types.NewEvictingStringQueue(len(urls))
for _, url := range urls {
visited.Add(url)
}
@@ -853,7 +845,7 @@ func (c *serverCommand) partialReRender(urls ...string) (err error) {
}
// Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyTouched: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
return
}
@@ -1006,13 +998,6 @@ func (c *serverCommand) serve() error {
c.r.Println("Press Ctrl+C to stop")
if c.openBrowser {
// There may be more than one baseURL in multihost mode, open the first.
if err := browser.OpenURL(baseURLs[0].String()); err != nil {
c.r.logger.Warnf("Failed to open browser: %s", err)
}
}
err = func() error {
for {
select {
+1 -3
View File
@@ -21,8 +21,6 @@ const (
ErrRemoteGetCSV = "error-remote-getcsv"
WarnFrontMatterParamsOverrides = "warning-frontmatter-params-overrides"
WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html"
WarnGoldmarkRawHTML = "warning-goldmark-raw-html"
)
// Field/method names with special meaning.
@@ -41,7 +39,7 @@ const (
ResourceTransformationFingerprint = "fingerprint"
)
// IsResourceTransformationPermalinkHash returns whether the given name is a resource transformation that changes the permalink based on the content.
// IsResourceTransformationLinkChange returns whether the given name is a resource transformation that changes the permalink based on the content.
func IsResourceTransformationPermalinkHash(name string) bool {
return name == ResourceTransformationFingerprint
}
+4 -32
View File
@@ -38,19 +38,6 @@ func XXHashFromReader(r io.Reader) (uint64, int64, error) {
return h.Sum64(), size, nil
}
// XxHashFromReaderHexEncoded calculates the xxHash for the given reader
// and returns the hash as a hex encoded string.
func XxHashFromReaderHexEncoded(r io.Reader) (string, error) {
h := getXxHashReadFrom()
defer putXxHashReadFrom(h)
_, err := io.Copy(h, r)
if err != nil {
return "", err
}
hash := h.Sum(nil)
return hex.EncodeToString(hash), nil
}
// XXHashFromString calculates the xxHash for the given string.
func XXHashFromString(s string) (uint64, error) {
h := xxhash.New()
@@ -83,13 +70,6 @@ func HashString(vs ...any) string {
return strconv.FormatUint(hash, 10)
}
// HashStringHex returns a hash from the given elements as a hex encoded string.
// See HashString for more information.
func HashStringHex(vs ...any) string {
hash := HashUint64(vs...)
return strconv.FormatUint(hash, 16)
}
var hashOptsPool = sync.Pool{
New: func() any {
return &hashstructure.HashOptions{
@@ -123,24 +103,16 @@ func HashUint64(vs ...any) uint64 {
o = elements
}
hash, err := Hash(o)
hashOpts := getHashOpts()
defer putHashOpts(hashOpts)
hash, err := hashstructure.Hash(o, hashOpts)
if err != nil {
panic(err)
}
return hash
}
// Hash returns a hash from vs.
func Hash(vs ...any) (uint64, error) {
hashOpts := getHashOpts()
defer putHashOpts(hashOpts)
var v any = vs
if len(vs) == 1 {
v = vs[0]
}
return hashstructure.Hash(v, hashOpts)
}
type keyer interface {
Key() string
}
-13
View File
@@ -142,16 +142,3 @@ func BenchmarkHashString(b *testing.B) {
})
}
}
func BenchmarkHashMap(b *testing.B) {
m := map[string]interface{}{}
for i := 0; i < 1000; i++ {
m[fmt.Sprintf("key%d", i)] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
HashString(m)
}
}
-15
View File
@@ -133,21 +133,6 @@ func IsNotExist(err error) bool {
return false
}
// IsExist returns true if the error is a file exists error.
// Unlike os.IsExist, this also considers wrapped errors.
func IsExist(err error) bool {
if os.IsExist(err) {
return true
}
// os.IsExist does not consider wrapped errors.
if os.IsExist(errors.Unwrap(err)) {
return true
}
return false
}
var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`)
const deferredPrefix = "__hdeferred/"
+14 -22
View File
@@ -20,6 +20,8 @@ import (
"io"
"path/filepath"
godartsassv1 "github.com/bep/godartsass"
"github.com/bep/godartsass/v2"
"github.com/bep/golibsass/libsass/libsasserrors"
"github.com/gohugoio/hugo/common/paths"
@@ -151,6 +153,8 @@ func (e *fileError) causeString() string {
// Avoid repeating the file info in the error message.
case godartsass.SassError:
return v.Message
case godartsassv1.SassError:
return v.Message
case libsasserrors.Error:
return v.Message
default:
@@ -258,27 +262,8 @@ func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
return f, realFilename, nil
}
// Cause returns the underlying error, that is,
// it unwraps errors until it finds one that does not implement
// the Unwrap method.
// For a shallow variant, see Unwrap.
// Cause returns the underlying error or itself if it does not implement Unwrap.
func Cause(err error) error {
type unwrapper interface {
Unwrap() error
}
for err != nil {
cause, ok := err.(unwrapper)
if !ok {
break
}
err = cause.Unwrap()
}
return err
}
// Unwrap returns the underlying error or itself if it does not implement Unwrap.
func Unwrap(err error) error {
if u := errors.Unwrap(err); u != nil {
return u
}
@@ -286,7 +271,7 @@ func Unwrap(err error) error {
}
func extractFileTypePos(err error) (string, text.Position) {
err = Unwrap(err)
err = Cause(err)
var fileType string
@@ -403,7 +388,14 @@ func extractPosition(e error) (pos text.Position) {
case godartsass.SassError:
span := v.Span
start := span.Start
filename, _ := paths.UrlStringToFilename(span.Url)
filename, _ := paths.UrlToFilename(span.Url)
pos.Filename = filename
pos.Offset = start.Offset
pos.ColumnNumber = start.Column
case godartsassv1.SassError:
span := v.Span
start := span.Start
filename, _ := paths.UrlToFilename(span.Url)
pos.Filename = filename
pos.Offset = start.Offset
pos.ColumnNumber = start.Column
+17 -85
View File
@@ -26,10 +26,7 @@ import (
"strings"
"sync"
"github.com/bep/logg"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/security"
)
@@ -89,7 +86,7 @@ var WithEnviron = func(env []string) func(c *commandeer) {
}
// New creates a new Exec using the provided security config.
func New(cfg security.Config, workingDir string, log loggers.Logger) *Exec {
func New(cfg security.Config, workingDir string) *Exec {
var baseEnviron []string
for _, v := range os.Environ() {
k, _ := config.SplitEnvVar(v)
@@ -99,11 +96,9 @@ func New(cfg security.Config, workingDir string, log loggers.Logger) *Exec {
}
return &Exec{
sc: cfg,
workingDir: workingDir,
infol: log.InfoCommand("exec"),
baseEnviron: baseEnviron,
newNPXRunnerCache: maps.NewCache[string, func(arg ...any) (Runner, error)](),
sc: cfg,
workingDir: workingDir,
baseEnviron: baseEnviron,
}
}
@@ -129,14 +124,12 @@ func SafeCommand(name string, arg ...string) (*exec.Cmd, error) {
type Exec struct {
sc security.Config
workingDir string
infol logg.LevelLogger
// os.Environ filtered by the Exec.OsEnviron whitelist filter.
baseEnviron []string
newNPXRunnerCache *maps.Cache[string, func(arg ...any) (Runner, error)]
npxInit sync.Once
npxAvailable bool
npxInit sync.Once
npxAvailable bool
}
func (e *Exec) New(name string, arg ...any) (Runner, error) {
@@ -162,86 +155,25 @@ func (e *Exec) new(name string, fullyQualifiedName string, arg ...any) (Runner,
return cm.command(arg...)
}
type binaryLocation int
func (b binaryLocation) String() string {
switch b {
case binaryLocationNodeModules:
return "node_modules/.bin"
case binaryLocationNpx:
return "npx"
case binaryLocationPath:
return "PATH"
}
return "unknown"
}
const (
binaryLocationNodeModules binaryLocation = iota + 1
binaryLocationNpx
binaryLocationPath
)
// Npx will in order:
// 1. Try fo find the binary in the WORKINGDIR/node_modules/.bin directory.
// 2. If not found, and npx is available, run npx --no-install <name> <args>.
// 3. Fall back to the PATH.
// If name is "tailwindcss", we will try the PATH as the second option.
func (e *Exec) Npx(name string, arg ...any) (Runner, error) {
if err := e.sc.CheckAllowedExec(name); err != nil {
return nil, err
// npx is slow, so first try the common case.
nodeBinFilename := filepath.Join(e.workingDir, nodeModulesBinPath, name)
_, err := safeexec.LookPath(nodeBinFilename)
if err == nil {
return e.new(name, nodeBinFilename, arg...)
}
newRunner, err := e.newNPXRunnerCache.GetOrCreate(name, func() (func(...any) (Runner, error), error) {
type tryFunc func() func(...any) (Runner, error)
tryFuncs := map[binaryLocation]tryFunc{
binaryLocationNodeModules: func() func(...any) (Runner, error) {
nodeBinFilename := filepath.Join(e.workingDir, nodeModulesBinPath, name)
_, err := safeexec.LookPath(nodeBinFilename)
if err != nil {
return nil
}
return func(arg2 ...any) (Runner, error) {
return e.new(name, nodeBinFilename, arg2...)
}
},
binaryLocationNpx: func() func(...any) (Runner, error) {
e.checkNpx()
if !e.npxAvailable {
return nil
}
return func(arg2 ...any) (Runner, error) {
return e.npx(name, arg2...)
}
},
binaryLocationPath: func() func(...any) (Runner, error) {
if _, err := safeexec.LookPath(name); err != nil {
return nil
}
return func(arg2 ...any) (Runner, error) {
return e.New(name, arg2...)
}
},
e.checkNpx()
if e.npxAvailable {
r, err := e.npx(name, arg...)
if err == nil {
return r, nil
}
locations := []binaryLocation{binaryLocationNodeModules, binaryLocationNpx, binaryLocationPath}
if name == "tailwindcss" {
// See https://github.com/gohugoio/hugo/issues/13221#issuecomment-2574801253
locations = []binaryLocation{binaryLocationNodeModules, binaryLocationPath, binaryLocationNpx}
}
for _, loc := range locations {
if f := tryFuncs[loc](); f != nil {
e.infol.Logf("resolve %q using %s", name, loc)
return f, nil
}
}
return nil, &NotFoundError{name: name, method: fmt.Sprintf("in %s", locations[len(locations)-1])}
})
if err != nil {
return nil, err
}
return newRunner(arg...)
return e.New(name, arg...)
}
const (
-21
View File
@@ -223,27 +223,6 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
return time.Time{}, false
}
// ToSliceAny converts the given value to a slice of any if possible.
func ToSliceAny(v any) ([]any, bool) {
if v == nil {
return nil, false
}
switch vv := v.(type) {
case []any:
return vv, true
default:
vvv := reflect.ValueOf(v)
if vvv.Kind() == reflect.Slice {
out := make([]any, vvv.Len())
for i := 0; i < vvv.Len(); i++ {
out[i] = vvv.Index(i).Interface()
}
return out, true
}
}
return nil, false
}
func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value {
fn := v.MethodByName(name)
var args []reflect.Value
-13
View File
@@ -50,19 +50,6 @@ func TestIsContextType(t *testing.T) {
c.Assert(IsContextType(reflect.TypeOf(valueCtx)), qt.IsTrue)
}
func TestToSliceAny(t *testing.T) {
c := qt.New(t)
checkOK := func(in any, expected []any) {
out, ok := ToSliceAny(in)
c.Assert(ok, qt.Equals, true)
c.Assert(out, qt.DeepEquals, expected)
}
checkOK([]any{1, 2, 3}, []any{1, 2, 3})
checkOK([]int{1, 2, 3}, []any{1, 2, 3})
}
func BenchmarkIsContextType(b *testing.B) {
type k string
b.Run("value", func(b *testing.B) {
+4 -4
View File
@@ -48,16 +48,16 @@ func TestHasBytesWriter(t *testing.T) {
for i := 0; i < 22; i++ {
h, w := neww()
fmt.Fprint(w, rndStr()+"abc __foobar"+rndStr())
fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr())
c.Assert(h.Patterns[0].Match, qt.Equals, true)
h, w = neww()
fmt.Fprint(w, rndStr()+"abc __f")
fmt.Fprint(w, "oo bar"+rndStr())
fmt.Fprintf(w, rndStr()+"abc __f")
fmt.Fprintf(w, "oo bar"+rndStr())
c.Assert(h.Patterns[0].Match, qt.Equals, true)
h, w = neww()
fmt.Fprint(w, rndStr()+"abc __moo bar")
fmt.Fprintf(w, rndStr()+"abc __moo bar")
c.Assert(h.Patterns[0].Match, qt.Equals, false)
}
+2 -2
View File
@@ -74,13 +74,13 @@ type StringReader interface {
ReadString() string
}
// NewReadSeekerNoOpCloserFromBytes uses bytes.NewReader to create a new ReadSeekerNoOpCloser
// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser
// from the given bytes slice.
func NewReadSeekerNoOpCloserFromBytes(content []byte) readSeekerNopCloser {
return readSeekerNopCloser{bytes.NewReader(content)}
}
// NewOpenReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker.
// NewReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker.
// The ReadSeeker will be seeked to the beginning before returned.
func NewOpenReadSeekCloser(r ReadSeekCloser) OpenReadSeekCloser {
return func() (ReadSeekCloser, error) {
+22 -26
View File
@@ -25,13 +25,14 @@ import (
"sync"
"time"
godartsassv1 "github.com/bep/godartsass"
"github.com/bep/logg"
"github.com/mitchellh/mapstructure"
"github.com/bep/godartsass/v2"
"github.com/gohugoio/hugo/common/hcontext"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/spf13/afero"
@@ -54,8 +55,6 @@ var (
vendorInfo string
)
var _ maps.StoreProvider = (*HugoInfo)(nil)
// HugoInfo contains information about the current Hugo environment
type HugoInfo struct {
CommitHash string
@@ -73,8 +72,6 @@ type HugoInfo struct {
conf ConfigProvider
deps []*Dependency
store *maps.Scratch
// Context gives access to some of the context scoped variables.
Context Context
}
@@ -119,10 +116,6 @@ func (i HugoInfo) Deps() []*Dependency {
return i.deps
}
func (i HugoInfo) Store() *maps.Scratch {
return i.store
}
// Deprecated: Use hugo.IsMultihost instead.
func (i HugoInfo) IsMultiHost() bool {
Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
@@ -192,7 +185,6 @@ func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo {
Environment: conf.Environment(),
conf: conf,
deps: deps,
store: maps.NewScratch(),
GoVersion: goVersion,
}
}
@@ -316,7 +308,7 @@ func GetDependencyListNonGo() []string {
if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" {
dartSassPath := "github.com/sass/dart-sass-embedded"
if IsDartSassGeV2() {
if IsDartSassV2() {
dartSassPath = "github.com/sass/dart-sass"
}
deps = append(deps,
@@ -363,15 +355,22 @@ type Dependency struct {
}
func dartSassVersion() godartsass.DartSassVersion {
if DartSassBinaryName == "" || !IsDartSassGeV2() {
if DartSassBinaryName == "" {
return godartsass.DartSassVersion{}
}
v, _ := godartsass.Version(DartSassBinaryName)
return v
if IsDartSassV2() {
v, _ := godartsass.Version(DartSassBinaryName)
return v
}
v, _ := godartsassv1.Version(DartSassBinaryName)
var vv godartsass.DartSassVersion
mapstructure.WeakDecode(v, &vv)
return vv
}
// DartSassBinaryName is the name of the Dart Sass binary to use.
// TODO(bep) find a better place for this.
// TODO(beop) find a better place for this.
var DartSassBinaryName string
func init() {
@@ -396,10 +395,7 @@ var (
dartSassBinaryNamesV2 = []string{"dart-sass", "sass"}
)
// TODO(bep) we eventually want to remove this, but keep it for a while to throw an informative error.
// We stopped supporting the old binary in Hugo 0.139.0.
func IsDartSassGeV2() bool {
// dart-sass-embedded was the first version of the embedded Dart Sass before it was moved into the main project.
func IsDartSassV2() bool {
return !strings.Contains(DartSassBinaryName, "embedded")
}
@@ -418,15 +414,15 @@ func Deprecate(item, alternative string, version string) {
func DeprecateLevel(item, alternative, version string, level logg.Level) {
var msg string
if level == logg.LevelError {
msg = fmt.Sprintf("%s was deprecated in Hugo %s and subsequently removed. %s", item, version, alternative)
msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in Hugo %s. %s", item, version, CurrentVersion.Next().ReleaseVersion(), alternative)
} else {
msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative)
}
loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf("%s", msg)
loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf(msg)
}
// We usually do about one minor version a month.
// We ususally do about one minor version a month.
// We want people to run at least the current and previous version without any warnings.
// We want people who don't update Hugo that often to see the warnings and errors before we remove the feature.
func deprecationLogLevelFromVersion(ver string) logg.Level {
@@ -434,11 +430,11 @@ func deprecationLogLevelFromVersion(ver string) logg.Level {
to := CurrentVersion
minorDiff := to.Minor - from.Minor
switch {
case minorDiff >= 15:
// Start failing the build after about 15 months.
case minorDiff >= 12:
// Start failing the build after about a year.
return logg.LevelError
case minorDiff >= 3:
// Start printing warnings after about 3 months.
case minorDiff >= 6:
// Start printing warnings after about six months.
return logg.LevelWarn
default:
return logg.LevelInfo
+4 -8
View File
@@ -57,16 +57,12 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
c.Assert(deprecationLogLevelFromVersion("0.55.0"), qt.Equals, logg.LevelError)
ver := CurrentVersion
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo)
ver.Minor -= 3
ver.Minor -= 1
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo)
ver.Minor -= 6
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn)
ver.Minor -= 4
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn)
ver.Minor -= 13
ver.Minor -= 6
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError)
// Added just to find the threshold for where we can remove deprecated items.
// Subtract 5 from the minor version of the first ERRORed version => 0.122.0.
c.Assert(deprecationLogLevelFromVersion("0.127.0"), qt.Equals, logg.LevelError)
}
func TestMarkupScope(t *testing.T) {
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !withdeploy
// +build !withdeploy
package hugo
var IsWithdeploy = false
-3
View File
@@ -152,9 +152,6 @@ func BuildVersionString() string {
if IsExtended {
version += "+extended"
}
if IsWithdeploy {
version += "+withdeploy"
}
osArch := bi.GoOS + "/" + bi.GoArch
+3 -3
View File
@@ -17,7 +17,7 @@ package hugo
// This should be the only one.
var CurrentVersion = Version{
Major: 0,
Minor: 143,
PatchLevel: 1,
Suffix: "",
Minor: 137,
PatchLevel: 0,
Suffix: "-DEV",
}
+9 -19
View File
@@ -18,19 +18,18 @@ package loggers
import (
"fmt"
"io"
"regexp"
"strings"
"sync"
"github.com/bep/logg"
)
// newNoAnsiEscapeHandler creates a new noAnsiEscapeHandler
func newNoAnsiEscapeHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noAnsiEscapeHandler {
// newNoColoursHandler creates a new NoColoursHandler
func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noColoursHandler {
if predicate == nil {
predicate = func(e *logg.Entry) bool { return true }
}
return &noAnsiEscapeHandler{
return &noColoursHandler{
noLevelPrefix: noLevelPrefix,
outWriter: outWriter,
errWriter: errWriter,
@@ -38,15 +37,15 @@ func newNoAnsiEscapeHandler(outWriter, errWriter io.Writer, noLevelPrefix bool,
}
}
type noAnsiEscapeHandler struct {
type noColoursHandler struct {
mu sync.Mutex
outWriter io.Writer
errWriter io.Writer
outWriter io.Writer // Defaults to os.Stdout.
errWriter io.Writer // Defaults to os.Stderr.
predicate func(*logg.Entry) bool
noLevelPrefix bool
}
func (h *noAnsiEscapeHandler) HandleLog(e *logg.Entry) error {
func (h *noColoursHandler) HandleLog(e *logg.Entry) error {
if !h.predicate(e) {
return nil
}
@@ -72,12 +71,10 @@ func (h *noAnsiEscapeHandler) HandleLog(e *logg.Entry) error {
prefix = prefix + ": "
}
msg := stripANSI(e.Message)
if h.noLevelPrefix {
fmt.Fprintf(w, "%s%s", prefix, msg)
fmt.Fprintf(w, "%s%s", prefix, e.Message)
} else {
fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, msg)
fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, e.Message)
}
for _, field := range e.Fields {
@@ -91,10 +88,3 @@ func (h *noAnsiEscapeHandler) HandleLog(e *logg.Entry) error {
return nil
}
var ansiRe = regexp.MustCompile(`\x1b\[[0-9;]*m`)
// stripANSI removes ANSI escape codes from s.
func stripANSI(s string) string {
return ansiRe.ReplaceAllString(s, "")
}
-40
View File
@@ -1,40 +0,0 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers
import (
"bytes"
"testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/terminal"
)
func TestNoAnsiEscapeHandler(t *testing.T) {
c := qt.New(t)
test := func(s string) {
c.Assert(stripANSI(terminal.Notice(s)), qt.Equals, s)
}
test(`error in "file.md:1:2"`)
var buf bytes.Buffer
h := newNoAnsiEscapeHandler(&buf, &buf, false, nil)
h.HandleLog(&logg.Entry{Message: terminal.Notice(`error in "file.md:1:2"`), Level: logg.LevelInfo})
c.Assert(buf.String(), qt.Equals, "INFO error in \"file.md:1:2\"\n")
}
+22 -26
View File
@@ -38,8 +38,8 @@ var (
// Options defines options for the logger.
type Options struct {
Level logg.Level
StdOut io.Writer
StdErr io.Writer
Stdout io.Writer
Stderr io.Writer
DistinctLevel logg.Level
StoreErrors bool
HandlerPost func(e *logg.Entry) error
@@ -48,22 +48,21 @@ type Options struct {
// New creates a new logger with the given options.
func New(opts Options) Logger {
if opts.StdOut == nil {
opts.StdOut = os.Stdout
if opts.Stdout == nil {
opts.Stdout = os.Stdout
}
if opts.StdErr == nil {
opts.StdErr = os.Stderr
if opts.Stderr == nil {
opts.Stderr = os.Stdout
}
if opts.Level == 0 {
opts.Level = logg.LevelWarn
}
var logHandler logg.Handler
if terminal.PrintANSIColors(os.Stderr) {
logHandler = newDefaultHandler(opts.StdErr, opts.StdErr)
if terminal.PrintANSIColors(os.Stdout) {
logHandler = newDefaultHandler(opts.Stdout, opts.Stderr)
} else {
logHandler = newNoAnsiEscapeHandler(opts.StdErr, opts.StdErr, false, nil)
logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil)
}
errorsw := &strings.Builder{}
@@ -96,7 +95,7 @@ func New(opts Options) Logger {
}
if opts.StoreErrors {
h := newNoAnsiEscapeHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
return e.Level >= logg.LevelError
})
@@ -138,8 +137,7 @@ func New(opts Options) Logger {
logCounters: logCounters,
errors: errorsw,
reset: reset,
stdOut: opts.StdOut,
stdErr: opts.StdErr,
out: opts.Stdout,
level: opts.Level,
logger: logger,
tracel: l.WithLevel(logg.LevelTrace),
@@ -155,6 +153,8 @@ func NewDefault() Logger {
opts := Options{
DistinctLevel: logg.LevelWarn,
Level: logg.LevelWarn,
Stdout: os.Stdout,
Stderr: os.Stdout,
}
return New(opts)
}
@@ -163,6 +163,8 @@ func NewTrace() Logger {
opts := Options{
DistinctLevel: logg.LevelWarn,
Level: logg.LevelTrace,
Stdout: os.Stdout,
Stderr: os.Stdout,
}
return New(opts)
}
@@ -187,8 +189,7 @@ type Logger interface {
Level() logg.Level
LoggCount(logg.Level) int
Logger() logg.Logger
StdOut() io.Writer
StdErr() io.Writer
Out() io.Writer
Printf(format string, v ...any)
Println(v ...any)
PrintTimerIfDelayed(start time.Time, name string)
@@ -206,8 +207,7 @@ type logAdapter struct {
logCounters *logLevelCounter
errors *strings.Builder
reset func()
stdOut io.Writer
stdErr io.Writer
out io.Writer
level logg.Level
logger logg.Logger
tracel logg.LevelLogger
@@ -259,12 +259,8 @@ func (l *logAdapter) Logger() logg.Logger {
return l.logger
}
func (l *logAdapter) StdOut() io.Writer {
return l.stdOut
}
func (l *logAdapter) StdErr() io.Writer {
return l.stdErr
func (l *logAdapter) Out() io.Writer {
return l.out
}
// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
@@ -275,7 +271,7 @@ func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
if milli < 500 {
return
}
fmt.Fprintf(l.stdErr, "%s in %v ms", name, milli)
l.Printf("%s in %v ms", name, milli)
}
func (l *logAdapter) Printf(format string, v ...any) {
@@ -283,11 +279,11 @@ func (l *logAdapter) Printf(format string, v ...any) {
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(l.stdOut, format, v...)
fmt.Fprintf(l.out, format, v...)
}
func (l *logAdapter) Println(v ...any) {
fmt.Fprintln(l.stdOut, v...)
fmt.Fprintln(l.out, v...)
}
func (l *logAdapter) Reset() {
+8 -8
View File
@@ -31,8 +31,8 @@ func TestLogDistinct(t *testing.T) {
opts := loggers.Options{
DistinctLevel: logg.LevelWarn,
StoreErrors: true,
StdOut: io.Discard,
StdErr: io.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
@@ -54,8 +54,8 @@ func TestHookLast(t *testing.T) {
HandlerPost: func(e *logg.Entry) error {
panic(e.Message)
},
StdOut: io.Discard,
StdErr: io.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
@@ -70,8 +70,8 @@ func TestOptionStoreErrors(t *testing.T) {
opts := loggers.Options{
StoreErrors: true,
StdErr: &sb,
StdOut: &sb,
Stderr: &sb,
Stdout: &sb,
}
l := loggers.New(opts)
@@ -131,8 +131,8 @@ func TestReset(t *testing.T) {
opts := loggers.Options{
StoreErrors: true,
DistinctLevel: logg.LevelWarn,
StdOut: io.Discard,
StdErr: io.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
+6 -55
View File
@@ -13,14 +13,11 @@
package maps
import (
"sync"
)
import "sync"
// Cache is a simple thread safe cache backed by a map.
type Cache[K comparable, T any] struct {
m map[K]T
hasBeenInitialized bool
m map[K]T
sync.RWMutex
}
@@ -37,13 +34,8 @@ func (c *Cache[K, T]) Get(key K) (T, bool) {
return zero, false
}
c.RLock()
v, found := c.get(key)
c.RUnlock()
return v, found
}
func (c *Cache[K, T]) get(key K) (T, bool) {
v, found := c.m[key]
c.RUnlock()
return v, found
}
@@ -69,58 +61,19 @@ func (c *Cache[K, T]) GetOrCreate(key K, create func() (T, error)) (T, error) {
return v, nil
}
// InitAndGet initializes the cache if not already done and returns the value for the given key.
// The init state will be reset on Reset or Drain.
func (c *Cache[K, T]) InitAndGet(key K, init func(get func(key K) (T, bool), set func(key K, value T)) error) (T, error) {
var v T
c.RLock()
if !c.hasBeenInitialized {
c.RUnlock()
if err := func() error {
c.Lock()
defer c.Unlock()
// Double check in case another goroutine has initialized it in the meantime.
if !c.hasBeenInitialized {
err := init(c.get, c.set)
if err != nil {
return err
}
c.hasBeenInitialized = true
}
return nil
}(); err != nil {
return v, err
}
// Reacquire the read lock.
c.RLock()
}
v = c.m[key]
c.RUnlock()
return v, nil
}
// Set sets the given key to the given value.
func (c *Cache[K, T]) Set(key K, value T) {
c.Lock()
c.set(key, value)
c.m[key] = value
c.Unlock()
}
func (c *Cache[K, T]) set(key K, value T) {
c.m[key] = value
}
// ForEeach calls the given function for each key/value pair in the cache.
// If the function returns false, the iteration stops.
func (c *Cache[K, T]) ForEeach(f func(K, T) bool) {
func (c *Cache[K, T]) ForEeach(f func(K, T)) {
c.RLock()
defer c.RUnlock()
for k, v := range c.m {
if !f(k, v) {
return
}
f(k, v)
}
}
@@ -128,7 +81,6 @@ func (c *Cache[K, T]) Drain() map[K]T {
c.Lock()
m := c.m
c.m = make(map[K]T)
c.hasBeenInitialized = false
c.Unlock()
return m
}
@@ -142,7 +94,6 @@ func (c *Cache[K, T]) Len() int {
func (c *Cache[K, T]) Reset() {
c.Lock()
c.m = make(map[K]T)
c.hasBeenInitialized = false
c.Unlock()
}
-4
View File
@@ -73,14 +73,10 @@ func TestPrepareParams(t *testing.T) {
for i, test := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
// PrepareParams modifies input.
prepareClone := PrepareParamsClone(test.input)
PrepareParams(test.input)
if !reflect.DeepEqual(test.expected, test.input) {
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
}
if !reflect.DeepEqual(test.expected, prepareClone) {
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, prepareClone)
}
})
}
}
-133
View File
@@ -1,133 +0,0 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package maps
import (
"github.com/gohugoio/hugo/common/hashing"
)
// Ordered is a map that can be iterated in the order of insertion.
// Note that insertion order is not affected if a key is re-inserted into the map.
// In a nil map, all operations are no-ops.
// This is not thread safe.
type Ordered[K comparable, T any] struct {
// The keys in the order they were added.
keys []K
// The values.
values map[K]T
}
// NewOrdered creates a new Ordered map.
func NewOrdered[K comparable, T any]() *Ordered[K, T] {
return &Ordered[K, T]{values: make(map[K]T)}
}
// Set sets the value for the given key.
// Note that insertion order is not affected if a key is re-inserted into the map.
func (m *Ordered[K, T]) Set(key K, value T) {
if m == nil {
return
}
// Check if key already exists.
if _, found := m.values[key]; !found {
m.keys = append(m.keys, key)
}
m.values[key] = value
}
// Get gets the value for the given key.
func (m *Ordered[K, T]) Get(key K) (T, bool) {
if m == nil {
var v T
return v, false
}
value, found := m.values[key]
return value, found
}
// Delete deletes the value for the given key.
func (m *Ordered[K, T]) Delete(key K) {
if m == nil {
return
}
delete(m.values, key)
for i, k := range m.keys {
if k == key {
m.keys = append(m.keys[:i], m.keys[i+1:]...)
break
}
}
}
// Clone creates a shallow copy of the map.
func (m *Ordered[K, T]) Clone() *Ordered[K, T] {
if m == nil {
return nil
}
clone := NewOrdered[K, T]()
for _, k := range m.keys {
clone.Set(k, m.values[k])
}
return clone
}
// Keys returns the keys in the order they were added.
func (m *Ordered[K, T]) Keys() []K {
if m == nil {
return nil
}
return m.keys
}
// Values returns the values in the order they were added.
func (m *Ordered[K, T]) Values() []T {
if m == nil {
return nil
}
var values []T
for _, k := range m.keys {
values = append(values, m.values[k])
}
return values
}
// Len returns the number of items in the map.
func (m *Ordered[K, T]) Len() int {
if m == nil {
return 0
}
return len(m.keys)
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
// TODO(bep) replace with iter.Seq2 when we bump go Go 1.24.
func (m *Ordered[K, T]) Range(f func(key K, value T) bool) {
if m == nil {
return
}
for _, k := range m.keys {
if !f(k, m.values[k]) {
return
}
}
}
// Hash calculates a hash from the values.
func (m *Ordered[K, T]) Hash() (uint64, error) {
if m == nil {
return 0, nil
}
return hashing.Hash(m.values)
}
-99
View File
@@ -1,99 +0,0 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package maps
import (
"testing"
qt "github.com/frankban/quicktest"
)
func TestOrdered(t *testing.T) {
c := qt.New(t)
m := NewOrdered[string, int]()
m.Set("a", 1)
m.Set("b", 2)
m.Set("c", 3)
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"})
c.Assert(m.Values(), qt.DeepEquals, []int{1, 2, 3})
v, found := m.Get("b")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 2)
m.Set("b", 22)
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"})
c.Assert(m.Values(), qt.DeepEquals, []int{1, 22, 3})
m.Delete("b")
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "c"})
c.Assert(m.Values(), qt.DeepEquals, []int{1, 3})
}
func TestOrderedHash(t *testing.T) {
c := qt.New(t)
m := NewOrdered[string, int]()
m.Set("a", 1)
m.Set("b", 2)
m.Set("c", 3)
h1, err := m.Hash()
c.Assert(err, qt.IsNil)
m.Set("d", 4)
h2, err := m.Hash()
c.Assert(err, qt.IsNil)
c.Assert(h1, qt.Not(qt.Equals), h2)
m = NewOrdered[string, int]()
m.Set("b", 2)
m.Set("a", 1)
m.Set("c", 3)
h3, err := m.Hash()
c.Assert(err, qt.IsNil)
// Order does not matter.
c.Assert(h1, qt.Equals, h3)
}
func TestOrderedNil(t *testing.T) {
c := qt.New(t)
var m *Ordered[string, int]
m.Set("a", 1)
c.Assert(m.Keys(), qt.IsNil)
c.Assert(m.Values(), qt.IsNil)
v, found := m.Get("a")
c.Assert(found, qt.Equals, false)
c.Assert(v, qt.Equals, 0)
m.Delete("a")
var b bool
m.Range(func(k string, v int) bool {
b = true
return true
})
c.Assert(b, qt.Equals, false)
c.Assert(m.Len(), qt.Equals, 0)
c.Assert(m.Clone(), qt.IsNil)
h, err := m.Hash()
c.Assert(err, qt.IsNil)
c.Assert(h, qt.Equals, uint64(0))
}
+1 -40
View File
@@ -303,7 +303,7 @@ func toMergeStrategy(v any) ParamsMergeStrategy {
}
// PrepareParams
// * makes all the keys in the given map lower cased and will do so recursively.
// * makes all the keys in the given map lower cased and will do so
// * This will modify the map given.
// * Any nested map[interface{}]interface{}, map[string]interface{},map[string]string will be converted to Params.
// * Any _merge value will be converted to proper type and value.
@@ -343,42 +343,3 @@ func PrepareParams(m Params) {
}
}
}
// PrepareParamsClone is like PrepareParams, but it does not modify the input.
func PrepareParamsClone(m Params) Params {
m2 := make(Params)
for k, v := range m {
var retyped bool
lKey := strings.ToLower(k)
if lKey == MergeStrategyKey {
v = toMergeStrategy(v)
retyped = true
} else {
switch vv := v.(type) {
case map[any]any:
var p Params = cast.ToStringMap(v)
v = PrepareParamsClone(p)
retyped = true
case map[string]any:
var p Params = v.(map[string]any)
v = PrepareParamsClone(p)
retyped = true
case map[string]string:
p := make(Params)
for k, v := range vv {
p[k] = v
}
v = p
PrepareParams(p)
retyped = true
}
}
if retyped || k != lKey {
m2[lKey] = v
} else {
m2[k] = v
}
}
return m2
}
+20 -7
View File
@@ -22,18 +22,31 @@ import (
"github.com/gohugoio/hugo/common/math"
)
type StoreProvider interface {
// Store returns a Scratch that can be used to store temporary state.
// Store is not reset on server rebuilds.
Store() *Scratch
}
// Scratch is a writable context used for stateful build operations
// Scratch is a writable context used for stateful operations in Page/Node rendering.
type Scratch struct {
values map[string]any
mu sync.RWMutex
}
// Scratcher provides a scratching service.
type Scratcher interface {
// Scratch returns a "scratch pad" that can be used to store state.
Scratch() *Scratch
}
type scratcher struct {
s *Scratch
}
func (s scratcher) Scratch() *Scratch {
return s.s
}
// NewScratcher creates a new Scratcher.
func NewScratcher() Scratcher {
return scratcher{s: NewScratch()}
}
// Add will, for single values, add (using the + operator) the addend to the existing addend (if found).
// Supports numeric values and strings.
//
+2 -2
View File
@@ -185,7 +185,7 @@ func TestScratchSetInMap(t *testing.T) {
scratch.SetInMap("key", "zyx", "Zyx")
scratch.SetInMap("key", "abc", "Abc (updated)")
scratch.SetInMap("key", "def", "Def")
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, any([]any{"Abc (updated)", "Def", "Lux", "Zyx"}))
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"})
}
func TestScratchDeleteInMap(t *testing.T) {
@@ -199,7 +199,7 @@ func TestScratchDeleteInMap(t *testing.T) {
scratch.DeleteInMap("key", "abc")
scratch.SetInMap("key", "def", "Def")
scratch.DeleteInMap("key", "lmn") // Do nothing
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, any([]any{"Def", "Lux", "Zyx"}))
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Def", 1: "Lux", 2: "Zyx"})
}
func TestScratchGetSortedMapValues(t *testing.T) {
+24 -22
View File
@@ -26,32 +26,29 @@ func DoArithmetic(a, b any, op rune) (any, error) {
var ai, bi int64
var af, bf float64
var au, bu uint64
var isInt, isFloat, isUint bool
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
ai = av.Int()
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
isInt = true
bi = bv.Int()
case reflect.Float32, reflect.Float64:
isFloat = true
af = float64(ai) // may overflow
ai = 0
bf = bv.Float()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
bu = bv.Uint()
if ai >= 0 {
isUint = true
au = uint64(ai)
ai = 0
} else {
isInt = true
bi = int64(bu) // may overflow
bu = 0
}
default:
return nil, errors.New("can't apply the operator to the values")
}
case reflect.Float32, reflect.Float64:
isFloat = true
af = av.Float()
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -69,18 +66,17 @@ func DoArithmetic(a, b any, op rune) (any, error) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
bi = bv.Int()
if bi >= 0 {
isUint = true
bu = uint64(bi)
bi = 0
} else {
isInt = true
ai = int64(au) // may overflow
au = 0
}
case reflect.Float32, reflect.Float64:
isFloat = true
af = float64(au) // may overflow
au = 0
bf = bv.Float()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
isUint = true
bu = bv.Uint()
default:
return nil, errors.New("can't apply the operator to the values")
@@ -98,32 +94,38 @@ func DoArithmetic(a, b any, op rune) (any, error) {
switch op {
case '+':
if isInt {
if ai != 0 || bi != 0 {
return ai + bi, nil
} else if isFloat {
} else if af != 0 || bf != 0 {
return af + bf, nil
} else if au != 0 || bu != 0 {
return au + bu, nil
}
return au + bu, nil
return 0, nil
case '-':
if isInt {
if ai != 0 || bi != 0 {
return ai - bi, nil
} else if isFloat {
} else if af != 0 || bf != 0 {
return af - bf, nil
} else if au != 0 || bu != 0 {
return au - bu, nil
}
return au - bu, nil
return 0, nil
case '*':
if isInt {
if ai != 0 || bi != 0 {
return ai * bi, nil
} else if isFloat {
} else if af != 0 || bf != 0 {
return af * bf, nil
} else if au != 0 || bu != 0 {
return au * bu, nil
}
return au * bu, nil
return 0, nil
case '/':
if isInt && bi != 0 {
if bi != 0 {
return ai / bi, nil
} else if isFloat && bf != 0 {
} else if bf != 0 {
return af / bf, nil
} else if isUint && bu != 0 {
} else if bu != 0 {
return au / bu, nil
}
return nil, errors.New("can't divide the value by 0")
+3 -8
View File
@@ -30,12 +30,10 @@ func TestDoArithmetic(t *testing.T) {
expect any
}{
{3, 2, '+', int64(5)},
{0, 0, '+', int64(0)},
{3, 2, '-', int64(1)},
{3, 2, '*', int64(6)},
{3, 2, '/', int64(1)},
{3.0, 2, '+', float64(5)},
{0.0, 0, '+', float64(0.0)},
{3.0, 2, '-', float64(1)},
{3.0, 2, '*', float64(6)},
{3.0, 2, '/', float64(1.5)},
@@ -44,22 +42,18 @@ func TestDoArithmetic(t *testing.T) {
{3, 2.0, '*', float64(6)},
{3, 2.0, '/', float64(1.5)},
{3.0, 2.0, '+', float64(5)},
{0.0, 0.0, '+', float64(0.0)},
{3.0, 2.0, '-', float64(1)},
{3.0, 2.0, '*', float64(6)},
{3.0, 2.0, '/', float64(1.5)},
{uint(3), uint(2), '+', uint64(5)},
{uint(0), uint(0), '+', uint64(0)},
{uint(3), uint(2), '-', uint64(1)},
{uint(3), uint(2), '*', uint64(6)},
{uint(3), uint(2), '/', uint64(1)},
{uint(3), 2, '+', uint64(5)},
{uint(0), 0, '+', uint64(0)},
{uint(3), 2, '-', uint64(1)},
{uint(3), 2, '*', uint64(6)},
{uint(3), 2, '/', uint64(1)},
{3, uint(2), '+', uint64(5)},
{0, uint(0), '+', uint64(0)},
{3, uint(2), '-', uint64(1)},
{3, uint(2), '*', uint64(6)},
{3, uint(2), '/', uint64(1)},
@@ -72,15 +66,16 @@ func TestDoArithmetic(t *testing.T) {
{-3, uint(2), '*', int64(-6)},
{-3, uint(2), '/', int64(-1)},
{uint(3), 2.0, '+', float64(5)},
{uint(0), 0.0, '+', float64(0)},
{uint(3), 2.0, '-', float64(1)},
{uint(3), 2.0, '*', float64(6)},
{uint(3), 2.0, '/', float64(1.5)},
{3.0, uint(2), '+', float64(5)},
{0.0, uint(0), '+', float64(0)},
{3.0, uint(2), '-', float64(1)},
{3.0, uint(2), '*', float64(6)},
{3.0, uint(2), '/', float64(1.5)},
{0, 0, '+', 0},
{0, 0, '-', 0},
{0, 0, '*', 0},
{"foo", "bar", '+', "foobar"},
{3, 0, '/', false},
{3.0, 0, '/', false},
+18 -96
View File
@@ -1,4 +1,4 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
// Copyright 2021 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ import (
"net/url"
"path"
"path/filepath"
"runtime"
"strings"
)
@@ -160,77 +159,9 @@ func Uglify(in string) string {
return path.Clean(in)
}
// URLEscape escapes unicode letters.
func URLEscape(uri string) string {
// escape unicode letters
u, err := url.Parse(uri)
if err != nil {
panic(err)
}
return u.String()
}
// TrimExt trims the extension from a path..
func TrimExt(in string) string {
return strings.TrimSuffix(in, path.Ext(in))
}
// From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45
func UrlFromFilename(filename string) (*url.URL, error) {
if !filepath.IsAbs(filename) {
return nil, fmt.Errorf("filepath must be absolute")
}
// If filename has a Windows volume name, convert the volume to a host and prefix
// per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.
if vol := filepath.VolumeName(filename); vol != "" {
if strings.HasPrefix(vol, `\\`) {
filename = filepath.ToSlash(filename[2:])
i := strings.IndexByte(filename, '/')
if i < 0 {
// A degenerate case.
// \\host.example.com (without a share name)
// becomes
// file://host.example.com/
return &url.URL{
Scheme: "file",
Host: filename,
Path: "/",
}, nil
}
// \\host.example.com\Share\path\to\file
// becomes
// file://host.example.com/Share/path/to/file
return &url.URL{
Scheme: "file",
Host: filename[:i],
Path: filepath.ToSlash(filename[i:]),
}, nil
}
// C:\path\to\file
// becomes
// file:///C:/path/to/file
return &url.URL{
Scheme: "file",
Path: "/" + filepath.ToSlash(filename),
}, nil
}
// /path/to/file
// becomes
// file:///path/to/file
return &url.URL{
Scheme: "file",
Path: filepath.ToSlash(filename),
}, nil
}
// UrlStringToFilename converts the URL s to a filename.
// UrlToFilename converts the URL s to a filename.
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
func UrlStringToFilename(s string) (string, bool) {
func UrlToFilename(s string) (string, bool) {
u, err := url.ParseRequestURI(s)
if err != nil {
return filepath.FromSlash(s), false
@@ -240,34 +171,25 @@ func UrlStringToFilename(s string) (string, bool) {
if p == "" {
p, _ = url.QueryUnescape(u.Opaque)
return filepath.FromSlash(p), false
}
if runtime.GOOS != "windows" {
return p, true
}
if len(p) == 0 || p[0] != '/' {
return filepath.FromSlash(p), false
return filepath.FromSlash(p), true
}
p = filepath.FromSlash(p)
if len(u.Host) == 1 {
// file://c/Users/...
return strings.ToUpper(u.Host) + ":" + p, true
if u.Host != "" {
// C:\data\file.txt
p = strings.ToUpper(u.Host) + ":" + p
}
if u.Host != "" && u.Host != "localhost" {
if filepath.VolumeName(u.Host) != "" {
return "", false
}
return `\\` + u.Host + p, true
}
if vol := filepath.VolumeName(p[1:]); vol == "" || strings.HasPrefix(vol, `\\`) {
return "", false
}
return p[1:], true
return p, true
}
// URLEscape escapes unicode letters.
func URLEscape(uri string) string {
// escape unicode letters
u, err := url.Parse(uri)
if err != nil {
panic(err)
}
return u.String()
}
-7
View File
@@ -19,13 +19,6 @@ type Closer interface {
Close() error
}
// CloserFunc is a convenience type to create a Closer from a function.
type CloserFunc func() error
func (f CloserFunc) Close() error {
return f()
}
type CloseAdder interface {
Add(Closer)
}
+17 -23
View File
@@ -18,24 +18,24 @@ import (
"sync"
)
// EvictingQueue is a queue which automatically evicts elements from the head of
// EvictingStringQueue is a queue which automatically evicts elements from the head of
// the queue when attempting to add new elements onto the queue and it is full.
// This queue orders elements LIFO (last-in-first-out). It throws away duplicates.
type EvictingQueue[T comparable] struct {
// Note: This queue currently does not contain any remove (poll etc.) methods.
type EvictingStringQueue struct {
size int
vals []T
set map[T]bool
vals []string
set map[string]bool
mu sync.Mutex
zero T
}
// NewEvictingQueue creates a new queue with the given size.
func NewEvictingQueue[T comparable](size int) *EvictingQueue[T] {
return &EvictingQueue[T]{size: size, set: make(map[T]bool)}
// NewEvictingStringQueue creates a new queue with the given size.
func NewEvictingStringQueue(size int) *EvictingStringQueue {
return &EvictingStringQueue{size: size, set: make(map[string]bool)}
}
// Add adds a new string to the tail of the queue if it's not already there.
func (q *EvictingQueue[T]) Add(v T) *EvictingQueue[T] {
func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue {
q.mu.Lock()
if q.set[v] {
q.mu.Unlock()
@@ -54,7 +54,7 @@ func (q *EvictingQueue[T]) Add(v T) *EvictingQueue[T] {
return q
}
func (q *EvictingQueue[T]) Len() int {
func (q *EvictingStringQueue) Len() int {
if q == nil {
return 0
}
@@ -64,22 +64,19 @@ func (q *EvictingQueue[T]) Len() int {
}
// Contains returns whether the queue contains v.
func (q *EvictingQueue[T]) Contains(v T) bool {
if q == nil {
return false
}
func (q *EvictingStringQueue) Contains(v string) bool {
q.mu.Lock()
defer q.mu.Unlock()
return q.set[v]
}
// Peek looks at the last element added to the queue.
func (q *EvictingQueue[T]) Peek() T {
func (q *EvictingStringQueue) Peek() string {
q.mu.Lock()
l := len(q.vals)
if l == 0 {
q.mu.Unlock()
return q.zero
return ""
}
elem := q.vals[l-1]
q.mu.Unlock()
@@ -87,12 +84,9 @@ func (q *EvictingQueue[T]) Peek() T {
}
// PeekAll looks at all the elements in the queue, with the newest first.
func (q *EvictingQueue[T]) PeekAll() []T {
if q == nil {
return nil
}
func (q *EvictingStringQueue) PeekAll() []string {
q.mu.Lock()
vals := make([]T, len(q.vals))
vals := make([]string, len(q.vals))
copy(vals, q.vals)
q.mu.Unlock()
for i, j := 0, len(vals)-1; i < j; i, j = i+1, j-1 {
@@ -102,9 +96,9 @@ func (q *EvictingQueue[T]) PeekAll() []T {
}
// PeekAllSet returns PeekAll as a set.
func (q *EvictingQueue[T]) PeekAllSet() map[T]bool {
func (q *EvictingStringQueue) PeekAllSet() map[string]bool {
all := q.PeekAll()
set := make(map[T]bool)
set := make(map[string]bool)
for _, v := range all {
set[v] = true
}
+2 -2
View File
@@ -23,7 +23,7 @@ import (
func TestEvictingStringQueue(t *testing.T) {
c := qt.New(t)
queue := NewEvictingQueue[string](3)
queue := NewEvictingStringQueue(3)
c.Assert(queue.Peek(), qt.Equals, "")
queue.Add("a")
@@ -53,7 +53,7 @@ func TestEvictingStringQueueConcurrent(t *testing.T) {
var wg sync.WaitGroup
val := "someval"
queue := NewEvictingQueue[string](3)
queue := NewEvictingStringQueue(3)
for j := 0; j < 100; j++ {
wg.Add(1)
+19
View File
@@ -133,3 +133,22 @@ func NewBool(b bool) *bool {
type PrintableValueProvider interface {
PrintableValue() any
}
var _ PrintableValueProvider = Result[any]{}
// Result is a generic result type.
type Result[T any] struct {
// The result value.
Value T
// The error value.
Err error
}
// PrintableValue returns the value or panics if there is an error.
func (r Result[T]) PrintableValue() any {
if r.Err != nil {
panic(r.Err)
}
return r.Value
}
+53 -83
View File
@@ -143,7 +143,7 @@ type Config struct {
// The cascade configuration section contains the top level front matter cascade configuration options,
// a slice of page matcher and params to apply to those pages.
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"`
// The segments defines segments for the site. Used for partial/segmented builds.
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
@@ -301,18 +301,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
}
}
defaultOutputFormat := outputFormats[0]
c.DefaultOutputFormat = strings.ToLower(c.DefaultOutputFormat)
if c.DefaultOutputFormat != "" {
f, found := outputFormats.GetByName(c.DefaultOutputFormat)
if !found {
return fmt.Errorf("unknown default output format %q", c.DefaultOutputFormat)
}
defaultOutputFormat = f
} else {
c.DefaultOutputFormat = defaultOutputFormat.Name
}
disabledLangs := make(map[string]bool)
for _, lang := range c.DisableLanguages {
disabledLangs[lang] = true
@@ -402,46 +390,23 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
c.Pagination.Path = c.PaginatePath
}
// Legacy privacy values.
if c.Privacy.Twitter.Disable {
hugo.Deprecate("site config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0")
c.Privacy.X.Disable = c.Privacy.Twitter.Disable
}
if c.Privacy.Twitter.EnableDNT {
hugo.Deprecate("site config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0")
c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT
}
if c.Privacy.Twitter.Simple {
hugo.Deprecate("site config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0")
c.Privacy.X.Simple = c.Privacy.Twitter.Simple
}
// Legacy services values.
if c.Services.Twitter.DisableInlineCSS {
hugo.Deprecate("site config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0")
c.Services.X.DisableInlineCSS = c.Services.Twitter.DisableInlineCSS
}
c.C = &ConfigCompiled{
Timeout: timeout,
BaseURL: baseURL,
BaseURLLiveReload: baseURL,
DisabledKinds: disabledKinds,
DisabledLanguages: disabledLangs,
IgnoredLogs: ignoredLogIDs,
KindOutputFormats: kindOutputFormats,
DefaultOutputFormat: defaultOutputFormat,
ContentTypes: media.DefaultContentTypes.FromTypes(c.MediaTypes.Config),
CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle),
IsUglyURLSection: isUglyURL,
IgnoreFile: ignoreFile,
SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...),
MainSections: c.MainSections,
Clock: clock,
HTTPCache: httpCache,
transientErr: transientErr,
Timeout: timeout,
BaseURL: baseURL,
BaseURLLiveReload: baseURL,
DisabledKinds: disabledKinds,
DisabledLanguages: disabledLangs,
IgnoredLogs: ignoredLogIDs,
KindOutputFormats: kindOutputFormats,
ContentTypes: media.DefaultContentTypes.FromTypes(c.MediaTypes.Config),
CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle),
IsUglyURLSection: isUglyURL,
IgnoreFile: ignoreFile,
SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...),
MainSections: c.MainSections,
Clock: clock,
HTTPCache: httpCache,
transientErr: transientErr,
}
for _, s := range allDecoderSetups {
@@ -465,23 +430,22 @@ func (c *Config) IsLangDisabled(lang string) bool {
// ConfigCompiled holds values and functions that are derived from the config.
type ConfigCompiled struct {
Timeout time.Duration
BaseURL urls.BaseURL
BaseURLLiveReload urls.BaseURL
ServerInterface string
KindOutputFormats map[string]output.Formats
DefaultOutputFormat output.Format
ContentTypes media.ContentTypes
DisabledKinds map[string]bool
DisabledLanguages map[string]bool
IgnoredLogs map[string]bool
CreateTitle func(s string) string
IsUglyURLSection func(section string) bool
IgnoreFile func(filename string) bool
SegmentFilter segments.SegmentFilter
MainSections []string
Clock time.Time
HTTPCache httpcache.ConfigCompiled
Timeout time.Duration
BaseURL urls.BaseURL
BaseURLLiveReload urls.BaseURL
ServerInterface string
KindOutputFormats map[string]output.Formats
ContentTypes media.ContentTypes
DisabledKinds map[string]bool
DisabledLanguages map[string]bool
IgnoredLogs map[string]bool
CreateTitle func(s string) string
IsUglyURLSection func(section string) bool
IgnoreFile func(filename string) bool
SegmentFilter segments.SegmentFilter
MainSections []string
Clock time.Time
HTTPCache httpcache.ConfigCompiled
// This is set to the last transient error found during config compilation.
// With themes/modules we compute the configuration in multiple passes, and
@@ -541,13 +505,6 @@ type RootConfig struct {
// Set this to true to put all languages below their language ID.
DefaultContentLanguageInSubdir bool
// The default output format to use for the site.
// If not set, we will use the first output format.
DefaultOutputFormat string
// Disable generation of redirect to the default language when DefaultContentLanguageInSubdir is enabled.
DisableDefaultLanguageRedirect bool
// Disable creation of alias redirect pages.
DisableAliases bool
@@ -766,16 +723,15 @@ type Configs struct {
}
func (c *Configs) Validate(logger loggers.Logger) error {
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
for p := range c.Base.Cascade.Config {
page.CheckCascadePattern(logger, p)
return true
})
}
return nil
}
// transientErr returns the last transient error found during config compilation.
func (c *Configs) transientErr() error {
for _, l := range c.LanguageConfigMap {
for _, l := range c.LanguageConfigSlice {
if l.C.transientErr != nil {
return l.C.transientErr
}
@@ -792,6 +748,7 @@ func (c *Configs) Init() error {
var languages langs.Languages
defaultContentLanguage := c.Base.DefaultContentLanguage
for k, v := range c.LanguageConfigMap {
c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
languageConf := v.Languages[k]
language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
if err != nil {
@@ -931,17 +888,30 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
var differentRootKeys []string
switch x := v.(type) {
case maps.Params:
_, found := x["params"]
if !found {
x["params"] = maps.Params{
var params maps.Params
pv, found := x["params"]
if found {
params = pv.(maps.Params)
} else {
params = maps.Params{
maps.MergeStrategyKey: maps.ParamsMergeStrategyDeep,
}
x["params"] = params
}
for kk, vv := range x {
if kk == "_merge" {
continue
}
if kk != maps.MergeStrategyKey && !configLanguageKeys[kk] {
// This should have been placed below params.
// We accidentally allowed it in the past, so we need to support it a little longer,
// But log a warning.
if _, found := params[kk]; !found {
hugo.Deprecate(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), "v0.112.0")
params[kk] = vv
}
}
if kk == "baseurl" {
// baseURL configure don the language level is a multihost setup.
isMultihost = true
@@ -175,43 +175,3 @@ func TestMapUglyURLs(t *testing.T) {
b.Assert(c.C.IsUglyURLSection("posts"), qt.IsTrue)
b.Assert(c.C.IsUglyURLSection("blog"), qt.IsFalse)
}
// Issue 13199
func TestInvalidOutputFormat(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
[outputs]
home = ['html','foo']
-- layouts/index.html --
x
`
b, err := hugolib.TestE(t, files)
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, `failed to create config: unknown output format "foo" for kind "home"`)
}
// Issue 13201
func TestLanguageConfigSlice(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
[languages.en]
title = 'TITLE_EN'
weight = 2
[languages.de]
title = 'TITLE_DE'
weight = 1
[languages.fr]
title = 'TITLE_FR'
weight = 3
`
b := hugolib.Test(t, files)
b.Assert(b.H.Configs.LanguageConfigSlice[0].Title, qt.Equals, `TITLE_DE`)
}
+2 -2
View File
@@ -137,11 +137,11 @@ func (c ConfigLanguage) Watching() bool {
return c.m.Base.Internal.Watch
}
func (c ConfigLanguage) NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager {
func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager {
if !c.Watching() {
return identity.NopManager
}
return identity.NewManager(name, opts...)
return identity.NewManager(name)
}
func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider {
+1 -1
View File
@@ -470,7 +470,7 @@ func (l *configLoader) loadModules(configs *Configs, ignoreModuleDoesNotExist bo
ignoreVendor, _ = hglob.GetGlob(hglob.NormalizePath(s))
}
ex := hexec.New(conf.Security, workingDir, l.Logger)
ex := hexec.New(conf.Security, workingDir)
hook := func(m *modules.ModulesConfig) error {
for _, tc := range m.AllModules {
+1 -1
View File
@@ -58,7 +58,7 @@ type AllProvider interface {
BuildDrafts() bool
Running() bool
Watching() bool
NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager
NewIdentityManager(name string) identity.Manager
FastRenderMode() bool
PrintUnusedTemplates() bool
EnableMissingTranslationPlaceholders() bool
+1 -1
View File
@@ -22,7 +22,7 @@ import (
func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) {
// Calculate the hash of the input (not including any defaults applied later).
// This allows us to introduce new config options without breaking the hash.
h := hashing.HashStringHex(configSource)
h := hashing.HashString(configSource)
// Build the config
c, ext, err := buildConfig(configSource)
+1 -1
View File
@@ -43,7 +43,7 @@ func TestNamespace(t *testing.T) {
c.Assert(err, qt.IsNil)
c.Assert(ns, qt.Not(qt.IsNil))
c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"})
c.Assert(ns.SourceHash, qt.Equals, "1420f6c7782f7459")
c.Assert(ns.SourceHash, qt.Equals, "1450430416588600409")
c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"})
c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil))
}
+4 -20
View File
@@ -30,10 +30,9 @@ type Config struct {
Disqus Disqus
GoogleAnalytics GoogleAnalytics
Instagram Instagram
Twitter Twitter // deprecated in favor of X in v0.141.0
Twitter Twitter
Vimeo Vimeo
YouTube YouTube
X X
}
// Disqus holds the privacy configuration settings related to the Disqus template.
@@ -59,8 +58,7 @@ type Instagram struct {
Simple bool
}
// Twitter holds the privacy configuration settings related to the Twitter shortcode.
// Deprecated in favor of X in v0.141.0.
// Twitter holds the privacy configuration settingsrelated to the Twitter shortcode.
type Twitter struct {
Service `mapstructure:",squash"`
@@ -72,7 +70,7 @@ type Twitter struct {
Simple bool
}
// Vimeo holds the privacy configuration settings related to the Vimeo shortcode.
// Vimeo holds the privacy configuration settingsrelated to the Vimeo shortcode.
type Vimeo struct {
Service `mapstructure:",squash"`
@@ -86,7 +84,7 @@ type Vimeo struct {
Simple bool
}
// YouTube holds the privacy configuration settings related to the YouTube shortcode.
// YouTube holds the privacy configuration settingsrelated to the YouTube shortcode.
type YouTube struct {
Service `mapstructure:",squash"`
@@ -96,20 +94,6 @@ type YouTube struct {
PrivacyEnhanced bool
}
// X holds the privacy configuration settings related to the X shortcode.
type X struct {
Service `mapstructure:",squash"`
// When set to true, the X post and its embedded page on your site are not
// used for purposes that include personalized suggestions and personalized
// ads.
EnableDNT bool
// If simple mode is enabled, a static and no-JS version of the X post will
// be built.
Simple bool
}
// DecodeConfig creates a privacy Config from a given Hugo configuration.
func DecodeConfig(cfg config.Provider) (pc Config, err error) {
if !cfg.IsSet(privacyConfigKey) {
+1 -6
View File
@@ -40,10 +40,6 @@ simple = true
disable = true
enableDNT = true
simple = true
[privacy.x]
disable = true
enableDNT = true
simple = true
[privacy.vimeo]
disable = true
enableDNT = true
@@ -65,8 +61,7 @@ simple = true
pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable,
pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT,
pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple,
pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable, pc.X.Disable, pc.X.EnableDNT,
pc.X.Simple,
pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable,
}
c.Assert(got, qt.All(qt.Equals), true)
+1 -11
View File
@@ -31,8 +31,7 @@ type Config struct {
Disqus Disqus
GoogleAnalytics GoogleAnalytics
Instagram Instagram
Twitter Twitter // deprecated in favor of X in v0.141.0
X X
Twitter Twitter
RSS RSS
}
@@ -62,7 +61,6 @@ type Instagram struct {
}
// Twitter holds the functional configuration settings related to the Twitter shortcodes.
// Deprecated in favor of X in v0.141.0.
type Twitter struct {
// The Simple variant of Twitter is decorated with a basic set of inline styles.
// This means that if you want to provide your own CSS, you want
@@ -70,14 +68,6 @@ type Twitter struct {
DisableInlineCSS bool
}
// X holds the functional configuration settings related to the X shortcodes.
type X struct {
// The Simple variant of X is decorated with a basic set of inline styles.
// This means that if you want to provide your own CSS, you want
// to disable the inline CSS provided by Hugo.
DisableInlineCSS bool
}
// RSS holds the functional configuration settings related to the RSS feeds.
type RSS struct {
// Limit the number of pages.
-2
View File
@@ -36,8 +36,6 @@ id = "ga_id"
disableInlineCSS = true
[services.twitter]
disableInlineCSS = true
[services.x]
disableInlineCSS = true
`
cfg, err := config.FromConfigString(tomlConfig, "toml")
c.Assert(err, qt.IsNil)
+4 -6
View File
@@ -82,13 +82,11 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string, force bool) error
b.setArcheTypeFilenameToUse(ext)
withBuildLock := func() (string, error) {
if !h.Configs.Base.NoBuildLock {
unlock, err := h.BaseFs.LockBuild()
if err != nil {
return "", fmt.Errorf("failed to acquire a build lock: %s", err)
}
defer unlock()
unlock, err := h.BaseFs.LockBuild()
if err != nil {
return "", fmt.Errorf("failed to acquire a build lock: %s", err)
}
defer unlock()
if b.isDir {
return "", b.buildDir()
+13 -55
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
@@ -24,7 +23,6 @@ import (
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/internal/js"
"github.com/gohugoio/hugo/internal/warpc"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/page"
@@ -49,9 +47,6 @@ type Deps struct {
// The templates to use. This will usually implement the full tpl.TemplateManager.
tmplHandlers *tpl.TemplateHandlers
// The template funcs.
TmplFuncMap map[string]any
// The file systems to use.
Fs *hugofs.Fs `json:"-"`
@@ -88,13 +83,10 @@ type Deps struct {
Metrics metrics.Provider
// BuildStartListeners will be notified before a build starts.
BuildStartListeners *Listeners[any]
BuildStartListeners *Listeners
// BuildEndListeners will be notified after a build finishes.
BuildEndListeners *Listeners[any]
// OnChangeListeners will be notified when something changes.
OnChangeListeners *Listeners[identity.Identity]
BuildEndListeners *Listeners
// Resources that gets closed when the build is done or the server shuts down.
BuildClosers *types.Closers
@@ -106,12 +98,6 @@ type Deps struct {
// TODO(bep) rethink this re. a plugin setup, but this will have to do for now.
WasmDispatchers *warpc.Dispatchers
// The JS batcher client.
JSBatcherClient js.BatcherClient
// The JS batcher client.
// JSBatcherClient *esbuild.BatcherClient
isClosed bool
*globalErrHandler
@@ -168,27 +154,23 @@ func (d *Deps) Init() error {
}
if d.BuildStartListeners == nil {
d.BuildStartListeners = &Listeners[any]{}
d.BuildStartListeners = &Listeners{}
}
if d.BuildEndListeners == nil {
d.BuildEndListeners = &Listeners[any]{}
d.BuildEndListeners = &Listeners{}
}
if d.BuildClosers == nil {
d.BuildClosers = &types.Closers{}
}
if d.OnChangeListeners == nil {
d.OnChangeListeners = &Listeners[identity.Identity]{}
}
if d.Metrics == nil && d.Conf.TemplateMetrics() {
d.Metrics = metrics.NewProvider(d.Conf.TemplateMetricsHints())
}
if d.ExecHelper == nil {
d.ExecHelper = hexec.New(d.Conf.GetConfigSection("security").(security.Config), d.Conf.WorkingDir(), d.Log)
d.ExecHelper = hexec.New(d.Conf.GetConfigSection("security").(security.Config), d.Conf.WorkingDir())
}
if d.MemCache == nil {
@@ -286,23 +268,6 @@ func (d *Deps) Compile(prototype *Deps) error {
return nil
}
// MkdirTemp returns a temporary directory path that will be cleaned up on exit.
func (d Deps) MkdirTemp(pattern string) (string, error) {
filename, err := os.MkdirTemp("", pattern)
if err != nil {
return "", err
}
d.BuildClosers.Add(
types.CloserFunc(
func() error {
return os.RemoveAll(filename)
},
),
)
return filename, nil
}
type globalErrHandler struct {
logger loggers.Logger
@@ -341,16 +306,15 @@ func (e *globalErrHandler) StopErrorCollector() {
}
// Listeners represents an event listener.
type Listeners[T any] struct {
type Listeners struct {
sync.Mutex
// A list of funcs to be notified about an event.
// If the return value is true, the listener will be removed.
listeners []func(...T) bool
listeners []func()
}
// Add adds a function to a Listeners instance.
func (b *Listeners[T]) Add(f func(...T) bool) {
func (b *Listeners) Add(f func()) {
if b == nil {
return
}
@@ -360,16 +324,12 @@ func (b *Listeners[T]) Add(f func(...T) bool) {
}
// Notify executes all listener functions.
func (b *Listeners[T]) Notify(vs ...T) {
func (b *Listeners) Notify() {
b.Lock()
defer b.Unlock()
temp := b.listeners[:0]
for _, notify := range b.listeners {
if !notify(vs...) {
temp = append(temp, notify)
}
notify()
}
b.listeners = temp
}
// ResourceProvider is used to create and refresh, and clone resources needed.
@@ -412,11 +372,9 @@ type DepsCfg struct {
// The logging level to use.
LogLevel logg.Level
// Logging output.
StdErr io.Writer
// The console output.
StdOut io.Writer
// Where to write the logs.
// Currently we typically write everything to stdout.
LogOut io.Writer
// The file systems to use
Fs *hugofs.Fs
-4
View File
@@ -85,7 +85,6 @@
"stringifier",
"struct",
"toclevels",
"unpublishdate",
"zgotmplz",
"# ----------------------------------------------------------------------",
"# cspell: ignore foreign language words",
@@ -130,7 +129,6 @@
"Samsa",
"Stucki",
"Thénardier",
"WASI",
"# ----------------------------------------------------------------------",
"# cspell: ignore operating systems and software packages",
"# ----------------------------------------------------------------------",
@@ -160,12 +158,10 @@
"achristie",
"ddmaurier",
"dring",
"fleqn",
"inor",
"jausten",
"jdoe",
"jsmith",
"leqno",
"milli",
"rgba",
"rsmith",
-1
View File
@@ -23,4 +23,3 @@ MD046: false
MD049: false
MD050: false
MD053: false
MD055: false
+3 -1
View File
@@ -7,10 +7,12 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
[![Netlify Status](https://api.netlify.com/api/v1/badges/e0dbbfc7-34f1-4393-a679-c16e80162705/deploy-status)](https://app.netlify.com/sites/gohugoio/deploys)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://gohugo.io/contribute/documentation/)
This is the repository for the [Hugo](https://github.com/gohugoio/hugo) documentation site.
This is the repository for the [Hugo](https://github.com/gohugoio/hugo) documentation site.
Please see the [contributing] section for guidelines, examples, and process.
[bep]: https://github.com/bep
[spf13]: https://github.com/spf13
[friends]: https://github.com/gohugoio/hugo/graphs/contributors
@@ -10,9 +10,9 @@
}
code {
padding: 2px 3px;
padding: 0.2em;
margin: 0;
font-size: 93.75%;
font-size: 85%;
background-color: rgba(27,31,35,0.05);
border-radius: 3px;
}
@@ -4,7 +4,13 @@ code, .code, pre code, .highlight pre {
}
.sans-serif {
font-family: 'Muli', Avenir, 'Helvetica Neue', Helvetica, Roboto, Noto, 'Segoe UI', Arial, sans-serif;
font-family: 'Muli',
avenir,
'helvetica neue', helvetica,
ubuntu,
roboto, noto,
'segoe ui', arial,
sans-serif;
}
@@ -18,8 +18,3 @@ h6:first-of-type {
h6 ~ p {
margin: 0.5em 0 2em 0;
}
/* QR codes */
img.qrcode {
width: initial;
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.1 KiB

@@ -4769,9 +4769,9 @@ h6:hover .header-link {
margin: 0;
}
code {
padding: 2px 3px;
padding: 0.2em;
margin: 0;
font-size: 93.75%;
font-size: 85%;
background-color: rgba(27, 31, 35, .05);
border-radius: 3px;
}
@@ -5002,7 +5002,13 @@ code, .code, pre code, .highlight pre {
font-family: 'inconsolata',Menlo,Monaco,'Courier New',monospace;
}
.sans-serif {
font-family: 'Muli', Avenir, 'Helvetica Neue', Helvetica, Roboto, Noto, 'Segoe UI', Arial, sans-serif;
font-family: 'Muli',
avenir,
'helvetica neue', helvetica,
ubuntu,
roboto, noto,
'segoe ui', arial,
sans-serif;
}
.serif {
font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif;
@@ -5333,11 +5339,6 @@ h6:first-of-type {
h6 ~ p {
margin: 0.5em 0 2em 0;
}
/* QR codes */
img.qrcode {
width: auto;
width: initial;
}
.nested-blockquote blockquote {
border-left: 4px solid #0594CB;
padding-left: 1em;
@@ -6,12 +6,13 @@
bgcolor = "#ffffff"
[[banners]]
name = "GoLand"
title = "The complete IDE crafted for professional Go developers."
name = "Route4Me"
link = "https://route4me.com/"
title = "Route Planning & Route Optimization Software"
no_query_params = true
link = "https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo"
logo = "images/sponsors/goland.svg"
bgcolor = "#f4f4f4"
utm_campaign = "hugosponsor"
bgcolor = "#334799"
link_attr = "style='color: #ffffff; font-weight: bold; text-decoration: none; text-align: center'"
[[banners]]
name = "Your Company?"
@@ -98,7 +98,12 @@ either of these shortcodes in conjunction with this render hook.
{{- end }}
{{- /* Determine content path for warning and error messages. */}}
{{- $contentPath := .Page.String }}
{{- $contentPath := "" }}
{{- with .Page.File }}
{{- $contentPath = .Path }}
{{- else }}
{{- $contentPath = .Path }}
{{- end }}
{{- /* Parse destination. */}}
{{- $u := urls.Parse .Destination }}
@@ -106,7 +106,9 @@
</head>
<body
class="ma0 sans-serif bg-primary-color-light{{ with getenv "HUGO_ENV" }} {{ . }}{{ end }}">
class="ma0 sans-serif bg-primary-color-light{{ with getenv "HUGO_ENV" }}
{{ . }}
{{ end }} {{ if $hasMath }}{{ print " dn" }}{{ end }}">
{{ partial "hooks/after-body-start.html" . }}
{{ block "nav" . }}{{ partial "site-nav.html" . }}{{ end }}
{{ block "header" . }}{{ end }}
@@ -22,15 +22,8 @@
{{ $releases = where $releases "draft" false }}
{{ $releases = where $releases "prerelease" false }}
{{ range $releases | first 20 }}
{{ $publishDate := .published_at | time.AsTime }}
{{/* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}}
{{ if eq .name "v0.138.0" }}
{{ $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }}
{{ end }}
{{ $ctx := dict
"Date" $publishDate
"Date" (.published_at | time.AsTime)
"Title" (printf "Release %s" .name)
"Permalink" .html_url
"Section" "news"
@@ -21,21 +21,14 @@
{{- $releases = where $releases "draft" false }}
{{- $releases = where $releases "prerelease" false }}
{{- range $releases | first 20 }}
{{- $publishDate := .published_at | time.AsTime }}
{{- /* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}}
{{- if eq .name "v0.138.0" }}
{{- $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }}
{{- end }}
{{- $summary := printf
"Hugo %s was released on %s. See [release notes](%s) for details."
.tag_name
($publishDate | time.AsTime | time.Format "2 Jan 2006")
(.published_at | time.AsTime | time.Format "2 Jan 2006")
.html_url
}}
{{- $ctx := dict
"PublishDate" $publishDate
"PublishDate" (.published_at | time.AsTime)
"Title" (printf "Release %s" .name)
"Permalink" .html_url
"Section" "news"
@@ -4,14 +4,14 @@
{{ $data := "" }}
{{ $url := urls.JoinPath "https://api.github.com/users" $author }}
{{ with try (resources.GetRemote $url) }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "%s" . }}
{{ else with .Value }}
{{ $data = . | transform.Unmarshal }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ end }}
<div class="tc">
@@ -4,14 +4,14 @@
{{ $data := "" }}
{{ $url := urls.JoinPath "https://api.github.com/users" $author }}
{{ with try (resources.GetRemote $url) }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "%s" . }}
{{ else with .Value }}
{{ $data = . | transform.Unmarshal }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ end }}
{{ with $data }}
@@ -25,7 +25,7 @@
{{ $query_params := .query_params | default "" }}
{{ $url := .link }}
{{ if not .no_query_params }}
{{ $url = printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" (.utm_medium | default "banner") "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }}
{{ $url = printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" "banner" "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }}
{{ end }}
{{ $logo := resources.Get .logo }}
{{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }}
@@ -2,8 +2,15 @@
<script>
MathJax = {
tex: {
displayMath: [['\\[', '\\]'], ['$$', '$$']], // block
inlineMath: [['\\(', '\\)']] // inline
inlineMath: [['$', '$'], ['\\(', '\\)']], // inline
displayMath: [['$$', '$$'], ['\\[', '\\]']] // block
},
startup: {
pageReady: () => {
return MathJax.startup.defaultPageReady().then(() => {
document.body.classList.remove("dn");
});
}
}
};
</script>
@@ -16,7 +16,7 @@
<h2>{{ $heading }}</h2>
<ul>
{{- range . }}
<li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{- end }}
</ul>
{{- end }}
@@ -1,3 +1,9 @@
<a
href="https://twitter.com/intent/follow?screen_name=gohugoiov2"
title="Follow on Twitter"
class="link-transition twitter link dib z-999 pt3 pt0-l mr2">
{{ partial "svg/twitter.svg" (dict "size" "32px") }}
</a>
<a
rel="me"
class="mstdn mr3"
@@ -11,13 +11,13 @@ Supports CSV, JSON, TOML, YAML, and XML.
{{ $url := . }}
{{ $data := dict }}
{{ with try (resources.GetRemote $url) }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "%s" . }}
{{ else with .Value }}
{{ $data = .Content | transform.Unmarshal }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ $data = .Content | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ end }}
{{ return $data }}
@@ -4,7 +4,7 @@ If you have an "older" site running on Netlify, you may have to set GO_VERSION t
For more information about Go Modules, see:
* https://go.dev/wiki/Modules
* https://github.com/golang/go/wiki/Modules
* https://blog.golang.org/using-go-modules
` }}
@@ -1,11 +0,0 @@
{{- /*
Returns syntax-highlighted code from the given text.
This is useful as a terse way to highlight inline code snippets. Calling the
highlight shortcode for inline snippets is verbose.
*/}}
{{- $code := .Inner | strings.TrimSpace }}
{{- $lang := or (.Get 0) "go" }}
{{- $opts := dict "hl_inline" true "noClasses" true }}
{{- transform.Highlight $code $lang $opts }}
@@ -120,8 +120,8 @@ Renders the given image using the given filter, if any.
{{- $validFilters := slice
"autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither"
"gamma" "gaussianblur" "grayscale" "hue" "invert" "mask" "none" "opacity"
"overlay" "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text"
"gamma" "gaussianblur" "grayscale" "hue" "invert" "none" "opacity" "overlay"
"padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text"
"unsharpmask"
}}
@@ -229,12 +229,6 @@ Renders the given image using the given filter, if any.
{{- $ctx = merge $ctx (dict "argsRequired" 0) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.Invert }}
{{- else if eq $filter "mask" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }}
{{- $maskImage := partial "inline/get-resource.html" $ctx }}
{{- $f = images.Mask $maskImage }}
{{- else if eq $filter "opacity" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
@@ -328,15 +322,11 @@ Renders the given image using the given filter, if any.
{{- end }}
{{- /* Render. */}}
{{- $class := "di va b--black-20" }}
{{- if eq $filter "mask" }}
{{- $class = "di va" }}
{{- end }}
{{- if $example }}
<p>Original</p>
<img class="{{ $class}}" style="width: initial;" src="{{ $i.RelPermalink }}" alt="{{ $alt }}">
<img class='di ba b--black-20' style="width: initial;" src="{{ $i.RelPermalink }}" alt="{{ $alt }}">
<p>Processed</p>
<img class="{{ $class }}" style="width: initial;" src="{{ $fi.RelPermalink }}" alt="{{ $alt }}">
<img class='di ba b--black-20' style="width: initial;" src="{{ $fi.RelPermalink }}" alt="{{ $alt }}">
{{- else -}}
<img class='di' style="width: initial;" src="{{ $fi.RelPermalink }}" alt="{{ $alt }}">
{{- end }}
@@ -364,15 +354,15 @@ Renders the given image using the given filter, if any.
{{- $u := urls.Parse .src }}
{{- $msg := "The %q shortcode was unable to resolve %s. See %s" }}
{{- if $u.IsAbs }}
{{- with try (resources.GetRemote $u.String) }}
{{- with resources.GetRemote $u.String }}
{{- with .Err }}
{{- errorf "%s" . }}
{{- else with .Value }}
{{- else }}
{{- /* This is a remote resource. */}}
{{- $r = . }}
{{- else }}
{{- errorf $msg $.name $u.String $.position }}
{{- end }}
{{- else }}
{{- errorf $msg $.name $u.String $.position }}
{{- end }}
{{- else }}
{{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }}
@@ -10,7 +10,7 @@ You must call this shortcode using the {{% %}} notation.
*/}}
{{- with .Get 0 }}
{{- with $.Page.GetPage . }}
{{- with site.GetPage . }}
{{- .RenderShortcodes }}
{{- else }}
{{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }}
@@ -1 +1 @@
Also see [Module Mounts Config](/hugo-modules/configuration/#module-configuration-mounts) for an alternative way to configure this directory.
Also see [Module Mounts Config](/hugo-modules/configuration/#module-configuration-mounts) for an alternative way to configure this directory (from Hugo 0.56).
+1 -1
View File
@@ -1 +1 @@
# github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743
# github.com/gohugoio/gohugoioTheme v0.0.0-20240815082608-66ccd383a90f
-19
View File
@@ -1,19 +0,0 @@
---
title: {{ replace .File.ContentBaseName "-" " " }}
---
<!--
You can insert these definitions in other pages using the `glossary-term` shortcode, so they must be self-contained.
Do this:
A _foo_ is big bar.
Not this:
A big bar.
Italicize the term whenever you use it in the definition.
An exception to this rule occurs when a term is an alias for another. In such cases, it is sufficient to use the phrase 'See [page kind]'."
-->
+1 -1
View File
@@ -20,7 +20,7 @@ byline: "[bep](https://github.com/bep), Hugo Lead"
To complete this showcase:
1. Write the story about your site in this file.
2. Add a summary to the `bio.md` file in this directory.
2. Add a summary to the `bio.md` file in this folder.
3. Replace the `featured-template.png` with a screenshot of your site. You can rename it, but it must contain the word `featured`.
4. Create a new pull request in https://github.com/gohugoio/hugoDocs/pulls
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

+7 -13
View File
@@ -54,27 +54,21 @@ weight = 90
identifier = 'render-hooks'
pageRef = '/render-hooks/'
[[docs]]
name = 'Shortcodes'
weight = 100
identifier = 'shortcodes'
pageRef = '/shortcodes/'
[[docs]]
name = 'Hugo Modules'
weight = 110
weight = 100
identifier = 'modules'
pageRef = '/hugo-modules/'
[[docs]]
name = 'Hugo Pipes'
weight = 120
weight = 110
identifier = 'hugo-pipes'
pageRef = '/hugo-pipes/'
[[docs]]
name = 'CLI'
weight = 130
weight = 120
post = 'break'
identifier = 'commands'
pageRef = '/commands/'
@@ -83,25 +77,25 @@ pageRef = '/commands/'
[[docs]]
name = 'Troubleshooting'
weight = 140
weight = 130
identifier = 'troubleshooting'
pageRef = '/troubleshooting/'
[[docs]]
name = 'Developer tools'
weight = 150
weight = 140
identifier = 'developer-tools'
pageRef = '/tools/'
[[docs]]
name = 'Hosting and deployment'
weight = 160
weight = 150
identifier = 'hosting-and-deployment'
pageRef = '/hosting-and-deployment/'
[[docs]]
name = 'Contribute'
weight = 170
weight = 160
post = 'break'
identifier = 'contribute'
pageRef = '/contribute/'
+3 -3
View File
@@ -55,7 +55,7 @@ toc: true
: Use fenced code blocks and Markdown render hooks to include diagrams in your content.
[Mathematics]
: Include mathematical equations and expressions in Markdown using LaTeX markup.
: Include mathematical equations and expressions in Markdown using LaTeX or TeX typesetting syntax.
[Syntax highlighting]
: Syntactically highlight code examples using Hugo's embedded syntax highlighter, enabled by default for fenced code blocks in Markdown. The syntax highlighter supports hundreds of code languages and dozens of styles.
@@ -123,7 +123,7 @@ toc: true
[Mathematics]: /content-management/mathematics/
[Menus]: /content-management/menus/
[Minification]: /getting-started/configuration/#configure-minify
[Modules]: /hugo-modules/
[Modules]: https://gohugo.io/hugo-modules/
[Multilingual]: /content-management/multilingual/
[Multiplatform]: /installation/
[Output formats]: /templates/output-formats/
@@ -134,6 +134,6 @@ toc: true
[Syntax highlighting]: /content-management/syntax-highlighting/
[Tailwind CSS processing]: /functions/css/tailwindcss/
[Taxonomies]: /content-management/taxonomies/
[Templates]: /templates/introduction/
[Templates]: templates/introduction/
[Themes]: https://themes.gohugo.io/
[URL management]: /content-management/urls/
+9 -9
View File
@@ -39,11 +39,11 @@ respectDoNotTrack = false
[privacy.instagram]
disable = false
simple = false
[privacy.vimeo]
[privacy.twitter]
disable = false
enableDNT = false
simple = false
[privacy.x]
[privacy.vimeo]
disable = false
enableDNT = false
simple = false
@@ -64,9 +64,9 @@ disable = true
disable = true
[privacy.instagram]
disable = true
[privacy.vimeo]
[privacy.twitter]
disable = true
[privacy.x]
[privacy.vimeo]
disable = true
[privacy.youtube]
disable = true
@@ -92,19 +92,19 @@ simple
disableInlineCSS = true
{{< /code-toggle >}}
### X
### Twitter
enableDNT
: Enabling this for the x shortcode, the post and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads.
: Enabling this for the twitter/tweet shortcode, the tweet and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads.
simple
: If simple mode is enabled, a static and no-JS version of a post will be built.
: If simple mode is enabled, a static and no-JS version of a tweet will be built.
**Note:** If you use the _simple mode_ for X, you may want to disable the inline styles provided by Hugo:
**Note:** If you use the _simple mode_ for Twitter, you may want to disable the inline styles provided by Hugo:
{{< code-toggle file=hugo >}}
[services]
[services.x]
[services.twitter]
disableInlineCSS = true
{{< /code-toggle >}}
+3
View File
@@ -33,6 +33,7 @@ hugo [flags]
--config string config file (default is hugo.yaml|json|toml)
--configDir string config dir (default "config")
-c, --contentDir string filesystem path to content directory
--debug debug output
-d, --destination string filesystem path to write files to
--disableKinds strings disable different kind of pages (home, RSS etc.)
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
@@ -63,6 +64,7 @@ hugo [flags]
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--themesDir string filesystem path to themes directory
--trace file write trace to file (not useful in general)
-v, --verbose verbose output
-w, --watch watch filesystem for changes and recreate as needed
```
@@ -72,6 +74,7 @@ hugo [flags]
* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell
* [hugo config](/commands/hugo_config/) - Display site configuration
* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format
* [hugo deploy](/commands/hugo_deploy/) - Deploy your site to a cloud provider
* [hugo env](/commands/hugo_env/) - Display version and environment info
* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles
* [hugo import](/commands/hugo_import/) - Import a site from another system
+2
View File
@@ -33,6 +33,7 @@ hugo build [flags]
--config string config file (default is hugo.yaml|json|toml)
--configDir string config dir (default "config")
-c, --contentDir string filesystem path to content directory
--debug debug output
-d, --destination string filesystem path to write files to
--disableKinds strings disable different kind of pages (home, RSS etc.)
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
@@ -63,6 +64,7 @@ hugo build [flags]
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--themesDir string filesystem path to themes directory
--trace file write trace to file (not useful in general)
-v, --verbose verbose output
-w, --watch watch filesystem for changes and recreate as needed
```
+2 -1
View File
@@ -25,15 +25,16 @@ See each sub-command's help for details on how to use the generated script.
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is hugo.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
-d, --destination string filesystem path to write files to
-e, --environment string build environment
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
--logLevel string log level (debug|info|warn|error)
--noBuildLock don't create .hugo_build.lock file
--quiet build in quiet mode
-M, --renderToMemory render to memory (mostly useful when running the server)
-s, --source string filesystem path to read files relative from
--themesDir string filesystem path to themes directory
-v, --verbose verbose output
```
### SEE ALSO

Some files were not shown because too many files have changed in this diff Show More