Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bcde806340 | |||
| df4ac34856 | |||
| 166d3ee1ce | |||
| a25af7facf | |||
| 723579ff54 | |||
| 85ad5e48eb | |||
| 7b5199fdef | |||
| e31ff547d2 | |||
| 87260e4a60 | |||
| 8405b802cf | |||
| bf05832d14 | |||
| a05736cb9f | |||
| 49dceb19f5 | |||
| 5e7099256e | |||
| 0805c734a4 | |||
| 76a5e1880a | |||
| 0bb337b22a | |||
| 03dc9170b3 | |||
| c829b736cc | |||
| 995a2159e5 | |||
| f88f0a9f59 | |||
| dd3f2731e0 | |||
| f772998fa3 | |||
| 8a55df7af2 | |||
| 52c9bd7908 | |||
| 44da086082 | |||
| 2ffaf1fc1e | |||
| a808f6e40d | |||
| 94f3908ec6 | |||
| 75fcc7524a | |||
| 33d1f2c853 | |||
| 64da6d7c62 | |||
| 70db201ed4 | |||
| 8a468df065 | |||
| b5fa03d3aa | |||
| 9da472dd3b | |||
| 615e45d6e8 | |||
| 635532a20f | |||
| a243a6154c | |||
| 9c71f600bf | |||
| 420527fc6b | |||
| 7fe786e3b0 | |||
| 03b244fc0b | |||
| 961181334f | |||
| e35b7f049d | |||
| 7d90277a28 | |||
| 0796fa7ace | |||
| 6bf15241a1 | |||
| 861ede6d10 | |||
| f228c87d41 | |||
| 1b701b72ca | |||
| a32d70b712 | |||
| 948cfb98f9 | |||
| 8930802ef2 | |||
| 7df45f615a | |||
| f961093ea9 | |||
| 89b8c32200 | |||
| 984358f047 | |||
| d1f191c58d | |||
| a198116669 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22600.20300
|
||||
- image: bepsays/ci-hugoreleaser:1.22700.20000
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22600.20300
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22700.20000
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
have_fun: false
|
||||
memory_config:
|
||||
disabled: false
|
||||
code_review:
|
||||
disable: false
|
||||
comment_severity_threshold: HIGH
|
||||
max_review_comments: -1
|
||||
pull_request_opened:
|
||||
help: true
|
||||
summary: false
|
||||
code_review: false
|
||||
include_drafts: false
|
||||
ignore_patterns: []
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.26.x]
|
||||
go-version: [1.27.x]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -51,6 +51,7 @@ jobs:
|
||||
run: |
|
||||
gem install asciidoctor -v "2.0.26"
|
||||
gem install asciidoctor-diagram -v "3.1.0"
|
||||
gem install asciidoctor-html5s -v "0.5.1"
|
||||
- name: Install GoAT
|
||||
run: go install github.com/blampe/goat/cmd/goat@177de93b192b8ffae608e5d9ec421cc99bf68402
|
||||
- name: Install Python
|
||||
|
||||
@@ -5,4 +5,5 @@ dist/
|
||||
public/
|
||||
.DS_Store
|
||||
cache/filecache/_gen/
|
||||
.claude/
|
||||
.claude/
|
||||
dump.txt
|
||||
@@ -2,8 +2,8 @@
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
ARG GO_VERSION="1.26"
|
||||
ARG ALPINE_VERSION="3.22"
|
||||
ARG GO_VERSION="1.27"
|
||||
ARG ALPINE_VERSION="3.24"
|
||||
ARG DART_SASS_VERSION="1.79.3"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
|
||||
|
||||
@@ -37,7 +37,6 @@ A fast and flexible static site generator built with love by [bep][], [spf13][],
|
||||
|
||||
[](https://godoc.org/github.com/gohugoio/hugo)
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
[Website][] | [Installation][] | [Documentation][] | [Support][] | [Contributing][] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
|
||||
|
||||
@@ -139,10 +138,6 @@ To build and install the extended/deploy edition, first install a C compiler suc
|
||||
CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest
|
||||
```
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#gohugoio/hugo&Timeline)
|
||||
|
||||
## Documentation
|
||||
|
||||
Hugo's [documentation][] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
|
||||
@@ -195,10 +190,48 @@ Hugo stands on the shoulders of great open source libraries. Run `hugo env --log
|
||||
<summary>See current dependencies</summary>
|
||||
|
||||
```text
|
||||
cel.dev/expr="v0.25.1"
|
||||
cloud.google.com/go/auth/oauth2adapt="v0.2.8"
|
||||
cloud.google.com/go/auth="v0.20.0"
|
||||
cloud.google.com/go/compute/metadata="v0.9.0"
|
||||
cloud.google.com/go/iam="v1.5.3"
|
||||
cloud.google.com/go/monitoring="v1.24.3"
|
||||
cloud.google.com/go/storage="v1.57.2"
|
||||
cloud.google.com/go="v0.123.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.20.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.13.1"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.11.2"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.6.3"
|
||||
github.com/Azure/go-autorest/autorest/to="v0.4.1"
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go="v1.6.0"
|
||||
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
|
||||
github.com/JohannesKaufmann/dom="v0.2.0"
|
||||
github.com/JohannesKaufmann/html-to-markdown/v2="v2.5.0"
|
||||
github.com/alecthomas/chroma/v2="v2.21.1"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp="v1.31.0"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric="v0.54.0"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping="v0.54.0"
|
||||
github.com/JohannesKaufmann/dom="v0.3.1"
|
||||
github.com/JohannesKaufmann/html-to-markdown/v2="v2.5.2"
|
||||
github.com/alecthomas/chroma/v2="v2.27.0"
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.7.8"
|
||||
github.com/aws/aws-sdk-go-v2/config="v1.32.2"
|
||||
github.com/aws/aws-sdk-go-v2/credentials="v1.19.2"
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.18.14"
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.20.12"
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.4.22"
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.7.22"
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini="v1.8.4"
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.4.22"
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront="v1.61.1"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.13.7"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.9.13"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.13.21"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.19.21"
|
||||
github.com/aws/aws-sdk-go-v2/service/s3="v1.97.3"
|
||||
github.com/aws/aws-sdk-go-v2/service/signin="v1.0.2"
|
||||
github.com/aws/aws-sdk-go-v2/service/sso="v1.30.5"
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.35.10"
|
||||
github.com/aws/aws-sdk-go-v2/service/sts="v1.41.2"
|
||||
github.com/aws/aws-sdk-go-v2="v1.41.6"
|
||||
github.com/aws/smithy-go="v1.25.0"
|
||||
github.com/aymerick/douceur="v0.2.0"
|
||||
github.com/bep/clocks="v0.5.0"
|
||||
github.com/bep/debounce="v1.2.0"
|
||||
@@ -206,105 +239,129 @@ github.com/bep/gitmap="v1.9.0"
|
||||
github.com/bep/goat="v0.5.0"
|
||||
github.com/bep/godartsass/v2="v2.5.0"
|
||||
github.com/bep/golibsass="v1.2.0"
|
||||
github.com/bep/goportabletext="v0.1.0"
|
||||
github.com/bep/helpers="v0.6.0"
|
||||
github.com/bep/imagemeta="v0.12.0"
|
||||
github.com/bep/lazycache="v0.8.0"
|
||||
github.com/bep/golocales="v0.2.0"
|
||||
github.com/bep/goportabletext="v0.2.0"
|
||||
github.com/bep/helpers="v0.12.0"
|
||||
github.com/bep/imagemeta="v0.17.2"
|
||||
github.com/bep/lazycache="v0.8.1"
|
||||
github.com/bep/logg="v0.4.0"
|
||||
github.com/bep/mclib="v1.20400.20402"
|
||||
github.com/bep/overlayfs="v0.10.0"
|
||||
github.com/bep/simplecobra="v0.6.1"
|
||||
github.com/bep/textandbinarywriter="v0.0.0-20251212174530-cd9f0732f60f"
|
||||
github.com/bep/tmc="v0.5.1"
|
||||
github.com/bits-and-blooms/bitset="v1.24.4"
|
||||
github.com/bep/mclib="v1.20401.20400"
|
||||
github.com/bep/overlayfs="v0.11.0"
|
||||
github.com/bep/simplecobra="v0.7.0"
|
||||
github.com/bep/textandbinarywriter="v0.1.0"
|
||||
github.com/bep/tmc="v0.6.0"
|
||||
github.com/bits-and-blooms/bitset="v1.24.5"
|
||||
github.com/cespare/xxhash/v2="v2.3.0"
|
||||
github.com/clbanning/mxj/v2="v2.7.0"
|
||||
github.com/clipperhouse/displaywidth="v0.6.0"
|
||||
github.com/clipperhouse/stringish="v0.1.1"
|
||||
github.com/clipperhouse/uax29/v2="v2.3.0"
|
||||
github.com/clipperhouse/displaywidth="v0.10.0"
|
||||
github.com/clipperhouse/uax29/v2="v2.6.0"
|
||||
github.com/cncf/xds/go="v0.0.0-20251210132809-ee656c7534f5"
|
||||
github.com/cpuguy83/go-md2man/v2="v2.0.6"
|
||||
github.com/disintegration/gift="v1.2.1"
|
||||
github.com/dlclark/regexp2="v1.11.5"
|
||||
github.com/evanw/esbuild="v0.27.2"
|
||||
github.com/dlclark/regexp2/v2="v2.2.1"
|
||||
github.com/dustin/go-humanize="v1.0.1"
|
||||
github.com/envoyproxy/go-control-plane/envoy="v1.36.0"
|
||||
github.com/envoyproxy/protoc-gen-validate="v1.3.0"
|
||||
github.com/evanw/esbuild="v0.28.1"
|
||||
github.com/fatih/color="v1.18.0"
|
||||
github.com/felixge/httpsnoop="v1.0.4"
|
||||
github.com/frankban/quicktest="v1.14.6"
|
||||
github.com/fsnotify/fsnotify="v1.9.0"
|
||||
github.com/getkin/kin-openapi="v0.133.0"
|
||||
github.com/go-openapi/jsonpointer="v0.21.0"
|
||||
github.com/go-openapi/swag="v0.23.0"
|
||||
github.com/getkin/kin-openapi="v0.140.0"
|
||||
github.com/go-jose/go-jose/v4="v4.1.4"
|
||||
github.com/go-logr/logr="v1.4.3"
|
||||
github.com/go-logr/stdr="v1.2.2"
|
||||
github.com/go-openapi/jsonpointer="v0.22.5"
|
||||
github.com/go-openapi/swag/jsonname="v0.25.5"
|
||||
github.com/gobuffalo/flect="v1.0.3"
|
||||
github.com/gobwas/glob="v0.2.3"
|
||||
github.com/goccy/go-yaml="v1.19.1"
|
||||
github.com/goccy/go-yaml="v1.19.2"
|
||||
github.com/gohugoio/gift="v0.2.0"
|
||||
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20251018145728-cfcc22d823c6"
|
||||
github.com/gohugoio/go-radix="v1.2.0"
|
||||
github.com/gohugoio/hashstructure="v0.6.0"
|
||||
github.com/gohugoio/httpcache="v0.8.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.5.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.3.1"
|
||||
github.com/gohugoio/locales="v0.14.0"
|
||||
github.com/gohugoio/localescompressed="v1.0.1"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.7.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.5.0"
|
||||
github.com/golang-jwt/jwt/v5="v5.3.0"
|
||||
github.com/google/go-cmp="v0.7.0"
|
||||
github.com/google/s2a-go="v0.1.9"
|
||||
github.com/google/uuid="v1.6.0"
|
||||
github.com/google/wire="v0.7.0"
|
||||
github.com/googleapis/enterprise-certificate-proxy="v0.3.14"
|
||||
github.com/googleapis/gax-go/v2="v2.21.0"
|
||||
github.com/gorilla/css="v1.0.1"
|
||||
github.com/gorilla/websocket="v1.5.3"
|
||||
github.com/hairyhenderson/go-codeowners="v0.7.0"
|
||||
github.com/hashicorp/golang-lru/v2="v2.0.7"
|
||||
github.com/jdkato/prose="v1.2.1"
|
||||
github.com/josharian/intern="v1.0.0"
|
||||
github.com/kr/pretty="v0.3.1"
|
||||
github.com/kr/text="v0.2.0"
|
||||
github.com/kylelemons/godebug="v1.1.0"
|
||||
github.com/kyokomi/emoji/v2="v2.2.13"
|
||||
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-colorable="v0.1.14"
|
||||
github.com/mattn/go-isatty="v0.0.22"
|
||||
github.com/mattn/go-runewidth="v0.0.19"
|
||||
github.com/microcosm-cc/bluemonday="v1.0.27"
|
||||
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.9.1"
|
||||
github.com/oasdiff/yaml3="v0.0.0-20250309153720-d2182401db90"
|
||||
github.com/oasdiff/yaml="v0.0.0-20250309154309-f31be36b4037"
|
||||
github.com/oasdiff/yaml3="v0.0.13"
|
||||
github.com/oasdiff/yaml="v0.1.0"
|
||||
github.com/olekukonko/cat="v0.0.0-20250911104152-50322a0618f6"
|
||||
github.com/olekukonko/errors="v1.1.0"
|
||||
github.com/olekukonko/ll="v0.1.3"
|
||||
github.com/olekukonko/tablewriter="v1.1.2"
|
||||
github.com/olekukonko/errors="v1.2.0"
|
||||
github.com/olekukonko/ll="v0.1.6"
|
||||
github.com/olekukonko/tablewriter="v1.1.4"
|
||||
github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58"
|
||||
github.com/pelletier/go-toml/v2="v2.2.4"
|
||||
github.com/perimeterx/marshmallow="v1.1.5"
|
||||
github.com/pelletier/go-toml/v2="v2.4.3"
|
||||
github.com/pkg/browser="v0.0.0-20240102092130-5ac0b6a4141c"
|
||||
github.com/pkg/errors="v0.9.1"
|
||||
github.com/rogpeppe/go-internal="v1.14.1"
|
||||
github.com/rogpeppe/go-internal="v1.15.0"
|
||||
github.com/russross/blackfriday/v2="v2.1.0"
|
||||
github.com/sass/dart-sass/compiler="1.97.1"
|
||||
github.com/sass/dart-sass/implementation="1.97.1"
|
||||
github.com/sass/dart-sass/protocol="3.2.0"
|
||||
github.com/santhosh-tekuri/jsonschema/v6="v6.0.2"
|
||||
github.com/spf13/afero="v1.15.0"
|
||||
github.com/spf13/cast="v1.10.0"
|
||||
github.com/spf13/cobra="v1.10.2"
|
||||
github.com/spf13/fsync="v0.10.1"
|
||||
github.com/spf13/pflag="v1.0.9"
|
||||
github.com/tdewolff/minify/v2="v2.24.8"
|
||||
github.com/tdewolff/parse/v2="v2.8.5"
|
||||
github.com/tetratelabs/wazero="v1.10.1"
|
||||
github.com/spf13/pflag="v1.0.10"
|
||||
github.com/spiffe/go-spiffe/v2="v2.6.0"
|
||||
github.com/tdewolff/minify/v2="v2.24.13"
|
||||
github.com/tdewolff/parse/v2="v2.8.12"
|
||||
github.com/tetratelabs/wazero="v1.12.0"
|
||||
github.com/webmproject/libwebp="v1.6.0"
|
||||
github.com/woodsbury/decimal128="v1.3.0"
|
||||
github.com/yuin/goldmark-emoji="v1.0.6"
|
||||
github.com/yuin/goldmark="v1.7.13"
|
||||
github.com/yuin/goldmark="v1.8.2"
|
||||
go.opentelemetry.io/auto/sdk="v1.2.1"
|
||||
go.opentelemetry.io/contrib/detectors/gcp="v1.39.0"
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc="v0.67.0"
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp="v0.67.0"
|
||||
go.opentelemetry.io/otel/metric="v1.43.0"
|
||||
go.opentelemetry.io/otel/sdk/metric="v1.43.0"
|
||||
go.opentelemetry.io/otel/sdk="v1.43.0"
|
||||
go.opentelemetry.io/otel/trace="v1.43.0"
|
||||
go.opentelemetry.io/otel="v1.43.0"
|
||||
go.uber.org/automaxprocs="v1.5.3"
|
||||
go.yaml.in/yaml/v3="v3.0.4"
|
||||
golang.org/x/crypto="v0.46.0"
|
||||
golang.org/x/image="v0.34.0"
|
||||
golang.org/x/mod="v0.31.0"
|
||||
golang.org/x/net="v0.48.0"
|
||||
golang.org/x/sync="v0.19.0"
|
||||
golang.org/x/sys="v0.39.0"
|
||||
golang.org/x/text="v0.32.0"
|
||||
golang.org/x/tools="v0.40.0"
|
||||
google.golang.org/protobuf="v1.36.10"
|
||||
gopkg.in/yaml.v3="v3.0.1"
|
||||
gocloud.dev="v0.45.0"
|
||||
golang.org/x/crypto="v0.53.0"
|
||||
golang.org/x/image="v0.43.0"
|
||||
golang.org/x/mod="v0.37.0"
|
||||
golang.org/x/net="v0.56.0"
|
||||
golang.org/x/oauth2="v0.36.0"
|
||||
golang.org/x/sync="v0.21.0"
|
||||
golang.org/x/sys="v0.46.0"
|
||||
golang.org/x/text="v0.38.0"
|
||||
golang.org/x/time="v0.15.0"
|
||||
golang.org/x/tools="v0.47.0"
|
||||
golang.org/x/xerrors="v0.0.0-20240903120638-7835f813f4da"
|
||||
google.golang.org/api="v0.276.0"
|
||||
google.golang.org/genproto/googleapis/api="v0.0.0-20260319201613-d00831a3d3e7"
|
||||
google.golang.org/genproto/googleapis/rpc="v0.0.0-20260401024825-9d38bb4040a9"
|
||||
google.golang.org/genproto="v0.0.0-20260319201613-d00831a3d3e7"
|
||||
google.golang.org/grpc="v1.80.0"
|
||||
google.golang.org/protobuf="v1.36.11"
|
||||
rsc.io/qr="v0.2.0"
|
||||
software.sslmate.com/src/go-pkcs12="v0.2.0"
|
||||
software.sslmate.com/src/go-pkcs12="v0.7.0"
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
@@ -59,6 +60,29 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
|
||||
counter := 0
|
||||
|
||||
seen := c.entryLocker.seen
|
||||
seenByLower := make(map[string]string, seen.Len())
|
||||
for id := range seen.All() {
|
||||
seenByLower[strings.ToLower(id)] = id
|
||||
}
|
||||
|
||||
// Names on disk matching a used cache key except for the case, and the used keys
|
||||
// actually walked. See the note about case-insensitive filesystems below.
|
||||
var candidates map[string]string
|
||||
visited := make(map[string]bool, seen.Len())
|
||||
|
||||
remove := func(name string) error {
|
||||
err := c.Fs.Remove(name)
|
||||
if err == nil {
|
||||
counter++
|
||||
return nil
|
||||
}
|
||||
if !herrors.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := afero.Walk(c.Fs, "", func(name string, info os.FileInfo, err error) error {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -93,27 +117,44 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
|
||||
shouldRemove := force || c.isExpired(info.ModTime())
|
||||
|
||||
if !shouldRemove && c.entryLocker.seen.Len() > 0 {
|
||||
// Remove it if it's not been touched/used in the last build.
|
||||
shouldRemove = !c.entryLocker.seen.Has(name)
|
||||
if seen.Has(name) {
|
||||
visited[name] = true
|
||||
} else if !shouldRemove && seen.Len() > 0 {
|
||||
if id, found := seenByLower[strings.ToLower(name)]; found {
|
||||
// On case-insensitive filesystems this is the same file as id; e.g. an
|
||||
// entry created before Hugo started lowercasing the content paths in
|
||||
// v0.123 (content/MyBundle => _gen/images/MyBundle). Decided once the
|
||||
// walk is done: if id is walked too, they are distinct files and this
|
||||
// one is stale. See issue 15101.
|
||||
if candidates == nil {
|
||||
candidates = make(map[string]string)
|
||||
}
|
||||
candidates[name] = id
|
||||
} else {
|
||||
// Remove it if it's not been touched/used in the last build.
|
||||
shouldRemove = true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldRemove {
|
||||
err := c.Fs.Remove(name)
|
||||
if err == nil {
|
||||
counter++
|
||||
}
|
||||
|
||||
if err != nil && !herrors.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return remove(name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return counter, err
|
||||
}
|
||||
|
||||
return counter, err
|
||||
for name, id := range candidates {
|
||||
if visited[id] {
|
||||
if err := remove(name); err != nil {
|
||||
return counter, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return counter, nil
|
||||
}
|
||||
|
||||
func (c *Cache) pruneRootDirs(force bool) (int, error) {
|
||||
|
||||
@@ -20,11 +20,72 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
// A cache entry created before Hugo started lowercasing content paths in v0.123
|
||||
// (e.g. _gen/images/MyBundle) is on a case-insensitive filesystem the same file as
|
||||
// the lowercased cache key used today, and must not be pruned.
|
||||
// See issue 15101.
|
||||
func TestPruneCacheEntryWithOtherCase(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
dir := t.TempDir()
|
||||
if isCaseInsensitive, err := htesting.IsCaseInsensitiveFs(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !isCaseInsensitive {
|
||||
t.Skip("skip test on case-sensitive filesystem")
|
||||
}
|
||||
|
||||
fs := afero.NewBasePathFs(afero.NewOsFs(), dir)
|
||||
newCache := func() *filecache.Cache {
|
||||
return filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
}
|
||||
|
||||
c.Assert(newCache().SetBytes("MyBundle/i1", []byte("abc")), qt.IsNil)
|
||||
|
||||
cache := newCache()
|
||||
_, b, err := cache.GetOrCreateBytes("mybundle/i1", func() ([]byte, error) {
|
||||
return []byte("def"), nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(string(b), qt.Equals, "abc")
|
||||
|
||||
count, err := cache.Prune(false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(count, qt.Equals, 0)
|
||||
c.Assert(cache.GetString("MyBundle/i1"), qt.Equals, "abc")
|
||||
}
|
||||
|
||||
// On a case-sensitive filesystem the entries above are distinct files,
|
||||
// and the one not used in this build should be pruned.
|
||||
func TestPruneCacheEntryWithOtherCaseCaseSensitiveFs(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
cache := filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
|
||||
c.Assert(cache.SetBytes("MyBundle/i1", []byte("abc")), qt.IsNil)
|
||||
|
||||
cache = filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
_, b, err := cache.GetOrCreateBytes("mybundle/i1", func() ([]byte, error) {
|
||||
return []byte("def"), nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(string(b), qt.Equals, "def")
|
||||
|
||||
count, err := cache.Prune(false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(count, qt.Equals, 1)
|
||||
c.Assert(cache.GetString("MyBundle/i1"), qt.Equals, "")
|
||||
c.Assert(cache.GetString("mybundle/i1"), qt.Equals, "def")
|
||||
}
|
||||
|
||||
func TestPrune(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -38,13 +38,20 @@ run_gofmt() {
|
||||
|
||||
# Run staticcheck
|
||||
run_staticcheck() {
|
||||
# Check if staticcheck is installed, install if not
|
||||
if ! command -v staticcheck &> /dev/null; then
|
||||
local staticcheck_bin
|
||||
if command -v staticcheck &> /dev/null; then
|
||||
staticcheck_bin=$(command -v staticcheck)
|
||||
else
|
||||
echo "==> Installing staticcheck..."
|
||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
staticcheck_bin="$(go env GOBIN)"
|
||||
if [ -z "$staticcheck_bin" ]; then
|
||||
staticcheck_bin="$(go env GOPATH)/bin"
|
||||
fi
|
||||
staticcheck_bin="$staticcheck_bin/staticcheck"
|
||||
fi
|
||||
echo "==> Running staticcheck..."
|
||||
staticcheck $PACKAGES
|
||||
"$staticcheck_bin" $PACKAGES
|
||||
echo " OK"
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd.Long = `Display project configuration, both default and custom settings.`
|
||||
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().StringVar(&c.lang, "lang", "", "the language to display config for (default is the default content language)")
|
||||
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)
|
||||
|
||||
@@ -21,12 +21,8 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
@@ -34,6 +30,7 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/markup/highlight"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
@@ -49,6 +46,8 @@ func newGenCommand() *genCommand {
|
||||
style string
|
||||
mode string
|
||||
modeSelector bool
|
||||
classDark string
|
||||
classLight string
|
||||
highlightStyle string
|
||||
lineNumbersInlineStyle string
|
||||
lineNumbersTableStyle string
|
||||
@@ -65,85 +64,26 @@ func newGenCommand() *genCommand {
|
||||
See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles.`,
|
||||
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
style = strings.ToLower(style)
|
||||
if !slices.Contains(styles.Names(), style) {
|
||||
return fmt.Errorf("invalid style: %s", style)
|
||||
}
|
||||
var chromaStyle *chroma.Style
|
||||
if mode != "" {
|
||||
var chromaMode chroma.Mode
|
||||
switch mode {
|
||||
case "light":
|
||||
chromaMode = chroma.Light
|
||||
case "dark":
|
||||
chromaMode = chroma.Dark
|
||||
default:
|
||||
return fmt.Errorf("invalid mode: %s", mode)
|
||||
}
|
||||
|
||||
chromaStyle = styles.GetForMode(style, chromaMode)
|
||||
if chromaStyle.Mode() != chromaMode {
|
||||
return fmt.Errorf("style %q does not have a %q mode", style, mode)
|
||||
}
|
||||
} else {
|
||||
chromaStyle = styles.Get(style)
|
||||
}
|
||||
builder := chromaStyle.Builder()
|
||||
if highlightStyle != "" {
|
||||
builder.Add(chroma.LineHighlight, highlightStyle)
|
||||
}
|
||||
if lineNumbersInlineStyle != "" {
|
||||
builder.Add(chroma.LineNumbers, lineNumbersInlineStyle)
|
||||
}
|
||||
if lineNumbersTableStyle != "" {
|
||||
builder.Add(chroma.LineNumbersTable, lineNumbersTableStyle)
|
||||
}
|
||||
style, err := builder.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if omitEmpty {
|
||||
// See https://github.com/alecthomas/chroma/commit/5b2a4c5a26c503c79bc86ba3c4ae5b330028bd3d
|
||||
hugo.Deprecate("--omitEmpty", "Flag is no longer needed, empty classes are now always omitted.", "v0.149.0")
|
||||
}
|
||||
options := []html.Option{
|
||||
html.WithCSSComments(!omitClassComments),
|
||||
}
|
||||
if !modeSelector {
|
||||
// Only needed for the shared-scope overlay; --modeSelector
|
||||
// gives each mode its own scope and makes this redundant.
|
||||
options = append(options, html.WithCustomCSS(chromaCSSOverrides(style)))
|
||||
}
|
||||
|
||||
formatter := html.New(options...)
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
|
||||
formatter.WriteCSS(&buf, style)
|
||||
css := buf.String()
|
||||
if modeSelector {
|
||||
// Scope every selector under a top level mode class, e.g. ".dark .chroma".
|
||||
// This allows generating both light and dark stylesheets and toggling
|
||||
// them with a parent class on the page.
|
||||
// There's no upstream option for this (I think), so do string replacements for now.
|
||||
// TODO(bep) upstream option for this.
|
||||
var prefix string
|
||||
switch style.Mode() {
|
||||
case chroma.Light:
|
||||
prefix = ".light "
|
||||
case chroma.Dark:
|
||||
prefix = ".dark "
|
||||
default:
|
||||
return fmt.Errorf("style %q does not have a %q mode", style.Name, mode)
|
||||
}
|
||||
replacer := strings.NewReplacer(
|
||||
".bg {", prefix+".bg {",
|
||||
".chroma ", prefix+".chroma ",
|
||||
)
|
||||
css = replacer.Replace(css)
|
||||
css, err := highlight.ChromaStylesCSS(highlight.ChromaStylesOptions{
|
||||
Style: style,
|
||||
Mode: mode,
|
||||
ModeSelector: modeSelector,
|
||||
ClassDark: classDark,
|
||||
ClassLight: classLight,
|
||||
HighlightStyle: highlightStyle,
|
||||
LineNumbersInlineStyle: lineNumbersInlineStyle,
|
||||
LineNumbersTableStyle: lineNumbersTableStyle,
|
||||
OmitClassComments: omitClassComments,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
|
||||
fmt.Print(css)
|
||||
return nil
|
||||
},
|
||||
@@ -155,6 +95,10 @@ See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview
|
||||
_ = cmd.RegisterFlagCompletionFunc("mode", cobra.FixedCompletions([]string{"light", "dark"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
cmd.PersistentFlags().BoolVar(&modeSelector, "modeSelector", false, `scope selectors under a top level mode class, e.g. ".dark .chroma"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("modeSelector", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&classDark, "classDark", "dark", `class name used by --modeSelector for dark styles`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("classDark", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&classLight, "classLight", "light", `class name used by --modeSelector for light styles`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("classLight", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", `foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&lineNumbersInlineStyle, "lineNumbersInlineStyle", "", `foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"`)
|
||||
@@ -330,38 +274,6 @@ url: %s
|
||||
}
|
||||
}
|
||||
|
||||
// chromaCSSOverrides re-emits leaf token colors that Chroma's minifier drops
|
||||
// because they equal the style's default foreground (the .chroma color). Without
|
||||
// modeSelector scoping, a paired light/dark stylesheet shares the same .chroma
|
||||
// scope, and the light sheet's explicit rule (e.g. .chroma .nx) would otherwise
|
||||
// leak into dark mode, since an explicit declaration beats inheritance.
|
||||
func chromaCSSOverrides(style *chroma.Style) map[chroma.TokenType]string {
|
||||
bg := style.Get(chroma.Background)
|
||||
m := make(map[chroma.TokenType]string)
|
||||
for tt := range chroma.StandardTypes {
|
||||
if tt == chroma.Background || !style.Has(tt) || !chromaLeafToken(tt) {
|
||||
continue
|
||||
}
|
||||
entry := style.Get(tt)
|
||||
if !entry.Sub(bg).IsZero() || !entry.Colour.IsSet() {
|
||||
continue
|
||||
}
|
||||
if css := html.StyleEntryToCSS(chroma.StyleEntry{Colour: entry.Colour}); css != "" {
|
||||
m[tt] = css
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func chromaLeafToken(tt chroma.TokenType) bool {
|
||||
for other := range chroma.StandardTypes {
|
||||
if other != tt && (other.Category() == tt || other.SubCategory() == tt) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type genCommand struct {
|
||||
rootCmd *rootCommand
|
||||
|
||||
|
||||
@@ -1108,7 +1108,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
|
||||
fromPath := ev.Name
|
||||
|
||||
relPath, found := sourceFs.MakePathRelative(fromPath, true)
|
||||
relPath, found := sourceFs.MakePathRelative(fromPath, false)
|
||||
|
||||
if !found {
|
||||
// Not member of this virtual host.
|
||||
|
||||
@@ -81,9 +81,9 @@ func (s *StackThreadSafe[T]) DrainMatching(predicate func(T) bool) []T {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
var items []T
|
||||
for i := len(s.items) - 1; i >= 0; i-- {
|
||||
if predicate(s.items[i]) {
|
||||
items = append(items, s.items[i])
|
||||
for i, v := range slices.Backward(s.items) {
|
||||
if predicate(v) {
|
||||
items = append(items, v)
|
||||
s.items = slices.Delete(s.items, i, i+1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -105,6 +106,16 @@ func MD5FromStringHexEncoded(f string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// MD5FromReaderHexEncoded returns the MD5 hash of the given reader.
|
||||
func MD5FromReaderHexEncoded(r io.Reader) string {
|
||||
h := md5.New()
|
||||
_, err := io.Copy(h, r)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// HashString returns a hash from the given elements.
|
||||
// It will panic if the hash cannot be calculated.
|
||||
// Note that this hash should be used primarily for identity, not for change detection as
|
||||
@@ -124,11 +135,38 @@ func HashStringHex(vs ...any) string {
|
||||
var hashOptsPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &hashstructure.HashOptions{
|
||||
Hasher: xxhash.New(),
|
||||
Hasher: xxhash.New(),
|
||||
UnwrapFunc: unwrapForHashing,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// hashstructure only sees exported struct fields, so rewrite known identity types before hashing,
|
||||
// e.g. a Resource or Page nested in an options map hashes by its Key.
|
||||
func unwrapForHashing(v reflect.Value) (reflect.Value, error) {
|
||||
if v.Kind() != reflect.Struct {
|
||||
return v, nil
|
||||
}
|
||||
var in any
|
||||
if v.CanAddr() {
|
||||
// The common case; pointer receiver methods on a struct
|
||||
// reached through a pointer.
|
||||
in = v.Addr().Interface()
|
||||
} else {
|
||||
in = v.Interface()
|
||||
}
|
||||
switch t := in.(type) {
|
||||
case hashstructure.Hashable:
|
||||
// Let hashstructure handle it.
|
||||
return v, nil
|
||||
case keyer:
|
||||
return reflect.ValueOf(t.Key()), nil
|
||||
case identity.IdentityProvider:
|
||||
return reflect.ValueOf(t.GetIdentity()), nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func getHashOpts() *hashstructure.HashOptions {
|
||||
return hashOptsPool.Get().(*hashstructure.HashOptions)
|
||||
}
|
||||
@@ -145,15 +183,10 @@ func putHashOpts(opts *hashstructure.HashOptions) {
|
||||
func HashUint64(vs ...any) uint64 {
|
||||
var o any
|
||||
if len(vs) == 1 {
|
||||
o = toHashable(vs[0])
|
||||
o = vs[0]
|
||||
} else {
|
||||
elements := make([]any, len(vs))
|
||||
for i, e := range vs {
|
||||
elements[i] = toHashable(e)
|
||||
}
|
||||
o = elements
|
||||
o = vs
|
||||
}
|
||||
|
||||
hash, err := Hash(o)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -176,19 +209,6 @@ type keyer interface {
|
||||
Key() string
|
||||
}
|
||||
|
||||
// For structs, hashstructure.Hash only works on the exported fields,
|
||||
// so rewrite the input slice for known identity types.
|
||||
func toHashable(v any) any {
|
||||
switch t := v.(type) {
|
||||
case keyer:
|
||||
return t.Key()
|
||||
case identity.IdentityProvider:
|
||||
return t.GetIdentity()
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
type xxhashReadFrom struct {
|
||||
buff []byte
|
||||
*xxhash.Digest
|
||||
|
||||
@@ -178,3 +178,14 @@ func improveIfNilPointerMsg(inErr error) string {
|
||||
s := fmt.Sprintf("– %s is nil; wrap it in if or with: {{ with %s }}{{ .%s }}{{ end }}", receiverName, receiver, field)
|
||||
return nilPointerErrRe.ReplaceAllString(inErr.Error(), s)
|
||||
}
|
||||
|
||||
// Or returns the first non-nil error from the given list of errors.
|
||||
// If all errors are nil, it returns nil.
|
||||
func Or(errs ...error) error {
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -131,14 +131,13 @@ func (c *Scratch) DeleteInMap(key string, mapKey string) string {
|
||||
// GetSortedMapValues returns a sorted map previously filled with SetInMap.
|
||||
func (c *Scratch) GetSortedMapValues(key string) any {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
if c.values[key] == nil {
|
||||
c.mu.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
unsortedMap := c.values[key].(map[string]any)
|
||||
c.mu.RUnlock()
|
||||
var keys []string
|
||||
for mapKey := range unsortedMap {
|
||||
keys = append(keys, mapKey)
|
||||
|
||||
@@ -207,6 +207,30 @@ func TestScratchGetSortedMapValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScratchGetSortedMapValuesConcurrentWithSetInMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := NewScratch()
|
||||
scratch.SetInMap("key", "initial", "initial")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range 1000 {
|
||||
scratch.SetInMap("key", "value", i)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range 1000 {
|
||||
scratch.GetSortedMapValues("key")
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkScratchGet(b *testing.B) {
|
||||
scratch := NewScratch()
|
||||
scratch.Add("A", 1)
|
||||
|
||||
@@ -82,6 +82,9 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if shouldCopy != nil && !shouldCopy(fromFilename) {
|
||||
continue
|
||||
}
|
||||
if err := CopyFile(fs, fromFilename, toFilename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/version"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/internal/warpc"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
@@ -211,7 +212,10 @@ func GetDependencyList() []string {
|
||||
|
||||
// GetDependencyListNonGo returns a list of non-Go dependencies.
|
||||
func GetDependencyListNonGo() []string {
|
||||
deps := []string{formatDep("github.com/webmproject/libwebp", "v1.6.0")} // via WASM. TODO(bep) get versions from the plugin setup.
|
||||
var deps []string
|
||||
for _, dep := range warpc.GetWASMDeps() {
|
||||
deps = append(deps, formatDep(dep[0], dep[1]))
|
||||
}
|
||||
|
||||
if IsExtended {
|
||||
deps = append(
|
||||
@@ -225,7 +229,8 @@ func GetDependencyListNonGo() []string {
|
||||
if IsDartSassGeV2() {
|
||||
dartSassPath = "github.com/sass/dart-sass"
|
||||
}
|
||||
deps = append(deps,
|
||||
deps = append(
|
||||
deps,
|
||||
formatDep(dartSassPath+"/protocol", dartSass.ProtocolVersion),
|
||||
formatDep(dartSassPath+"/compiler", dartSass.CompilerVersion),
|
||||
formatDep(dartSassPath+"/implementation", dartSass.ImplementationVersion),
|
||||
|
||||
@@ -35,8 +35,8 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
|
||||
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.136.0.
|
||||
c.Assert(deprecationLogLevelFromVersion("0.141.0"), qt.Equals, logg.LevelError)
|
||||
// Subtract 5 from the minor version of the first ERRORed version => 0.145.0.
|
||||
c.Assert(deprecationLogLevelFromVersion("0.150.0"), qt.Equals, logg.LevelError)
|
||||
}
|
||||
|
||||
func TestMarkupScope(t *testing.T) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import "github.com/gohugoio/hugo/common/version"
|
||||
// This should be the only one.
|
||||
var CurrentVersion = version.Version{
|
||||
Major: 0,
|
||||
Minor: 164,
|
||||
Minor: 166,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -458,36 +458,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy privacy values.
|
||||
if c.Privacy.Twitter.Disable {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.Disable = c.Privacy.Twitter.Disable
|
||||
}
|
||||
if c.Privacy.Twitter.EnableDNT {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT
|
||||
}
|
||||
if c.Privacy.Twitter.Simple {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.Simple = c.Privacy.Twitter.Simple
|
||||
}
|
||||
|
||||
// Legacy services values.
|
||||
if c.Services.Twitter.DisableInlineCSS {
|
||||
hugo.DeprecateWithLogger("project config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0", logger.Logger())
|
||||
c.Services.X.DisableInlineCSS = c.Services.Twitter.DisableInlineCSS
|
||||
}
|
||||
|
||||
// Legacy permalink tokens
|
||||
for _, pc := range c.Permalinks {
|
||||
if strings.Contains(pc.Pattern, ":filename") {
|
||||
hugo.DeprecateWithLogger("the \":filename\" permalink token", "Use \":contentbasename\" instead.", "0.144.0", logger.Logger())
|
||||
}
|
||||
if strings.Contains(pc.Pattern, ":slugorfilename") {
|
||||
hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger())
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy render hook values.
|
||||
alternativeDetails := fmt.Sprintf(
|
||||
"Set to %q if previous value was false, or set to %q if previous value was true.",
|
||||
@@ -1035,29 +1005,30 @@ func (c Configs) GetByLang(lang string) config.AllProvider {
|
||||
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
|
||||
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
|
||||
RootConfig: RootConfig{
|
||||
Environment: hugo.EnvironmentProduction,
|
||||
TitleCaseStyle: "AP",
|
||||
PluralizeListTitles: true,
|
||||
CapitalizeListTitles: true,
|
||||
StaticDir: []string{"static"},
|
||||
SummaryLength: 70,
|
||||
Timeout: "60s",
|
||||
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
|
||||
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
|
||||
Environment: hugo.EnvironmentProduction,
|
||||
TitleCaseStyle: "AP",
|
||||
PluralizeListTitles: true,
|
||||
CapitalizeListTitles: true,
|
||||
StaticDir: []string{"static"},
|
||||
SummaryLength: 70,
|
||||
Timeout: "60s",
|
||||
|
||||
CommonDirs: config.CommonDirs{
|
||||
ArcheTypeDir: "archetypes",
|
||||
ContentDir: "content",
|
||||
ResourceDir: "resources",
|
||||
PublishDir: "public",
|
||||
ThemesDir: "themes",
|
||||
AssetDir: "assets",
|
||||
LayoutDir: "layouts",
|
||||
I18nDir: "i18n",
|
||||
DataDir: "data",
|
||||
},
|
||||
},
|
||||
//lint:ignore SA1019 Keep as adapter for now.
|
||||
ArcheTypeDir: "archetypes",
|
||||
ContentDir: "content",
|
||||
ResourceDir: "resources",
|
||||
PublishDir: "public",
|
||||
ThemesDir: "themes",
|
||||
//lint:ignore SA1019 Keep as adapter for now.
|
||||
AssetDir: "assets",
|
||||
//lint:ignore SA1019 Keep as adapter for now.
|
||||
LayoutDir: "layouts",
|
||||
//lint:ignore SA1019 Keep as adapter for now.
|
||||
I18nDir: "i18n",
|
||||
//lint:ignore SA1019 Keep as adapter for now.
|
||||
DataDir: "data",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram
|
||||
Twitter Twitter `json:"-"` // deprecated in favor of X in v0.141.0
|
||||
Vimeo Vimeo
|
||||
YouTube YouTube
|
||||
X X
|
||||
@@ -59,19 +58,6 @@ 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.
|
||||
type Twitter struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// When set to true, the Tweet 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 Tweet will be built.
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// Vimeo holds the privacy configuration settings related to the Vimeo shortcode.
|
||||
type Vimeo struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
@@ -39,12 +39,11 @@ const securityConfigKey = "security"
|
||||
var DefaultConfig = Config{
|
||||
Exec: Exec{
|
||||
Allow: MustNewWhitelist(
|
||||
"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
|
||||
"^go$", // for Go Modules
|
||||
"^git$", // For Git info
|
||||
"^node$", // Used as the runtime for Node tools.
|
||||
"^(dart-)?sass$", // sass, dart-sass
|
||||
"^go$", // for Go Modules
|
||||
"^git$", // For Git info
|
||||
"^node$", // Used as the runtime for Node tools.
|
||||
"^postcss$",
|
||||
"^tailwindcss$",
|
||||
),
|
||||
// These have been tested to work with Hugo's external programs
|
||||
// on Windows, Linux and MacOS.
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n allowContent = ['! ^text/html$']\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^node$', '^postcss$', '^tailwindcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z0-9]', '! ^https?://\\d+\\.', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowChildProcess = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
|
||||
"[security]\n allowContent = ['! ^text/html$']\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass$', '^go$', '^git$', '^node$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z0-9]', '! ^https?://\\d+\\.', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowChildProcess = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram `json:"-"` // the embedded instagram shortcode no longer uses this
|
||||
Twitter Twitter `json:"-"` // deprecated in favor of X in v0.141.0
|
||||
X X
|
||||
RSS RSS
|
||||
}
|
||||
@@ -61,15 +60,6 @@ type Instagram struct {
|
||||
AccessToken string // this is no longer used by the embedded instagram shortcode
|
||||
}
|
||||
|
||||
// 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
|
||||
// to disable the inline CSS provided by Hugo.
|
||||
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.
|
||||
|
||||
@@ -317,7 +317,7 @@ func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string {
|
||||
b, err := afero.ReadFile(fs, filename)
|
||||
if err != nil {
|
||||
// Print some debug info
|
||||
root := strings.Split(filename, helpers.FilePathSeparator)[0]
|
||||
root, _, _ := strings.Cut(filename, helpers.FilePathSeparator)
|
||||
afero.Walk(fs, root, func(path string, info os.FileInfo, err error) error {
|
||||
if info != nil && !info.IsDir() {
|
||||
fmt.Println(" ", path)
|
||||
|
||||
@@ -459,7 +459,7 @@ func (c TestConfig) IsZero() bool {
|
||||
|
||||
// BuildState are state used during a build.
|
||||
type BuildState struct {
|
||||
counter uint64
|
||||
counter atomic.Uint64
|
||||
|
||||
// Tracks invocations of the Build method.
|
||||
BuildCounter atomic.Uint64
|
||||
@@ -538,5 +538,5 @@ func (b *BuildState) GetFilenamesWithPostPrefix() []string {
|
||||
}
|
||||
|
||||
func (b *BuildState) Incr() int {
|
||||
return int(atomic.AddUint64(&b.counter, uint64(1)))
|
||||
return int(b.counter.Add(uint64(1)))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/bep/golocales v0.2.0
|
||||
github.com/bep/goportabletext v0.2.0
|
||||
github.com/bep/helpers v0.12.0
|
||||
github.com/bep/imagemeta v0.17.2
|
||||
github.com/bep/imagemeta v1.0.0
|
||||
github.com/bep/lazycache v0.8.1
|
||||
github.com/bep/logg v0.4.0
|
||||
github.com/bep/mclib v1.20401.20400
|
||||
@@ -27,19 +27,19 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/clbanning/mxj/v2 v2.7.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanw/esbuild v0.28.1
|
||||
github.com/evanw/esbuild v0.28.2
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/getkin/kin-openapi v0.140.0
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/getkin/kin-openapi v0.146.0
|
||||
github.com/gobuffalo/flect v1.0.3
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/goccy/go-yaml v1.19.2
|
||||
github.com/gohugoio/gift v0.2.0
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6
|
||||
github.com/gohugoio/go-radix v1.2.0
|
||||
github.com/gohugoio/hashstructure v0.6.0
|
||||
github.com/gohugoio/hashstructure v1.0.0
|
||||
github.com/gohugoio/httpcache v0.8.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.7.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.5.0
|
||||
@@ -47,11 +47,11 @@ require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hairyhenderson/go-codeowners v0.7.0
|
||||
github.com/jdkato/prose v1.2.1
|
||||
github.com/kyokomi/emoji/v2 v2.2.13
|
||||
github.com/kyokomi/emoji/v2 v2.2.14
|
||||
github.com/magefile/mage v1.17.2
|
||||
github.com/makeworld-the-better-one/dither/v2 v2.4.0
|
||||
github.com/marekm4/color-extractor v1.2.1
|
||||
github.com/mattn/go-isatty v0.0.22
|
||||
github.com/mattn/go-isatty v0.0.24
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
@@ -60,25 +60,25 @@ require (
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/pelletier/go-toml/v2 v2.4.3
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/rogpeppe/go-internal v1.15.0
|
||||
github.com/rogpeppe/go-internal v1.16.0
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/fsync v0.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tdewolff/minify/v2 v2.24.13
|
||||
github.com/tdewolff/parse/v2 v2.8.12
|
||||
github.com/tdewolff/minify/v2 v2.24.16
|
||||
github.com/tdewolff/parse/v2 v2.8.15
|
||||
github.com/tetratelabs/wazero v1.12.0
|
||||
github.com/yuin/goldmark v1.8.2
|
||||
github.com/yuin/goldmark v1.8.5
|
||||
github.com/yuin/goldmark-emoji v1.0.6
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.45.0
|
||||
golang.org/x/image v0.43.0
|
||||
golang.org/x/mod v0.37.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/sync v0.21.0
|
||||
golang.org/x/text v0.38.0
|
||||
golang.org/x/tools v0.47.0
|
||||
golang.org/x/image v0.44.0
|
||||
golang.org/x/mod v0.38.0
|
||||
golang.org/x/net v0.57.0
|
||||
golang.org/x/sync v0.22.0
|
||||
golang.org/x/text v0.40.0
|
||||
golang.org/x/tools v0.48.0
|
||||
google.golang.org/api v0.276.0
|
||||
rsc.io/qr v0.2.0
|
||||
)
|
||||
@@ -99,7 +99,7 @@ require (
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect
|
||||
github.com/JohannesKaufmann/dom v0.3.1 // indirect
|
||||
@@ -125,12 +125,12 @@ require (
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/dlclark/regexp2 v1.12.0 // indirect
|
||||
github.com/dlclark/regexp2/v2 v2.2.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
@@ -152,8 +152,8 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/oasdiff/yaml v0.1.0 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.13 // indirect
|
||||
github.com/oasdiff/yaml v0.1.1 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.14 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.2.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.6 // indirect
|
||||
@@ -163,7 +163,7 @@ require (
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
@@ -172,18 +172,18 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
google.golang.org/grpc v1.82.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
howett.net/plist v1.0.1 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect
|
||||
)
|
||||
|
||||
go 1.26.0
|
||||
go 1.27.0
|
||||
|
||||
@@ -84,8 +84,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0 h1:xfK3bbi6F2RDtaZFtUdKO3osOBIhNb+xTs8lFW6yx9o=
|
||||
@@ -164,8 +164,8 @@ github.com/bep/goportabletext v0.2.0 h1:CZ9f8jADBWqHwBymQiJJPCTSV/tHSA+PYzlUf86Y
|
||||
github.com/bep/goportabletext v0.2.0/go.mod h1:xDeA5+qcgKzJq6Q6XjAiBKtxLD3Yn7f6XP4joD3J3qU=
|
||||
github.com/bep/helpers v0.12.0 h1:tD6V2DQW0B+FUynF2etR/106S/TO9akm+vA/Hk24GxY=
|
||||
github.com/bep/helpers v0.12.0/go.mod h1:PfE7MGdA8sSQ19nyDh4tYbs5rAlStlJaDI21f/fnNps=
|
||||
github.com/bep/imagemeta v0.17.2 h1:fDyXM1eAqCfBeqGLqS6UsN4OfuLM0cdu70KuLCehjOg=
|
||||
github.com/bep/imagemeta v0.17.2/go.mod h1:+Hlp195TfZpzsqCxtDKTG6eWdyz2+F2V/oCYfr3CZKA=
|
||||
github.com/bep/imagemeta v1.0.0 h1:8JZmahegn5p1M9CpFmga7LoSxErpu1uFfpSzMUNju7A=
|
||||
github.com/bep/imagemeta v1.0.0/go.mod h1:+Hlp195TfZpzsqCxtDKTG6eWdyz2+F2V/oCYfr3CZKA=
|
||||
github.com/bep/lazycache v0.8.1 h1:ko6ASLjkPxyV5DMWoNNZ8B2M0weyjqXX8IZkjBoBtvg=
|
||||
github.com/bep/lazycache v0.8.1/go.mod h1:pbEiFsZoq7cLXvrTll0AHOPEurB1aGGxx4jKjOtlx9w=
|
||||
github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ=
|
||||
@@ -198,8 +198,8 @@ github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsV
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -220,15 +220,15 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/evanw/esbuild v0.28.1 h1:ds+yuRyUaZGx++GR56CrCeuXh8PVhVM4xq8v7PNELFc=
|
||||
github.com/evanw/esbuild v0.28.1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/evanw/esbuild v0.28.2 h1:A2uETn4jrQTcXaT/shwTDTYBxDjl7fV7nXmUrJxfA2w=
|
||||
github.com/evanw/esbuild v0.28.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
@@ -238,10 +238,10 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
|
||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/getkin/kin-openapi v0.140.0 h1:JFn675aXRFjyiZKa/BFWploGldQlI0gobp4J5k0EZ2g=
|
||||
github.com/getkin/kin-openapi v0.140.0/go.mod h1:lISrB64F0CPcuDJ3LdtPTMJBY8VENjR9wJBdrcT6J3g=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/getkin/kin-openapi v0.146.0 h1:RA/1RdxrSJW4oc1+6IfnYB6AO9CaGy8GTKPh0k4Ordo=
|
||||
github.com/getkin/kin-openapi v0.146.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -272,8 +272,8 @@ github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6 h1:pxlAea9eR
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20251018145728-cfcc22d823c6/go.mod h1:m5hu1im5Qc7LDycVLvee6MPobJiRLBYHklypFJR0/aE=
|
||||
github.com/gohugoio/go-radix v1.2.0 h1:D5GTk8jIoeXirBSc2P4E4NdHKDrenk9k9N0ctU5Yrhg=
|
||||
github.com/gohugoio/go-radix v1.2.0/go.mod h1:k6vDa0ebpbpgtzSj9lPGJcA4AZwJ9xUNObUy2vczPFM=
|
||||
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
|
||||
github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ=
|
||||
github.com/gohugoio/hashstructure v1.0.0 h1:vWYuyzs1n0LdI0F54TJQeYAiB44fHX7H9hCp9X6gHKg=
|
||||
github.com/gohugoio/hashstructure v1.0.0/go.mod h1:FSbTK4QwxucJ2bC4Lvrs9a6x0DbQDXNoyBO+h4nlCgE=
|
||||
github.com/gohugoio/httpcache v0.8.0 h1:hNdsmGSELztetYCsPVgjA960zSa4dfEqqF/SficorCU=
|
||||
github.com/gohugoio/httpcache v0.8.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.7.0 h1:I/n6v7VImJ3aISLnn73JAHXyjcQsMVvbguQPTk9Ehus=
|
||||
@@ -392,8 +392,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.14 h1:YOF6VL52613M0Qr9v4puJDD9QQPmyyjXedDDlrGzH80=
|
||||
github.com/kyokomi/emoji/v2 v2.2.14/go.mod h1:1AnYl9IgmJZXKd5m1PEijyyUw85SqYsuAr8lpU/s+9s=
|
||||
github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40=
|
||||
github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA=
|
||||
github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE=
|
||||
@@ -402,8 +402,8 @@ github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mr
|
||||
github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
@@ -418,10 +418,10 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
||||
github.com/oasdiff/yaml v0.1.0 h1:0bqZjfKc/8S9urj4JuwepX41WX9EoA6ifhU3SV06cXg=
|
||||
github.com/oasdiff/yaml v0.1.0/go.mod h1:kOlRmMdL2X3vucLCEQO5u61SU22RysnfXvcttrZA1O0=
|
||||
github.com/oasdiff/yaml3 v0.0.13 h1:06svmvOHOVBqF81+sY2EUScvUI/iS/vl2VIeUUxZQwg=
|
||||
github.com/oasdiff/yaml3 v0.0.13/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY=
|
||||
github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU=
|
||||
github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw=
|
||||
github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||
@@ -450,8 +450,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc=
|
||||
github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
@@ -489,11 +489,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tdewolff/minify/v2 v2.24.13 h1:xrcF7gKDnUszseEY9WX9mUlZII2v2Go/QAcAwRASw58=
|
||||
github.com/tdewolff/minify/v2 v2.24.13/go.mod h1:emvwoYeIl8bfAKqRU5ww95LX9Gpggpqv/naal9a8Yq0=
|
||||
github.com/tdewolff/parse/v2 v2.8.12 h1:5BBjfaCv482v3nltlS0u6wH1xJaxjR6ofDrWttNvROg=
|
||||
github.com/tdewolff/parse/v2 v2.8.12/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tdewolff/minify/v2 v2.24.16 h1:1d+IHElvmYM2zJtuykMK/esiqc4H/7pqPKAa7jpSUUU=
|
||||
github.com/tdewolff/minify/v2 v2.24.16/go.mod h1:G6yep6HFo3u2eoOcIVBKSNwpoW2wXvPjvnFRj3w1zlc=
|
||||
github.com/tdewolff/parse/v2 v2.8.15 h1:3/Psth16LHpsy2NS/7ShXDsoWt1x0f3tQMSbmQi+jO8=
|
||||
github.com/tdewolff/parse/v2 v2.8.15/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ=
|
||||
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
||||
@@ -502,8 +501,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
|
||||
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
|
||||
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -514,8 +513,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
@@ -545,8 +544,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -559,8 +558,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
|
||||
golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -582,8 +581,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -615,8 +614,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -638,8 +637,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -676,8 +675,8 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -685,8 +684,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -739,8 +738,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -815,10 +814,10 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -835,8 +834,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -16,6 +16,7 @@ package htesting
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -58,6 +59,27 @@ func CreateTempDir(fs afero.Fs, prefix string) (string, func(), error) {
|
||||
return tempDir, func() { fs.RemoveAll(tempDir) }, nil
|
||||
}
|
||||
|
||||
// IsCaseInsensitiveFs reports whether dir lives on a case-insensitive filesystem
|
||||
// (e.g. the default on macOS and Windows).
|
||||
func IsCaseInsensitiveFs(dir string) (bool, error) {
|
||||
f, err := os.CreateTemp(dir, "case-*.tmp")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
_, err = os.Stat(filepath.Join(dir, strings.ToUpper(filepath.Base(f.Name()))))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// BailOut panics with a stack trace after the given duration. Useful for
|
||||
// hanging tests.
|
||||
func BailOut(after time.Duration) {
|
||||
|
||||
@@ -15,10 +15,37 @@ package hugofs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// hasSymlinkParent reports whether any directory between name and base is a symlink.
|
||||
// Lstat only refrains from following the last element of a path, so callers checking
|
||||
// name itself need this to keep a symlinked parent from escaping base.
|
||||
// base itself is not checked; an empty base walks to the root of fs.
|
||||
func hasSymlinkParent(fs afero.Fs, base, name string) (bool, error) {
|
||||
for name != base {
|
||||
parent := filepath.Dir(name)
|
||||
if parent == name || parent == base || parent == "." {
|
||||
return false, nil
|
||||
}
|
||||
fi, err := LstatIfPossible(fs, parent)
|
||||
if err != nil {
|
||||
if herrors.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return true, nil
|
||||
}
|
||||
name = parent
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// NewDropSymlinksFs returns an afero.Fs wrapper that treats symlinks as non-existing files.
|
||||
func NewDropSymlinksFs(base afero.Fs) *DropSymlinksFs {
|
||||
return &DropSymlinksFs{base}
|
||||
@@ -48,5 +75,12 @@ func (fs *DropSymlinksFs) Stat(name string) (os.FileInfo, error) {
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
symlinkParent, err := hasSymlinkParent(fs.Fs, "", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if symlinkParent {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
@@ -729,10 +729,18 @@ func (fs *RootMappingFs) statRoot(root *RootMapping, filename string) (FileMetaI
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Don't allow symlinks to escape the mount.
|
||||
// Don't allow symlinks to escape the mount, neither as the file itself
|
||||
// nor as any directory between it and the mount root.
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
symlinkParent, err := hasSymlinkParent(fs.Fs, root.To, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if symlinkParent {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
var opener func() (afero.File, error)
|
||||
if !fi.IsDir() {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
@@ -23,6 +22,7 @@ import (
|
||||
|
||||
"github.com/bep/helpers/maphelpers"
|
||||
"github.com/gohugoio/go-radix"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
@@ -134,7 +134,7 @@ func (a *allPagesAssembler) createAllPages() error {
|
||||
}()
|
||||
}
|
||||
|
||||
if err := cmp.Or(a.doCreatePages("", 0), a.g.Wait()); err != nil {
|
||||
if err := herrors.Or(a.doCreatePages("", 0), a.g.Wait()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.pwRoot.WalkContext.HandleEventsAndHooks(); err != nil {
|
||||
@@ -362,7 +362,7 @@ func (a *allPagesAssembler) doCreatePages(prefix string, depth int) error {
|
||||
|
||||
switch v := n.(type) {
|
||||
case contentNodeSeq, contentNodes:
|
||||
return handleContentNodeSeq(contentNodeToSeq(v))
|
||||
return handleContentNodeSeq(cnh.contentNodeToSeq(v))
|
||||
case *pageMetaSource:
|
||||
n2, err = handlePageMetaSource(v, nil, false)
|
||||
if err != nil {
|
||||
@@ -781,7 +781,7 @@ func (a *allPagesAssembler) doCreatePages(prefix string, depth int) error {
|
||||
|
||||
if _, found := nodes[p.s.siteVector]; !found {
|
||||
var rs *resourceSource
|
||||
match := cnh.findContentNodeForSiteVector(p.s.siteVector, duplicateResourceFiles, contentNodeToSeq(n))
|
||||
match := cnh.findContentNodeForSiteVector(p.s.siteVector, duplicateResourceFiles, cnh.contentNodeToSeq(n))
|
||||
if match == nil {
|
||||
return true
|
||||
}
|
||||
@@ -1385,8 +1385,11 @@ func (a *allPagesAssembler) createMissingTaxonomies() error {
|
||||
for viewName, languages := range viewLanguages {
|
||||
key := viewName.pluralTreeKey
|
||||
if a.h.isRebuild() {
|
||||
if v := tree.Get(key); v != nil {
|
||||
// Already there.
|
||||
// Note that we cannot use tree.Get here, as the tree at this point
|
||||
// may hold not yet assembled *pageMetaSource nodes.
|
||||
// We're only interested in whether the auto created (not file backed)
|
||||
// taxonomy node is still there.
|
||||
if n, found := tree.GetRaw(key); found && cnh.hasAutoContentNode(n) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ var _ contentNode = (*contentNodeSeq)(nil)
|
||||
|
||||
var (
|
||||
_ contentNode = (*resourceSource)(nil)
|
||||
_ contentNodeContentWeightProvider = (*pageMetaSource)(nil)
|
||||
_ contentNodeContentWeightProvider = (*pageState)(nil)
|
||||
_ contentNodeForSites = (*pageState)(nil)
|
||||
_ contentNodePage = (*contentNodes)(nil)
|
||||
@@ -395,7 +396,15 @@ func contentNodeToContentNodesPage(n contentNode) (contentNodesMap, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func contentNodeToSeq(n contentNodeForEach) contentNodeSeq {
|
||||
// hasAutoContentNode reports whether n holds at least one node that is not backed by a file.
|
||||
func (h helperContentNode) hasAutoContentNode(n contentNodeForEach) bool {
|
||||
return !n.forEeachContentNode(func(_ sitesmatrix.Vector, nn contentNode) bool {
|
||||
wp, ok := nn.(contentNodeContentWeightProvider)
|
||||
return ok && wp.contentWeight() > 0
|
||||
})
|
||||
}
|
||||
|
||||
func (h helperContentNode) contentNodeToSeq(n contentNodeForEach) contentNodeSeq {
|
||||
if nn, ok := n.(contentNodeSeq); ok {
|
||||
return nn
|
||||
}
|
||||
@@ -406,7 +415,7 @@ func contentNodeToSeq(n contentNodeForEach) contentNodeSeq {
|
||||
}
|
||||
}
|
||||
|
||||
func contentNodeToSeq2(n contentNodeForEach) contentNodeSeq2 {
|
||||
func (h helperContentNode) contentNodeToSeq2(n contentNodeForEach) contentNodeSeq2 {
|
||||
if nn, ok := n.(contentNodeSeq2); ok {
|
||||
return nn
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type contentNodeShifter struct {
|
||||
conf config.AllProvider // Used for logging/debugging.
|
||||
}
|
||||
|
||||
func (s *contentNodeShifter) Delete(n contentNode, vec sitesmatrix.Vector) (contentNode, bool, bool) {
|
||||
func (s *contentNodeShifter) Delete(n contentNode, vec sitesmatrix.Vector) (contentNode, contentNode, bool, bool) {
|
||||
switch v := n.(type) {
|
||||
case contentNodesMap:
|
||||
deleted, wasDeleted := v[vec]
|
||||
@@ -33,49 +33,53 @@ func (s *contentNodeShifter) Delete(n contentNode, vec sitesmatrix.Vector) (cont
|
||||
delete(v, vec)
|
||||
resource.MarkStale(deleted)
|
||||
}
|
||||
return deleted, wasDeleted, len(v) == 0
|
||||
return v, deleted, wasDeleted, len(v) == 0
|
||||
case contentNodeForSite:
|
||||
if v.siteVector() != vec {
|
||||
return nil, false, false
|
||||
return v, nil, false, false
|
||||
}
|
||||
resource.MarkStale(v)
|
||||
return v, true, true
|
||||
return nil, v, true, true
|
||||
case contentNodes:
|
||||
var deleted contentNodes
|
||||
for i, nn := range v {
|
||||
if vv, ok, _ := s.Delete(nn, vec); ok {
|
||||
var deleted, remaining contentNodes
|
||||
for _, nn := range v {
|
||||
updated, vv, ok, isEmpty := s.Delete(nn, vec)
|
||||
if ok {
|
||||
deleted = append(deleted, vv)
|
||||
v = append(v[:i], v[i+1:]...)
|
||||
}
|
||||
if !isEmpty {
|
||||
remaining = append(remaining, updated)
|
||||
}
|
||||
}
|
||||
if len(deleted) == 0 {
|
||||
return nil, false, false
|
||||
return v, nil, false, false
|
||||
}
|
||||
return deleted, true, len(v) == 0
|
||||
return remaining, deleted, true, len(remaining) == 0
|
||||
default:
|
||||
v = v.(contentNodeSingle) // Ensure single node.
|
||||
resource.MarkStale(v)
|
||||
return v, true, true
|
||||
|
||||
vv := v.(contentNodeSingle) // Ensure single node.
|
||||
resource.MarkStale(vv)
|
||||
return nil, vv, true, true
|
||||
}
|
||||
}
|
||||
|
||||
func (s *contentNodeShifter) DeleteFunc(v contentNode, f func(n contentNode) bool) bool {
|
||||
func (s *contentNodeShifter) DeleteFunc(v contentNode, f func(n contentNode) bool) (contentNode, bool) {
|
||||
switch ss := v.(type) {
|
||||
case contentNodeSingle:
|
||||
if f(ss) {
|
||||
resource.MarkStale(ss)
|
||||
return true
|
||||
return nil, true
|
||||
}
|
||||
return false
|
||||
return ss, false
|
||||
case contentNodes:
|
||||
for i, n := range ss {
|
||||
var remaining contentNodes
|
||||
for _, n := range ss {
|
||||
if f(n) {
|
||||
resource.MarkStale(n)
|
||||
ss = append(ss[:i], ss[i+1:]...)
|
||||
} else {
|
||||
remaining = append(remaining, n)
|
||||
}
|
||||
}
|
||||
return len(ss) == 0
|
||||
return remaining, len(remaining) == 0
|
||||
case contentNodesMap:
|
||||
for k, n := range ss {
|
||||
if f(n) {
|
||||
@@ -83,7 +87,7 @@ func (s *contentNodeShifter) DeleteFunc(v contentNode, f func(n contentNode) boo
|
||||
delete(ss, k)
|
||||
}
|
||||
}
|
||||
return len(ss) == 0
|
||||
return ss, len(ss) == 0
|
||||
default:
|
||||
panic(fmt.Sprintf("DeleteFunc: unknown type %T", v))
|
||||
}
|
||||
@@ -105,7 +109,7 @@ func (s *contentNodeShifter) ForEeachInAllDimensions(n contentNode, f func(conte
|
||||
|
||||
func (s *contentNodeShifter) ForEeachInDimension(n contentNode, vec sitesmatrix.Vector, d int, f func(contentNode) bool) {
|
||||
LOOP1:
|
||||
for vec2, v := range contentNodeToSeq2(n) {
|
||||
for vec2, v := range cnh.contentNodeToSeq2(n) {
|
||||
for i, v := range vec2 {
|
||||
if i != d && v != vec[i] {
|
||||
continue LOOP1
|
||||
@@ -170,7 +174,7 @@ func (s *contentNodeShifter) Shift(n contentNode, siteVector sitesmatrix.Vector,
|
||||
}
|
||||
// The exact match is an auto page (not backed by a file).
|
||||
// Check if there's a file-backed complement that should take precedence.
|
||||
if vvv := cnh.findContentNodeForSiteVector(siteVector, fallback, contentNodeToSeq(n)); vvv != nil {
|
||||
if vvv := cnh.findContentNodeForSiteVector(siteVector, fallback, cnh.contentNodeToSeq(n)); vvv != nil {
|
||||
return vvv, true
|
||||
}
|
||||
return exact, true
|
||||
@@ -180,7 +184,7 @@ func (s *contentNodeShifter) Shift(n contentNode, siteVector sitesmatrix.Vector,
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if vvv := cnh.findContentNodeForSiteVector(siteVector, fallback, contentNodeToSeq(n)); vvv != nil {
|
||||
if vvv := cnh.findContentNodeForSiteVector(siteVector, fallback, cnh.contentNodeToSeq(n)); vvv != nil {
|
||||
return vvv, true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2025 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 hugolib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/hugolib/sitesmatrix"
|
||||
)
|
||||
|
||||
type testContentNodeForSite struct {
|
||||
vec sitesmatrix.Vector
|
||||
}
|
||||
|
||||
func (n *testContentNodeForSite) Path() string { return "/test" }
|
||||
func (n *testContentNodeForSite) nodeCategorySingle() {}
|
||||
func (n *testContentNodeForSite) siteVector() sitesmatrix.Vector { return n.vec }
|
||||
func (n *testContentNodeForSite) forEeachContentNode(f func(sitesmatrix.Vector, contentNode) bool) bool {
|
||||
return f(n.vec, n)
|
||||
}
|
||||
|
||||
// See issue 15207.
|
||||
func TestContentNodeShifterDeleteMultipleFromNodes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
s := &contentNodeShifter{}
|
||||
vec := sitesmatrix.Vector{2, 0, 0}
|
||||
other := sitesmatrix.Vector{1, 0, 0}
|
||||
|
||||
newNodes := func() contentNodes {
|
||||
return contentNodes{
|
||||
&testContentNodeForSite{vec: other},
|
||||
&testContentNodeForSite{vec: vec},
|
||||
&testContentNodeForSite{vec: other},
|
||||
&testContentNodeForSite{vec: other},
|
||||
&testContentNodeForSite{vec: vec},
|
||||
}
|
||||
}
|
||||
|
||||
updated, deleted, wasDeleted, isEmpty := s.Delete(newNodes(), vec)
|
||||
c.Assert(wasDeleted, qt.IsTrue)
|
||||
c.Assert(isEmpty, qt.IsFalse)
|
||||
c.Assert(deleted.(contentNodes), qt.HasLen, 2)
|
||||
remaining := updated.(contentNodes)
|
||||
c.Assert(remaining, qt.HasLen, 3)
|
||||
for _, n := range remaining {
|
||||
c.Assert(n.(contentNodeForSite).siteVector(), qt.Equals, other)
|
||||
}
|
||||
|
||||
// Delete all.
|
||||
updated, deleted, wasDeleted, isEmpty = s.Delete(contentNodes{
|
||||
&testContentNodeForSite{vec: vec},
|
||||
&testContentNodeForSite{vec: vec},
|
||||
}, vec)
|
||||
c.Assert(wasDeleted, qt.IsTrue)
|
||||
c.Assert(isEmpty, qt.IsTrue)
|
||||
c.Assert(deleted.(contentNodes), qt.HasLen, 2)
|
||||
c.Assert(updated, qt.IsNil)
|
||||
|
||||
// Delete none.
|
||||
nodes := contentNodes{&testContentNodeForSite{vec: other}}
|
||||
updated, deleted, wasDeleted, isEmpty = s.Delete(nodes, vec)
|
||||
c.Assert(wasDeleted, qt.IsFalse)
|
||||
c.Assert(isEmpty, qt.IsFalse)
|
||||
c.Assert(deleted, qt.IsNil)
|
||||
c.Assert(updated.(contentNodes), qt.HasLen, 1)
|
||||
}
|
||||
@@ -258,12 +258,15 @@ func (s *testShifter) Insert(old, new *testValue) (*testValue, *testValue, bool)
|
||||
return new, old, true
|
||||
}
|
||||
|
||||
func (s *testShifter) Delete(n *testValue, dimension sitesmatrix.Vector) (*testValue, bool, bool) {
|
||||
return nil, true, true
|
||||
func (s *testShifter) Delete(n *testValue, dimension sitesmatrix.Vector) (*testValue, *testValue, bool, bool) {
|
||||
return nil, n, true, true
|
||||
}
|
||||
|
||||
func (s *testShifter) DeleteFunc(v *testValue, f func(*testValue) bool) bool {
|
||||
return f(v)
|
||||
func (s *testShifter) DeleteFunc(v *testValue, f func(*testValue) bool) (*testValue, bool) {
|
||||
if f(v) {
|
||||
return nil, true
|
||||
}
|
||||
return v, false
|
||||
}
|
||||
|
||||
func (s *testShifter) Shift(n *testValue, dimension sitesmatrix.Vector, fallback bool) (v *testValue, ok bool) {
|
||||
|
||||
@@ -49,12 +49,14 @@ type (
|
||||
// and a bool indicating if an existing record is updated.
|
||||
Insert(old, new T) (T, T, bool)
|
||||
|
||||
// Delete deletes T from the given dimension and returns the deleted T and whether the dimension was deleted and if it's empty after the delete.
|
||||
Delete(v T, dimension sitesmatrix.Vector) (T, bool, bool)
|
||||
// Delete deletes T from the given dimension.
|
||||
// It returns the updated T, the deleted T, whether anything was deleted
|
||||
// and whether T is empty after the delete.
|
||||
Delete(v T, dimension sitesmatrix.Vector) (T, T, bool, bool)
|
||||
|
||||
// DeleteFunc deletes nodes in v from the tree where the given function returns true.
|
||||
// It returns true if it's empty after the delete.
|
||||
DeleteFunc(v T, f func(n T) bool) bool
|
||||
// DeleteFunc deletes nodes in v where the given function returns true.
|
||||
// It returns the updated T and whether it's empty after the delete.
|
||||
DeleteFunc(v T, f func(n T) bool) (T, bool)
|
||||
|
||||
// Shift shifts v into the given dimension,
|
||||
// if fallback is true, it will fall back a fallback match if found.
|
||||
@@ -118,7 +120,7 @@ func (r *NodeShiftTree[T]) DeleteFuncRaw(key string, f func(T) bool) (T, int) {
|
||||
return lastDeleted, count
|
||||
}
|
||||
|
||||
isEmpty := r.shifter.DeleteFunc(v, func(n T) bool {
|
||||
updated, isEmpty := r.shifter.DeleteFunc(v, func(n T) bool {
|
||||
if f(n) {
|
||||
count++
|
||||
lastDeleted = n
|
||||
@@ -129,6 +131,8 @@ func (r *NodeShiftTree[T]) DeleteFuncRaw(key string, f func(T) bool) (T, int) {
|
||||
|
||||
if isEmpty {
|
||||
r.tree.Delete(key)
|
||||
} else if count > 0 {
|
||||
r.tree.Insert(key, updated)
|
||||
}
|
||||
|
||||
return lastDeleted, count
|
||||
@@ -161,10 +165,15 @@ func (r *NodeShiftTree[T]) delete(key string) (T, bool) {
|
||||
var wasDeleted bool
|
||||
var deleted T
|
||||
if v, ok := r.tree.Get(key); ok {
|
||||
var isEmpty bool
|
||||
deleted, wasDeleted, isEmpty = r.shifter.Delete(v, r.siteVector)
|
||||
var (
|
||||
updated T
|
||||
isEmpty bool
|
||||
)
|
||||
updated, deleted, wasDeleted, isEmpty = r.shifter.Delete(v, r.siteVector)
|
||||
if isEmpty {
|
||||
r.tree.Delete(key)
|
||||
} else if wasDeleted {
|
||||
r.tree.Insert(key, updated)
|
||||
}
|
||||
}
|
||||
return deleted, wasDeleted
|
||||
|
||||
@@ -16,6 +16,7 @@ package doctree
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -251,8 +252,8 @@ func (ctx *WalkContext[T]) HandleEvents() error {
|
||||
// Loop the event handlers in reverse order so
|
||||
// that events created by the handlers themselves will
|
||||
// be picked up further up the tree.
|
||||
for i := len(ctx.eventHandlers[event.Name]) - 1; i >= 0; i-- {
|
||||
ctx.eventHandlers[event.Name][i](event)
|
||||
for _, v := range slices.Backward(ctx.eventHandlers[event.Name]) {
|
||||
v(event)
|
||||
if event.stopPropagation {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -426,16 +426,10 @@ func (d *SourceFilesystem) RealFilename(rel string) string {
|
||||
}
|
||||
|
||||
// Contains returns whether the given filename is a member of the current filesystem.
|
||||
// It's stat-free, so it also works for deleted files.
|
||||
func (d *SourceFilesystem) Contains(filename string) bool {
|
||||
for _, dir := range d.mounts() {
|
||||
if !dir.IsDir() {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(filename, dir.Meta().Filename) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
cps, _ := d.ReverseLookup(filename, false)
|
||||
return len(cps) > 0
|
||||
}
|
||||
|
||||
// RealDirs gets a list of absolute paths to directories starting from the given
|
||||
|
||||
@@ -1080,6 +1080,8 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
|
||||
changes = append(changes, identity.GenghisKhan)
|
||||
case files.ComponentFolderArchetypes:
|
||||
// Ignore for now.
|
||||
case files.ComponentFolderStatic:
|
||||
// Handled by the static file syncer.
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown component: %q", pathInfo.Component()))
|
||||
}
|
||||
|
||||
@@ -231,11 +231,12 @@ type IntegrationTestBuilder struct {
|
||||
|
||||
Cfg IntegrationTestConfig
|
||||
|
||||
changedFiles []string
|
||||
createdFiles []string
|
||||
removedFiles []string
|
||||
renamedFiles []string
|
||||
renamedDirs []string
|
||||
changedFiles []string
|
||||
createdFiles []string
|
||||
removedFiles []string
|
||||
renamedFiles []string
|
||||
atomicSavedFiles []string
|
||||
renamedDirs []string
|
||||
|
||||
buildCount int
|
||||
GCCount int
|
||||
@@ -748,6 +749,14 @@ func (s *IntegrationTestBuilder) EditFileReplaceAll(filename, old, new string) *
|
||||
})
|
||||
}
|
||||
|
||||
// EditFileAtomicReplaceAll edits a file and simulates an editor atomic save by
|
||||
// emitting a fsnotify.Remove event for an existing path (see changeEvents).
|
||||
func (s *IntegrationTestBuilder) EditFileAtomicReplaceAll(filename, old, new string) *IntegrationTestBuilder {
|
||||
return s.EditFileAtomicReplaceFunc(filename, func(s string) string {
|
||||
return strings.ReplaceAll(s, old, new)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) EditFileReplaceFunc(filename string, replacementFunc func(s string) string) *IntegrationTestBuilder {
|
||||
absFilename := s.absFilename(filename)
|
||||
b, err := afero.ReadFile(s.fs.Source, absFilename)
|
||||
@@ -758,6 +767,16 @@ func (s *IntegrationTestBuilder) EditFileReplaceFunc(filename string, replacemen
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) EditFileAtomicReplaceFunc(filename string, replacementFunc func(s string) string) *IntegrationTestBuilder {
|
||||
absFilename := s.absFilename(filename)
|
||||
b, err := afero.ReadFile(s.fs.Source, absFilename)
|
||||
s.Assert(err, qt.IsNil)
|
||||
s.atomicSavedFiles = append(s.atomicSavedFiles, absFilename)
|
||||
oldContent := string(b)
|
||||
s.writeSource(absFilename, replacementFunc(oldContent))
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) EditFileAppend(filename, contnt string) *IntegrationTestBuilder {
|
||||
absFilename := s.absFilename(filename)
|
||||
b, err := afero.ReadFile(s.fs.Source, absFilename)
|
||||
@@ -905,8 +924,9 @@ func (s *IntegrationTestBuilder) initBuilder() error {
|
||||
|
||||
if s.Cfg.Running {
|
||||
flags.Set("internal", hmaps.Params{
|
||||
"running": s.Cfg.Running,
|
||||
"watch": s.Cfg.Running,
|
||||
"running": s.Cfg.Running,
|
||||
"watch": s.Cfg.Running,
|
||||
"fastRenderMode": s.Cfg.FastRenderMode,
|
||||
})
|
||||
} else if s.Cfg.Watching {
|
||||
flags.Set("internal", hmaps.Params{
|
||||
@@ -1043,6 +1063,7 @@ func (s *IntegrationTestBuilder) reset() {
|
||||
s.createdFiles = nil
|
||||
s.removedFiles = nil
|
||||
s.renamedFiles = nil
|
||||
s.atomicSavedFiles = nil
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) build(cfg BuildCfg) error {
|
||||
@@ -1091,6 +1112,15 @@ func (s *IntegrationTestBuilder) changeEvents() []fsnotify.Event {
|
||||
})
|
||||
}
|
||||
|
||||
for _, v := range s.atomicSavedFiles {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
// The watcher was watching the inode that got replaced by the rename,
|
||||
// so we get a Remove for a file that's still on disk.
|
||||
Op: fsnotify.Remove,
|
||||
})
|
||||
}
|
||||
|
||||
for _, v := range s.renamedDirs {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
@@ -1150,23 +1180,7 @@ func (s *IntegrationTestBuilder) readFileFromFs(t testing.TB, fs afero.Fs, filen
|
||||
t.Helper()
|
||||
filename = filepath.Clean(filename)
|
||||
b, err := afero.ReadFile(fs, filename)
|
||||
if err != nil {
|
||||
// Print some debug info
|
||||
hadSlash := strings.HasPrefix(filename, helpers.FilePathSeparator)
|
||||
start := 0
|
||||
if hadSlash {
|
||||
start = 1
|
||||
}
|
||||
end := start + 1
|
||||
|
||||
parts := strings.Split(filename, helpers.FilePathSeparator)
|
||||
if parts[start] == "work" {
|
||||
end++
|
||||
}
|
||||
|
||||
s.Assert(err, qt.IsNil)
|
||||
|
||||
}
|
||||
s.Assert(err, qt.IsNil)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
@@ -1203,6 +1217,10 @@ type IntegrationTestConfig struct {
|
||||
// Whether to simulate server mode.
|
||||
Running bool
|
||||
|
||||
// Whether to simulate the server's fast render mode.
|
||||
// Only used when Running is set.
|
||||
FastRenderMode bool
|
||||
|
||||
// Watch for changes.
|
||||
// This is (currently) always set to true when Running is set.
|
||||
// Note that the CLI for the server does allow for --watch=false, but that is not used in these test.
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"iter"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -174,10 +173,6 @@ func (ps *pageState) Param(key any) (any, error) {
|
||||
return resource.Param(ps, ps.s.Params(), key)
|
||||
}
|
||||
|
||||
func (ps *pageState) Key() string {
|
||||
return "page-" + strconv.FormatUint(ps.pid, 10)
|
||||
}
|
||||
|
||||
// RelatedKeywords implements the related.Document interface needed for fast page searches.
|
||||
func (ps *pageState) RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error) {
|
||||
v, found, err := page.NamedPageMetaValue(ps, cfg.Name)
|
||||
@@ -225,10 +220,7 @@ func (ps *pageState) forEeachContentNode(f func(v sitesmatrix.Vector, n contentN
|
||||
}
|
||||
|
||||
func (ps *pageState) contentWeight() int {
|
||||
if ps.m.f == nil {
|
||||
return 0
|
||||
}
|
||||
return ps.m.f.FileInfo().Meta().Weight
|
||||
return ps.m.pageMetaSource.contentWeight()
|
||||
}
|
||||
|
||||
func (ps *pageState) nodeSourceEntryID() any {
|
||||
@@ -340,10 +332,8 @@ func (ps *pageState) RegularPagesRecursive() page.Pages {
|
||||
case kinds.KindSection, kinds.KindHome:
|
||||
return ps.s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: ps.Path(),
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
},
|
||||
Path: ps.Path(),
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
Recursive: true,
|
||||
},
|
||||
)
|
||||
@@ -362,10 +352,8 @@ func (ps *pageState) RegularPages() page.Pages {
|
||||
case kinds.KindSection, kinds.KindHome, kinds.KindTaxonomy:
|
||||
return ps.s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: ps.Path(),
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
},
|
||||
Path: ps.Path(),
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
},
|
||||
)
|
||||
case kinds.KindTerm:
|
||||
@@ -387,13 +375,11 @@ func (ps *pageState) Pages() page.Pages {
|
||||
case kinds.KindSection, kinds.KindHome:
|
||||
return ps.s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: ps.Path(),
|
||||
KeyPart: "page-section",
|
||||
Include: pagePredicates.ShouldListLocal.And(
|
||||
pagePredicates.KindPage.Or(pagePredicates.KindSection),
|
||||
).BoolFunc(),
|
||||
},
|
||||
Path: ps.Path(),
|
||||
KeyPart: "page-section",
|
||||
Include: pagePredicates.ShouldListLocal.And(
|
||||
pagePredicates.KindPage.Or(pagePredicates.KindSection),
|
||||
).BoolFunc(),
|
||||
},
|
||||
)
|
||||
case kinds.KindTerm:
|
||||
@@ -405,11 +391,9 @@ func (ps *pageState) Pages() page.Pages {
|
||||
case kinds.KindTaxonomy:
|
||||
return ps.s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: ps.Path(),
|
||||
KeyPart: "term",
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindTerm).BoolFunc(),
|
||||
},
|
||||
Path: ps.Path(),
|
||||
KeyPart: "term",
|
||||
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindTerm).BoolFunc(),
|
||||
Recursive: true,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -807,13 +807,13 @@ func (c *cachedContentScope) contentPlain(ctx context.Context) (contentPlainPlai
|
||||
|
||||
// TODO(bep) is set in a test. Fix that.
|
||||
if result.fuzzyWordCount == 0 {
|
||||
result.fuzzyWordCount = (result.wordCount + 100) / 100 * 100
|
||||
result.fuzzyWordCount = (result.wordCount + 99) / 100 * 100
|
||||
}
|
||||
|
||||
if isCJKLanguage {
|
||||
result.readingTime = (result.wordCount + 500) / 501
|
||||
result.readingTime = (result.wordCount + 499) / 500
|
||||
} else {
|
||||
result.readingTime = (result.wordCount + 212) / 213
|
||||
result.readingTime = (result.wordCount + 211) / 212
|
||||
}
|
||||
|
||||
rs.Value = result
|
||||
@@ -941,10 +941,8 @@ func (c *cachedContentScope) RenderString(ctx context.Context, args ...any) (tem
|
||||
|
||||
if pageparser.HasShortcode(contentToRender) {
|
||||
ct := contentTableOfContents{
|
||||
sourceInfo: sourceInfo{
|
||||
filename: pco.po.p.pathOrTitle() + " (rendered from string)",
|
||||
source: []byte(contentToRender),
|
||||
},
|
||||
filename: pco.po.p.pathOrTitle() + " (rendered from string)",
|
||||
source: []byte(contentToRender),
|
||||
}
|
||||
ct.contentToRender = ct.source
|
||||
// String contains a shortcode.
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gobuffalo/flect"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
@@ -98,6 +97,13 @@ func (m *pageMetaSource) String() string {
|
||||
return fmt.Sprintf("pageMetaSource(%s)", m.pathInfo)
|
||||
}
|
||||
|
||||
func (m *pageMetaSource) contentWeight() int {
|
||||
if m.f == nil {
|
||||
return 0
|
||||
}
|
||||
return m.f.FileInfo().Meta().Weight
|
||||
}
|
||||
|
||||
func (m *pageMetaSource) nodeCategoryPage() {
|
||||
// Marker method.
|
||||
}
|
||||
@@ -693,16 +699,6 @@ params:
|
||||
continue
|
||||
}
|
||||
|
||||
if loki == "path" || loki == "kind" || loki == "lang" {
|
||||
// See issue 12484.
|
||||
// Only warn when the key was set at the top level of the
|
||||
// original front matter; cascade.params can legitimately carry
|
||||
// these names as user params (issue 14848).
|
||||
if _, ok := pm.pageConfigSource.Frontmatter[loki]; ok {
|
||||
hugo.DeprecateLevelMin(loki+" in front matter", "", "v0.144.0", logg.LevelWarn)
|
||||
}
|
||||
}
|
||||
|
||||
switch loki {
|
||||
case "title":
|
||||
pcfg.Title = cast.ToString(v)
|
||||
|
||||
@@ -149,6 +149,11 @@ func (po *pageOutput) Aliases() []string {
|
||||
return aliases
|
||||
}
|
||||
|
||||
// Key returns a unique key for this page output, used to e.g. hashing.
|
||||
func (po *pageOutput) Key() string {
|
||||
return po.p.Path() + po.f.Name
|
||||
}
|
||||
|
||||
func (po *pageOutput) incrRenderState() {
|
||||
po.renderState++
|
||||
po.renderOnce = true
|
||||
|
||||
@@ -104,21 +104,34 @@ func (pco *pageContentOutput) Reset() {
|
||||
pco.renderHooks = &renderHooks{}
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) Render(ctx context.Context, layout ...string) (template.HTML, error) {
|
||||
if len(layout) == 0 {
|
||||
return "", errors.New("no layout given")
|
||||
func (pco *pageContentOutput) Render(ctx context.Context, args ...any) (template.HTML, error) {
|
||||
if len(args) == 0 {
|
||||
return "", errors.New("no view given")
|
||||
}
|
||||
templ, found, err := pco.po.p.resolveTemplate(layout...)
|
||||
if len(args) > 2 {
|
||||
return "", errors.New("too many arguments, expected VIEW [CONTEXT]")
|
||||
}
|
||||
view, err := cast.ToStringE(args[0])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert view argument to string: %w", err)
|
||||
}
|
||||
|
||||
// Make sure to send the *pageState and not the *pageContentOutput to the template.
|
||||
var data any = pco.po.p
|
||||
if len(args) == 2 {
|
||||
data = args[1]
|
||||
}
|
||||
|
||||
templ, found, err := pco.po.p.resolveTemplate(view)
|
||||
if err != nil {
|
||||
return "", pco.po.p.wrapError(err)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return "", fmt.Errorf("template %q not found", layout[0])
|
||||
return "", fmt.Errorf("template %q not found", view)
|
||||
}
|
||||
|
||||
// Make sure to send the *pageState and not the *pageContentOutput to the template.
|
||||
res, err := executeToString(ctx, pco.po.p.s.GetTemplateStore(), templ, pco.po.p)
|
||||
res, err := executeToString(ctx, pco.po.p.s.GetTemplateStore(), templ, data)
|
||||
if err != nil {
|
||||
return "", pco.po.p.wrapError(fmt.Errorf("failed to execute template %s: %w", templ.Name(), err))
|
||||
}
|
||||
|
||||
@@ -412,7 +412,8 @@ baseURL = "http://example.com/"
|
||||
p := b.H.Sites[0].RegularPages()[0]
|
||||
|
||||
b.Assert(p.Summary(context.Background()), qt.Equals, template.HTML(
|
||||
"<p>The <a href=\"http://gohugo.io/\">best static site generator</a>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup></p>"))
|
||||
"<p>The <a href=\"http://gohugo.io/\">best static site generator</a>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup></p>",
|
||||
))
|
||||
|
||||
cnt := content(p)
|
||||
b.Assert(cnt, qt.Equals, "<p>The <a href=\"http://gohugo.io/\">best static site generator</a>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup></p>\n<div class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\">\n<p>Many people say so. <a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">↩︎</a></p>\n</li>\n</ol>\n</div>")
|
||||
@@ -730,7 +731,8 @@ This is **content**.
|
||||
Summary: {{ .Summary }}|Truncated: {{ .Truncated }}|
|
||||
Content: {{ .Content }}|
|
||||
|
||||
`).AssertFileContent("public/simple/index.html",
|
||||
`).AssertFileContent(
|
||||
"public/simple/index.html",
|
||||
"Summary: <p>This is <strong>summary</strong>.</p>|",
|
||||
"Truncated: true|",
|
||||
"Content: <p>This is <strong>summary</strong>.</p>\n<p>This is <strong>content</strong>.</p>|",
|
||||
@@ -1286,13 +1288,15 @@ AllTranslations: {{ range .AllTranslations }}{{ .Language.Lang }}|{{ end }}|
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
b.AssertFileContent("public/en/sect/p1/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/en/sect/p1/index.html",
|
||||
"TranslationKey: adfasdf|",
|
||||
"AllTranslations: en|nn||",
|
||||
"Translations: nn||",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/nn/sect/p1/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/nn/sect/p1/index.html",
|
||||
"TranslationKey: adfasdf|",
|
||||
"Translations: en||",
|
||||
"AllTranslations: en|nn||",
|
||||
@@ -1378,12 +1382,14 @@ Resources: {{ range .Resources }}{{ .RelPermalink }}|{{ .Content }}|{{ end }}|
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
b.AssertFileContent("public/en/sect/mybundle_en/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/en/sect/mybundle_en/index.html",
|
||||
"TranslationKey: adfasdf|",
|
||||
"Resources: /en/sect/mybundle_en/f1.txt|f1.en|/en/sect/mybundle_en/f2.txt|f2.en||",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/nn/sect/mybundle_nn/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/nn/sect/mybundle_nn/index.html",
|
||||
"TranslationKey: adfasdf|",
|
||||
"Title: mybundle nn|TranslationKey: adfasdf|\nResources: /en/sect/mybundle_en/f1.txt|f1.en|/nn/sect/mybundle_nn/f2.nn.txt|f2.nn||",
|
||||
)
|
||||
@@ -1497,31 +1503,37 @@ CONTENT:{{ .Content }}
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/page-md-shortcode/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-md-shortcode/index.html",
|
||||
"SUMMARY:<p>This is a a shortcode.</p>:END",
|
||||
"CONTENT:<p>This is a a shortcode.</p>\n\n<p>Content.</p>\n",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/page-md-shortcode-same-line/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-md-shortcode-same-line/index.html",
|
||||
"SUMMARY:<p>This is a a shortcode</p>:END",
|
||||
"CONTENT:<p>This is a a shortcode</p>\n\n<p>Same line.</p>\n",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/page-md-shortcode-same-line-after/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-md-shortcode-same-line-after/index.html",
|
||||
"SUMMARY:<p>Summary</p>:END",
|
||||
"CONTENT:<p>Summary</p>\n\na shortcode",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/page-org-shortcode/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-org-shortcode/index.html",
|
||||
"SUMMARY:<p>\nThis is a a shortcode.\n</p>:END",
|
||||
"CONTENT:<p>\nThis is a a shortcode.\n</p>\n<p>\nContent.\t\n</p>\n",
|
||||
)
|
||||
b.AssertFileContent("public/page-org-variant1/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-org-variant1/index.html",
|
||||
"SUMMARY:<p>\nSummary.\n</p>:END",
|
||||
"CONTENT:<p>\nSummary.\n</p>\n<p>\nContent.\t\n</p>\n",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/page-md-only-shortcode/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/page-md-only-shortcode/index.html",
|
||||
"SUMMARY:a shortcode:END",
|
||||
"CONTENT:a shortcode\n\na shortcode\n",
|
||||
)
|
||||
@@ -1731,7 +1743,8 @@ c: {{ .Scratch.Get "c" }}
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/index.html",
|
||||
".Scratch eq .Store: true",
|
||||
"a: b",
|
||||
"c: d",
|
||||
@@ -1900,6 +1913,56 @@ func TestRenderWithoutArgument(t *testing.T) {
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
}
|
||||
|
||||
// See issue 15077.
|
||||
func TestRenderWithContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
-- layouts/page.html --
|
||||
{{ .Render "li" }}|{{ .Render "li" (dict "Title" "Custom") }}
|
||||
-- layouts/li.html --
|
||||
Title: {{ .Title }}{{- /**/ -}}
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "Title: P1|Title: Custom")
|
||||
}
|
||||
|
||||
// See issue 15077.
|
||||
func TestRenderWithContextErrors(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
filesTemplate := `
|
||||
-- hugo.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
-- layouts/li.html --
|
||||
li
|
||||
-- layouts/page.html --
|
||||
RENDER
|
||||
`
|
||||
|
||||
for _, test := range []struct {
|
||||
render string
|
||||
message string
|
||||
}{
|
||||
{`{{ .Render "li" "foo" "bar" }}`, `(?s).*too many arguments, expected VIEW \[CONTEXT\].*`},
|
||||
{`{{ .Render (slice "li") }}`, `(?s).*failed to convert view argument to string: unable to cast \[\]string{"li"} of type \[\]string to string.*`},
|
||||
} {
|
||||
files := strings.ReplaceAll(filesTemplate, "RENDER", test.render)
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.ErrorMatches, test.message)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #13021
|
||||
func TestAllStores(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -1928,7 +1991,8 @@ Site: {{ site.Store.Get "Site" }}|
|
||||
|
||||
b := TestRunning(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/index.html",
|
||||
`
|
||||
Shortcode: sh-Home|
|
||||
Page: p-Home|
|
||||
@@ -1939,7 +2003,8 @@ Hugo: h-Home|
|
||||
|
||||
b.EditFileReplaceAll("content/_index.md", "Home", "Homer").Build()
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/index.html",
|
||||
`
|
||||
Shortcode: sh-Homer|
|
||||
Page: p-Homer|
|
||||
@@ -2149,7 +2214,8 @@ EF
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/s1/p1/index.html",
|
||||
b.AssertFileContent(
|
||||
"public/s1/p1/index.html",
|
||||
"ab|ab|ab",
|
||||
"cD|cD|cD",
|
||||
"EF|EF|EF",
|
||||
@@ -2341,3 +2407,104 @@ layouts/s1/p3/_views/a.html
|
||||
b.AssertFileContent("public/s1/p2/index.html", "p2: layouts/s1/_views/a.html") // fails
|
||||
b.AssertFileContent("public/s1/p3/index.html", "p3: layouts/s1/p3/_views/a.html") // fails
|
||||
}
|
||||
|
||||
func TestPageConttentWeight(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/mysection/page1.md --
|
||||
-- content/myothersection/page2.md --
|
||||
-- content/myothersection/_index.md --
|
||||
-- layouts/all.html --
|
||||
All.
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
check := func(p page.Page, ok bool) {
|
||||
cw := p.(contentNodeContentWeightProvider).contentWeight()
|
||||
b.Assert(ok, qt.Equals, cw > 0)
|
||||
}
|
||||
|
||||
s := b.H.Sites[0]
|
||||
for _, p := range s.RegularPages() {
|
||||
check(p, true)
|
||||
}
|
||||
check(s.home, false)
|
||||
mysection, _ := s.GetPage("mysection")
|
||||
check(mysection, false)
|
||||
myothersection, _ := s.GetPage("myothersection") // backed by a content file.
|
||||
check(myothersection, true)
|
||||
}
|
||||
|
||||
// See issue 15206.
|
||||
func TestReadingTimeAndFuzzyWordCountBoundaries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ["home", "section", "taxonomy", "term", "rss", "sitemap"]
|
||||
-- content/p99.md --
|
||||
---
|
||||
title: p99
|
||||
---
|
||||
` + strings.Repeat("word ", 99) + `
|
||||
-- content/p100.md --
|
||||
---
|
||||
title: p100
|
||||
---
|
||||
` + strings.Repeat("word ", 100) + `
|
||||
-- content/p101.md --
|
||||
---
|
||||
title: p101
|
||||
---
|
||||
` + strings.Repeat("word ", 101) + `
|
||||
-- content/p211.md --
|
||||
---
|
||||
title: p211
|
||||
---
|
||||
` + strings.Repeat("word ", 211) + `
|
||||
-- content/p212.md --
|
||||
---
|
||||
title: p212
|
||||
---
|
||||
` + strings.Repeat("word ", 212) + `
|
||||
-- content/p213.md --
|
||||
---
|
||||
title: p213
|
||||
---
|
||||
` + strings.Repeat("word ", 213) + `
|
||||
-- content/p499.md --
|
||||
---
|
||||
title: p499
|
||||
isCJKLanguage: true
|
||||
---
|
||||
` + strings.Repeat("你", 499) + `
|
||||
-- content/p500.md --
|
||||
---
|
||||
title: p500
|
||||
isCJKLanguage: true
|
||||
---
|
||||
` + strings.Repeat("你", 500) + `
|
||||
-- content/p501.md --
|
||||
---
|
||||
title: p501
|
||||
isCJKLanguage: true
|
||||
---
|
||||
` + strings.Repeat("你", 501) + `
|
||||
-- layouts/page.html --
|
||||
{{ .WordCount }}|{{ .FuzzyWordCount }}|{{ .ReadingTime }}
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p99/index.html", "99|100|1")
|
||||
b.AssertFileContent("public/p100/index.html", "100|100|1")
|
||||
b.AssertFileContent("public/p101/index.html", "101|200|1")
|
||||
|
||||
b.AssertFileContent("public/p211/index.html", "211|300|1")
|
||||
b.AssertFileContent("public/p212/index.html", "212|300|1")
|
||||
b.AssertFileContent("public/p213/index.html", "213|300|2")
|
||||
|
||||
b.AssertFileContent("public/p499/index.html", "499|500|1")
|
||||
b.AssertFileContent("public/p500/index.html", "500|500|1")
|
||||
b.AssertFileContent("public/p501/index.html", "501|600|2")
|
||||
}
|
||||
|
||||
@@ -897,6 +897,17 @@ func TestRebuildEditSectionRemoveDate(t *testing.T) {
|
||||
b.AssertFileContent("public/mysection/index.html", "all. My Section|Param: |Lastmod: 2021-02-01|")
|
||||
}
|
||||
|
||||
// Atomic save (write temp file, then rename into place) of a section's branch bundle.
|
||||
// See issue 15130.
|
||||
func TestRebuildEditAtomicSection(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := TestRunning(t, rebuildBasicFiles)
|
||||
b.AssertFileContent("public/mysection/index.html", "My Section")
|
||||
b.EditFileAtomicReplaceAll("content/mysection/_index.md", "My Section", "My Changed Section").Build()
|
||||
b.AssertRenderCountPage(5)
|
||||
b.AssertFileContent("public/mysection/index.html", "My Changed Section")
|
||||
}
|
||||
|
||||
func TestRebuildVariations(t *testing.T) {
|
||||
// t.Parallel() not supported, see https://github.com/fortytw2/leaktest/issues/4
|
||||
// This leaktest seems to be a little bit shaky on Travis.
|
||||
@@ -2170,3 +2181,160 @@ Foo.
|
||||
b.EditFileReplaceAll("layouts/tags/list.html", "Foo", "Bar").Build()
|
||||
b.AssertFileContent("public/tags/index.html", "Bar.")
|
||||
}
|
||||
|
||||
// Atomic save (write temp file, then rename into place) of a content backed taxonomy root.
|
||||
// See issue 15130.
|
||||
func TestRebuildEditAtomicTaxonomyRoot(t *testing.T) {
|
||||
t.Parallel()
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
[taxonomies]
|
||||
object = "objects"
|
||||
-- content/objects/_index.md --
|
||||
---
|
||||
title: "Objects"
|
||||
---
|
||||
Objects content.
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
objects: ["o1"]
|
||||
---
|
||||
-- layouts/all.html --
|
||||
{{ .Title }}|{{ .Kind }}|{{ .Content }}
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/objects/index.html", "Objects|taxonomy|<p>Objects content.</p>")
|
||||
b.EditFileAtomicReplaceAll("content/objects/_index.md", "Objects content.", "Objects content edited.").Build()
|
||||
b.AssertFileContent("public/objects/index.html", "Objects|taxonomy|<p>Objects content edited.</p>")
|
||||
b.AssertFileContent("public/objects/o1/index.html", "O1|term|")
|
||||
}
|
||||
|
||||
func TestRebuildEditMountedAssetOnlyRelPermalinkUsed(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
disableKinds = ["taxonomy", "term", "rss", "sitemap", "section"]
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
[[module.mounts]]
|
||||
source = "out"
|
||||
target = "assets/out"
|
||||
-- out/data.json --
|
||||
{"version": "v1"}
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "Home"
|
||||
---
|
||||
-- content/mytext.txt --
|
||||
mytext v1
|
||||
-- layouts/home.html --
|
||||
{{ $data := resources.Get "out/data.json" | minify }}
|
||||
Data: {{ $data.RelPermalink }}|
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "Data: /out/data.min.json|")
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v1"}`)
|
||||
|
||||
// Simulate the periodic data refresh.
|
||||
b.EditFileReplaceAll("out/data.json", "v1", "v2").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v2"}`)
|
||||
|
||||
// An unrelated edit to a home bundle resource must not revert the
|
||||
// published data to an older version.
|
||||
b.EditFileReplaceAll("content/mytext.txt", "mytext v1", "mytext v2").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v2"}`)
|
||||
|
||||
// One more refresh round.
|
||||
b.EditFileReplaceAll("out/data.json", "v2", "v3").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v3"}`)
|
||||
}
|
||||
|
||||
func TestRebuildEditMountedAssetContentUsed(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
disableKinds = ["taxonomy", "term", "rss", "sitemap", "section"]
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
[[module.mounts]]
|
||||
source = "out"
|
||||
target = "assets/out"
|
||||
-- out/data.json --
|
||||
{"version": "v1"}
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "Home"
|
||||
---
|
||||
-- content/mytext.txt --
|
||||
mytext v1
|
||||
-- layouts/home.html --
|
||||
{{ $data := resources.Get "out/data.json" | minify }}
|
||||
Data: {{ $data.RelPermalink }}|{{ $data.Content | safeHTML }}|
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "Data: /out/data.min.json|")
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v1"}`)
|
||||
|
||||
b.EditFileReplaceAll("out/data.json", "v1", "v2").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v2"}`)
|
||||
b.AssertFileContent("public/index.html", `{"version":"v2"}`)
|
||||
|
||||
b.EditFileReplaceAll("content/mytext.txt", "mytext v1", "mytext v2").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v2"}`)
|
||||
|
||||
b.EditFileReplaceAll("out/data.json", "v2", "v3").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v3"}`)
|
||||
b.AssertFileContent("public/index.html", `{"version":"v3"}`)
|
||||
}
|
||||
|
||||
func TestRebuildFastRenderEditMountedAssetNotRecentlyVisited(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
disableKinds = ["taxonomy", "term", "rss", "sitemap", "section"]
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
[[module.mounts]]
|
||||
source = "out"
|
||||
target = "assets/out"
|
||||
-- out/data.json --
|
||||
{"version": "v1"}
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "Home"
|
||||
---
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
-- layouts/home.html --
|
||||
{{ $data := resources.Get "out/data.json" | minify }}
|
||||
Data: {{ $data.RelPermalink }}|
|
||||
-- layouts/single.html --
|
||||
Single: {{ .Title }}|
|
||||
`
|
||||
recentlyVisited := types.NewEvictingQueue[string](20).Add("/p1/")
|
||||
b := TestRunning(t, files, func(cfg *IntegrationTestConfig) {
|
||||
cfg.FastRenderMode = true
|
||||
cfg.BuildCfg = BuildCfg{RecentlyTouched: recentlyVisited}
|
||||
})
|
||||
|
||||
b.AssertFileContent("public/index.html", "Data: /out/data.min.json|")
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v1"}`)
|
||||
|
||||
// Simulate the periodic data refresh.
|
||||
b.EditFileReplaceAll("out/data.json", "v1", "v2").Build()
|
||||
b.AssertFileContent("public/out/data.min.json", `{"version":"v2"}`)
|
||||
}
|
||||
|
||||
@@ -759,11 +759,9 @@ func (s *Site) Pages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.BoolFunc(),
|
||||
},
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.BoolFunc(),
|
||||
Recursive: true,
|
||||
IncludeSelf: true,
|
||||
},
|
||||
@@ -776,11 +774,9 @@ func (s *Site) RegularPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
},
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage).BoolFunc(),
|
||||
Recursive: true,
|
||||
},
|
||||
)
|
||||
@@ -922,11 +918,9 @@ func (s *Site) prepareInits() {
|
||||
|
||||
sections := s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "sectionorhome",
|
||||
Include: pagePredicates.KindSection.Or(pagePredicates.KindHome).BoolFunc(),
|
||||
},
|
||||
Path: "",
|
||||
KeyPart: "sectionorhome",
|
||||
Include: pagePredicates.KindSection.Or(pagePredicates.KindHome).BoolFunc(),
|
||||
IncludeSelf: true,
|
||||
Recursive: true,
|
||||
},
|
||||
@@ -1257,20 +1251,14 @@ func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo
|
||||
removed := false
|
||||
added := false
|
||||
|
||||
if ev.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
removed = true
|
||||
}
|
||||
|
||||
fi, statErr := h.Fs.Source.Stat(ev.Name)
|
||||
|
||||
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
|
||||
// Sometimes a rename operation means that file has been renamed other times it means
|
||||
// it's been updated.
|
||||
if ev.Op.Has(fsnotify.Rename) {
|
||||
// If the file is still on disk, it's only been updated, if it's not, it's been moved
|
||||
if statErr != nil {
|
||||
removed = true
|
||||
}
|
||||
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file,
|
||||
// and an atomic save (write temp file, then rename it into place) makes the watcher
|
||||
// report the replaced file as removed (kqueue/macOS).
|
||||
// So, if the file is still on disk, it's only been updated, if it's not, it's gone.
|
||||
if ev.Op.Has(fsnotify.Remove) || ev.Op.Has(fsnotify.Rename) {
|
||||
removed = statErr != nil
|
||||
}
|
||||
if ev.Op.Has(fsnotify.Create) {
|
||||
added = true
|
||||
@@ -1470,12 +1458,10 @@ func (s *Site) assembleMenus() (navigation.Menus, error) {
|
||||
return false, nil
|
||||
}
|
||||
me := navigation.MenuEntry{
|
||||
MenuConfig: navigation.MenuConfig{
|
||||
Identifier: id,
|
||||
Name: p.LinkTitle(),
|
||||
Weight: p.Weight(),
|
||||
},
|
||||
Page: p,
|
||||
Identifier: id,
|
||||
Name: p.LinkTitle(),
|
||||
Weight: p.Weight(),
|
||||
Page: p,
|
||||
}
|
||||
|
||||
navigation.SetPageValues(&me, p)
|
||||
@@ -1514,9 +1500,7 @@ func (s *Site) assembleMenus() (navigation.Menus, error) {
|
||||
if !ok {
|
||||
// if parent does not exist, create one without a URL
|
||||
flat[twoD{p.MenuName, p.EntryName}] = &navigation.MenuEntry{
|
||||
MenuConfig: navigation.MenuConfig{
|
||||
Name: p.EntryName,
|
||||
},
|
||||
Name: p.EntryName,
|
||||
}
|
||||
}
|
||||
flat[twoD{p.MenuName, p.EntryName}].Children = childmenu
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package sitesmatrix
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"iter"
|
||||
"maps"
|
||||
@@ -780,7 +779,7 @@ func (b *IntSetsBuilder) Build() *IntSets {
|
||||
}
|
||||
|
||||
func (b *IntSetsBuilder) WithConfig(cfg IntSetsConfig) *IntSetsBuilder {
|
||||
applyFilter := func(what string, values []string, matcher ConfiguredDimension) (*hmaps.OrderedIntSet, error) {
|
||||
applyFilter := func(what string, values []string, matcher ConfiguredDimension) *hmaps.OrderedIntSet {
|
||||
var result *hmaps.OrderedIntSet
|
||||
if len(values) == 0 {
|
||||
|
||||
@@ -800,16 +799,16 @@ func (b *IntSetsBuilder) WithConfig(cfg IntSetsConfig) *IntSetsBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result
|
||||
}
|
||||
|
||||
filter, err := predicate.NewIndexStringPredicateFromGlobsAndRanges(values, matcher.ResolveIndex, hglob.GetGlobDot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create filter for %s: %w", what, err)
|
||||
panic(fmt.Errorf("failed to create filter for %s: %w", what, err))
|
||||
}
|
||||
iter, err := matcher.IndexMatch(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to match %s %q: %w", what, values, err)
|
||||
panic(fmt.Errorf("failed to match %s %q: %w", what, values, err))
|
||||
}
|
||||
for i := range iter {
|
||||
if result == nil {
|
||||
@@ -818,16 +817,12 @@ func (b *IntSetsBuilder) WithConfig(cfg IntSetsConfig) *IntSetsBuilder {
|
||||
result.Set(i)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result
|
||||
}
|
||||
|
||||
l, err1 := applyFilter("languages", cfg.Globs.Languages, b.cfg.ConfiguredLanguages)
|
||||
v, err2 := applyFilter("versions", cfg.Globs.Versions, b.cfg.ConfiguredVersions)
|
||||
r, err3 := applyFilter("roles", cfg.Globs.Roles, b.cfg.ConfiguredRoles)
|
||||
|
||||
if err := cmp.Or(err1, err2, err3); err != nil {
|
||||
panic(fmt.Errorf("failed to apply filters: %w", err))
|
||||
}
|
||||
l := applyFilter("languages", cfg.Globs.Languages, b.cfg.ConfiguredLanguages)
|
||||
v := applyFilter("versions", cfg.Globs.Versions, b.cfg.ConfiguredVersions)
|
||||
r := applyFilter("roles", cfg.Globs.Roles, b.cfg.ConfiguredRoles)
|
||||
|
||||
b.GlobFilterMisses = Bools{
|
||||
len(cfg.Globs.Languages) > 0 && l == nil,
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
// https://github.com/gohugoio/hugo/issues/4895
|
||||
@@ -314,6 +316,70 @@ X123X
|
||||
`)
|
||||
}
|
||||
|
||||
// See issue 15212.
|
||||
func TestTemplateReturnEarly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "http://example.com/"
|
||||
-- layouts/home.html --
|
||||
home-start|{{ if true }}{{ return }}{{ end }}home-end
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "home-start|", "! home-end")
|
||||
}
|
||||
|
||||
// See issue 15212.
|
||||
func TestTemplateReturnEarlyFromBlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "http://example.com/"
|
||||
-- layouts/baseof.html --
|
||||
header|{{ block "main" . }}{{ end }}|footer
|
||||
-- layouts/home.html --
|
||||
{{ define "main" }}main-start|{{ return }}main-end{{ end }}
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "header|main-start||footer", "! main-end")
|
||||
}
|
||||
|
||||
// See issue 15212.
|
||||
func TestTemplateReturnEarlyFromTemplateInclude(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "http://example.com/"
|
||||
-- layouts/home.html --
|
||||
{{ template "foo" . }}|after
|
||||
{{ define "foo" }}foo-start|{{ return }}foo-end{{ end }}
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "foo-start||after", "! foo-end")
|
||||
}
|
||||
|
||||
// See issue 15212.
|
||||
func TestTemplateReturnWithValueOutsidePartialFails(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "http://example.com/"
|
||||
-- layouts/home.html --
|
||||
{{ return 32 }}
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, "return with a value is only supported in partials")
|
||||
}
|
||||
|
||||
func TestPartialCached(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.163.3
|
||||
HUGORELEASER_COMMITISH=4d22555aebf458d5d150500c9ac4bee5b24cf0d3
|
||||
HUGORELEASER_TAG=v0.165.0
|
||||
HUGORELEASER_COMMITISH=76a5e1880ab46688155b02e99bab9be2a6134492
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -252,11 +252,11 @@ type IsRebuildProvider interface {
|
||||
|
||||
// IncrementByOne implements Incrementer adding 1 every time Incr is called.
|
||||
type IncrementByOne struct {
|
||||
counter uint64
|
||||
counter atomic.Uint64
|
||||
}
|
||||
|
||||
func (c *IncrementByOne) Incr() int {
|
||||
return int(atomic.AddUint64(&c.counter, uint64(1)))
|
||||
return int(c.counter.Add(uint64(1)))
|
||||
}
|
||||
|
||||
// Incrementer increments and returns the value.
|
||||
|
||||
@@ -528,96 +528,94 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) {
|
||||
}
|
||||
|
||||
jsOpts := Options{
|
||||
ExternalOptions: externalOptions,
|
||||
InternalOptions: InternalOptions{
|
||||
DependencyManager: b.dependencyManager,
|
||||
Splitting: true,
|
||||
ImportOnResolveFunc: func(imp string, args api.OnResolveArgs) string {
|
||||
var importContextPath string
|
||||
if args.Kind == api.ResolveEntryPoint {
|
||||
importContextPath = args.Path
|
||||
} else {
|
||||
importContextPath = args.Importer
|
||||
}
|
||||
importContext, importContextFound := state.importerImportContext.Get(importContextPath)
|
||||
ExternalOptions: externalOptions,
|
||||
DependencyManager: b.dependencyManager,
|
||||
Splitting: true,
|
||||
ImportOnResolveFunc: func(imp string, args api.OnResolveArgs) string {
|
||||
var importContextPath string
|
||||
if args.Kind == api.ResolveEntryPoint {
|
||||
importContextPath = args.Path
|
||||
} else {
|
||||
importContextPath = args.Importer
|
||||
}
|
||||
importContext, importContextFound := state.importerImportContext.Get(importContextPath)
|
||||
|
||||
// We want to track the dependencies closest to where they're used.
|
||||
dm := b.dependencyManager
|
||||
if importContextFound {
|
||||
dm = importContext.dm
|
||||
}
|
||||
// We want to track the dependencies closest to where they're used.
|
||||
dm := b.dependencyManager
|
||||
if importContextFound {
|
||||
dm = importContext.dm
|
||||
}
|
||||
|
||||
if r, found := state.importResource.Get(imp); found {
|
||||
dm.AddIdentity(identity.FirstIdentity(r))
|
||||
if r, found := state.importResource.Get(imp); found {
|
||||
dm.AddIdentity(identity.FirstIdentity(r))
|
||||
return imp
|
||||
}
|
||||
|
||||
if importContext.resourceGetter != nil {
|
||||
resolved := ResolveResource(imp, importContext.resourceGetter)
|
||||
if resolved != nil {
|
||||
resolvePath := resources.InternalResourceTargetPath(resolved)
|
||||
dm.AddIdentity(identity.FirstIdentity(resolved))
|
||||
imp := PrefixHugoVirtual + resolvePath
|
||||
state.importResource.Set(imp, resolved)
|
||||
state.importerImportContext.Set(imp, importContext)
|
||||
return imp
|
||||
}
|
||||
|
||||
if importContext.resourceGetter != nil {
|
||||
resolved := ResolveResource(imp, importContext.resourceGetter)
|
||||
if resolved != nil {
|
||||
resolvePath := resources.InternalResourceTargetPath(resolved)
|
||||
dm.AddIdentity(identity.FirstIdentity(resolved))
|
||||
imp := PrefixHugoVirtual + resolvePath
|
||||
state.importResource.Set(imp, resolved)
|
||||
state.importerImportContext.Set(imp, importContext)
|
||||
return imp
|
||||
|
||||
}
|
||||
}
|
||||
return ""
|
||||
},
|
||||
ImportOnLoadFunc: func(args api.OnLoadArgs) (string, error) {
|
||||
imp := args.Path
|
||||
|
||||
if r, found := state.importResource.Get(imp); found {
|
||||
content, err := resources.InternalResourceSourceContent(ctx, r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read import %q: %w", resources.InternalResourceSourcePathBestEffort(r), err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
ImportParamsOnLoadFunc: func(args api.OnLoadArgs) json.RawMessage {
|
||||
if importContext, found := state.importerImportContext.Get(args.Path); found {
|
||||
if !importContext.scriptOptions.IsZero() {
|
||||
return importContext.scriptOptions.Params
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ErrorMessageResolveFunc: func(args api.Message) *ErrorMessageResolved {
|
||||
if loc := args.Location; loc != nil {
|
||||
path := strings.TrimPrefix(loc.File, NsHugoImportResolveFunc+":")
|
||||
if r, found := state.importResource.Get(path); found {
|
||||
sourcePath := resources.InternalResourceSourcePathBestEffort(r)
|
||||
|
||||
var contentr hugio.ReadSeekCloser
|
||||
if cp, ok := r.(hugio.ReadSeekCloserProvider); ok {
|
||||
contentr, _ = cp.ReadSeekCloser()
|
||||
}
|
||||
return &ErrorMessageResolved{
|
||||
Content: contentr,
|
||||
Path: sourcePath,
|
||||
Message: args.Text,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ResolveSourceMapSource: func(s string) string {
|
||||
if r, found := state.importResource.Get(s); found {
|
||||
if ss := resources.InternalResourceSourcePath(r); ss != "" {
|
||||
return ss
|
||||
}
|
||||
return PrefixHugoMemory + s
|
||||
}
|
||||
return ""
|
||||
},
|
||||
EntryPoints: entryPoints,
|
||||
}
|
||||
return ""
|
||||
},
|
||||
ImportOnLoadFunc: func(args api.OnLoadArgs) (string, error) {
|
||||
imp := args.Path
|
||||
|
||||
if r, found := state.importResource.Get(imp); found {
|
||||
content, err := resources.InternalResourceSourceContent(ctx, r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read import %q: %w", resources.InternalResourceSourcePathBestEffort(r), err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
ImportParamsOnLoadFunc: func(args api.OnLoadArgs) json.RawMessage {
|
||||
if importContext, found := state.importerImportContext.Get(args.Path); found {
|
||||
if !importContext.scriptOptions.IsZero() {
|
||||
return importContext.scriptOptions.Params
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ErrorMessageResolveFunc: func(args api.Message) *ErrorMessageResolved {
|
||||
if loc := args.Location; loc != nil {
|
||||
path := strings.TrimPrefix(loc.File, NsHugoImportResolveFunc+":")
|
||||
if r, found := state.importResource.Get(path); found {
|
||||
sourcePath := resources.InternalResourceSourcePathBestEffort(r)
|
||||
|
||||
var contentr hugio.ReadSeekCloser
|
||||
if cp, ok := r.(hugio.ReadSeekCloserProvider); ok {
|
||||
contentr, _ = cp.ReadSeekCloser()
|
||||
}
|
||||
return &ErrorMessageResolved{
|
||||
Content: contentr,
|
||||
Path: sourcePath,
|
||||
Message: args.Text,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ResolveSourceMapSource: func(s string) string {
|
||||
if r, found := state.importResource.Get(s); found {
|
||||
if ss := resources.InternalResourceSourcePath(r); ss != "" {
|
||||
return ss
|
||||
}
|
||||
return PrefixHugoMemory + s
|
||||
}
|
||||
return ""
|
||||
},
|
||||
EntryPoints: entryPoints,
|
||||
}
|
||||
|
||||
result, err := b.client.buildClient.Build(jsOpts)
|
||||
|
||||
@@ -183,6 +183,9 @@ type ExternalOptions struct {
|
||||
// Maps a component import to another.
|
||||
Shims map[string]string
|
||||
|
||||
// User provided import context. If set, we will look here first.
|
||||
ImportContext any
|
||||
|
||||
// Configuring a loader for a given file type lets you load that file type with an
|
||||
// import statement or a require call. For example, configuring the .png file extension
|
||||
// to use the data URL loader means importing a .png file gives you a data URL
|
||||
|
||||
@@ -27,10 +27,8 @@ func TestToBuildOptions(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := Options{
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -46,16 +44,12 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Target: []string{"es2018"},
|
||||
Format: "cjs",
|
||||
Minify: true,
|
||||
AvoidTDZ: true,
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
Target: []string{"es2018"},
|
||||
Format: "cjs",
|
||||
Minify: true,
|
||||
AvoidTDZ: true,
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -74,14 +68,10 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "inline",
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "inline",
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -101,14 +91,10 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "inline",
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "inline",
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -128,14 +114,10 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "external",
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
Target: []string{"es2018"}, Format: "cjs", Minify: true,
|
||||
SourceMap: "external",
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -155,13 +137,9 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
JSX: "automatic", JSXImportSource: "preact",
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
},
|
||||
JSX: "automatic", JSXImportSource: "preact",
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
Stdin: true,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
@@ -179,24 +157,18 @@ func TestToBuildOptions(t *testing.T) {
|
||||
})
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Drop: "console",
|
||||
},
|
||||
Drop: "console",
|
||||
}
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
c.Assert(opts.compiled.Drop, qt.Equals, api.DropConsole)
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Drop: "debugger",
|
||||
},
|
||||
Drop: "debugger",
|
||||
}
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
c.Assert(opts.compiled.Drop, qt.Equals, api.DropDebugger)
|
||||
|
||||
opts = Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Drop: "adsfadsf",
|
||||
},
|
||||
Drop: "adsfadsf",
|
||||
}
|
||||
c.Assert(opts.compile(), qt.ErrorMatches, `unsupported drop type: "adsfadsf"`)
|
||||
}
|
||||
@@ -222,12 +194,8 @@ func TestToBuildOptionsTarget(t *testing.T) {
|
||||
} {
|
||||
c.Run(test.target, func(c *qt.C) {
|
||||
opts := Options{
|
||||
ExternalOptions: ExternalOptions{
|
||||
Target: []string{test.target},
|
||||
},
|
||||
InternalOptions: InternalOptions{
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
},
|
||||
Target: []string{test.target},
|
||||
MediaType: media.Builtin.JavascriptType,
|
||||
}
|
||||
|
||||
c.Assert(opts.compile(), qt.IsNil)
|
||||
|
||||
@@ -260,6 +260,19 @@ func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsMana
|
||||
}
|
||||
}
|
||||
|
||||
if opts.ImportOnResolveFunc != nil {
|
||||
// Relative imports resolved against the importing file's directory,
|
||||
// e.g. "./foo.css". The path as written was tried above.
|
||||
for _, p := range pathsToTry {
|
||||
if p == impPath {
|
||||
continue
|
||||
}
|
||||
if s := opts.ImportOnResolveFunc(filepath.ToSlash(p), args); s != "" {
|
||||
return api.OnResolveResult{Path: s, Namespace: NsHugoImportResolveFunc}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var m *hugofs.FileMeta
|
||||
for _, p := range pathsToTry {
|
||||
m = assetsResolver.resolveComponent(p, isCSSToken)
|
||||
|
||||
@@ -25,6 +25,13 @@ import (
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
)
|
||||
|
||||
// Keep in sync with genavif/Makefile.
|
||||
const (
|
||||
libavifVersion = "v1.4.1"
|
||||
aomVersion = "v3.14.1"
|
||||
dav1dVersion = "1.5.3"
|
||||
)
|
||||
|
||||
var (
|
||||
_ SourceProvider = AvifInput{}
|
||||
_ DestinationProvider = AvifInput{}
|
||||
|
||||
@@ -280,6 +280,15 @@ static avifPixelFormat avifFormatForHint(const char *hint)
|
||||
return AVIF_PIXEL_FORMAT_YUV420; // photo, picture, and the default.
|
||||
}
|
||||
|
||||
// encoder_errf writes "prefix: result[: diag]", where diag is libavif's
|
||||
// detailed diagnostics (e.g. the underlying aom error, such as an allocation
|
||||
// failure hidden behind the generic "Encoding of color planes failed").
|
||||
static void encoder_errf(char *dst, size_t size, const char *prefix, avifResult result, const avifEncoder *encoder)
|
||||
{
|
||||
snprintf(dst, size, "%s: %s%s%s", prefix, avifResultToString(result),
|
||||
encoder->diag.error[0] ? ": " : "", encoder->diag.error);
|
||||
}
|
||||
|
||||
void handle_commands(FILE *stream)
|
||||
{
|
||||
|
||||
@@ -725,7 +734,7 @@ void handle_commands(FILE *stream)
|
||||
|
||||
result = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
snprintf(output.header.err, sizeof(output.header.err), "encodeNRGBA: Failed to add image to encoder: %s", avifResultToString(result));
|
||||
encoder_errf(output.header.err, sizeof(output.header.err), "encodeNRGBA: Failed to add image to encoder", result, encoder);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
write_output_message(&output);
|
||||
@@ -735,7 +744,7 @@ void handle_commands(FILE *stream)
|
||||
avifRWData raw = { NULL, 0 };
|
||||
result = avifEncoderFinish(encoder, &raw);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
snprintf(output.header.err, sizeof(output.header.err), "encodeNRGBA: Failed to finish encoding: %s", avifResultToString(result));
|
||||
encoder_errf(output.header.err, sizeof(output.header.err), "encodeNRGBA: Failed to finish encoding", result, encoder);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
write_output_message(&output);
|
||||
@@ -850,7 +859,7 @@ void handle_commands(FILE *stream)
|
||||
|
||||
avifResult result = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
snprintf(output.header.err, sizeof(output.header.err), "encodeGray: Failed to add image to encoder: %s", avifResultToString(result));
|
||||
encoder_errf(output.header.err, sizeof(output.header.err), "encodeGray: Failed to add image to encoder", result, encoder);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
write_output_message(&output);
|
||||
@@ -860,7 +869,7 @@ void handle_commands(FILE *stream)
|
||||
avifRWData raw = { NULL, 0 };
|
||||
result = avifEncoderFinish(encoder, &raw);
|
||||
if (result != AVIF_RESULT_OK) {
|
||||
snprintf(output.header.err, sizeof(output.header.err), "encodeGray: Failed to finish encoding: %s", avifResultToString(result));
|
||||
encoder_errf(output.header.err, sizeof(output.header.err), "encodeGray: Failed to finish encoding", result, encoder);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
write_output_message(&output);
|
||||
|
||||
@@ -17,6 +17,9 @@ import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// Keep in sync with js/package.json.
|
||||
const katexVersion = "0.16.21"
|
||||
|
||||
//go:embed wasm/renderkatex.wasm
|
||||
var katexWasm []byte
|
||||
|
||||
|
||||
@@ -48,6 +48,17 @@ const (
|
||||
|
||||
const currentVersion = 1
|
||||
|
||||
// GetWASMDeps returns version information for the C libraries bundled as WASM.
|
||||
func GetWASMDeps() [][2]string {
|
||||
return [][2]string{
|
||||
{"aomedia.googlesource.com/aom", aomVersion},
|
||||
{"code.videolan.org/videolan/dav1d", dav1dVersion},
|
||||
{"github.com/AOMediaCodec/libavif", libavifVersion},
|
||||
{"github.com/KaTeX/KaTeX", katexVersion},
|
||||
{"github.com/webmproject/libwebp", libwebpVersion},
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed wasm/quickjs.wasm
|
||||
var quickjsWasm []byte
|
||||
|
||||
@@ -348,7 +359,8 @@ func (d *dispatcher[Q, R]) inputBlobs() error {
|
||||
call.done()
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
},
|
||||
); err != nil {
|
||||
inputErr = err
|
||||
break
|
||||
}
|
||||
@@ -384,7 +396,8 @@ func (d *dispatcher[Q, R]) inputJSON() error {
|
||||
call.done()
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
},
|
||||
); err != nil {
|
||||
inputErr = err
|
||||
break
|
||||
}
|
||||
@@ -569,6 +582,9 @@ func (p *dispatcherPool[Q, R]) Err() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for data race, see https://github.com/wazero/wazero/issues/2532
|
||||
var wazeroCacheMu sync.Mutex
|
||||
|
||||
func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
||||
if opts.Ctx == nil {
|
||||
opts.Ctx = context.Background()
|
||||
@@ -597,6 +613,8 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
||||
runtimeConfig = runtimeConfig.WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesExceptionHandling | experimental.CoreFeaturesThreads)
|
||||
|
||||
if opts.CompilationCacheDir != "" {
|
||||
wazeroCacheMu.Lock()
|
||||
defer wazeroCacheMu.Unlock()
|
||||
compilationCache, err := wazero.NewCompilationCacheWithDir(opts.CompilationCacheDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -679,7 +697,8 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
||||
if err != nil {
|
||||
return toErr("quickjs", errBuff, err)
|
||||
}
|
||||
ctx = experimental.WithImportResolver(ctx,
|
||||
ctx = experimental.WithImportResolver(
|
||||
ctx,
|
||||
func(name string) api.Module {
|
||||
if name == opts.Runtime.Name {
|
||||
return runtimeInstance
|
||||
@@ -752,7 +771,9 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
||||
}
|
||||
|
||||
for _, d := range dp.dispatchers {
|
||||
if err := d.inGroup.Wait(); err != nil {
|
||||
// ErrShutdown is expected here; since Go 1.27 (json/v2), Decode
|
||||
// surfaces the read error from the pipes we just closed.
|
||||
if err := d.inGroup.Wait(); err != nil && err != ErrShutdown {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import (
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
)
|
||||
|
||||
// Keep in sync with genwebp/Makefile.
|
||||
const libwebpVersion = "v1.6.0"
|
||||
|
||||
var (
|
||||
_ SourceProvider = WebpInput{}
|
||||
_ DestinationProvider = WebpInput{}
|
||||
|
||||
@@ -15,6 +15,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -78,10 +80,22 @@ func TestMain(m *testing.M) {
|
||||
})
|
||||
}
|
||||
|
||||
var isCaseInsensitiveFs = sync.OnceValues(func() (bool, error) {
|
||||
return htesting.IsCaseInsensitiveFs(os.TempDir())
|
||||
})
|
||||
|
||||
var commonTestScriptsParam = testscript.Params{
|
||||
Setup: func(env *testscript.Env) error {
|
||||
return testSetupFunc()(env)
|
||||
},
|
||||
Condition: func(cond string) (bool, error) {
|
||||
switch cond {
|
||||
case "caseinsensitivefs":
|
||||
return isCaseInsensitiveFs()
|
||||
default:
|
||||
return false, fmt.Errorf("unknown condition %q", cond)
|
||||
}
|
||||
},
|
||||
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
|
||||
// log prints to stderr.
|
||||
"log": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||
@@ -252,6 +266,34 @@ var commonTestScriptsParam = testscript.Params{
|
||||
ts.Fatalf("failed to create symlink: %v", err)
|
||||
}
|
||||
},
|
||||
// base64decode decodes a base64-encoded file into a binary file.
|
||||
"base64decode": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||
if len(args) != 2 {
|
||||
ts.Fatalf("usage: base64decode src_base64_file dest_binary_file")
|
||||
}
|
||||
|
||||
// Resolve paths relative to the test sandbox's current directory
|
||||
src := ts.MkAbs(args[0])
|
||||
dest := ts.MkAbs(args[1])
|
||||
|
||||
// Read the base64 text
|
||||
base64Data, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
ts.Fatalf("failed to read base64 file: %v", err)
|
||||
}
|
||||
|
||||
// Decode base64 back to raw bytes.
|
||||
binaryBytes, err := base64.StdEncoding.DecodeString(strings.TrimSpace(string(base64Data)))
|
||||
if err != nil {
|
||||
ts.Fatalf("failed to decode base64 data: %v", err)
|
||||
}
|
||||
|
||||
// Write the binary file.
|
||||
err = os.WriteFile(dest, binaryBytes, 0o644)
|
||||
if err != nil {
|
||||
ts.Fatalf("failed to write binary file: %v", err)
|
||||
}
|
||||
},
|
||||
|
||||
// httpget checks that a HTTP resource's body matches (if it compiles as a regexp) or contains all of the strings given as arguments.
|
||||
"httpget": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||
|
||||
@@ -1153,3 +1153,59 @@ func validatePublishedSite_20(b *hugolib.IntegrationTestBuilder) {
|
||||
b.AssertFileExists(path, true)
|
||||
}
|
||||
}
|
||||
|
||||
// See issue 15121
|
||||
func TestAsciiDocHTML5BackendTOC(t *testing.T) {
|
||||
if !htesting.IsRealCI() {
|
||||
t.Skip()
|
||||
}
|
||||
if ok, err := asciidocext.Supports(); !ok {
|
||||
t.Skip(err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
resetDefaultAsciiDocExtConfig()
|
||||
})
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
|
||||
[markup.asciidocExt]
|
||||
backend = 'html5s'
|
||||
extensions = ['asciidoctor-html5s']
|
||||
|
||||
[security.exec]
|
||||
allow = ['^asciidoctor$']
|
||||
-- content/p1.adoc --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
|
||||
:toc:
|
||||
|
||||
## Section 1
|
||||
|
||||
### Section 1.1
|
||||
|
||||
### Section 1.2
|
||||
|
||||
## Section 2
|
||||
-- layouts/page.html --
|
||||
{{ .TableOfContents }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContentExact("public/p1/index.html", `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#_section_1">Section 1</a>
|
||||
<ul>
|
||||
<li><a href="#_section_1_1">Section 1.1</a></li>
|
||||
<li><a href="#_section_1_2">Section 1.2</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_section_2">Section 2</a></li>
|
||||
</ul>
|
||||
</nav>`)
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ func (a *AsciiDocConverter) extractTOC(src []byte) ([]byte, *tableofcontents.Fra
|
||||
toVisit []*html.Node
|
||||
)
|
||||
f = func(n *html.Node) bool {
|
||||
if n.Type == html.ElementNode && n.Data == "div" && attr(n, "id") == "toc" {
|
||||
if n.Type == html.ElementNode && (n.Data == "div" || n.Data == "nav") && attr(n, "id") == "toc" {
|
||||
toc = parseTOC(n)
|
||||
if !a.Cfg.MarkupConfig().AsciiDocExt.PreserveTOC {
|
||||
n.Parent.RemoveChild(n)
|
||||
@@ -364,7 +364,7 @@ func parseTOC(doc *html.Node) *tableofcontents.Fragments {
|
||||
f = func(n *html.Node, row, level int) {
|
||||
if n.Type == html.ElementNode {
|
||||
switch n.Data {
|
||||
case "ul":
|
||||
case "ul", "ol":
|
||||
if level == 0 {
|
||||
row++
|
||||
}
|
||||
|
||||
@@ -326,9 +326,7 @@ func (a *hugoContextExtension) Extend(m goldmark.Markdown) {
|
||||
renderer.WithNodeRenderers(
|
||||
util.Prioritized(&hugoContextRenderer{
|
||||
logger: a.logger,
|
||||
Config: html.Config{
|
||||
Writer: html.DefaultWriter,
|
||||
},
|
||||
Writer: html.DefaultWriter,
|
||||
}, 50),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -36,9 +36,7 @@ var _ renderer.SetOptioner = (*hookedRenderer)(nil)
|
||||
func newLinkRenderer(cfg goldmark_config.Config) renderer.NodeRenderer {
|
||||
r := &hookedRenderer{
|
||||
linkifyProtocol: []byte(cfg.Extensions.LinkifyProtocol),
|
||||
Config: html.Config{
|
||||
Writer: html.DefaultWriter,
|
||||
},
|
||||
Writer: html.DefaultWriter,
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -169,15 +167,13 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
|
||||
ctx.RenderContext().Ctx,
|
||||
w,
|
||||
imageLinkContext{
|
||||
linkContext: linkContext{
|
||||
BaseContext: render.NewBaseContext(ctx, lr, node, source, ordinal),
|
||||
destination: string(n.Destination),
|
||||
title: string(n.Title),
|
||||
text: hstring.HTML(text),
|
||||
plainText: render.TextPlain(n, source),
|
||||
AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerGeneral),
|
||||
},
|
||||
isBlock: isBlock,
|
||||
BaseContext: render.NewBaseContext(ctx, lr, node, source, ordinal),
|
||||
destination: string(n.Destination),
|
||||
title: string(n.Title),
|
||||
text: hstring.HTML(text),
|
||||
plainText: render.TextPlain(n, source),
|
||||
AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerGeneral),
|
||||
isBlock: isBlock,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2026 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 highlight
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
)
|
||||
|
||||
// ChromaStylesOptions holds options for ChromaStylesCSS.
|
||||
type ChromaStylesOptions struct {
|
||||
// Highlighter style, e.g. "monokai".
|
||||
Style string
|
||||
|
||||
// Style mode, "light" or "dark".
|
||||
Mode string
|
||||
|
||||
// Scope selectors under a top level mode class, e.g. ".dark .chroma".
|
||||
ModeSelector bool
|
||||
|
||||
// Class name used by ModeSelector for dark styles, defaults to "dark".
|
||||
ClassDark string
|
||||
|
||||
// Class name used by ModeSelector for light styles, defaults to "light".
|
||||
ClassLight string
|
||||
|
||||
// Foreground and background colors for highlighted lines, e.g. "#fff000 bg:#000fff".
|
||||
HighlightStyle string
|
||||
|
||||
// Foreground and background colors for inline line numbers.
|
||||
LineNumbersInlineStyle string
|
||||
|
||||
// Foreground and background colors for table line numbers.
|
||||
LineNumbersTableStyle string
|
||||
|
||||
// Omit CSS class comment prefixes in the generated CSS.
|
||||
OmitClassComments bool
|
||||
}
|
||||
|
||||
// ChromaStylesCSS generates a CSS stylesheet for the Chroma code highlighter.
|
||||
// This stylesheet is needed if markup.highlight.noClasses is disabled in config.
|
||||
func ChromaStylesCSS(opts ChromaStylesOptions) (string, error) {
|
||||
name := strings.ToLower(opts.Style)
|
||||
if !slices.Contains(styles.Names(), name) {
|
||||
return "", fmt.Errorf("invalid style: %s", name)
|
||||
}
|
||||
var chromaStyle *chroma.Style
|
||||
if opts.Mode != "" {
|
||||
var mode chroma.Mode
|
||||
switch opts.Mode {
|
||||
case "light":
|
||||
mode = chroma.Light
|
||||
case "dark":
|
||||
mode = chroma.Dark
|
||||
default:
|
||||
return "", fmt.Errorf("invalid mode: %s", opts.Mode)
|
||||
}
|
||||
|
||||
chromaStyle = styles.GetForMode(name, mode)
|
||||
if chromaStyle.Mode() != mode {
|
||||
return "", fmt.Errorf("style %q does not have a %q mode", name, opts.Mode)
|
||||
}
|
||||
} else {
|
||||
chromaStyle = styles.Get(name)
|
||||
}
|
||||
builder := chromaStyle.Builder()
|
||||
if opts.HighlightStyle != "" {
|
||||
builder.Add(chroma.LineHighlight, opts.HighlightStyle)
|
||||
}
|
||||
if opts.LineNumbersInlineStyle != "" {
|
||||
builder.Add(chroma.LineNumbers, opts.LineNumbersInlineStyle)
|
||||
}
|
||||
if opts.LineNumbersTableStyle != "" {
|
||||
builder.Add(chroma.LineNumbersTable, opts.LineNumbersTableStyle)
|
||||
}
|
||||
style, err := builder.Build()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
options := []html.Option{
|
||||
html.WithCSSComments(!opts.OmitClassComments),
|
||||
html.WithCustomCSS(chromaCSSOverrides(style)),
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := html.New(options...).WriteCSS(&buf, style); err != nil {
|
||||
return "", err
|
||||
}
|
||||
css := buf.String()
|
||||
if opts.ModeSelector {
|
||||
// Scope every selector under a top level mode class, e.g. ".dark .chroma".
|
||||
// This allows generating both light and dark stylesheets and toggling
|
||||
// them with a parent class on the page.
|
||||
// There's no upstream option for this (I think), so do string replacements for now.
|
||||
// TODO(bep) upstream option for this.
|
||||
classDark, classLight := opts.ClassDark, opts.ClassLight
|
||||
if classDark == "" {
|
||||
classDark = "dark"
|
||||
}
|
||||
if classLight == "" {
|
||||
classLight = "light"
|
||||
}
|
||||
var prefix string
|
||||
switch style.Mode() {
|
||||
case chroma.Light:
|
||||
prefix = "." + classLight + " "
|
||||
case chroma.Dark:
|
||||
prefix = "." + classDark + " "
|
||||
default:
|
||||
return "", fmt.Errorf("style %q does not have a light or dark mode", style.Name)
|
||||
}
|
||||
replacer := strings.NewReplacer(
|
||||
".bg {", prefix+".bg {",
|
||||
".chroma ", prefix+".chroma ",
|
||||
)
|
||||
css = replacer.Replace(css)
|
||||
}
|
||||
|
||||
return css, nil
|
||||
}
|
||||
|
||||
// chromaCSSOverrides re-emits leaf token colors that Chroma's minifier drops
|
||||
// because they equal the style's default foreground (the .chroma color).
|
||||
// In a paired light/dark setup, the other sheet's explicit rule (e.g. .chroma .nx)
|
||||
// would otherwise leak into this mode, since an explicit declaration beats
|
||||
// inheritance from .chroma.
|
||||
// TODO(bep): replace with an upstream option, see issue 15161.
|
||||
func chromaCSSOverrides(style *chroma.Style) map[chroma.TokenType]string {
|
||||
bg := style.Get(chroma.Background)
|
||||
m := make(map[chroma.TokenType]string)
|
||||
for tt := range chroma.StandardTypes {
|
||||
if tt == chroma.Background || !style.Has(tt) || !chromaLeafToken(tt) {
|
||||
continue
|
||||
}
|
||||
entry := style.Get(tt)
|
||||
if !entry.Sub(bg).IsZero() || !entry.Colour.IsSet() {
|
||||
continue
|
||||
}
|
||||
if css := html.StyleEntryToCSS(chroma.StyleEntry{Colour: entry.Colour}); css != "" {
|
||||
m[tt] = css
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func chromaLeafToken(tt chroma.TokenType) bool {
|
||||
for other := range chroma.StandardTypes {
|
||||
if other != tt && (other.Category() == tt || other.SubCategory() == tt) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -21,6 +21,7 @@ type BuiltinTypes struct {
|
||||
TextType Type
|
||||
TOMLType Type
|
||||
YAMLType Type
|
||||
SourceMapType Type
|
||||
|
||||
// Common image types
|
||||
PNGType Type
|
||||
@@ -36,6 +37,8 @@ type BuiltinTypes struct {
|
||||
// Common font types
|
||||
TrueTypeFontType Type
|
||||
OpenTypeFontType Type
|
||||
WOFFFontType Type
|
||||
WOFF2FontType Type
|
||||
|
||||
// Common document types
|
||||
PDFType Type
|
||||
@@ -80,6 +83,7 @@ var Builtin = BuiltinTypes{
|
||||
TextType: Type{Type: "text/plain"},
|
||||
TOMLType: Type{Type: "application/toml"},
|
||||
YAMLType: Type{Type: "application/yaml"},
|
||||
SourceMapType: Type{Type: "application/source-map"},
|
||||
|
||||
// Common image types
|
||||
PNGType: Type{Type: "image/png"},
|
||||
@@ -95,6 +99,8 @@ var Builtin = BuiltinTypes{
|
||||
// Common font types
|
||||
TrueTypeFontType: Type{Type: "font/ttf"},
|
||||
OpenTypeFontType: Type{Type: "font/otf"},
|
||||
WOFFFontType: Type{Type: "font/woff"},
|
||||
WOFF2FontType: Type{Type: "font/woff2"},
|
||||
|
||||
// Common document types
|
||||
PDFType: Type{Type: "application/pdf"},
|
||||
@@ -139,6 +145,7 @@ var defaultMediaTypesConfig = map[string]any{
|
||||
"text/plain": map[string]any{"suffixes": []string{"txt"}},
|
||||
"application/toml": map[string]any{"suffixes": []string{"toml"}},
|
||||
"application/yaml": map[string]any{"suffixes": []string{"yaml", "yml"}},
|
||||
"application/source-map": map[string]any{"suffixes": []string{"map"}},
|
||||
|
||||
// Common image types
|
||||
"image/png": map[string]any{"suffixes": []string{"png"}},
|
||||
@@ -152,8 +159,10 @@ var defaultMediaTypesConfig = map[string]any{
|
||||
"image/heic": map[string]any{"suffixes": []string{"heic"}},
|
||||
|
||||
// Common font types
|
||||
"font/ttf": map[string]any{"suffixes": []string{"ttf"}},
|
||||
"font/otf": map[string]any{"suffixes": []string{"otf"}},
|
||||
"font/ttf": map[string]any{"suffixes": []string{"ttf"}},
|
||||
"font/otf": map[string]any{"suffixes": []string{"otf"}},
|
||||
"font/woff": map[string]any{"suffixes": []string{"woff"}},
|
||||
"font/woff2": map[string]any{"suffixes": []string{"woff2"}},
|
||||
|
||||
// Common document types
|
||||
"application/pdf": map[string]any{"suffixes": []string{"pdf"}},
|
||||
|
||||
@@ -142,6 +142,9 @@ func TestDefaultTypes(t *testing.T) {
|
||||
{Builtin.PDFType, "application", "pdf", "pdf", "application/pdf", "application/pdf"},
|
||||
{Builtin.TrueTypeFontType, "font", "ttf", "ttf", "font/ttf", "font/ttf"},
|
||||
{Builtin.OpenTypeFontType, "font", "otf", "otf", "font/otf", "font/otf"},
|
||||
{Builtin.WOFFFontType, "font", "woff", "woff", "font/woff", "font/woff"},
|
||||
{Builtin.WOFF2FontType, "font", "woff2", "woff2", "font/woff2", "font/woff2"},
|
||||
{Builtin.SourceMapType, "application", "source-map", "map", "application/source-map", "application/source-map"},
|
||||
} {
|
||||
c.Assert(test.tp.MainType, qt.Equals, test.expectedMainType)
|
||||
c.Assert(test.tp.SubType, qt.Equals, test.expectedSubType)
|
||||
@@ -151,5 +154,5 @@ func TestDefaultTypes(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
c.Assert(len(DefaultTypes), qt.Equals, 44)
|
||||
c.Assert(len(DefaultTypes), qt.Equals, 47)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ type SuffixInfo struct {
|
||||
// If http.DetectContentType resolves to application/octet-stream, a zero Type is returned.
|
||||
// If http.DetectContentType resolves to text/plain or application/xml, we try to get more specific using types and ext.
|
||||
func FromContent(types Types, extensionHints []string, content []byte) Type {
|
||||
t := strings.Split(http.DetectContentType(content), ";")[0]
|
||||
t, _, _ := strings.Cut(http.DetectContentType(content), ";")
|
||||
if t == "application/octet-stream" {
|
||||
return zero
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func FromString(t string) (Type, error) {
|
||||
mainType := parts[0]
|
||||
subParts := strings.Split(parts[1], "+")
|
||||
|
||||
subType := strings.Split(subParts[0], ";")[0]
|
||||
subType, _, _ := strings.Cut(subParts[0], ";")
|
||||
|
||||
var suffix string
|
||||
|
||||
|
||||
@@ -178,15 +178,29 @@ func (s *Store) WriteMetrics(w io.Writer) {
|
||||
}
|
||||
|
||||
sort.Sort(bySum(results))
|
||||
|
||||
for _, v := range results {
|
||||
if s.calculateHints {
|
||||
fmt.Fprintf(w, " %15s %12s %12s %9d %7.f %6d %5d %s\n", v.sum, v.avg, v.max, v.cacheFactor, float64(v.cacheCount)/float64(v.count)*100, v.cacheCount, v.count, v.key)
|
||||
fmt.Fprintf(w, " %15s %12s %12s %9d %7.f %6d %5d %s\n", formatDuration(v.sum), formatDuration(v.avg), formatDuration(v.max), v.cacheFactor, float64(v.cacheCount)/float64(v.count)*100, v.cacheCount, v.count, v.key)
|
||||
} else {
|
||||
fmt.Fprintf(w, " %15s %12s %12s %5d %s\n", v.sum, v.avg, v.max, v.count, v.key)
|
||||
fmt.Fprintf(w, " %15s %12s %12s %5d %s\n", formatDuration(v.sum), formatDuration(v.avg), formatDuration(v.max), v.count, v.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatDuration(d time.Duration) string {
|
||||
switch {
|
||||
case d >= time.Second:
|
||||
return fmt.Sprintf("%.2f s", float64(d)/float64(time.Second)) // additional spacing between value and unit
|
||||
case d >= time.Millisecond:
|
||||
return fmt.Sprintf("%.2f ms", float64(d)/float64(time.Millisecond))
|
||||
case d >= time.Microsecond:
|
||||
return fmt.Sprintf("%.2f µs", float64(d)/float64(time.Microsecond))
|
||||
default:
|
||||
return fmt.Sprintf("%.2f ns", float64(d)/float64(time.Nanosecond))
|
||||
}
|
||||
}
|
||||
|
||||
// A result represents the calculated results for a given metric.
|
||||
type result struct {
|
||||
key string
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
|
||||
@@ -66,3 +67,21 @@ func BenchmarkHowSimilar(b *testing.B) {
|
||||
howSimilar(s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatDuration(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
tests := []struct {
|
||||
duration time.Duration
|
||||
want string
|
||||
}{
|
||||
{4*time.Second + 342*time.Millisecond, "4.34 s"}, // additional spacing between value and unit
|
||||
{170*time.Millisecond + 289*time.Microsecond, "170.29 ms"},
|
||||
{16*time.Microsecond + 90*time.Nanosecond, "16.09 µs"},
|
||||
{147 * time.Nanosecond, "147.00 ns"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := formatDuration(tt.duration)
|
||||
c.Assert(got, qt.Equals, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ func (c *Client) writeHugoDirectSum(mods Modules) error {
|
||||
continue
|
||||
}
|
||||
if m.IsGoMod() && m.VersionQuery() != "" {
|
||||
sums = append(sums, modSum{pathVersionKey: pathVersionKey{path: m.Path(), version: m.Version()}, sum: m.Sum()})
|
||||
sums = append(sums, modSum{path: m.Path(), version: m.Version(), sum: m.Sum()})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,15 +195,28 @@ func Pack(sourceFs, assetsWithDuplicatesPreservedFs afero.Fs, mods modules.Modul
|
||||
}
|
||||
}
|
||||
|
||||
name := "project"
|
||||
rfi, err := sourceFs.Stat("")
|
||||
if err == nil {
|
||||
name = rfi.Name()
|
||||
// Stable defaults; mark the workspace private so npm never publishes it.
|
||||
// Preserve any hand-set values from an existing file.
|
||||
name, version, private := "hugoautogen", "0.1.0", true
|
||||
if data, err := afero.ReadFile(sourceFs, workspacePackageJSON); err == nil {
|
||||
var existing map[string]any
|
||||
if err := json.Unmarshal(data, &existing); err == nil {
|
||||
if s, ok := existing["name"].(string); ok && s != "" {
|
||||
name = s
|
||||
}
|
||||
if s, ok := existing["version"].(string); ok && s != "" {
|
||||
version = s
|
||||
}
|
||||
if b, ok := existing["private"].(bool); ok {
|
||||
private = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoGenPkg := map[string]any{
|
||||
"name": name,
|
||||
"version": "0.1.0",
|
||||
"version": version,
|
||||
"private": private,
|
||||
dependenciesKey: moduleDeps,
|
||||
devDependenciesKey: moduleDevDeps,
|
||||
}
|
||||
|
||||
@@ -92,6 +92,40 @@ func TestPackageBuilder(t *testing.T) {
|
||||
b.Assert(string(meta2), qt.Equals, string(meta1))
|
||||
}
|
||||
|
||||
// The generated workspace package.json should get a stable name and
|
||||
// "private": true, and preserve hand-set name/version/private on regeneration.
|
||||
// See issue 15245.
|
||||
func TestPackStableNameAndPrivate(t *testing.T) {
|
||||
files := getPackageBuilderTestFiles()
|
||||
b := hugolib.Test(t, files)
|
||||
sourceFs := b.H.BaseFs.ProjectSourceFs
|
||||
assetsFs := b.H.BaseFs.AssetsWithDuplicatesPreserved.Fs
|
||||
mods := b.H.Configs.Modules
|
||||
|
||||
b.Assert(npm.Pack(sourceFs, assetsFs, mods), qt.IsNil)
|
||||
|
||||
pkg, err := afero.ReadFile(sourceFs, "packages/hugoautogen/package.json")
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.Assert(string(pkg), qt.Contains, `"name": "hugoautogen"`)
|
||||
b.Assert(string(pkg), qt.Contains, `"private": true`)
|
||||
b.Assert(string(pkg), qt.Contains, `"version": "0.1.0"`)
|
||||
|
||||
// Hand-set fields survive a re-pack.
|
||||
edited := strings.NewReplacer(
|
||||
`"name": "hugoautogen"`, `"name": "@foo/hugoautogen"`,
|
||||
`"version": "0.1.0"`, `"version": "1.2.3"`,
|
||||
`"private": true`, `"private": false`,
|
||||
).Replace(string(pkg))
|
||||
b.Assert(afero.WriteFile(sourceFs, "packages/hugoautogen/package.json", []byte(edited), 0o666), qt.IsNil)
|
||||
|
||||
b.Assert(npm.Pack(sourceFs, assetsFs, mods), qt.IsNil)
|
||||
pkg, err = afero.ReadFile(sourceFs, "packages/hugoautogen/package.json")
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.Assert(string(pkg), qt.Contains, `"name": "@foo/hugoautogen"`)
|
||||
b.Assert(string(pkg), qt.Contains, `"version": "1.2.3"`)
|
||||
b.Assert(string(pkg), qt.Contains, `"private": false`)
|
||||
}
|
||||
|
||||
func BenchmarkPackageFilesSum(b *testing.B) {
|
||||
files := getPackageBuilderTestFiles()
|
||||
bb := hugolib.Test(b, files)
|
||||
|
||||
@@ -40,8 +40,8 @@ func TestMenuCache(t *testing.T) {
|
||||
m[0].MenuConfig.Title = "changed"
|
||||
}
|
||||
|
||||
var o1 uint64
|
||||
var o2 uint64
|
||||
var o1 atomic.Uint64
|
||||
var o2 atomic.Uint64
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestMenuCache(t *testing.T) {
|
||||
for k, menu := range testMenuSets {
|
||||
l1.Lock()
|
||||
m, ca := c1.get("k1", nil, menu)
|
||||
c.Assert(ca, qt.Equals, !atomic.CompareAndSwapUint64(&o1, uint64(k), uint64(k+1)))
|
||||
c.Assert(ca, qt.Equals, !o1.CompareAndSwap(uint64(k), uint64(k+1)))
|
||||
l1.Unlock()
|
||||
m2, c2 := c1.get("k1", nil, m)
|
||||
c.Assert(c2, qt.Equals, true)
|
||||
@@ -69,7 +69,7 @@ func TestMenuCache(t *testing.T) {
|
||||
|
||||
l2.Lock()
|
||||
m3, c3 := c1.get("k2", changeFirst, menu)
|
||||
c.Assert(c3, qt.Equals, !atomic.CompareAndSwapUint64(&o2, uint64(k), uint64(k+1)))
|
||||
c.Assert(c3, qt.Equals, !o2.CompareAndSwap(uint64(k), uint64(k+1)))
|
||||
l2.Unlock()
|
||||
c.Assert(m3, qt.Not(qt.IsNil))
|
||||
c.Assert("changed", qt.Equals, m3[0].Title)
|
||||
|
||||
@@ -72,16 +72,14 @@ type Config struct {
|
||||
// can be set if position of first shortcode is known
|
||||
func newPageLexer(input []byte, stateStart stateFunc, cfg Config) *pageLexer {
|
||||
lexer := &pageLexer{
|
||||
input: input,
|
||||
stateStart: stateStart,
|
||||
summaryDivider: summaryDivider,
|
||||
cfg: cfg,
|
||||
lexerShortcodeState: lexerShortcodeState{
|
||||
currLeftDelimItem: tLeftDelimScNoMarkup,
|
||||
currRightDelimItem: tRightDelimScNoMarkup,
|
||||
openShortcodes: make(map[unique.Handle[string]]bool),
|
||||
},
|
||||
items: make([]Item, 0, 5),
|
||||
input: input,
|
||||
stateStart: stateStart,
|
||||
summaryDivider: summaryDivider,
|
||||
cfg: cfg,
|
||||
currLeftDelimItem: tLeftDelimScNoMarkup,
|
||||
currRightDelimItem: tRightDelimScNoMarkup,
|
||||
openShortcodes: make(map[unique.Handle[string]]bool),
|
||||
items: make([]Item, 0, 5),
|
||||
}
|
||||
|
||||
lexer.sectionHandlers = createSectionHandlers(lexer)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2025 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 resources
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
)
|
||||
|
||||
// Artifact represents an additional output file published as part of a
|
||||
// resource transformation, e.g. a source map or a file emitted by ESBuild's
|
||||
// file loader. It is exposed to templates via Resource.Data.Artifacts.
|
||||
type Artifact interface {
|
||||
resource.MediaTypeProvider
|
||||
resource.ResourceLinksProvider
|
||||
}
|
||||
|
||||
// NewArtifact creates a new Artifact with the given permalinks and media type.
|
||||
func NewArtifact(permalink, relPermalink string, mediaType media.Type) Artifact {
|
||||
return &artifact{permalink: permalink, relPermalink: relPermalink, mediaType: mediaType}
|
||||
}
|
||||
|
||||
type artifact struct {
|
||||
permalink string
|
||||
relPermalink string
|
||||
mediaType media.Type
|
||||
}
|
||||
|
||||
func (a *artifact) MediaType() media.Type {
|
||||
return a.mediaType
|
||||
}
|
||||
|
||||
func (a *artifact) Permalink() string {
|
||||
return a.permalink
|
||||
}
|
||||
|
||||
func (a *artifact) RelPermalink() string {
|
||||
return a.relPermalink
|
||||
}
|
||||
@@ -16,13 +16,12 @@ package images
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"image/color"
|
||||
"math"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hstrings"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type colorGoProvider interface {
|
||||
@@ -65,10 +64,8 @@ func (c Color) String() string {
|
||||
// For hashstructure. This struct is used in template func options
|
||||
// that needs to be able to hash a Color.
|
||||
// For internal use only.
|
||||
func (c Color) Hash() (uint64, error) {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(c.hex))
|
||||
return h.Sum64(), nil
|
||||
func (c Color) Key() string {
|
||||
return c.hex
|
||||
}
|
||||
|
||||
func (c *Color) init() error {
|
||||
|
||||
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
@@ -13,7 +13,9 @@
|
||||
|
||||
package internal
|
||||
|
||||
import "github.com/gohugoio/hugo/common/hashing"
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
)
|
||||
|
||||
// ResourceTransformationKey are provided by the different transformation implementations.
|
||||
// It identifies the transformation (name) and its configuration (elements).
|
||||
|
||||
@@ -39,6 +39,11 @@ func (b *Builder) Build(dir string) *Config {
|
||||
}
|
||||
var roots []string
|
||||
for root := range b.sourceRoots.All() {
|
||||
if root == dir {
|
||||
// We need mappings for other source roots (e.g. mounted modules), but not for the current one.
|
||||
// See issue 15169.
|
||||
continue
|
||||
}
|
||||
rel, err := filepath.Rel(dir, filepath.Join(root, "*"))
|
||||
if err == nil {
|
||||
roots = append(roots, rel)
|
||||
|
||||
@@ -37,4 +37,10 @@ func TestJsConfigBuilder(t *testing.T) {
|
||||
c.Assert(string(data), qt.Not(qt.Contains), "baseUrl")
|
||||
|
||||
c.Assert(NewBuilder().Build("/a/b"), qt.IsNil)
|
||||
|
||||
// Issue 15169.
|
||||
b = NewBuilder()
|
||||
b.AddSourceRoot("/c/assets")
|
||||
conf = b.Build("/c/assets")
|
||||
c.Assert(conf, qt.IsNil)
|
||||
}
|
||||
|
||||
@@ -100,12 +100,6 @@ func (i HugoInfo) Store() *hstore.Scratch {
|
||||
return i.store
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultihost instead.
|
||||
func (i HugoInfo) IsMultiHost() bool {
|
||||
hugo.Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
|
||||
return i.opts.Conf.IsMultihost()
|
||||
}
|
||||
|
||||
// IsMultihost reports whether each configured language has a unique baseURL.
|
||||
func (i HugoInfo) IsMultihost() bool {
|
||||
return i.opts.Conf.IsMultihost()
|
||||
@@ -168,7 +162,7 @@ func NewHugoInfo(opts HugoInfoOptions) HugoInfo {
|
||||
BuildDate: opts.BuildDate,
|
||||
GoVersion: opts.GoVersion,
|
||||
|
||||
hugoInfoProviders: hugoInfoProviders{HugoInfoHugoSitesProvider: opts.HugoInfoHugoSitesProvider},
|
||||
HugoInfoHugoSitesProvider: opts.HugoInfoHugoSitesProvider,
|
||||
|
||||
opts: opts,
|
||||
store: hstore.NewScratch(),
|
||||
|
||||
@@ -327,8 +327,9 @@ type PageMetaInternalProvider interface {
|
||||
|
||||
// PageRenderProvider provides a way for a Page to render content.
|
||||
type PageRenderProvider interface {
|
||||
// Render renders the given layout with this Page as context.
|
||||
Render(ctx context.Context, layout ...string) (template.HTML, error)
|
||||
// Render renders the given view (a layout template) with this Page as
|
||||
// the data context, or, if given, the second argument CONTEXT.
|
||||
Render(ctx context.Context, args ...any) (template.HTML, error)
|
||||
// RenderString renders the first value in args with the content renderer defined
|
||||
// for this Page.
|
||||
// It takes an optional map as a second argument:
|
||||
|
||||
@@ -117,8 +117,8 @@ func (lcp *LazyContentProvider) Len(ctx context.Context) int {
|
||||
return lcp.init.Value(ctx).Len(ctx)
|
||||
}
|
||||
|
||||
func (lcp *LazyContentProvider) Render(ctx context.Context, layout ...string) (template.HTML, error) {
|
||||
return lcp.init.Value(ctx).Render(ctx, layout...)
|
||||
func (lcp *LazyContentProvider) Render(ctx context.Context, args ...any) (template.HTML, error) {
|
||||
return lcp.init.Value(ctx).Render(ctx, args...)
|
||||
}
|
||||
|
||||
func (lcp *LazyContentProvider) RenderString(ctx context.Context, args ...any) (template.HTML, error) {
|
||||
|
||||
@@ -399,7 +399,7 @@ func (p *nopPage) RelRef(argsm map[string]any) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (p *nopPage) Render(ctx context.Context, layout ...string) (template.HTML, error) {
|
||||
func (p *nopPage) Render(ctx context.Context, args ...any) (template.HTML, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,11 @@ func (pages Pages) ProbablyEq(other any) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IndexOf returns the index of page in p, or -1 if not found.
|
||||
func (p Pages) IndexOf(page Page) int {
|
||||
return searchPage(page, p)
|
||||
}
|
||||
|
||||
// PagesFactory somehow creates some Pages.
|
||||
// We do a lot of lazy Pages initialization in Hugo, so we need a type.
|
||||
type PagesFactory func() Pages
|
||||
|
||||
@@ -31,8 +31,8 @@ func TestPageCache(t *testing.T) {
|
||||
p[0].(*testPage).description = "changed"
|
||||
}
|
||||
|
||||
var o1 uint64
|
||||
var o2 uint64
|
||||
var o1 atomic.Uint64
|
||||
var o2 atomic.Uint64
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestPageCache(t *testing.T) {
|
||||
for k, pages := range testPageSets {
|
||||
l1.Lock()
|
||||
p, ca := c1.get("k1", nil, pages)
|
||||
c.Assert(ca, qt.Equals, !atomic.CompareAndSwapUint64(&o1, uint64(k), uint64(k+1)))
|
||||
c.Assert(ca, qt.Equals, !o1.CompareAndSwap(uint64(k), uint64(k+1)))
|
||||
l1.Unlock()
|
||||
p2, c2 := c1.get("k1", nil, p)
|
||||
c.Assert(c2, qt.Equals, true)
|
||||
@@ -60,7 +60,7 @@ func TestPageCache(t *testing.T) {
|
||||
|
||||
l2.Lock()
|
||||
p3, c3 := c1.get("k2", changeFirst, pages)
|
||||
c.Assert(c3, qt.Equals, !atomic.CompareAndSwapUint64(&o2, uint64(k), uint64(k+1)))
|
||||
c.Assert(c3, qt.Equals, !o2.CompareAndSwap(uint64(k), uint64(k+1)))
|
||||
l2.Unlock()
|
||||
c.Assert(p3, qt.Not(qt.IsNil))
|
||||
c.Assert("changed", qt.Equals, p3[0].(*testPage).description)
|
||||
|
||||
@@ -70,3 +70,15 @@ func TestToPages(t *testing.T) {
|
||||
_, err := ToPages("not a page")
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
}
|
||||
|
||||
func TestIndexOf(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
p1, p2, p3, p4 := &testPage{title: "p1"}, &testPage{title: "p2"}, &testPage{title: "p3"}, &testPage{title: "p4"}
|
||||
|
||||
pages := Pages{p1, p2, p3}
|
||||
c.Assert(pages.IndexOf(p1), qt.Equals, 0)
|
||||
c.Assert(pages.IndexOf(p2), qt.Equals, 1)
|
||||
c.Assert(pages.IndexOf(p3), qt.Equals, 2)
|
||||
c.Assert(pages.IndexOf(p4), qt.Equals, -1)
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ func (p *testPage) RelRefFrom(argsm map[string]any, source any) (string, error)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (p *testPage) Render(ctx context.Context, layout ...string) (template.HTML, error) {
|
||||
func (p *testPage) Render(ctx context.Context, args ...any) (template.HTML, error) {
|
||||
panic("testpage: not implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ type transformationKeyer interface {
|
||||
func (spec *Spec) PostProcess(r resource.Resource) (postpub.PostPublishedResource, error) {
|
||||
key := r.(transformationKeyer).TransformationKey()
|
||||
return spec.PostProcessResources.GetOrCreate(key, func() (postpub.PostPublishedResource, error) {
|
||||
result := postpub.NewPostPublishResource(spec.incr.Incr(), r)
|
||||
if result == nil {
|
||||
panic("got nil result")
|
||||
}
|
||||
return result, nil
|
||||
return postpub.NewPostPublishResource(spec.incr.Incr(), r), nil
|
||||
})
|
||||
}
|
||||
|
||||