Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cff26309e5 | |||
| 6206b44ba6 | |||
| 665ac949bd | |||
| 4c162deb03 | |||
| c6227f1d85 | |||
| 1781b18427 | |||
| d6d9dddff3 | |||
| 10891f305e | |||
| c07e94fa6d | |||
| 8f8d12ab2f | |||
| 69455fa422 | |||
| 2fcc53780f | |||
| bfbee17932 | |||
| 944fcc216f | |||
| 914fa13efd | |||
| 51f09b17fd | |||
| be643580dd | |||
| 20e4dd2ee3 | |||
| f41149979a | |||
| 47fee839f6 | |||
| 99665ef227 | |||
| eb2eeee687 | |||
| bea5a46de4 | |||
| f1a7f3d01a | |||
| 08243be1d2 | |||
| 0490a8a162 | |||
| d795cacd71 | |||
| a5db76fe62 | |||
| 566fe7ba12 | |||
| 92573012e8 | |||
| 071f8b4466 | |||
| ae9d5e7d83 | |||
| 78c9a18b0c | |||
| 79aa2ab618 | |||
| 216a69a1ef | |||
| e67886c038 | |||
| d5eda13cb2 | |||
| 8b5d796989 | |||
| c9d7577cb4 | |||
| 2babd6404e | |||
| 78db8aebca | |||
| 3140e0b994 | |||
| 9989404d97 | |||
| b81ba2a0f0 | |||
| afdd87db59 | |||
| e45eae4d67 | |||
| 9b1b11c8a5 | |||
| 9d2b5f98d0 | |||
| 0e00561620 | |||
| 71842140d0 | |||
| cb95a033c1 | |||
| 3240511153 | |||
| 72ff937e11 | |||
| a28bed0817 | |||
| 979423f4d5 | |||
| e85be29867 | |||
| 4d8bfa7f1c | |||
| f0ed91caba | |||
| 7be0377505 | |||
| 439f07eac4 | |||
| ce5a2ce001 | |||
| 0f42d975e0 | |||
| cc2d19e19b | |||
| 094f746751 | |||
| fb8909d5b0 | |||
| 0ee2610d7c | |||
| 251a23ef76 | |||
| 644d55475d | |||
| edeed52fc5 | |||
| de36c1a95d | |||
| 8cf96f244a | |||
| 82af94d1f5 | |||
| 0ff542b4b9 | |||
| a95fe50616 | |||
| 7b6dafc53a | |||
| 932ab4c058 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20201
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20501
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20201
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20501
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
name: Build Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
paths:
|
||||
- Dockerfile
|
||||
- .github/workflows/image.yml
|
||||
pull_request:
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REGISTRY_IMAGE: ghcr.io/gohugoio/hugo
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
|
||||
|
||||
- name: Login to GHCR
|
||||
# Login is only needed when the image is pushed
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # v6.6.1
|
||||
with:
|
||||
context: .
|
||||
push: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
if: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
if: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
@@ -5,6 +5,7 @@
|
||||
[documentation repository]: https://github.com/gohugoio/hugoDocs
|
||||
[documentation]: https://gohugo.io/documentation
|
||||
[dragonfly bsd, freebsd, netbsd, and openbsd]: https://gohugo.io/installation/bsd
|
||||
[features]: https://gohugo.io/about/features/
|
||||
[forum]: https://discourse.gohugo.io
|
||||
[friends]: https://github.com/gohugoio/hugo/graphs/contributors
|
||||
[go]: https://go.dev/
|
||||
@@ -19,7 +20,6 @@
|
||||
[static site generator]: https://en.wikipedia.org/wiki/Static_site_generator
|
||||
[support]: https://discourse.gohugo.io
|
||||
[themes]: https://themes.gohugo.io/
|
||||
[twitter]: https://twitter.com/gohugoio
|
||||
[website]: https://gohugo.io
|
||||
[windows]: https://gohugo.io/installation/windows
|
||||
|
||||
@@ -33,7 +33,7 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
|
||||
[](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>
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a> | <a rel="me" href="https://x.com/gohugoiov2">X</a>
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -52,12 +52,15 @@ Use Hugo's embedded web server during development to instantly see changes to co
|
||||
|
||||
Hugo's fast asset pipelines include:
|
||||
|
||||
- CSS bundling – transpilation (Sass), tree shaking, minification, source maps, SRI hashing, and PostCSS integration
|
||||
- JavaScript bundling – transpilation (TypeScript, JSX), tree shaking, minification, source maps, and SRI hashing
|
||||
- Image processing – convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data
|
||||
- Image processing – Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data
|
||||
- JavaScript bundling – Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing.
|
||||
- Sass processing – Transpile Sass to CSS, bundle, tree shake, minify, create source maps, perform SRI hashing, and integrate with PostCSS
|
||||
- Tailwind CSS processing – Compile Tailwind CSS utility classes into standard CSS, bundle, tree shake, optimize, minify, perform SRI hashing, and integrate with PostCSS
|
||||
|
||||
And with [Hugo Modules], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
|
||||
|
||||
See the [features] section of the documentation for a comprehensive summary of Hugo's capabilities.
|
||||
|
||||
## Sponsors
|
||||
|
||||
<p> </p>
|
||||
@@ -99,6 +102,9 @@ Build the extended edition:
|
||||
```text
|
||||
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
|
||||
```
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#gohugoio/hugo&Timeline)
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -143,40 +149,39 @@ Hugo stands on the shoulders of great open source libraries. Run `hugo env --log
|
||||
|
||||
```text
|
||||
cloud.google.com/go/compute/metadata="v0.2.3"
|
||||
cloud.google.com/go/iam="v1.1.3"
|
||||
cloud.google.com/go/storage="v1.31.0"
|
||||
cloud.google.com/go="v0.110.8"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.7.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.3.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.3.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.1.0"
|
||||
cloud.google.com/go/iam="v1.1.5"
|
||||
cloud.google.com/go/storage="v1.35.1"
|
||||
cloud.google.com/go="v0.110.10"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.9.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.4.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.5.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.2.0"
|
||||
github.com/Azure/go-autorest/autorest/to="v0.4.0"
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go="v1.0.0"
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go="v1.2.0"
|
||||
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
|
||||
github.com/PuerkitoBio/purell="v1.1.1"
|
||||
github.com/PuerkitoBio/urlesc="v0.0.0-20170810143723-de5bf2ad4578"
|
||||
github.com/alecthomas/chroma/v2="v2.11.1"
|
||||
github.com/armon/go-radix="v1.0.0"
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.4.11"
|
||||
github.com/aws/aws-sdk-go-v2/config="v1.18.32"
|
||||
github.com/aws/aws-sdk-go-v2/credentials="v1.13.31"
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.13.7"
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.11.76"
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.1.37"
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.4.31"
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini="v1.3.38"
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.1.0"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.9.12"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.1.32"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.9.31"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.15.0"
|
||||
github.com/aws/aws-sdk-go-v2/service/s3="v1.38.1"
|
||||
github.com/aws/aws-sdk-go-v2/service/sso="v1.13.1"
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.15.1"
|
||||
github.com/aws/aws-sdk-go-v2/service/sts="v1.21.1"
|
||||
github.com/aws/aws-sdk-go-v2="v1.20.0"
|
||||
github.com/aws/aws-sdk-go="v1.48.2"
|
||||
github.com/aws/smithy-go="v1.14.0"
|
||||
github.com/alecthomas/chroma/v2="v2.14.0"
|
||||
github.com/armon/go-radix="v1.0.1-0.20221118154546-54df44f2176c"
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.5.4"
|
||||
github.com/aws/aws-sdk-go-v2/config="v1.26.1"
|
||||
github.com/aws/aws-sdk-go-v2/credentials="v1.16.12"
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.14.10"
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.15.7"
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.3.5"
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.6.5"
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini="v1.7.2"
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.2.9"
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront="v1.35.4"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.10.4"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.2.9"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.10.9"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.16.9"
|
||||
github.com/aws/aws-sdk-go-v2/service/s3="v1.47.5"
|
||||
github.com/aws/aws-sdk-go-v2/service/sso="v1.18.5"
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.21.5"
|
||||
github.com/aws/aws-sdk-go-v2/service/sts="v1.26.5"
|
||||
github.com/aws/aws-sdk-go-v2="v1.26.1"
|
||||
github.com/aws/aws-sdk-go="v1.50.7"
|
||||
github.com/aws/smithy-go="v1.20.2"
|
||||
github.com/bep/clocks="v0.5.0"
|
||||
github.com/bep/debounce="v1.2.0"
|
||||
github.com/bep/gitmap="v1.1.2"
|
||||
@@ -185,32 +190,35 @@ github.com/bep/godartsass/v2="v2.0.0"
|
||||
github.com/bep/godartsass="v1.2.0"
|
||||
github.com/bep/golibsass="v1.1.1"
|
||||
github.com/bep/gowebp="v0.3.0"
|
||||
github.com/bep/lazycache="v0.2.0"
|
||||
github.com/bep/logg="v0.3.0"
|
||||
github.com/bep/lazycache="v0.4.0"
|
||||
github.com/bep/logg="v0.4.0"
|
||||
github.com/bep/mclib="v1.20400.20402"
|
||||
github.com/bep/overlayfs="v0.6.0"
|
||||
github.com/bep/simplecobra="v0.3.2"
|
||||
github.com/bep/overlayfs="v0.9.2"
|
||||
github.com/bep/simplecobra="v0.4.0"
|
||||
github.com/bep/tmc="v0.5.1"
|
||||
github.com/clbanning/mxj/v2="v2.7.0"
|
||||
github.com/cli/safeexec="v1.0.1"
|
||||
github.com/cpuguy83/go-md2man/v2="v2.0.2"
|
||||
github.com/cpuguy83/go-md2man/v2="v2.0.3"
|
||||
github.com/disintegration/gift="v1.2.1"
|
||||
github.com/dlclark/regexp2="v1.10.0"
|
||||
github.com/dlclark/regexp2="v1.11.0"
|
||||
github.com/dustin/go-humanize="v1.0.1"
|
||||
github.com/evanw/esbuild="v0.19.7"
|
||||
github.com/evanw/esbuild="v0.21.4"
|
||||
github.com/fatih/color="v1.16.0"
|
||||
github.com/frankban/quicktest="v1.14.6"
|
||||
github.com/fsnotify/fsnotify="v1.7.0"
|
||||
github.com/getkin/kin-openapi="v0.120.0"
|
||||
github.com/getkin/kin-openapi="v0.123.0"
|
||||
github.com/ghodss/yaml="v1.0.0"
|
||||
github.com/go-openapi/jsonpointer="v0.19.6"
|
||||
github.com/go-openapi/swag="v0.22.4"
|
||||
github.com/go-openapi/jsonpointer="v0.20.2"
|
||||
github.com/go-openapi/swag="v0.22.8"
|
||||
github.com/gobuffalo/flect="v1.0.2"
|
||||
github.com/gobwas/glob="v0.2.3"
|
||||
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20230805085216-e63c13218d0e"
|
||||
github.com/gohugoio/httpcache="v0.7.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.2.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.2.0"
|
||||
github.com/gohugoio/locales="v0.14.0"
|
||||
github.com/gohugoio/localescompressed="v1.0.1"
|
||||
github.com/golang-jwt/jwt/v4="v4.5.0"
|
||||
github.com/golang-jwt/jwt/v5="v5.1.0"
|
||||
github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da"
|
||||
github.com/golang/protobuf="v1.5.3"
|
||||
github.com/google/go-cmp="v0.6.0"
|
||||
@@ -221,7 +229,7 @@ github.com/googleapis/enterprise-certificate-proxy="v0.3.2"
|
||||
github.com/googleapis/gax-go/v2="v2.12.0"
|
||||
github.com/gorilla/websocket="v1.5.1"
|
||||
github.com/hairyhenderson/go-codeowners="v0.4.0"
|
||||
github.com/hashicorp/golang-lru/v2="v2.0.1"
|
||||
github.com/hashicorp/golang-lru/v2="v2.0.7"
|
||||
github.com/invopop/yaml="v0.2.0"
|
||||
github.com/jdkato/prose="v1.2.1"
|
||||
github.com/jmespath/go-jmespath="v0.4.0"
|
||||
@@ -231,63 +239,62 @@ github.com/kr/text="v0.2.0"
|
||||
github.com/kylelemons/godebug="v1.1.0"
|
||||
github.com/kyokomi/emoji/v2="v2.2.12"
|
||||
github.com/mailru/easyjson="v0.7.7"
|
||||
github.com/makeworld-the-better-one/dither/v2="v2.4.0"
|
||||
github.com/marekm4/color-extractor="v1.2.1"
|
||||
github.com/mattn/go-colorable="v0.1.13"
|
||||
github.com/mattn/go-isatty="v0.0.20"
|
||||
github.com/mattn/go-runewidth="v0.0.9"
|
||||
github.com/mitchellh/hashstructure="v1.1.0"
|
||||
github.com/mitchellh/mapstructure="v1.5.0"
|
||||
github.com/mitchellh/mapstructure="v1.5.1-0.20231216201459-8508981c8b6c"
|
||||
github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826"
|
||||
github.com/muesli/smartcrop="v0.3.0"
|
||||
github.com/niklasfasching/go-org="v1.7.0"
|
||||
github.com/olekukonko/tablewriter="v0.0.5"
|
||||
github.com/pelletier/go-toml/v2="v2.1.0"
|
||||
github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58"
|
||||
github.com/pelletier/go-toml/v2="v2.2.2"
|
||||
github.com/perimeterx/marshmallow="v1.1.5"
|
||||
github.com/pkg/browser="v0.0.0-20210911075715-681adbf594b8"
|
||||
github.com/pkg/errors="v0.9.1"
|
||||
github.com/rogpeppe/go-internal="v1.11.0"
|
||||
github.com/rogpeppe/go-internal="v1.12.0"
|
||||
github.com/russross/blackfriday/v2="v2.1.0"
|
||||
github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd"
|
||||
github.com/sanity-io/litter="v1.5.5"
|
||||
github.com/sass/dart-sass/compiler="1.63.2"
|
||||
github.com/sass/dart-sass/implementation="1.63.2"
|
||||
github.com/sass/dart-sass/protocol="2.0.0"
|
||||
github.com/sass/dart-sass/compiler="1.77.5"
|
||||
github.com/sass/dart-sass/implementation="1.77.5"
|
||||
github.com/sass/dart-sass/protocol="2.7.1"
|
||||
github.com/sass/libsass="3.6.5"
|
||||
github.com/spf13/afero="v1.10.0"
|
||||
github.com/spf13/cast="v1.5.1"
|
||||
github.com/spf13/cobra="v1.7.0"
|
||||
github.com/spf13/fsync="v0.9.0"
|
||||
github.com/spf13/afero="v1.11.0"
|
||||
github.com/spf13/cast="v1.6.0"
|
||||
github.com/spf13/cobra="v1.8.0"
|
||||
github.com/spf13/fsync="v0.10.1"
|
||||
github.com/spf13/pflag="v1.0.5"
|
||||
github.com/tdewolff/minify/v2="v2.20.7"
|
||||
github.com/tdewolff/parse/v2="v2.7.5"
|
||||
github.com/tdewolff/minify/v2="v2.20.20"
|
||||
github.com/tdewolff/parse/v2="v2.7.13"
|
||||
github.com/webmproject/libwebp="v1.3.2"
|
||||
github.com/yuin/goldmark-emoji="v1.0.2"
|
||||
github.com/yuin/goldmark="v1.6.0"
|
||||
github.com/yuin/goldmark-emoji="v1.0.3"
|
||||
github.com/yuin/goldmark="v1.7.4"
|
||||
go.opencensus.io="v0.24.0"
|
||||
go.uber.org/atomic="v1.11.0"
|
||||
go.uber.org/automaxprocs="v1.5.3"
|
||||
gocloud.dev="v0.34.0"
|
||||
golang.org/x/crypto="v0.15.0"
|
||||
gocloud.dev="v0.36.0"
|
||||
golang.org/x/crypto="v0.23.0"
|
||||
golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326"
|
||||
golang.org/x/image="v0.13.0"
|
||||
golang.org/x/mod="v0.14.0"
|
||||
golang.org/x/net="v0.18.0"
|
||||
golang.org/x/oauth2="v0.13.0"
|
||||
golang.org/x/sync="v0.5.0"
|
||||
golang.org/x/sys="v0.14.0"
|
||||
golang.org/x/text="v0.14.0"
|
||||
golang.org/x/time="v0.3.0"
|
||||
golang.org/x/tools="v0.15.0"
|
||||
golang.org/x/xerrors="v0.0.0-20220907171357-04be3eba64a2"
|
||||
google.golang.org/api="v0.151.0"
|
||||
google.golang.org/genproto/googleapis/api="v0.0.0-20231016165738-49dd2c1f3d0b"
|
||||
google.golang.org/genproto/googleapis/rpc="v0.0.0-20231030173426-d783a09b4405"
|
||||
google.golang.org/genproto="v0.0.0-20231016165738-49dd2c1f3d0b"
|
||||
golang.org/x/image="v0.16.0"
|
||||
golang.org/x/mod="v0.17.0"
|
||||
golang.org/x/net="v0.25.0"
|
||||
golang.org/x/oauth2="v0.15.0"
|
||||
golang.org/x/sync="v0.7.0"
|
||||
golang.org/x/sys="v0.20.0"
|
||||
golang.org/x/text="v0.15.0"
|
||||
golang.org/x/time="v0.5.0"
|
||||
golang.org/x/tools="v0.20.0"
|
||||
golang.org/x/xerrors="v0.0.0-20231012003039-104605ab7028"
|
||||
google.golang.org/api="v0.152.0"
|
||||
google.golang.org/genproto/googleapis/api="v0.0.0-20231120223509-83a465c0220f"
|
||||
google.golang.org/genproto/googleapis/rpc="v0.0.0-20231120223509-83a465c0220f"
|
||||
google.golang.org/genproto="v0.0.0-20231120223509-83a465c0220f"
|
||||
google.golang.org/grpc="v1.59.0"
|
||||
google.golang.org/protobuf="v1.31.0"
|
||||
google.golang.org/protobuf="v1.33.0"
|
||||
gopkg.in/yaml.v2="v2.4.0"
|
||||
gopkg.in/yaml.v3="v3.0.1"
|
||||
howett.net/plist="v1.0.0"
|
||||
software.sslmate.com/src/go-pkcs12="v0.2.0"
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -744,6 +744,20 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
staticEvents := []fsnotify.Event{}
|
||||
dynamicEvents := []fsnotify.Event{}
|
||||
|
||||
filterDuplicateEvents := func(evs []fsnotify.Event) []fsnotify.Event {
|
||||
seen := make(map[string]bool)
|
||||
var n int
|
||||
for _, ev := range evs {
|
||||
if seen[ev.Name] {
|
||||
continue
|
||||
}
|
||||
seen[ev.Name] = true
|
||||
evs[n] = ev
|
||||
n++
|
||||
}
|
||||
return evs[:n]
|
||||
}
|
||||
|
||||
h, err := c.hugo()
|
||||
if err != nil {
|
||||
c.r.logger.Errorln("Error getting the Hugo object:", err)
|
||||
@@ -833,6 +847,11 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
}
|
||||
}
|
||||
|
||||
lrl := c.r.logger.InfoCommand("livereload")
|
||||
|
||||
staticEvents = filterDuplicateEvents(staticEvents)
|
||||
dynamicEvents = filterDuplicateEvents(dynamicEvents)
|
||||
|
||||
if len(staticEvents) > 0 {
|
||||
c.printChangeDetected("Static files")
|
||||
|
||||
@@ -853,19 +872,20 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
if c.s != nil && c.s.doLiveReload {
|
||||
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
|
||||
|
||||
// force refresh when more than one file
|
||||
if !c.errState.wasErr() && len(staticEvents) == 1 {
|
||||
ev := staticEvents[0]
|
||||
h, err := c.hugo()
|
||||
if err != nil {
|
||||
c.r.logger.Errorln("Error getting the Hugo object:", err)
|
||||
return
|
||||
}
|
||||
path := h.BaseFs.SourceFilesystems.MakeStaticPathRelative(ev.Name)
|
||||
|
||||
path := h.BaseFs.SourceFilesystems.MakeStaticPathRelative(staticEvents[0].Name)
|
||||
path = h.RelURL(paths.ToSlashTrimLeading(path), false)
|
||||
|
||||
lrl.Logf("refreshing static file %q", path)
|
||||
livereload.RefreshPath(path)
|
||||
} else {
|
||||
lrl.Logf("got %d static file change events, force refresh", len(staticEvents))
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
}
|
||||
@@ -889,43 +909,30 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
}()
|
||||
|
||||
if c.s != nil && c.s.doLiveReload {
|
||||
if len(partitionedEvents.ContentEvents) == 0 && len(partitionedEvents.AssetEvents) > 0 {
|
||||
if c.errState.wasErr() {
|
||||
livereload.ForceRefresh()
|
||||
return
|
||||
}
|
||||
changed := c.changeDetector.changed()
|
||||
if c.changeDetector != nil && len(changed) == 0 {
|
||||
if c.errState.wasErr() {
|
||||
livereload.ForceRefresh()
|
||||
return
|
||||
}
|
||||
|
||||
changed := c.changeDetector.changed()
|
||||
if c.changeDetector != nil {
|
||||
lrl.Logf("build changed %d files", len(changed))
|
||||
if len(changed) == 0 {
|
||||
// Nothing has changed.
|
||||
return
|
||||
} else if len(changed) == 1 {
|
||||
pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(changed[0]), false)
|
||||
livereload.RefreshPath(pathToRefresh)
|
||||
}
|
||||
}
|
||||
|
||||
// If this change set also contains one or more CSS files, we need to
|
||||
// refresh these as well.
|
||||
var cssChanges []string
|
||||
var otherChanges []string
|
||||
|
||||
for _, ev := range changed {
|
||||
if strings.HasSuffix(ev, ".css") {
|
||||
cssChanges = append(cssChanges, ev)
|
||||
} else {
|
||||
livereload.ForceRefresh()
|
||||
// See https://github.com/gohugoio/hugo/issues/12600.
|
||||
// If this change set also contains one or more CSS files, we need to
|
||||
// refresh these as well.
|
||||
var cssChanges []string
|
||||
var otherChanges []string
|
||||
|
||||
for _, ev := range changed {
|
||||
if strings.HasSuffix(ev, ".css") {
|
||||
cssChanges = append(cssChanges, ev)
|
||||
} else {
|
||||
otherChanges = append(otherChanges, ev)
|
||||
}
|
||||
}
|
||||
|
||||
if len(otherChanges) > 0 {
|
||||
livereload.ForceRefresh()
|
||||
// Allow some time for the live reload script to get reconnected.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
for _, ev := range cssChanges {
|
||||
livereload.RefreshPath(h.PathSpec.RelURL(paths.ToSlashTrimLeading(ev), false))
|
||||
}
|
||||
otherChanges = append(otherChanges, ev)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,11 +948,36 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
}
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
livereload.NavigateToPathForPort(p.RelPermalink(), p.Site().ServerPort())
|
||||
if p != nil && p.RelPermalink() != "" {
|
||||
link, port := p.RelPermalink(), p.Site().ServerPort()
|
||||
lrl.Logf("navigating to %q using port %d", link, port)
|
||||
livereload.NavigateToPathForPort(link, port)
|
||||
} else {
|
||||
lrl.Logf("no page to navigate to, force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
} else if len(otherChanges) > 0 {
|
||||
if len(otherChanges) == 1 {
|
||||
// Allow single changes to be refreshed without a full page reload.
|
||||
pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(otherChanges[0]), false)
|
||||
lrl.Logf("refreshing %q", pathToRefresh)
|
||||
livereload.RefreshPath(pathToRefresh)
|
||||
} else if len(cssChanges) == 0 {
|
||||
lrl.Logf("force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
if len(cssChanges) > 0 {
|
||||
// Allow some time for the live reload script to get reconnected.
|
||||
if len(otherChanges) > 0 {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
for _, ev := range cssChanges {
|
||||
pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(ev), false)
|
||||
lrl.Logf("refreshing CSS %q", pathToRefresh)
|
||||
livereload.RefreshPath(pathToRefresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,13 +268,14 @@ Run "go help get" for more information. All flags available for "go get" is also
|
||||
if info.Name() == "go.mod" {
|
||||
// Found a module.
|
||||
dir := filepath.Dir(path)
|
||||
r.Println("Update module in", dir)
|
||||
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", dir)
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Add(1), flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Println("Update module in", conf.configs.Base.WorkingDir)
|
||||
client := conf.configs.ModulesClient
|
||||
return client.Get(args...)
|
||||
|
||||
|
||||
@@ -162,16 +162,16 @@ type dynamicEvents struct {
|
||||
|
||||
type fileChangeDetector struct {
|
||||
sync.Mutex
|
||||
current map[string]string
|
||||
prev map[string]string
|
||||
current map[string]uint64
|
||||
prev map[string]uint64
|
||||
|
||||
irrelevantRe *regexp.Regexp
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) OnFileClose(name, md5sum string) {
|
||||
func (f *fileChangeDetector) OnFileClose(name string, checksum uint64) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
f.current[name] = md5sum
|
||||
f.current[name] = checksum
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) PrepareNew() {
|
||||
@@ -183,16 +183,16 @@ func (f *fileChangeDetector) PrepareNew() {
|
||||
defer f.Unlock()
|
||||
|
||||
if f.current == nil {
|
||||
f.current = make(map[string]string)
|
||||
f.prev = make(map[string]string)
|
||||
f.current = make(map[string]uint64)
|
||||
f.prev = make(map[string]uint64)
|
||||
return
|
||||
}
|
||||
|
||||
f.prev = make(map[string]string)
|
||||
f.prev = make(map[string]uint64)
|
||||
for k, v := range f.current {
|
||||
f.prev[k] = v
|
||||
}
|
||||
f.current = make(map[string]string)
|
||||
f.current = make(map[string]uint64)
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) changed() []string {
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package hashing provides common hashing utilities.
|
||||
package hashing
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/gohugoio/hashstructure"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
)
|
||||
|
||||
// XXHashFromReader calculates the xxHash for the given reader.
|
||||
func XXHashFromReader(r io.Reader) (uint64, int64, error) {
|
||||
h := getXxHashReadFrom()
|
||||
defer putXxHashReadFrom(h)
|
||||
|
||||
size, err := io.Copy(h, r)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return h.Sum64(), size, nil
|
||||
}
|
||||
|
||||
// XXHashFromString calculates the xxHash for the given string.
|
||||
func XXHashFromString(s string) (uint64, error) {
|
||||
h := xxhash.New()
|
||||
h.WriteString(s)
|
||||
return h.Sum64(), nil
|
||||
}
|
||||
|
||||
// XxHashFromStringHexEncoded calculates the xxHash for the given string
|
||||
// and returns the hash as a hex encoded string.
|
||||
func XxHashFromStringHexEncoded(f string) string {
|
||||
h := xxhash.New()
|
||||
h.WriteString(f)
|
||||
hash := h.Sum(nil)
|
||||
return hex.EncodeToString(hash)
|
||||
}
|
||||
|
||||
// MD5FromStringHexEncoded returns the MD5 hash of the given string.
|
||||
func MD5FromStringHexEncoded(f string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(f))
|
||||
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
|
||||
// it in the more complex values (e.g. Page) will not hash the full content.
|
||||
func HashString(vs ...any) string {
|
||||
hash := HashUint64(vs...)
|
||||
return strconv.FormatUint(hash, 10)
|
||||
}
|
||||
|
||||
var hashOptsPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &hashstructure.HashOptions{
|
||||
Hasher: xxhash.New(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getHashOpts() *hashstructure.HashOptions {
|
||||
return hashOptsPool.Get().(*hashstructure.HashOptions)
|
||||
}
|
||||
|
||||
func putHashOpts(opts *hashstructure.HashOptions) {
|
||||
opts.Hasher.Reset()
|
||||
hashOptsPool.Put(opts)
|
||||
}
|
||||
|
||||
// HashUint64 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
|
||||
// it in the more complex values (e.g. Page) will not hash the full content.
|
||||
func HashUint64(vs ...any) uint64 {
|
||||
var o any
|
||||
if len(vs) == 1 {
|
||||
o = toHashable(vs[0])
|
||||
} else {
|
||||
elements := make([]any, len(vs))
|
||||
for i, e := range vs {
|
||||
elements[i] = toHashable(e)
|
||||
}
|
||||
o = elements
|
||||
}
|
||||
|
||||
hashOpts := getHashOpts()
|
||||
defer putHashOpts(hashOpts)
|
||||
|
||||
hash, err := hashstructure.Hash(o, hashOpts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (x *xxhashReadFrom) ReadFrom(r io.Reader) (int64, error) {
|
||||
for {
|
||||
n, err := r.Read(x.buff)
|
||||
if n > 0 {
|
||||
x.Digest.Write(x.buff[:n])
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return int64(n), err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var xXhashReadFromPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &xxhashReadFrom{Digest: xxhash.New(), buff: make([]byte, 48*1024)}
|
||||
},
|
||||
}
|
||||
|
||||
func getXxHashReadFrom() *xxhashReadFrom {
|
||||
return xXhashReadFromPool.Get().(*xxhashReadFrom)
|
||||
}
|
||||
|
||||
func putXxHashReadFrom(h *xxhashReadFrom) {
|
||||
h.Reset()
|
||||
xXhashReadFromPool.Put(h)
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hashing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestXxHashFromReader(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
s := "Hello World"
|
||||
r := strings.NewReader(s)
|
||||
got, size, err := XXHashFromReader(r)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(size, qt.Equals, int64(len(s)))
|
||||
c.Assert(got, qt.Equals, uint64(7148569436472236994))
|
||||
}
|
||||
|
||||
func TestXxHashFromReaderPara(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 100; j++ {
|
||||
s := strings.Repeat("Hello ", i+j+1*42)
|
||||
r := strings.NewReader(s)
|
||||
got, size, err := XXHashFromReader(r)
|
||||
c.Assert(size, qt.Equals, int64(len(s)))
|
||||
c.Assert(err, qt.IsNil)
|
||||
expect, _ := XXHashFromString(s)
|
||||
c.Assert(got, qt.Equals, expect)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestXxHashFromString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
s := "Hello World"
|
||||
got, err := XXHashFromString(s)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(got, qt.Equals, uint64(7148569436472236994))
|
||||
}
|
||||
|
||||
func TestXxHashFromStringHexEncoded(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
s := "The quick brown fox jumps over the lazy dog"
|
||||
got := XxHashFromStringHexEncoded(s)
|
||||
// Facit: https://asecuritysite.com/encryption/xxhash?val=The%20quick%20brown%20fox%20jumps%20over%20the%20lazy%20dog
|
||||
c.Assert(got, qt.Equals, "0b242d361fda71bc")
|
||||
}
|
||||
|
||||
func BenchmarkXXHashFromReader(b *testing.B) {
|
||||
r := strings.NewReader("Hello World")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
XXHashFromReader(r)
|
||||
r.Seek(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkXXHashFromString(b *testing.B) {
|
||||
s := "Hello World"
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
XXHashFromString(s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkXXHashFromStringHexEncoded(b *testing.B) {
|
||||
s := "The quick brown fox jumps over the lazy dog"
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
XxHashFromStringHexEncoded(s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(HashString("a", "b"), qt.Equals, "3176555414984061461")
|
||||
c.Assert(HashString("ab"), qt.Equals, "7347350983217793633")
|
||||
|
||||
var vals []any = []any{"a", "b", tstKeyer{"c"}}
|
||||
|
||||
c.Assert(HashString(vals...), qt.Equals, "4438730547989914315")
|
||||
c.Assert(vals[2], qt.Equals, tstKeyer{"c"})
|
||||
}
|
||||
|
||||
type tstKeyer struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (t tstKeyer) Key() string {
|
||||
return t.key
|
||||
}
|
||||
|
||||
func (t tstKeyer) String() string {
|
||||
return "key: " + t.key
|
||||
}
|
||||
|
||||
func BenchmarkHashString(b *testing.B) {
|
||||
word := " hello "
|
||||
|
||||
var tests []string
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
sentence := strings.Repeat(word, int(math.Pow(4, float64(i))))
|
||||
tests = append(tests, sentence)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for _, test := range tests {
|
||||
b.Run(fmt.Sprintf("n%d", len(test)), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
HashString(test)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -268,7 +268,8 @@ func IsContextType(tp reflect.Type) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
return isContextCache.GetOrCreate(tp, func() bool {
|
||||
return tp.Implements(contextInterface)
|
||||
isContext, _ := isContextCache.GetOrCreate(tp, func() (bool, error) {
|
||||
return tp.Implements(contextInterface), nil
|
||||
})
|
||||
return isContext
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 129,
|
||||
Minor: 132,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
)
|
||||
|
||||
// PanicOnWarningHook panics on warnings.
|
||||
@@ -85,7 +85,7 @@ func (h *logOnceHandler) HandleLog(e *logg.Entry) error {
|
||||
}
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
hash := identity.HashUint64(e.Level, e.Message, e.Fields)
|
||||
hash := hashing.HashUint64(e.Level, e.Message, e.Fields)
|
||||
if h.seen[hash] {
|
||||
return errStop
|
||||
}
|
||||
|
||||
@@ -40,22 +40,25 @@ func (c *Cache[K, T]) Get(key K) (T, bool) {
|
||||
}
|
||||
|
||||
// GetOrCreate gets the value for the given key if it exists, or creates it if not.
|
||||
func (c *Cache[K, T]) GetOrCreate(key K, create func() T) T {
|
||||
func (c *Cache[K, T]) GetOrCreate(key K, create func() (T, error)) (T, error) {
|
||||
c.RLock()
|
||||
v, found := c.m[key]
|
||||
c.RUnlock()
|
||||
if found {
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
v, found = c.m[key]
|
||||
if found {
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
v, err := create()
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v = create()
|
||||
c.m[key] = v
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Set sets the given key to the given value.
|
||||
|
||||
@@ -16,13 +16,13 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
)
|
||||
|
||||
func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) {
|
||||
// Calculate the hash of the input (not including any defaults applied later).
|
||||
// This allows us to introduce new config options without breaking the hash.
|
||||
h := identity.HashString(configSource)
|
||||
h := hashing.HashString(configSource)
|
||||
|
||||
// Build the config
|
||||
c, ext, err := buildConfig(configSource)
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestNamespace(t *testing.T) {
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(ns, qt.Not(qt.IsNil))
|
||||
c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"})
|
||||
c.Assert(ns.SourceHash, qt.Equals, "14368731254619220105")
|
||||
c.Assert(ns.SourceHash, qt.Equals, "1450430416588600409")
|
||||
c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"})
|
||||
c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil))
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ var DefaultConfig = Config{
|
||||
Allow: MustNewWhitelist(
|
||||
"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
|
||||
"^go$", // for Go Modules
|
||||
"^git$", // For Git info
|
||||
"^npx$", // used by all Node tools (Babel, PostCSS).
|
||||
"^postcss$",
|
||||
"^tailwindcss$",
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^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)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^npx$', '^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)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
baseURL = 'https://example.org/'
|
||||
languageCode = 'en-us'
|
||||
languageCode = 'en-US'
|
||||
title = 'My New Hugo Site'
|
||||
|
||||
[[menus.main]]
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Acos
|
||||
description: Returns the arccosine, in radians, of the given number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Asin
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
returnType: float64
|
||||
signatures: [math.Acos VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Acos 1 }} → 0
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Asin
|
||||
description: Returns the arcsine, in radians, of the given number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Acos
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
returnType: float64
|
||||
signatures: [math.Asin VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Asin 1 }} → 1.5707963267948966
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Atan
|
||||
description: Returns the arctangent, in radians, of the given number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Atan2
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
returnType: float64
|
||||
signatures: [math.Atan VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Atan 1 }} → 0.7853981633974483
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Atan2
|
||||
description: Returns the arctangent, in radians, of the given number pair, determining the correct quadrant from their signs.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Atan
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
returnType: float64
|
||||
signatures: [math.Atan2 VALUE VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Atan2 1 2 }} → 0.4636476090008061
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Cos
|
||||
description: Returns the cosine of the given radian number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Tan
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
returnType: float64
|
||||
signatures: [math.Cos VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Cos 1 }} → 0.5403023058681398
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Pi
|
||||
description: Returns the mathematical constant pi.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
returnType: float64
|
||||
signatures: [math.Pi]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Pi }} → 3.141592653589793
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Sin
|
||||
description: Returns the sine of the given radian number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Pi
|
||||
- functions/math/Cos
|
||||
- functions/math/Tan
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
returnType: float64
|
||||
signatures: [math.Sin VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Sin 1 }} → 0.8414709848078965
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: math.Tan
|
||||
description: Returns the tangent of the given radian number.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/Pi
|
||||
- functions/math/Sin
|
||||
- functions/math/Cos
|
||||
- functions/math/Asin
|
||||
- functions/math/Acos
|
||||
- functions/math/Atan
|
||||
- functions/math/Atan2
|
||||
returnType: float64
|
||||
signatures: [math.Tan VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.Tan 1 }} → 1.557407724654902
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: math.ToDegrees
|
||||
description: ToDegrees converts radians into degrees.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/ToRadians
|
||||
- functions/math/Pi
|
||||
returnType: float64
|
||||
signatures: [math.ToDegrees VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.ToDegrees 1.5707963267948966 }} → 90
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: math.ToRadians
|
||||
description: ToRadians converts degrees into radians.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
aliases: []
|
||||
related:
|
||||
- functions/math/ToDegrees
|
||||
- functions/math/Pi
|
||||
returnType: float64
|
||||
signatures: [math.ToRadians VALUE]
|
||||
---
|
||||
|
||||
{{< new-in 0.130.0 >}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.ToRadians 90 }} → 1.5707963267948966
|
||||
```
|
||||
@@ -113,6 +113,16 @@ This is configurable. See [details].
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
###### Body
|
||||
|
||||
(`string`) The commit message body.
|
||||
|
||||
```go-html-template
|
||||
{{ with .GitInfo }}
|
||||
{{ .Body }} → - Two new pages added.
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
## Last modified date
|
||||
|
||||
By default, when `enableGitInfo` is `true`, the `Lastmod` method on a `Page` object returns the Git AuthorDate of the last commit that included the file.
|
||||
|
||||
@@ -307,6 +307,9 @@ chroma:
|
||||
- gherkin
|
||||
- Gherkin
|
||||
Name: Gherkin
|
||||
- Aliases:
|
||||
- gleam>
|
||||
Name: Gleam
|
||||
- Aliases:
|
||||
- glsl
|
||||
Name: GLSL
|
||||
@@ -1079,6 +1082,8 @@ config:
|
||||
escapedSpace: false
|
||||
definitionList: true
|
||||
extras:
|
||||
delete:
|
||||
enable: false
|
||||
insert:
|
||||
enable: false
|
||||
mark:
|
||||
@@ -1331,6 +1336,7 @@ config:
|
||||
minifyOutput: false
|
||||
tdewolff:
|
||||
css:
|
||||
inline: false
|
||||
keepCSS2: true
|
||||
precision: 0
|
||||
html:
|
||||
@@ -1353,6 +1359,7 @@ config:
|
||||
keepNumbers: false
|
||||
precision: 0
|
||||
svg:
|
||||
inline: false
|
||||
keepComments: false
|
||||
precision: 0
|
||||
xml:
|
||||
@@ -1364,37 +1371,44 @@ config:
|
||||
min: ""
|
||||
imports: null
|
||||
mounts:
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: content
|
||||
target: content
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: data
|
||||
target: data
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: layouts
|
||||
target: layouts
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: i18n
|
||||
target: i18n
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: archetypes
|
||||
target: archetypes
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: assets
|
||||
target: assets
|
||||
- excludeFiles: null
|
||||
- disableWatch: false
|
||||
excludeFiles: null
|
||||
includeFiles: null
|
||||
lang: ""
|
||||
source: static
|
||||
@@ -1583,8 +1597,12 @@ config:
|
||||
term:
|
||||
- html
|
||||
- rss
|
||||
paginate: 10
|
||||
paginatePath: page
|
||||
paginate: 0
|
||||
paginatePath: ""
|
||||
pagination:
|
||||
disableAliases: false
|
||||
pagerSize: 10
|
||||
path: page
|
||||
panicOnWarning: false
|
||||
params: {}
|
||||
permalinks:
|
||||
@@ -1656,8 +1674,10 @@ config:
|
||||
allow:
|
||||
- ^(dart-)?sass(-embedded)?$
|
||||
- ^go$
|
||||
- ^git$
|
||||
- ^npx$
|
||||
- ^postcss$
|
||||
- ^tailwindcss$
|
||||
osEnv:
|
||||
- (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$
|
||||
funcs:
|
||||
@@ -1761,6 +1781,8 @@ config_helpers:
|
||||
_merge: shallow
|
||||
outputs:
|
||||
_merge: none
|
||||
pagination:
|
||||
_merge: none
|
||||
params:
|
||||
_merge: deep
|
||||
permalinks:
|
||||
@@ -2738,14 +2760,9 @@ tpl:
|
||||
crypto:
|
||||
FNV32a:
|
||||
Aliases: null
|
||||
Args:
|
||||
- v
|
||||
Description: |-
|
||||
FNV32a hashes v using fnv32a algorithm.
|
||||
<docsmeta>{"newIn": "0.98.0" }</docsmeta>
|
||||
Examples:
|
||||
- - '{{ crypto.FNV32a "Hugo Rocks!!" }}'
|
||||
- "1515779328"
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
HMAC:
|
||||
Aliases:
|
||||
- hmac
|
||||
@@ -2788,11 +2805,30 @@ tpl:
|
||||
- - '{{ sha256 "Hello world, gophers!" }}'
|
||||
- 6ec43b78da9669f50e4e422575c54bf87536954ccd58280219c393f2ce352b46
|
||||
css:
|
||||
PostCSS:
|
||||
Aliases:
|
||||
- postCSS
|
||||
Args:
|
||||
- args
|
||||
Description: PostCSS processes the given Resource with PostCSS.
|
||||
Examples: []
|
||||
Quoted:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Sass:
|
||||
Aliases:
|
||||
- toCSS
|
||||
Args:
|
||||
- args
|
||||
Description: Sass processes the given Resource with SASS.
|
||||
Examples: []
|
||||
TailwindCSS:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Unquoted:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3013,6 +3049,24 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
hash:
|
||||
FNV32a:
|
||||
Aliases: null
|
||||
Args:
|
||||
- v
|
||||
Description: FNV32a hashes v using fnv32a algorithm.
|
||||
Examples:
|
||||
- - '{{ hash.FNV32a "Hugo Rocks!!" }}'
|
||||
- "1515779328"
|
||||
XxHash:
|
||||
Aliases:
|
||||
- xxhash
|
||||
Args:
|
||||
- v
|
||||
Description: XxHash returns the xxHash of the input string.
|
||||
Examples:
|
||||
- - '{{ hash.XxHash "The quick brown fox jumps over the lazy dog" }}'
|
||||
- 0b242d361fda71bc
|
||||
hugo:
|
||||
Deps:
|
||||
Aliases: null
|
||||
@@ -3228,6 +3282,13 @@ tpl:
|
||||
- - '{{ "cats" | singularize }}'
|
||||
- cat
|
||||
js:
|
||||
Babel:
|
||||
Aliases:
|
||||
- babel
|
||||
Args:
|
||||
- args
|
||||
Description: Babel processes the given Resource with Babel.
|
||||
Examples: []
|
||||
Build:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3341,6 +3402,14 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ math.Abs -2.1 }}'
|
||||
- "2.1"
|
||||
Acos:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Acos returns the arccosine, in radians, of n.
|
||||
Examples:
|
||||
- - '{{ math.Acos 1 }}'
|
||||
- "0"
|
||||
Add:
|
||||
Aliases:
|
||||
- add
|
||||
@@ -3350,6 +3419,32 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ add 1 2 }}'
|
||||
- "3"
|
||||
Asin:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Asin returns the arcsine, in radians, of n.
|
||||
Examples:
|
||||
- - '{{ math.Asin 1 }}'
|
||||
- "1.5707963267948966"
|
||||
Atan:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Atan returns the arctangent, in radians, of n.
|
||||
Examples:
|
||||
- - '{{ math.Atan 1 }}'
|
||||
- "0.7853981633974483"
|
||||
Atan2:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
- m
|
||||
Description: Atan2 returns the arc tangent of n/m, using the signs of the
|
||||
two to determine the quadrant of the return value.
|
||||
Examples:
|
||||
- - '{{ math.Atan2 1 2 }}'
|
||||
- "0.4636476090008061"
|
||||
Ceil:
|
||||
Aliases: null
|
||||
Args:
|
||||
@@ -3359,6 +3454,14 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ math.Ceil 2.1 }}'
|
||||
- "3"
|
||||
Cos:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Cos returns the cosine of the radian argument n.
|
||||
Examples:
|
||||
- - '{{ math.Cos 1 }}'
|
||||
- "0.5403023058681398"
|
||||
Counter:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3438,6 +3541,13 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ mul 2 3 }}'
|
||||
- "6"
|
||||
Pi:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: Pi returns the mathematical constant pi.
|
||||
Examples:
|
||||
- - '{{ math.Pi }}'
|
||||
- "3.141592653589793"
|
||||
Pow:
|
||||
Aliases:
|
||||
- pow
|
||||
@@ -3470,6 +3580,14 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ math.Round 1.5 }}'
|
||||
- "2"
|
||||
Sin:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Sin returns the sine of the radian argument n.
|
||||
Examples:
|
||||
- - '{{ math.Sin 1 }}'
|
||||
- "0.8414709848078965"
|
||||
Sqrt:
|
||||
Aliases: null
|
||||
Args:
|
||||
@@ -3492,6 +3610,30 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Tan:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: Tan returns the tangent of the radian argument n.
|
||||
Examples:
|
||||
- - '{{ math.Tan 1 }}'
|
||||
- "1.557407724654902"
|
||||
ToDegrees:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: ToDegrees converts radians into degrees.
|
||||
Examples:
|
||||
- - '{{ math.ToDegrees 1.5707963267948966 }}'
|
||||
- "90"
|
||||
ToRadians:
|
||||
Aliases: null
|
||||
Args:
|
||||
- "n"
|
||||
Description: ToRadians converts degrees into radians.
|
||||
Examples:
|
||||
- - '{{ math.ToRadians 90 }}'
|
||||
- "1.5707963267948966"
|
||||
openapi3:
|
||||
Unmarshal:
|
||||
Aliases: null
|
||||
@@ -3657,12 +3799,10 @@ tpl:
|
||||
- Slice
|
||||
resources:
|
||||
Babel:
|
||||
Aliases:
|
||||
- babel
|
||||
Args:
|
||||
- args
|
||||
Description: Babel processes the given Resource with Babel.
|
||||
Examples: []
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
ByType:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3738,27 +3878,20 @@ tpl:
|
||||
minifier.
|
||||
Examples: []
|
||||
PostCSS:
|
||||
Aliases:
|
||||
- postCSS
|
||||
Args:
|
||||
- args
|
||||
Description: PostCSS processes the given Resource with PostCSS
|
||||
Examples: []
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
PostProcess:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
ToCSS:
|
||||
Aliases:
|
||||
- toCSS
|
||||
Args:
|
||||
- args
|
||||
Description: |-
|
||||
ToCSS converts the given Resource to CSS. You can optional provide an Options object
|
||||
as second argument. As an option, you can e.g. specify e.g. the target path (string)
|
||||
for the converted CSS resource.
|
||||
Examples: []
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
safe:
|
||||
CSS:
|
||||
Aliases:
|
||||
@@ -3838,6 +3971,11 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
CheckReady:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Config:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -4339,6 +4477,23 @@ tpl:
|
||||
- - '{{ "With [Markdown](/markdown) inside." | markdownify | truncate 14 }}'
|
||||
- With <a href="/markdown">Markdown …</a>
|
||||
templates:
|
||||
Defer:
|
||||
Aliases: null
|
||||
Args:
|
||||
- args
|
||||
Description: Defer defers the execution of a template block.
|
||||
Examples: []
|
||||
DoDefer:
|
||||
Aliases:
|
||||
- doDefer
|
||||
Args:
|
||||
- ctx
|
||||
- id
|
||||
- optsv
|
||||
Description: |-
|
||||
DoDefer defers the execution of a template block.
|
||||
For internal use only.
|
||||
Examples: []
|
||||
Exists:
|
||||
Aliases: null
|
||||
Args:
|
||||
|
||||
@@ -4,29 +4,31 @@ require (
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/alecthomas/chroma/v2 v2.14.0
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4
|
||||
github.com/bep/clocks v0.5.0
|
||||
github.com/bep/debounce v1.2.0
|
||||
github.com/bep/gitmap v1.1.2
|
||||
github.com/bep/gitmap v1.6.0
|
||||
github.com/bep/goat v0.5.0
|
||||
github.com/bep/godartsass v1.2.0
|
||||
github.com/bep/godartsass/v2 v2.0.0
|
||||
github.com/bep/godartsass/v2 v2.1.0
|
||||
github.com/bep/golibsass v1.1.1
|
||||
github.com/bep/gowebp v0.3.0
|
||||
github.com/bep/helpers v0.4.0
|
||||
github.com/bep/imagemeta v0.7.6
|
||||
github.com/bep/lazycache v0.4.0
|
||||
github.com/bep/logg v0.4.0
|
||||
github.com/bep/mclib v1.20400.20402
|
||||
github.com/bep/overlayfs v0.9.2
|
||||
github.com/bep/simplecobra v0.4.0
|
||||
github.com/bep/tmc v0.5.1
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/clbanning/mxj/v2 v2.7.0
|
||||
github.com/cli/safeexec v1.0.1
|
||||
github.com/disintegration/gift v1.2.1
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanw/esbuild v0.21.4
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/evanw/esbuild v0.23.0
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
@@ -35,23 +37,23 @@ require (
|
||||
github.com/gobuffalo/flect v1.0.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e
|
||||
github.com/gohugoio/hashstructure v0.1.0
|
||||
github.com/gohugoio/httpcache v0.7.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0
|
||||
github.com/gohugoio/locales v0.14.0
|
||||
github.com/gohugoio/localescompressed v1.0.1
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/hairyhenderson/go-codeowners v0.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0
|
||||
github.com/jdkato/prose v1.2.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/kyokomi/emoji/v2 v2.2.12
|
||||
github.com/kyokomi/emoji/v2 v2.2.13
|
||||
github.com/magefile/mage v1.15.0
|
||||
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.20
|
||||
github.com/mitchellh/hashstructure v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
@@ -59,74 +61,77 @@ require (
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/rogpeppe/go-internal v1.12.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/cast v1.7.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/fsync v0.10.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tdewolff/minify/v2 v2.20.20
|
||||
github.com/tdewolff/parse/v2 v2.7.13
|
||||
github.com/tdewolff/minify/v2 v2.20.37
|
||||
github.com/tdewolff/parse/v2 v2.7.15
|
||||
github.com/yuin/goldmark v1.7.4
|
||||
github.com/yuin/goldmark-emoji v1.0.3
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.36.0
|
||||
gocloud.dev v0.38.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.16.0
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/text v0.15.0
|
||||
golang.org/x/tools v0.20.0
|
||||
google.golang.org/api v0.152.0
|
||||
golang.org/x/image v0.19.0
|
||||
golang.org/x/mod v0.19.0
|
||||
golang.org/x/net v0.28.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/text v0.17.0
|
||||
golang.org/x/tools v0.23.0
|
||||
google.golang.org/api v0.189.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.10 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.5 // indirect
|
||||
cloud.google.com/go/storage v1.35.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 // indirect
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.7.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.10 // indirect
|
||||
cloud.google.com/go/storage v1.41.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.50.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect
|
||||
github.com/aws/smithy-go v1.20.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.51.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/google/wire v0.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/invopop/yaml v0.2.0 // indirect
|
||||
@@ -140,24 +145,28 @@ require (
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
||||
go.opentelemetry.io/otel v1.26.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.26.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.26.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
|
||||
)
|
||||
|
||||
go 1.20
|
||||
go 1.21.8
|
||||
|
||||
@@ -17,22 +17,24 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
|
||||
cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic=
|
||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
|
||||
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
|
||||
cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI=
|
||||
cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -43,95 +45,100 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
|
||||
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
|
||||
cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0=
|
||||
cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+3LoSsYf9YMjkupeAnHMX8O9mmY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
|
||||
github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1/go.mod h1:ZB+CuKHRbb5v5F0oJtGdhFTelmrxd4iWO1lf0rQwSAg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 h1:v/WgB8NxprNvr5inKIiVVrXPuuTegM+K8nncFkr1usU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12/go.mod h1:X21k0FjEJe+/pauud82HYiQbEr9jRKY3kXEIQ4hXeTQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4 h1:a4gfRHHCzvV0jEjOUdZOK0oJ4H21x5WT+E4ucWk4jeM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4/go.mod h1:Pphkts8iBnexoEpcMti5fUvN3/yoGRLtl2heOeppF70=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 h1:Keso8lIOS+IzI2MkPZyK6G0LYcK3My2LQ+T5bxghEAY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
|
||||
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
|
||||
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/aws/aws-sdk-go v1.51.30 h1:RVFkjn9P0JMwnuZCVH0TlV5k9zepHzlbc4943eZMhGw=
|
||||
github.com/aws/aws-sdk-go v1.51.30/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.15 h1:7Zwtt/lP3KNRkeZre7soMELMGNoBrutx8nobg1jKWmo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.15/go.mod h1:436h2adoHb57yd+8W+gYPrrA9U/R/SuAuOO42Ushzhw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 h1:81KE7vaZzrl7yHBYHVEzYB8sypz11NMOZ40YlWvPxsU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5/go.mod h1:LIt2rg7Mcgn09Ygbdh/RdIm0rQ+3BNkbP1gyVMFtRK0=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4 h1:I/sQ9uGOs72/483obb2SPoa9ZEsYGbel6jcTTwD/0zU=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4/go.mod h1:P6ByphKl2oNQZlv4WsCaLSmRncKEcOnbitYLtJPfqZI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 h1:ZMeFZ5yk+Ek+jNr1+uwCd2tG89t6oTS5yVWpa6yy2es=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7/go.mod h1:mxV05U+4JiHqIpGqqYXOHLPKUC6bDXC44bsUhNjOEwY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 h1:f9RyWNtS8oH7cZlbn+/JNPpjUk5+5fLd5lM9M0i49Ys=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5/go.mod h1:h5CoMZV2VF297/VLhRhO1WF+XYWOzXo+4HsObA4HjBQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 h1:6cnno47Me9bRykw9AEv9zkXE+5or7jz8TsskTTccbgc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
||||
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
||||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=
|
||||
github.com/bep/gitmap v1.1.2/go.mod h1:g9VRETxFUXNWzMiuxOwcudo6DfZkW9jOsOW0Ft4kYaY=
|
||||
github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA=
|
||||
github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw=
|
||||
github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
|
||||
github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c=
|
||||
github.com/bep/godartsass v1.2.0 h1:E2VvQrxAHAFwbjyOIExAMmogTItSKodoKuijNrGm5yU=
|
||||
github.com/bep/godartsass v1.2.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8=
|
||||
github.com/bep/godartsass/v2 v2.0.0 h1:Ruht+BpBWkpmW+yAM2dkp7RSSeN0VLaTobyW0CiSP3Y=
|
||||
github.com/bep/godartsass/v2 v2.0.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo=
|
||||
github.com/bep/godartsass/v2 v2.1.0 h1:fq5Y1xYf4diu4tXABiekZUCA+5l/dmNjGKCeQwdy+s0=
|
||||
github.com/bep/godartsass/v2 v2.1.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo=
|
||||
github.com/bep/golibsass v1.1.1 h1:xkaet75ygImMYjM+FnHIT3xJn7H0xBA9UxSOJjk8Khw=
|
||||
github.com/bep/golibsass v1.1.1/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||
github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY=
|
||||
github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
|
||||
github.com/bep/helpers v0.4.0 h1:ab9veaAiWY4ST48Oxp5usaqivDmYdB744fz+tcZ3Ifs=
|
||||
github.com/bep/helpers v0.4.0/go.mod h1:/QpHdmcPagDw7+RjkLFCvnlUc8lQ5kg4KDrEkb2Yyco=
|
||||
github.com/bep/imagemeta v0.7.6 h1:No64uhsEgUg/wz19yUC8BmHkFNMGhNu3X5puvsuvi2E=
|
||||
github.com/bep/imagemeta v0.7.6/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s=
|
||||
github.com/bep/lazycache v0.4.0 h1:X8yVyWNVupPd4e1jV7efi3zb7ZV/qcjKQgIQ5aPbkYI=
|
||||
github.com/bep/lazycache v0.4.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc=
|
||||
github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ=
|
||||
@@ -147,6 +154,8 @@ github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
|
||||
github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
|
||||
github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -159,8 +168,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
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/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -170,7 +179,6 @@ github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvd
|
||||
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -179,10 +187,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanw/esbuild v0.21.4 h1:pe4SEQMoR1maEjhgWPEPWmUy11Jp6nidxd1mOvMrFFU=
|
||||
github.com/evanw/esbuild v0.21.4/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/evanw/esbuild v0.23.0 h1:PLUwTn2pzQfIBRrMKcD3M0g1ALOKIHMDefdFCk7avwM=
|
||||
github.com/evanw/esbuild v0.23.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
|
||||
@@ -200,31 +210,41 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
||||
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=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
|
||||
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
||||
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
|
||||
github.com/gohugoio/hashstructure v0.1.0 h1:kBSTMLMyTXbrJVAxaKI+wv30MMJJxn9Q8kfQtJaZ400=
|
||||
github.com/gohugoio/hashstructure v0.1.0/go.mod h1:8ohPTAfQLTs2WdzB6k9etmQYclDUeNsIHGPAFejbsEA=
|
||||
github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs=
|
||||
github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0 h1:MNdY6hYCTQEekY0oAfsxWZU1CDt6iH+tMLgyMJQh/sg=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0/go.mod h1:oBdBVuiZ0fv9xd8xflUgt53QxW5jOCb1S+xntcN4SKo=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0 h1:PCtO5l++psZf48yen2LxQ3JiOXxaRC6v0594NeHvGZg=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0/go.mod h1:g9CCh+Ci2IMbPUrVJuXbBTrA+rIIx5+hDQ4EXYaQDoM=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0 h1:7PY5PIJ2mck7v6R52yCFvvYHvsPMEbulgRviw3I9lP4=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0/go.mod h1:r8g5S7bHfdj0+9ShBog864ufCsVODKQZNjYYY8OnJpM=
|
||||
github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
|
||||
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
|
||||
github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
|
||||
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 h1:sgew0XCnZwnzpWxTt3V8LLiCO7OQi3C6dycaE67wfkU=
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95/go.mod h1:bOlVlCa1/RajcHpXkrUXPSHB/Re1UnlXxD1Qp8SKOd8=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -252,10 +272,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -274,12 +292,15 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE=
|
||||
github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk=
|
||||
github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk=
|
||||
github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
@@ -293,28 +314,29 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hairyhenderson/go-codeowners v0.4.0 h1:Wx/tRXb07sCyHeC8mXfio710Iu35uAy5KYiBdLHdv4Q=
|
||||
github.com/hairyhenderson/go-codeowners v0.4.0/go.mod h1:iJgZeCt+W/GzXo5uchFCqvVHZY2T4TAIpvuVlKVkLxc=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0 h1:dpQB+hVHiRc2VVvc2BHxkuM+tmu9Qej/as3apqUbsWc=
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0/go.mod h1:R3uW1OQXEj2Gu6/OvZ7bt6hr0qdkLvUWPiqNaWnexpo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -345,8 +367,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.12 h1:sSVA5nH9ebR3Zji1o31wu3yOwD1zKXQA2z0zUyeit60=
|
||||
github.com/kyokomi/emoji/v2 v2.2.12/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
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/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
@@ -362,8 +384,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
|
||||
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
@@ -384,8 +404,8 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -394,6 +414,7 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@@ -410,10 +431,10 @@ github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2h
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE=
|
||||
github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -433,13 +454,15 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.20 h1:vhULb+VsW2twkplgsawAoUY957efb+EdiZ7zu5fUhhk=
|
||||
github.com/tdewolff/minify/v2 v2.20.20/go.mod h1:GYaLXFpIIwsX99apQHXfGdISUdlA98wmaoWxjT9C37k=
|
||||
github.com/tdewolff/parse/v2 v2.7.13 h1:iSiwOUkCYLNfapHoqdLcqZVgvQ0jrsao8YYKP/UJYTI=
|
||||
github.com/tdewolff/parse/v2 v2.7.13/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/minify/v2 v2.20.37 h1:Q97cx4STXCh1dlWDlNHZniE8BJ2EBL0+2b0n92BJQhw=
|
||||
github.com/tdewolff/minify/v2 v2.20.37/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU=
|
||||
github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw=
|
||||
github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -459,10 +482,22 @@ 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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=
|
||||
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
|
||||
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
|
||||
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
|
||||
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
|
||||
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus=
|
||||
gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg=
|
||||
gocloud.dev v0.38.0 h1:SpxfaOc/Fp4PeO8ui7wRcCZV0EgXZ+IWcVSLn6ZMSw0=
|
||||
gocloud.dev v0.38.0/go.mod h1:3XjKvd2E5iVNu/xFImRzjN0d/fkNHe4s0RiKidpEUMQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -472,8 +507,10 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
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=
|
||||
@@ -489,8 +526,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ
|
||||
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.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
|
||||
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
|
||||
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
||||
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
||||
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=
|
||||
@@ -514,8 +551,11 @@ 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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=
|
||||
@@ -551,8 +591,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
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=
|
||||
@@ -562,8 +606,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -576,8 +620,11 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
@@ -615,16 +662,24 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
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=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -633,9 +688,12 @@ 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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=
|
||||
@@ -647,7 +705,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@@ -691,8 +748,11 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
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=
|
||||
@@ -718,8 +778,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY=
|
||||
google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
|
||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -727,8 +787,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -765,12 +823,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
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=
|
||||
@@ -787,8 +845,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.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
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=
|
||||
@@ -799,13 +857,12 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
|
||||
@@ -15,8 +15,6 @@ package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -27,11 +25,11 @@ import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
bp "github.com/gohugoio/hugo/bufferpool"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/jdkato/prose/transform"
|
||||
|
||||
bp "github.com/gohugoio/hugo/bufferpool"
|
||||
)
|
||||
|
||||
// FilePathSeparator as defined by os.Separator.
|
||||
@@ -257,64 +255,6 @@ func SliceToLower(s []string) []string {
|
||||
return l
|
||||
}
|
||||
|
||||
// MD5String takes a string and returns its MD5 hash.
|
||||
func MD5String(f string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(f))
|
||||
return hex.EncodeToString(h.Sum([]byte{}))
|
||||
}
|
||||
|
||||
// MD5FromReaderFast creates a MD5 hash from the given file. It only reads parts of
|
||||
// the file for speed, so don't use it if the files are very subtly different.
|
||||
// It will not close the file.
|
||||
// It will return the MD5 hash and the size of r in bytes.
|
||||
func MD5FromReaderFast(r io.ReadSeeker) (string, int64, error) {
|
||||
const (
|
||||
// Do not change once set in stone!
|
||||
maxChunks = 8
|
||||
peekSize = 64
|
||||
seek = 2048
|
||||
)
|
||||
|
||||
h := md5.New()
|
||||
buff := make([]byte, peekSize)
|
||||
|
||||
for i := 0; i < maxChunks; i++ {
|
||||
if i > 0 {
|
||||
_, err := r.Seek(seek, 0)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", 0, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := io.ReadAtLeast(r, buff, peekSize)
|
||||
if err != nil {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
h.Write(buff)
|
||||
break
|
||||
}
|
||||
return "", 0, err
|
||||
}
|
||||
h.Write(buff)
|
||||
}
|
||||
|
||||
size, _ := r.Seek(0, io.SeekEnd)
|
||||
|
||||
return hex.EncodeToString(h.Sum(nil)), size, nil
|
||||
}
|
||||
|
||||
// MD5FromReader creates a MD5 hash from the given reader.
|
||||
func MD5FromReader(r io.Reader) (string, error) {
|
||||
h := md5.New()
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// IsWhitespace determines if the given rune is whitespace.
|
||||
func IsWhitespace(r rune) bool {
|
||||
return r == ' ' || r == '\t' || r == '\n' || r == '\r'
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
func TestResolveMarkup(t *testing.T) {
|
||||
@@ -256,93 +254,6 @@ func TestUniqueStringsSorted(t *testing.T) {
|
||||
c.Assert(helpers.UniqueStringsSorted(nil), qt.IsNil)
|
||||
}
|
||||
|
||||
func TestFastMD5FromFile(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
|
||||
if err := afero.WriteFile(fs, "small.txt", []byte("abc"), 0o777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := afero.WriteFile(fs, "small2.txt", []byte("abd"), 0o777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := afero.WriteFile(fs, "bigger.txt", []byte(strings.Repeat("a bc d e", 100)), 0o777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := afero.WriteFile(fs, "bigger2.txt", []byte(strings.Repeat("c d e f g", 100)), 0o777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
sf1, err := fs.Open("small.txt")
|
||||
c.Assert(err, qt.IsNil)
|
||||
sf2, err := fs.Open("small2.txt")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
bf1, err := fs.Open("bigger.txt")
|
||||
c.Assert(err, qt.IsNil)
|
||||
bf2, err := fs.Open("bigger2.txt")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
defer sf1.Close()
|
||||
defer sf2.Close()
|
||||
defer bf1.Close()
|
||||
defer bf2.Close()
|
||||
|
||||
m1, _, err := helpers.MD5FromReaderFast(sf1)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m1, qt.Equals, "e9c8989b64b71a88b4efb66ad05eea96")
|
||||
|
||||
m2, _, err := helpers.MD5FromReaderFast(sf2)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m2, qt.Not(qt.Equals), m1)
|
||||
|
||||
m3, _, err := helpers.MD5FromReaderFast(bf1)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m3, qt.Not(qt.Equals), m2)
|
||||
|
||||
m4, _, err := helpers.MD5FromReaderFast(bf2)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m4, qt.Not(qt.Equals), m3)
|
||||
|
||||
m5, err := helpers.MD5FromReader(bf2)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m5, qt.Not(qt.Equals), m4)
|
||||
}
|
||||
|
||||
func BenchmarkMD5FromFileFast(b *testing.B) {
|
||||
fs := afero.NewMemMapFs()
|
||||
|
||||
for _, full := range []bool{false, true} {
|
||||
b.Run(fmt.Sprintf("full=%t", full), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
if err := afero.WriteFile(fs, "file.txt", []byte(strings.Repeat("1234567890", 2000)), 0o777); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
f, err := fs.Open("file.txt")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
if full {
|
||||
if _, err := helpers.MD5FromReader(f); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if _, _, err := helpers.MD5FromReaderFast(f); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUniqueStrings(b *testing.B) {
|
||||
input := []string{"a", "b", "d", "e", "d", "h", "a", "i"}
|
||||
|
||||
|
||||
@@ -14,25 +14,24 @@
|
||||
package hugofs
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"os"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
var (
|
||||
_ afero.Fs = (*md5HashingFs)(nil)
|
||||
_ FilesystemUnwrapper = (*md5HashingFs)(nil)
|
||||
_ afero.Fs = (*hashingFs)(nil)
|
||||
_ FilesystemUnwrapper = (*hashingFs)(nil)
|
||||
)
|
||||
|
||||
// FileHashReceiver will receive the filename an the content's MD5 sum on file close.
|
||||
type FileHashReceiver interface {
|
||||
OnFileClose(name, md5sum string)
|
||||
OnFileClose(name string, checksum uint64)
|
||||
}
|
||||
|
||||
type md5HashingFs struct {
|
||||
type hashingFs struct {
|
||||
afero.Fs
|
||||
hashReceiver FileHashReceiver
|
||||
}
|
||||
@@ -45,14 +44,14 @@ type md5HashingFs struct {
|
||||
// Note that this will only work for file operations that use the io.Writer
|
||||
// to write content to file, but that is fine for the "publish content" use case.
|
||||
func NewHashingFs(delegate afero.Fs, hashReceiver FileHashReceiver) afero.Fs {
|
||||
return &md5HashingFs{Fs: delegate, hashReceiver: hashReceiver}
|
||||
return &hashingFs{Fs: delegate, hashReceiver: hashReceiver}
|
||||
}
|
||||
|
||||
func (fs *md5HashingFs) UnwrapFilesystem() afero.Fs {
|
||||
func (fs *hashingFs) UnwrapFilesystem() afero.Fs {
|
||||
return fs.Fs
|
||||
}
|
||||
|
||||
func (fs *md5HashingFs) Create(name string) (afero.File, error) {
|
||||
func (fs *hashingFs) Create(name string) (afero.File, error) {
|
||||
f, err := fs.Fs.Create(name)
|
||||
if err == nil {
|
||||
f = fs.wrapFile(f)
|
||||
@@ -60,7 +59,7 @@ func (fs *md5HashingFs) Create(name string) (afero.File, error) {
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (fs *md5HashingFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
||||
func (fs *hashingFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
||||
f, err := fs.Fs.OpenFile(name, flag, perm)
|
||||
if err == nil && isWrite(flag) {
|
||||
f = fs.wrapFile(f)
|
||||
@@ -68,17 +67,17 @@ func (fs *md5HashingFs) OpenFile(name string, flag int, perm os.FileMode) (afero
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (fs *md5HashingFs) wrapFile(f afero.File) afero.File {
|
||||
return &hashingFile{File: f, h: md5.New(), hashReceiver: fs.hashReceiver}
|
||||
func (fs *hashingFs) wrapFile(f afero.File) afero.File {
|
||||
return &hashingFile{File: f, h: xxhash.New(), hashReceiver: fs.hashReceiver}
|
||||
}
|
||||
|
||||
func (fs *md5HashingFs) Name() string {
|
||||
return "md5HashingFs"
|
||||
func (fs *hashingFs) Name() string {
|
||||
return "hashingFs"
|
||||
}
|
||||
|
||||
type hashingFile struct {
|
||||
hashReceiver FileHashReceiver
|
||||
h hash.Hash
|
||||
h hash.Hash64
|
||||
afero.File
|
||||
}
|
||||
|
||||
@@ -91,7 +90,6 @@ func (h *hashingFile) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (h *hashingFile) Close() error {
|
||||
sum := hex.EncodeToString(h.h.Sum(nil))
|
||||
h.hashReceiver.OnFileClose(h.Name(), sum)
|
||||
h.hashReceiver.OnFileClose(h.Name(), h.h.Sum64())
|
||||
return h.File.Close()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
package hugofs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -21,13 +23,13 @@ import (
|
||||
)
|
||||
|
||||
type testHashReceiver struct {
|
||||
sum string
|
||||
name string
|
||||
sum uint64
|
||||
}
|
||||
|
||||
func (t *testHashReceiver) OnFileClose(name, md5hash string) {
|
||||
func (t *testHashReceiver) OnFileClose(name string, checksum uint64) {
|
||||
t.name = name
|
||||
t.sum = md5hash
|
||||
t.sum = checksum
|
||||
}
|
||||
|
||||
func TestHashingFs(t *testing.T) {
|
||||
@@ -42,11 +44,34 @@ func TestHashingFs(t *testing.T) {
|
||||
_, err = f.Write([]byte("content"))
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f.Close(), qt.IsNil)
|
||||
c.Assert(observer.sum, qt.Equals, "9a0364b9e99bb480dd25e1f0284c8555")
|
||||
c.Assert(observer.sum, qt.Equals, uint64(7807861979271768572))
|
||||
c.Assert(observer.name, qt.Equals, "hashme")
|
||||
|
||||
f, err = ofs.Create("nowrites")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f.Close(), qt.IsNil)
|
||||
c.Assert(observer.sum, qt.Equals, "d41d8cd98f00b204e9800998ecf8427e")
|
||||
c.Assert(observer.sum, qt.Equals, uint64(17241709254077376921))
|
||||
}
|
||||
|
||||
func BenchmarkHashingFs(b *testing.B) {
|
||||
fs := afero.NewMemMapFs()
|
||||
observer := &testHashReceiver{}
|
||||
ofs := NewHashingFs(fs, observer)
|
||||
content := []byte(strings.Repeat("lorem ipsum ", 1000))
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
f, err := ofs.Create(fmt.Sprintf("file%d", i))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_, err = f.Write(content)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1428,7 +1428,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
}
|
||||
|
||||
if wasZeroDates {
|
||||
pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
|
||||
pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodAndPublishDateIfAfter(sp.m.pageConfig.Dates)
|
||||
}
|
||||
|
||||
if pageBundle.IsHome() {
|
||||
@@ -1565,7 +1565,7 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
||||
return
|
||||
}
|
||||
|
||||
p.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
|
||||
p.m.pageConfig.Dates.UpdateDateAndLastmodAndPublishDateIfAfter(sp.m.pageConfig.Dates)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -266,3 +266,86 @@ Home's Date should be equal mydata date: true
|
||||
Full time: 6:00:00 am UTC
|
||||
`)
|
||||
}
|
||||
|
||||
func TestPublisDateRollupIssue12438(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// To future Hugo maintainers, this test will start to fail in 2099.
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','sitemap']
|
||||
[taxonomies]
|
||||
tag = 'tags'
|
||||
-- layouts/_default/list.html --
|
||||
Date: {{ .Date.Format "2006-01-02" }}
|
||||
PublishDate: {{ .PublishDate.Format "2006-01-02" }}
|
||||
Lastmod: {{ .Lastmod.Format "2006-01-02" }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
-- content/s1/p1.md --
|
||||
---
|
||||
title: p1
|
||||
date: 2024-03-01
|
||||
lastmod: 2024-03-02
|
||||
tags: [t1]
|
||||
---
|
||||
-- content/s1/p2.md --
|
||||
---
|
||||
title: p2
|
||||
date: 2024-04-03
|
||||
lastmod: 2024-04-04
|
||||
tags: [t1]
|
||||
---
|
||||
-- content/s1/p3.md --
|
||||
---
|
||||
title: p3
|
||||
lastmod: 2099-05-06
|
||||
tags: [t1]
|
||||
---
|
||||
|
||||
`
|
||||
|
||||
// Test without publishDate in front matter.
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/s1/index.html", `
|
||||
Date: 2099-05-06
|
||||
PublishDate: 2024-04-03
|
||||
Lastmod: 2099-05-06
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/tags/index.html", `
|
||||
Date: 2024-04-03
|
||||
PublishDate: 2024-04-03
|
||||
Lastmod: 2024-04-04
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/tags/t1/index.html", `
|
||||
Date: 2024-04-03
|
||||
PublishDate: 2024-04-03
|
||||
Lastmod: 2024-04-04
|
||||
`)
|
||||
|
||||
// Test with publishDate in front matter.
|
||||
files = strings.ReplaceAll(files, "lastmod", "publishDate")
|
||||
|
||||
b = Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/s1/index.html", `
|
||||
Date: 2099-05-06
|
||||
PublishDate: 2024-04-04
|
||||
Lastmod: 2099-05-06
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/tags/index.html", `
|
||||
Date: 2024-04-03
|
||||
PublishDate: 2024-04-04
|
||||
Lastmod: 2024-04-03
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/tags/t1/index.html", `
|
||||
Date: 2024-04-03
|
||||
PublishDate: 2024-04-04
|
||||
Lastmod: 2024-04-03
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bep/gitmap"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/source"
|
||||
)
|
||||
@@ -38,10 +40,24 @@ func (g *gitInfo) forPage(p page.Page) source.GitInfo {
|
||||
return source.NewGitInfo(*gi)
|
||||
}
|
||||
|
||||
func newGitInfo(conf config.AllProvider) (*gitInfo, error) {
|
||||
workingDir := conf.BaseConfig().WorkingDir
|
||||
func newGitInfo(d *deps.Deps) (*gitInfo, error) {
|
||||
opts := gitmap.Options{
|
||||
Repository: d.Conf.BaseConfig().WorkingDir,
|
||||
GetGitCommandFunc: func(stdout, stderr io.Writer, args ...string) (gitmap.Runner, error) {
|
||||
var argsv []any
|
||||
for _, arg := range args {
|
||||
argsv = append(argsv, arg)
|
||||
}
|
||||
argsv = append(
|
||||
argsv,
|
||||
hexec.WithStdout(stdout),
|
||||
hexec.WithStderr(stderr),
|
||||
)
|
||||
return d.ExecHelper.New("git", argsv...)
|
||||
},
|
||||
}
|
||||
|
||||
gitRepo, err := gitmap.Map(workingDir, "")
|
||||
gitRepo, err := gitmap.Map(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ func (h *HugoSites) GetContentPage(filename string) page.Page {
|
||||
|
||||
func (h *HugoSites) loadGitInfo() error {
|
||||
if h.Configs.Base.EnableGitInfo {
|
||||
gi, err := newGitInfo(h.Conf)
|
||||
gi, err := newGitInfo(h.Deps)
|
||||
if err != nil {
|
||||
h.Log.Errorln("Failed to read Git log:", err)
|
||||
} else {
|
||||
|
||||
@@ -203,9 +203,9 @@ title: mybundle-en
|
||||
b.AssertFileExists("public/de/mybundle/pixel.png", true)
|
||||
b.AssertFileExists("public/en/mybundle/pixel.png", true)
|
||||
|
||||
b.AssertFileExists("public/de/mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_2x2_resize_box_3.png", true)
|
||||
b.AssertFileExists("public/de/mybundle/pixel_hu8581513846771248023.png", true)
|
||||
// failing test below
|
||||
b.AssertFileExists("public/en/mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_2x2_resize_box_3.png", true)
|
||||
b.AssertFileExists("public/en/mybundle/pixel_hu8581513846771248023.png", true)
|
||||
}
|
||||
|
||||
func TestMultihostResourceOneBaseURLWithSuPath(t *testing.T) {
|
||||
|
||||
@@ -72,22 +72,21 @@ SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Ex
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg/200/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg/200/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg/123/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg/123/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667")
|
||||
b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667")
|
||||
|
||||
b.AssertImage(200, 200, "public/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg")
|
||||
b.AssertImage(200, 200, "public/bundle/sunset_hu13235715490294913361.jpg")
|
||||
|
||||
// Check the file cache
|
||||
b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_resize_q75_box.jpg")
|
||||
b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu13235715490294913361.jpg")
|
||||
|
||||
b.AssertFileContent("resources/_gen/images/bundle/sunset_3166614710256882113.json",
|
||||
"DateTimeDigitized|time.Time", "PENTAX")
|
||||
b.AssertFileContent("resources/_gen/images/bundle/sunset_17710516992648092201.json",
|
||||
"FocalLengthIn35mmFormat|uint16", "PENTAX")
|
||||
|
||||
b.AssertImage(123, 234, "resources/_gen/images/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg")
|
||||
b.AssertFileContent("resources/_gen/images/images/sunset_3166614710256882113.json",
|
||||
"DateTimeDigitized|time.Time", "PENTAX")
|
||||
b.AssertFileContent("resources/_gen/images/images/sunset_17710516992648092201.json",
|
||||
"FocalLengthIn35mmFormat|uint16", "PENTAX")
|
||||
|
||||
b.AssertNoDuplicateWrites()
|
||||
}
|
||||
|
||||
@@ -104,8 +104,7 @@ FormatCurrency: {{ 512.5032 | lang.FormatCurrency 2 "USD" }}
|
||||
FormatAccounting: {{ 512.5032 | lang.FormatAccounting 2 "NOK" }}
|
||||
FormatNumberCustom: {{ lang.FormatNumberCustom 2 12345.6789 }}
|
||||
|
||||
# We renamed this to FormatNumberCustom in 0.87.0.
|
||||
NumFmt: {{ -98765.4321 | lang.NumFmt 2 }}
|
||||
|
||||
|
||||
|
||||
`)
|
||||
@@ -120,7 +119,6 @@ FormatCurrency: $512.50
|
||||
FormatAccounting: NOK512.50
|
||||
FormatNumberCustom: 12,345.68
|
||||
|
||||
NumFmt: -98,765.43
|
||||
`,
|
||||
)
|
||||
|
||||
@@ -131,8 +129,6 @@ FormatCurrency: 512,50 USD
|
||||
FormatAccounting: 512,50 kr
|
||||
FormatNumberCustom: 12,345.68
|
||||
|
||||
# We renamed this to FormatNumberCustom in 0.87.0.
|
||||
NumFmt: -98,765.43
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gobuffalo/flect"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
@@ -32,6 +31,7 @@ import (
|
||||
"github.com/gohugoio/hugo/source"
|
||||
|
||||
"github.com/gohugoio/hugo/common/constants"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
@@ -328,7 +328,7 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
ps.m.setMetaPostCount++
|
||||
var cascadeHashPre uint64
|
||||
if ps.m.setMetaPostCount > 1 {
|
||||
cascadeHashPre = identity.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
||||
cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
||||
ps.m.pageConfig.CascadeCompiled = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal)
|
||||
|
||||
}
|
||||
@@ -360,7 +360,7 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
}
|
||||
|
||||
if ps.m.setMetaPostCount > 1 {
|
||||
ps.m.setMetaPostCascadeChanged = cascadeHashPre != identity.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
||||
ps.m.setMetaPostCascadeChanged = cascadeHashPre != hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
||||
if !ps.m.setMetaPostCascadeChanged {
|
||||
|
||||
// No changes, restore any value that may be changed by aggregation.
|
||||
|
||||
@@ -141,6 +141,19 @@ func createTargetPathDescriptor(p *pageState) (page.TargetPathDescriptor, error)
|
||||
desc.PrefixFilePath = s.getLanguageTargetPathLang(alwaysInSubDir)
|
||||
desc.PrefixLink = s.getLanguagePermalinkLang(alwaysInSubDir)
|
||||
|
||||
if desc.URL != "" && strings.IndexByte(desc.URL, ':') >= 0 {
|
||||
// Attempt to parse and expand an url
|
||||
opath, err := d.ResourceSpec.Permalinks.ExpandPattern(desc.URL, p)
|
||||
if err != nil {
|
||||
return desc, err
|
||||
}
|
||||
|
||||
if opath != "" {
|
||||
opath, _ = url.QueryUnescape(opath)
|
||||
desc.URL = opath
|
||||
}
|
||||
}
|
||||
|
||||
opath, err := d.ResourceSpec.Permalinks.Expand(p.Section(), p)
|
||||
if err != nil {
|
||||
return desc, err
|
||||
|
||||
@@ -440,8 +440,8 @@ func (pco *pageContentOutput) initRenderHooks() error {
|
||||
var offset int
|
||||
|
||||
switch v := ctx.(type) {
|
||||
case hooks.CodeblockContext:
|
||||
offset = bytes.Index(source, []byte(v.Inner()))
|
||||
case hooks.PositionerSourceTargetProvider:
|
||||
offset = bytes.Index(source, v.PositionerSourceTarget())
|
||||
}
|
||||
|
||||
pos := pco.po.p.posFromInput(source, offset)
|
||||
@@ -476,6 +476,16 @@ func (pco *pageContentOutput) initRenderHooks() error {
|
||||
layoutDescriptor.Kind = "render-image"
|
||||
case hooks.HeadingRendererType:
|
||||
layoutDescriptor.Kind = "render-heading"
|
||||
case hooks.PassthroughRendererType:
|
||||
layoutDescriptor.Kind = "render-passthrough"
|
||||
if id != nil {
|
||||
layoutDescriptor.KindVariants = id.(string)
|
||||
}
|
||||
case hooks.BlockquoteRendererType:
|
||||
layoutDescriptor.Kind = "render-blockquote"
|
||||
if id != nil {
|
||||
layoutDescriptor.KindVariants = id.(string)
|
||||
}
|
||||
case hooks.CodeBlockRendererType:
|
||||
layoutDescriptor.Kind = "render-codeblock"
|
||||
if id != nil {
|
||||
|
||||
@@ -59,6 +59,8 @@ func TestPermalink(t *testing.T) {
|
||||
|
||||
// test URL overrides
|
||||
{"x/y/z/boofar.md", "", "", "/z/y/q/", false, false, "/z/y/q/", "/z/y/q/"},
|
||||
// test URL override with expands
|
||||
{"x/y/z/boofar.md", "", "test", "/z/:slug/", false, false, "/z/test/", "/z/test/"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
@@ -23,13 +23,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bep/clocks"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/markup/asciidocext"
|
||||
"github.com/gohugoio/hugo/markup/rst"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
@@ -2040,8 +2040,8 @@ title: "p2"
|
||||
|
||||
b.Assert(p1, qt.Not(qt.Equals), p2)
|
||||
|
||||
b.Assert(identity.HashString(p1), qt.Not(qt.Equals), identity.HashString(p2))
|
||||
b.Assert(identity.HashString(sites[0]), qt.Not(qt.Equals), identity.HashString(sites[1]))
|
||||
b.Assert(hashing.HashString(p1), qt.Not(qt.Equals), hashing.HashString(p2))
|
||||
b.Assert(hashing.HashString(sites[0]), qt.Not(qt.Equals), hashing.HashString(sites[1]))
|
||||
}
|
||||
|
||||
// Issue #11243
|
||||
|
||||
@@ -19,12 +19,11 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
@@ -701,13 +700,13 @@ bundle min min key: {{ $jsonMinMin.Key }}
|
||||
b.AssertFileContent(index, fmt.Sprintf("data content unmarshaled: v%d", i))
|
||||
b.AssertFileContent(index, fmt.Sprintf("data assets content unmarshaled: v%d", i))
|
||||
|
||||
md5Asset := helpers.MD5String(fmt.Sprintf(`vdata: v%d`, i))
|
||||
md5Asset := hashing.MD5FromStringHexEncoded(fmt.Sprintf(`vdata: v%d`, i))
|
||||
b.AssertFileContent(index, fmt.Sprintf("assets fingerprinted: /data%d/data.%s.yaml", i, md5Asset))
|
||||
|
||||
// The original is not used, make sure it's not published.
|
||||
b.Assert(b.CheckExists(fmt.Sprintf("public/data%d/data.yaml", i)), qt.Equals, false)
|
||||
|
||||
md5Bundle := helpers.MD5String(fmt.Sprintf(`data: v%d`, i))
|
||||
md5Bundle := hashing.MD5FromStringHexEncoded(fmt.Sprintf(`data: v%d`, i))
|
||||
b.AssertFileContent(index, fmt.Sprintf("bundle fingerprinted: /bundle%d/data.%s.yaml", i, md5Bundle))
|
||||
|
||||
b.AssertFileContent(index,
|
||||
|
||||
@@ -110,11 +110,6 @@ func (c *pageFinder) getPageForRefs(ref ...string) (page.Page, error) {
|
||||
key = refs[1]
|
||||
}
|
||||
|
||||
key = filepath.ToSlash(key)
|
||||
if !strings.HasPrefix(key, "/") {
|
||||
key = "/" + key
|
||||
}
|
||||
|
||||
return c.getPage(nil, key)
|
||||
}
|
||||
|
||||
@@ -211,9 +206,7 @@ func (c *pageFinder) getContentNodeForRef(context page.Page, isReflink, hadExten
|
||||
var doSimpleLookup bool
|
||||
if isReflink || context == nil {
|
||||
slashCount := strings.Count(inRef, "/")
|
||||
if slashCount <= 1 {
|
||||
doSimpleLookup = slashCount == 0 || ref[0] == '/'
|
||||
}
|
||||
doSimpleLookup = slashCount == 0
|
||||
}
|
||||
|
||||
if !doSimpleLookup {
|
||||
|
||||
@@ -413,6 +413,35 @@ layout: p2
|
||||
b.AssertFileContent("public/s1/p2/index.html", "p1")
|
||||
}
|
||||
|
||||
func TestGetPageNewsVsTagsNewsIssue12638(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['rss','section','sitemap']
|
||||
[taxonomies]
|
||||
tag = "tags"
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
tags: [news]
|
||||
---
|
||||
-- layouts/index.html --
|
||||
/tags/news: {{ with .Site.GetPage "/tags/news" }}{{ .Title }}{{ end }}|
|
||||
news: {{ with .Site.GetPage "news" }}{{ .Title }}{{ end }}|
|
||||
/news: {{ with .Site.GetPage "/news" }}{{ .Title }}{{ end }}|
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"/tags/news: News|",
|
||||
"news: News|",
|
||||
"/news: |",
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetPageBundleToRegular(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
@@ -219,7 +220,7 @@ type BuildState struct {
|
||||
}
|
||||
|
||||
func (b *BuildState) hash(v any) uint64 {
|
||||
return identity.HashUint64(v)
|
||||
return hashing.HashUint64(v)
|
||||
}
|
||||
|
||||
func (b *BuildState) checkHasChangedAndSetSourceInfo(changedPath string, v any) bool {
|
||||
|
||||
@@ -119,7 +119,7 @@ docs/p1/sub/mymixcasetext2.txt
|
||||
"RelPermalink: /docs/p1/sub/mymixcasetext2.txt|Name: sub/mymixcasetext2.txt|",
|
||||
"RelPermalink: /mydata.yaml|Name: sub/data1.yaml|Title: Sub data|Params: map[]|",
|
||||
"Featured Image: /a/pixel.png|featured.png|",
|
||||
"Resized Featured Image: /a/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_10x10_resize_box_3.png|10|",
|
||||
"Resized Featured Image: /a/pixel_hu16809842526914527184.png|10|",
|
||||
// Resource from string
|
||||
"RelPermalink: /docs/p1/mytext.txt|Name: textresource|Title: My Text Resource|Params: map[param1:param1v]|",
|
||||
// Dates
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
|
||||
)
|
||||
|
||||
@@ -106,12 +106,12 @@ FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with .
|
||||
b.AssertFileContent("public/index.html",
|
||||
fmt.Sprintf(`
|
||||
SUNSET: /images/sunset.jpg|/images/sunset.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587
|
||||
FIT: /images/sunset.jpg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_fit_q75_box.jpg|200
|
||||
FIT: /images/sunset.jpg|/images/sunset_hu15210517121918042184.jpg|200
|
||||
CSS integrity Data first: sha256-od9YaHw8nMOL8mUy97Sy8sKwMV3N4hI3aVmZXATxH+8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css
|
||||
CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03+ZmKY/1t2GCOjEEOXj2x2qow94vCc7o=
|
||||
|
||||
SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587
|
||||
FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_fit_q75_box.jpg|200
|
||||
FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200
|
||||
REMOTE NOT FOUND: OK
|
||||
LOCAL NOT FOUND: OK
|
||||
PRINT PROTOCOL ERROR DETAILS: Err: error calling resources.GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"||
|
||||
@@ -119,7 +119,7 @@ FAILED REMOTE ERROR DETAILS CONTENT: |failed to fetch remote resource: Not Imple
|
||||
|StatusCode: 501|ContentLength: 16|ContentType: text/plain; charset=utf-8|
|
||||
|
||||
|
||||
`, identity.HashString(ts.URL+"/sunset.jpg", map[string]any{})))
|
||||
`, hashing.HashString(ts.URL+"/sunset.jpg", map[string]any{})))
|
||||
|
||||
b.AssertFileContent("public/styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css", "body{background-color:#add8e6}")
|
||||
b.AssertFileContent("public//styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css", "body{background-color:orange}")
|
||||
|
||||
@@ -915,6 +915,14 @@ func (hr hookRendererTemplate) RenderCodeblock(cctx context.Context, w hugio.Fle
|
||||
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
|
||||
}
|
||||
|
||||
func (hr hookRendererTemplate) RenderPassthrough(cctx context.Context, w io.Writer, ctx hooks.PassthroughContext) error {
|
||||
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
|
||||
}
|
||||
|
||||
func (hr hookRendererTemplate) RenderBlockquote(cctx context.Context, w hugio.FlexiWriter, ctx hooks.BlockquoteContext) error {
|
||||
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
|
||||
}
|
||||
|
||||
func (hr hookRendererTemplate) ResolvePosition(ctx any) text.Position {
|
||||
return hr.resolvePosition(ctx)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.128.0
|
||||
HUGORELEASER_COMMITISH=e6d2712ee062321dc2fc49e963597dd5a6157660
|
||||
HUGORELEASER_TAG=v0.131.0
|
||||
HUGORELEASER_COMMITISH=bfbee17932ff24009008aa94cdd75c0c41f59279
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package identity
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
// 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
|
||||
// it in the more complex values (e.g. Page) will not hash the full content.
|
||||
func HashString(vs ...any) string {
|
||||
hash := HashUint64(vs...)
|
||||
return strconv.FormatUint(hash, 10)
|
||||
}
|
||||
|
||||
// HashUint64 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
|
||||
// it in the more complex values (e.g. Page) will not hash the full content.
|
||||
func HashUint64(vs ...any) uint64 {
|
||||
var o any
|
||||
if len(vs) == 1 {
|
||||
o = toHashable(vs[0])
|
||||
} else {
|
||||
elements := make([]any, len(vs))
|
||||
for i, e := range vs {
|
||||
elements[i] = toHashable(e)
|
||||
}
|
||||
o = elements
|
||||
}
|
||||
|
||||
hash, err := hashstructure.Hash(o, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
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 IdentityProvider:
|
||||
return t.GetIdentity()
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,49 @@ type CodeblockContext interface {
|
||||
Ordinal() int
|
||||
}
|
||||
|
||||
// BlockquoteContext is the context passed to a blockquote render hook.
|
||||
type BlockquoteContext interface {
|
||||
AttributesProvider
|
||||
text.Positioner
|
||||
PageProvider
|
||||
|
||||
// Zero-based ordinal for all block quotes in the current document.
|
||||
Ordinal() int
|
||||
|
||||
// The blockquote text.
|
||||
// If type is "alert", this will be the alert text.
|
||||
Text() hstring.RenderedString
|
||||
|
||||
/// Returns the blockquote type, one of "regular" and "alert".
|
||||
// Type "alert" indicates that this is a GitHub type alert.
|
||||
Type() string
|
||||
|
||||
// The GitHub alert type converted to lowercase, e.g. "note".
|
||||
// Only set if Type is "alert".
|
||||
AlertType() string
|
||||
}
|
||||
|
||||
type PositionerSourceTargetProvider interface {
|
||||
// For internal use.
|
||||
PositionerSourceTarget() []byte
|
||||
}
|
||||
|
||||
// PassThroughContext is the context passed to a passthrough render hook.
|
||||
type PassthroughContext interface {
|
||||
AttributesProvider
|
||||
text.Positioner
|
||||
PageProvider
|
||||
|
||||
// Currently one of "inline" or "block".
|
||||
Type() string
|
||||
|
||||
// The inner content of the passthrough element, excluding the delimiters.
|
||||
Inner() string
|
||||
|
||||
// Zero-based ordinal for all passthrough elements in the document.
|
||||
Ordinal() int
|
||||
}
|
||||
|
||||
type AttributesOptionsSliceProvider interface {
|
||||
AttributesSlice() []attributes.Attribute
|
||||
OptionsSlice() []attributes.Attribute
|
||||
@@ -91,6 +134,14 @@ type CodeBlockRenderer interface {
|
||||
RenderCodeblock(cctx context.Context, w hugio.FlexiWriter, ctx CodeblockContext) error
|
||||
}
|
||||
|
||||
type BlockquoteRenderer interface {
|
||||
RenderBlockquote(cctx context.Context, w hugio.FlexiWriter, ctx BlockquoteContext) error
|
||||
}
|
||||
|
||||
type PassthroughRenderer interface {
|
||||
RenderPassthrough(cctx context.Context, w io.Writer, ctx PassthroughContext) error
|
||||
}
|
||||
|
||||
type IsDefaultCodeBlockRendererProvider interface {
|
||||
IsDefaultCodeBlockRenderer() bool
|
||||
}
|
||||
@@ -143,6 +194,8 @@ const (
|
||||
ImageRendererType
|
||||
HeadingRendererType
|
||||
CodeBlockRendererType
|
||||
PassthroughRendererType
|
||||
BlockquoteRendererType
|
||||
)
|
||||
|
||||
type GetRendererFunc func(t RendererType, id any) any
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package blockquotes
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
htext "github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/common/types/hstring"
|
||||
"github.com/gohugoio/hugo/markup/converter/hooks"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/gohugoio/hugo/markup/internal/attributes"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
type (
|
||||
blockquotesExtension struct{}
|
||||
htmlRenderer struct{}
|
||||
)
|
||||
|
||||
func New() goldmark.Extender {
|
||||
return &blockquotesExtension{}
|
||||
}
|
||||
|
||||
func (e *blockquotesExtension) Extend(m goldmark.Markdown) {
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(newHTMLRenderer(), 100),
|
||||
))
|
||||
}
|
||||
|
||||
func newHTMLRenderer() renderer.NodeRenderer {
|
||||
r := &htmlRenderer{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(ast.KindBlockquote, r.renderBlockquote)
|
||||
}
|
||||
|
||||
const (
|
||||
typeRegular = "regular"
|
||||
typeAlert = "alert"
|
||||
)
|
||||
|
||||
func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
ctx := w.(*render.Context)
|
||||
|
||||
n := node.(*ast.Blockquote)
|
||||
|
||||
if entering {
|
||||
// Store the current pos so we can capture the rendered text.
|
||||
ctx.PushPos(ctx.Buffer.Len())
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
pos := ctx.PopPos()
|
||||
text := ctx.Buffer.Bytes()[pos:]
|
||||
ctx.Buffer.Truncate(pos)
|
||||
|
||||
// Extract a source sample to use for position information.
|
||||
nn := n.FirstChild()
|
||||
var start, stop int
|
||||
for i := 0; i < nn.Lines().Len() && i < 2; i++ {
|
||||
line := nn.Lines().At(i)
|
||||
if i == 0 {
|
||||
start = line.Start
|
||||
}
|
||||
stop = line.Stop
|
||||
}
|
||||
|
||||
// We do not mutate the source, so this is safe.
|
||||
sourceRef := src[start:stop]
|
||||
|
||||
ordinal := ctx.GetAndIncrementOrdinal(ast.KindBlockquote)
|
||||
|
||||
texts := string(text)
|
||||
typ := typeRegular
|
||||
alertType := resolveGitHubAlert(texts)
|
||||
if alertType != "" {
|
||||
typ = typeAlert
|
||||
}
|
||||
|
||||
renderer := ctx.RenderContext().GetRenderer(hooks.BlockquoteRendererType, typ)
|
||||
if renderer == nil {
|
||||
return r.renderBlockquoteDefault(w, n, texts)
|
||||
}
|
||||
|
||||
if typ == typeAlert {
|
||||
// Trim preamble: <p>[!NOTE]<br>\n but preserve leading paragraph.
|
||||
// We could possibly complicate this by moving this to the parser, but
|
||||
// keep it simple for now.
|
||||
texts = "<p>" + texts[strings.Index(texts, "\n")+1:]
|
||||
}
|
||||
|
||||
bqctx := &blockquoteContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
typ: typ,
|
||||
alertType: alertType,
|
||||
text: hstring.RenderedString(texts),
|
||||
sourceRef: sourceRef,
|
||||
ordinal: ordinal,
|
||||
AttributesHolder: attributes.New(n.Attributes(), attributes.AttributesOwnerGeneral),
|
||||
}
|
||||
|
||||
bqctx.createPos = func() htext.Position {
|
||||
if resolver, ok := renderer.(hooks.ElementPositionResolver); ok {
|
||||
return resolver.ResolvePosition(bqctx)
|
||||
}
|
||||
|
||||
return htext.Position{
|
||||
Filename: ctx.DocumentContext().Filename,
|
||||
LineNumber: 1,
|
||||
ColumnNumber: 1,
|
||||
}
|
||||
}
|
||||
|
||||
cr := renderer.(hooks.BlockquoteRenderer)
|
||||
|
||||
err := cr.RenderBlockquote(
|
||||
ctx.RenderContext().Ctx,
|
||||
w,
|
||||
bqctx,
|
||||
)
|
||||
if err != nil {
|
||||
return ast.WalkContinue, herrors.NewFileErrorFromPos(err, bqctx.createPos())
|
||||
}
|
||||
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) getPageInner(rctx *render.Context) any {
|
||||
pid := rctx.PeekPid()
|
||||
if pid > 0 {
|
||||
if lookup := rctx.DocumentContext().DocumentLookup; lookup != nil {
|
||||
if v := rctx.DocumentContext().DocumentLookup(pid); v != nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return rctx.DocumentContext().Document
|
||||
}
|
||||
|
||||
// Code borrowed from goldmark's html renderer.
|
||||
func (r *htmlRenderer) renderBlockquoteDefault(
|
||||
w util.BufWriter, n ast.Node, text string,
|
||||
) (ast.WalkStatus, error) {
|
||||
if n.Attributes() != nil {
|
||||
_, _ = w.WriteString("<blockquote")
|
||||
html.RenderAttributes(w, n, html.BlockquoteAttributeFilter)
|
||||
_ = w.WriteByte('>')
|
||||
} else {
|
||||
_, _ = w.WriteString("<blockquote>\n")
|
||||
}
|
||||
|
||||
_, _ = w.WriteString(text)
|
||||
|
||||
_, _ = w.WriteString("</blockquote>\n")
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
type blockquoteContext struct {
|
||||
page any
|
||||
pageInner any
|
||||
text hstring.RenderedString
|
||||
typ string
|
||||
sourceRef []byte
|
||||
alertType string
|
||||
ordinal int
|
||||
|
||||
// This is only used in error situations and is expensive to create,
|
||||
// so delay creation until needed.
|
||||
pos htext.Position
|
||||
posInit sync.Once
|
||||
createPos func() htext.Position
|
||||
|
||||
*attributes.AttributesHolder
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Type() string {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) AlertType() string {
|
||||
return c.alertType
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Page() any {
|
||||
return c.page
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) PageInner() any {
|
||||
return c.pageInner
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Text() hstring.RenderedString {
|
||||
return c.text
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Ordinal() int {
|
||||
return c.ordinal
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Position() htext.Position {
|
||||
c.posInit.Do(func() {
|
||||
c.pos = c.createPos()
|
||||
})
|
||||
return c.pos
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) PositionerSourceTarget() []byte {
|
||||
return c.sourceRef
|
||||
}
|
||||
|
||||
var _ hooks.PositionerSourceTargetProvider = (*blockquoteContext)(nil)
|
||||
|
||||
// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
|
||||
// Five types:
|
||||
// [!NOTE], [!TIP], [!WARNING], [!IMPORTANT], [!CAUTION]
|
||||
var gitHubAlertRe = regexp.MustCompile(`^<p>\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\]`)
|
||||
|
||||
// resolveGitHubAlert returns one of note, tip, warning, important or caution.
|
||||
// An empty string if no match.
|
||||
func resolveGitHubAlert(s string) string {
|
||||
m := gitHubAlertRe.FindStringSubmatch(s)
|
||||
if len(m) == 2 {
|
||||
return strings.ToLower(m[1])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package blockquotes_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestBlockquoteHook(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
[markup]
|
||||
[markup.goldmark]
|
||||
[markup.goldmark.parser]
|
||||
[markup.goldmark.parser.attribute]
|
||||
block = true
|
||||
title = true
|
||||
-- layouts/_default/_markup/render-blockquote.html --
|
||||
Blockquote: |{{ .Text | safeHTML }}|{{ .Type }}|
|
||||
-- layouts/_default/_markup/render-blockquote-alert.html --
|
||||
{{ $text := .Text | safeHTML }}
|
||||
Blockquote Alert: |{{ $text }}|{{ .Type }}|
|
||||
Blockquote Alert Attributes: |{{ $text }}|{{ .Attributes }}|
|
||||
Blockquote Alert Page: |{{ $text }}|{{ .Page.Title }}|{{ .PageInner.Title }}|
|
||||
{{ if .Attributes.showpos }}
|
||||
Blockquote Alert Position: |{{ $text }}|{{ .Position | safeHTML }}|
|
||||
{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
Content: {{ .Content }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
> [!NOTE]
|
||||
> This is a note with some whitespace after the alert type.
|
||||
|
||||
|
||||
> [!TIP]
|
||||
> This is a tip.
|
||||
|
||||
> [!CAUTION]
|
||||
> This is a caution with some whitespace before the alert type.
|
||||
|
||||
> A regular blockquote.
|
||||
|
||||
> [!TIP]
|
||||
> This is a tip with attributes.
|
||||
{class="foo bar" id="baz"}
|
||||
|
||||
> [!NOTE]
|
||||
> Note triggering showing the position.
|
||||
{showpos="true"}
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Blockquote Alert: |<p>This is a note with some whitespace after the alert type.</p>\n|alert|",
|
||||
"Blockquote Alert: |<p>This is a tip.</p>",
|
||||
"Blockquote Alert: |<p>This is a caution with some whitespace before the alert type.</p>\n|alert|",
|
||||
"Blockquote: |<p>A regular blockquote.</p>\n|regular|",
|
||||
"Blockquote Alert Attributes: |<p>This is a tip with attributes.</p>\n|map[class:foo bar id:baz]|",
|
||||
filepath.FromSlash("/content/p1.md:20:3"),
|
||||
"Blockquote Alert Page: |<p>This is a tip with attributes.</p>\n|p1|p1|",
|
||||
)
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package identity
|
||||
package blockquotes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -19,26 +19,42 @@ import (
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestHashString(t *testing.T) {
|
||||
func TestResolveGitHubAlert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(HashString("a", "b"), qt.Equals, "2712570657419664240")
|
||||
c.Assert(HashString("ab"), qt.Equals, "590647783936702392")
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "[!NOTE]",
|
||||
expected: "note",
|
||||
},
|
||||
{
|
||||
input: "[!WARNING]",
|
||||
expected: "warning",
|
||||
},
|
||||
{
|
||||
input: "[!TIP]",
|
||||
expected: "tip",
|
||||
},
|
||||
{
|
||||
input: "[!IMPORTANT]",
|
||||
expected: "important",
|
||||
},
|
||||
{
|
||||
input: "[!CAUTION]",
|
||||
expected: "caution",
|
||||
},
|
||||
{
|
||||
input: "[!FOO]",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
var vals []any = []any{"a", "b", tstKeyer{"c"}}
|
||||
|
||||
c.Assert(HashString(vals...), qt.Equals, "12599484872364427450")
|
||||
c.Assert(vals[2], qt.Equals, tstKeyer{"c"})
|
||||
}
|
||||
|
||||
type tstKeyer struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (t tstKeyer) Key() string {
|
||||
return t.key
|
||||
}
|
||||
|
||||
func (t tstKeyer) String() string {
|
||||
return "key: " + t.key
|
||||
for _, test := range tests {
|
||||
c.Assert(resolveGitHubAlert("<p>"+test.input), qt.Equals, test.expected)
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,6 @@ func New() goldmark.Extender {
|
||||
}
|
||||
|
||||
func (e *codeBlocksExtension) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(
|
||||
parser.WithASTTransformers(
|
||||
util.Prioritized(&Transformer{}, 100),
|
||||
),
|
||||
)
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(newHTMLRenderer(), 100),
|
||||
))
|
||||
@@ -60,7 +55,7 @@ func newHTMLRenderer() renderer.NodeRenderer {
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(KindCodeBlock, r.renderCodeBlock)
|
||||
reg.Register(ast.KindFencedCodeBlock, r.renderCodeBlock)
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
@@ -70,28 +65,29 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
n := node.(*codeBlock)
|
||||
lang := getLang(n.b, src)
|
||||
n := node.(*ast.FencedCodeBlock)
|
||||
|
||||
lang := getLang(n, src)
|
||||
renderer := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang)
|
||||
if renderer == nil {
|
||||
return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang)
|
||||
}
|
||||
|
||||
ordinal := n.ordinal
|
||||
ordinal := ctx.GetAndIncrementOrdinal(ast.KindFencedCodeBlock)
|
||||
|
||||
var buff bytes.Buffer
|
||||
|
||||
l := n.b.Lines().Len()
|
||||
l := n.Lines().Len()
|
||||
for i := 0; i < l; i++ {
|
||||
line := n.b.Lines().At(i)
|
||||
line := n.Lines().At(i)
|
||||
buff.Write(line.Value(src))
|
||||
}
|
||||
|
||||
s := htext.Chomp(buff.String())
|
||||
|
||||
var info []byte
|
||||
if n.b.Info != nil {
|
||||
info = n.b.Info.Segment.Value(src)
|
||||
if n.Info != nil {
|
||||
info = n.Info.Segment.Value(src)
|
||||
}
|
||||
|
||||
attrtp := attributes.AttributesOwnerCodeBlockCustom
|
||||
@@ -101,8 +97,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
attrtp = attributes.AttributesOwnerCodeBlockChroma
|
||||
}
|
||||
|
||||
// IsDefaultCodeBlockRendererProvider
|
||||
attrs, attrStr, err := getAttributes(n.b, info)
|
||||
attrs, attrStr, err := getAttributes(n, info)
|
||||
if err != nil {
|
||||
return ast.WalkStop, &herrors.TextSegmentError{Err: err, Segment: attrStr}
|
||||
}
|
||||
@@ -152,6 +147,8 @@ func (r *htmlRenderer) getPageInner(rctx *render.Context) any {
|
||||
return rctx.DocumentContext().Document
|
||||
}
|
||||
|
||||
var _ hooks.PositionerSourceTargetProvider = (*codeBlockContext)(nil)
|
||||
|
||||
type codeBlockContext struct {
|
||||
page any
|
||||
pageInner any
|
||||
@@ -160,7 +157,7 @@ type codeBlockContext struct {
|
||||
ordinal int
|
||||
|
||||
// This is only used in error situations and is expensive to create,
|
||||
// to delay creation until needed.
|
||||
// so delay creation until needed.
|
||||
pos htext.Position
|
||||
posInit sync.Once
|
||||
createPos func() htext.Position
|
||||
@@ -195,6 +192,11 @@ func (c *codeBlockContext) Position() htext.Position {
|
||||
return c.pos
|
||||
}
|
||||
|
||||
// For internal use.
|
||||
func (c *codeBlockContext) PositionerSourceTarget() []byte {
|
||||
return []byte(c.code)
|
||||
}
|
||||
|
||||
func getLang(node *ast.FencedCodeBlock, src []byte) string {
|
||||
langWithAttributes := string(node.Language(src))
|
||||
lang, _, _ := strings.Cut(langWithAttributes, "{")
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package codeblocks
|
||||
|
||||
import (
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/text"
|
||||
)
|
||||
|
||||
// KindCodeBlock is the kind of an Hugo code block.
|
||||
var KindCodeBlock = ast.NewNodeKind("HugoCodeBlock")
|
||||
|
||||
// Its raw contents are the plain text of the code block.
|
||||
type codeBlock struct {
|
||||
ast.BaseBlock
|
||||
ordinal int
|
||||
b *ast.FencedCodeBlock
|
||||
}
|
||||
|
||||
func (*codeBlock) Kind() ast.NodeKind { return KindCodeBlock }
|
||||
|
||||
func (*codeBlock) IsRaw() bool { return true }
|
||||
|
||||
func (b *codeBlock) Dump(src []byte, level int) {
|
||||
}
|
||||
|
||||
type Transformer struct{}
|
||||
|
||||
// Transform transforms the provided Markdown AST.
|
||||
func (*Transformer) Transform(doc *ast.Document, reader text.Reader, pctx parser.Context) {
|
||||
var codeBlocks []*ast.FencedCodeBlock
|
||||
|
||||
ast.Walk(doc, func(node ast.Node, enter bool) (ast.WalkStatus, error) {
|
||||
if !enter {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
cb, ok := node.(*ast.FencedCodeBlock)
|
||||
if !ok {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
codeBlocks = append(codeBlocks, cb)
|
||||
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
|
||||
for i, cb := range codeBlocks {
|
||||
b := &codeBlock{b: cb, ordinal: i}
|
||||
parent := cb.Parent()
|
||||
if parent != nil {
|
||||
parent.ReplaceChild(parent, cb, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,15 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gohugoio/hugo-goldmark-extensions/extras"
|
||||
"github.com/gohugoio/hugo-goldmark-extensions/passthrough"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/hugocontext"
|
||||
"github.com/yuin/goldmark/util"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/goldmark/blockquotes"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/hugocontext"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/images"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/passthrough"
|
||||
"github.com/yuin/goldmark/util"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
emoji "github.com/yuin/goldmark-emoji"
|
||||
@@ -108,6 +108,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||
hugocontext.New(),
|
||||
newLinks(cfg),
|
||||
newTocExtension(tocRendererOptions),
|
||||
blockquotes.New(),
|
||||
}
|
||||
parserOptions []parser.Option
|
||||
)
|
||||
@@ -177,32 +178,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||
}
|
||||
|
||||
if cfg.Extensions.Passthrough.Enable {
|
||||
configuredInlines := cfg.Extensions.Passthrough.Delimiters.Inline
|
||||
configuredBlocks := cfg.Extensions.Passthrough.Delimiters.Block
|
||||
|
||||
inlineDelimiters := make([]passthrough.Delimiters, len(configuredInlines))
|
||||
blockDelimiters := make([]passthrough.Delimiters, len(configuredBlocks))
|
||||
|
||||
for i, d := range configuredInlines {
|
||||
inlineDelimiters[i] = passthrough.Delimiters{
|
||||
Open: d[0],
|
||||
Close: d[1],
|
||||
}
|
||||
}
|
||||
|
||||
for i, d := range configuredBlocks {
|
||||
blockDelimiters[i] = passthrough.Delimiters{
|
||||
Open: d[0],
|
||||
Close: d[1],
|
||||
}
|
||||
}
|
||||
|
||||
extensions = append(extensions, passthrough.New(
|
||||
passthrough.Config{
|
||||
InlineDelimiters: inlineDelimiters,
|
||||
BlockDelimiters: blockDelimiters,
|
||||
},
|
||||
))
|
||||
extensions = append(extensions, passthrough.New(cfg.Extensions.Passthrough))
|
||||
}
|
||||
|
||||
if pcfg.Conf.EnableEmoji() {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"math/bits"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
)
|
||||
|
||||
type BufWriter struct {
|
||||
@@ -40,9 +41,19 @@ func (b *BufWriter) Flush() error {
|
||||
|
||||
type Context struct {
|
||||
*BufWriter
|
||||
ContextData
|
||||
positions []int
|
||||
pids []uint64
|
||||
ContextData
|
||||
ordinals map[ast.NodeKind]int
|
||||
}
|
||||
|
||||
func (ctx *Context) GetAndIncrementOrdinal(kind ast.NodeKind) int {
|
||||
if ctx.ordinals == nil {
|
||||
ctx.ordinals = make(map[ast.NodeKind]int)
|
||||
}
|
||||
i := ctx.ordinals[kind]
|
||||
ctx.ordinals[kind]++
|
||||
return i
|
||||
}
|
||||
|
||||
func (ctx *Context) PushPos(n int) {
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package passthrough
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
htext "github.com/gohugoio/hugo/common/text"
|
||||
|
||||
"github.com/gohugoio/hugo-goldmark-extensions/passthrough"
|
||||
"github.com/gohugoio/hugo/markup/converter/hooks"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/gohugoio/hugo/markup/internal/attributes"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
func New(cfg goldmark_config.Passthrough) goldmark.Extender {
|
||||
if !cfg.Enable {
|
||||
return nil
|
||||
}
|
||||
return &passthroughExtension{cfg: cfg}
|
||||
}
|
||||
|
||||
type (
|
||||
passthroughExtension struct {
|
||||
cfg goldmark_config.Passthrough
|
||||
}
|
||||
htmlRenderer struct{}
|
||||
)
|
||||
|
||||
func (e *passthroughExtension) Extend(m goldmark.Markdown) {
|
||||
configuredInlines := e.cfg.Delimiters.Inline
|
||||
configuredBlocks := e.cfg.Delimiters.Block
|
||||
|
||||
inlineDelimiters := make([]passthrough.Delimiters, len(configuredInlines))
|
||||
blockDelimiters := make([]passthrough.Delimiters, len(configuredBlocks))
|
||||
|
||||
for i, d := range configuredInlines {
|
||||
inlineDelimiters[i] = passthrough.Delimiters{
|
||||
Open: d[0],
|
||||
Close: d[1],
|
||||
}
|
||||
}
|
||||
|
||||
for i, d := range configuredBlocks {
|
||||
blockDelimiters[i] = passthrough.Delimiters{
|
||||
Open: d[0],
|
||||
Close: d[1],
|
||||
}
|
||||
}
|
||||
|
||||
pse := passthrough.New(
|
||||
passthrough.Config{
|
||||
InlineDelimiters: inlineDelimiters,
|
||||
BlockDelimiters: blockDelimiters,
|
||||
},
|
||||
)
|
||||
|
||||
pse.Extend(m)
|
||||
|
||||
// Set up render hooks if configured.
|
||||
// Upstream passthrough inline = 101
|
||||
// Upstream passthrough block = 99
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(newHTMLRenderer(), 90),
|
||||
))
|
||||
}
|
||||
|
||||
func newHTMLRenderer() renderer.NodeRenderer {
|
||||
r := &htmlRenderer{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(passthrough.KindPassthroughBlock, r.renderPassthroughBlock)
|
||||
reg.Register(passthrough.KindPassthroughInline, r.renderPassthroughBlock)
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) renderPassthroughBlock(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
ctx := w.(*render.Context)
|
||||
|
||||
if entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
var (
|
||||
s string
|
||||
typ string
|
||||
delims *passthrough.Delimiters
|
||||
)
|
||||
|
||||
switch nn := node.(type) {
|
||||
case *passthrough.PassthroughInline:
|
||||
s = string(nn.Text(src))
|
||||
typ = "inline"
|
||||
delims = nn.Delimiters
|
||||
case (*passthrough.PassthroughBlock):
|
||||
l := nn.Lines().Len()
|
||||
var buff bytes.Buffer
|
||||
for i := 0; i < l; i++ {
|
||||
line := nn.Lines().At(i)
|
||||
buff.Write(line.Value(src))
|
||||
}
|
||||
s = buff.String()
|
||||
typ = "block"
|
||||
delims = nn.Delimiters
|
||||
}
|
||||
|
||||
renderer := ctx.RenderContext().GetRenderer(hooks.PassthroughRendererType, typ)
|
||||
if renderer == nil {
|
||||
// Write the raw content if no renderer is found.
|
||||
ctx.WriteString(s)
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// Inline and block passthroughs share the same ordinal counter.
|
||||
ordinal := ctx.GetAndIncrementOrdinal(passthrough.KindPassthroughBlock)
|
||||
|
||||
// Trim the delimiters.
|
||||
s = s[len(delims.Open) : len(s)-len(delims.Close)]
|
||||
|
||||
pctx := &passthroughContext{
|
||||
ordinal: ordinal,
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
inner: s,
|
||||
typ: typ,
|
||||
AttributesHolder: attributes.New(node.Attributes(), attributes.AttributesOwnerGeneral),
|
||||
}
|
||||
|
||||
pctx.createPos = func() htext.Position {
|
||||
if resolver, ok := renderer.(hooks.ElementPositionResolver); ok {
|
||||
return resolver.ResolvePosition(pctx)
|
||||
}
|
||||
return htext.Position{
|
||||
Filename: ctx.DocumentContext().Filename,
|
||||
LineNumber: 1,
|
||||
ColumnNumber: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pr := renderer.(hooks.PassthroughRenderer)
|
||||
|
||||
if err := pr.RenderPassthrough(ctx.RenderContext().Ctx, w, pctx); err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) getPageInner(rctx *render.Context) any {
|
||||
pid := rctx.PeekPid()
|
||||
if pid > 0 {
|
||||
if lookup := rctx.DocumentContext().DocumentLookup; lookup != nil {
|
||||
if v := rctx.DocumentContext().DocumentLookup(pid); v != nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return rctx.DocumentContext().Document
|
||||
}
|
||||
|
||||
type passthroughContext struct {
|
||||
page any
|
||||
pageInner any
|
||||
typ string // inner or block
|
||||
inner string
|
||||
ordinal int
|
||||
|
||||
// This is only used in error situations and is expensive to create,
|
||||
// so delay creation until needed.
|
||||
pos htext.Position
|
||||
posInit sync.Once
|
||||
createPos func() htext.Position
|
||||
*attributes.AttributesHolder
|
||||
}
|
||||
|
||||
func (p *passthroughContext) Page() any {
|
||||
return p.page
|
||||
}
|
||||
|
||||
func (p *passthroughContext) PageInner() any {
|
||||
return p.pageInner
|
||||
}
|
||||
|
||||
func (p *passthroughContext) Type() string {
|
||||
return p.typ
|
||||
}
|
||||
|
||||
func (p *passthroughContext) Inner() string {
|
||||
return p.inner
|
||||
}
|
||||
|
||||
func (p *passthroughContext) Ordinal() int {
|
||||
return p.ordinal
|
||||
}
|
||||
|
||||
func (p *passthroughContext) Position() htext.Position {
|
||||
p.posInit.Do(func() {
|
||||
p.pos = p.createPos()
|
||||
})
|
||||
return p.pos
|
||||
}
|
||||
|
||||
// For internal use.
|
||||
func (p *passthroughContext) PositionerSourceTarget() []byte {
|
||||
return []byte(p.inner)
|
||||
}
|
||||
|
||||
var _ hooks.PositionerSourceTargetProvider = (*passthroughContext)(nil)
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package passthrough_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestPassthroughRenderHook(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
[markup.goldmark.extensions.passthrough]
|
||||
enable = true
|
||||
[markup.goldmark.extensions.passthrough.delimiters]
|
||||
block = [['$$', '$$']]
|
||||
inline = [['$', '$']]
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
## LaTeX test
|
||||
|
||||
Some inline LaTeX 1: $a^*=x-b^*$.
|
||||
|
||||
Block equation that would be mangled by default parser:
|
||||
|
||||
$$a^*=x-b^*$$
|
||||
|
||||
Some inline LaTeX 2: $a^*=x-b^*$.
|
||||
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-passthrough-block.html --
|
||||
Passthrough block: {{ .Inner | safeHTML }}|{{ .Type }}|{{ .Ordinal }}:END
|
||||
-- layouts/_default/_markup/render-passthrough-inline.html --
|
||||
Passthrough inline: {{ .Inner | safeHTML }}|{{ .Type }}|{{ .Ordinal }}:END
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
Some inline LaTeX 1: Passthrough inline: a^*=x-b^*|inline|0:END
|
||||
Passthrough block: a^*=x-b^*|block|1:END
|
||||
Some inline LaTeX 2: Passthrough inline: a^*=x-b^*|inline|2:END
|
||||
|
||||
`)
|
||||
}
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/compare"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
)
|
||||
|
||||
// The Provider interface defines an interface for measuring metrics.
|
||||
@@ -241,7 +241,7 @@ func howSimilar(a, b any) int {
|
||||
return 90
|
||||
}
|
||||
|
||||
h1, h2 := identity.HashString(a), identity.HashString(b)
|
||||
h1, h2 := hashing.HashString(a), hashing.HashString(b)
|
||||
if h1 == h2 {
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import (
|
||||
color_extractor "github.com/marekm4/color-extractor"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/hstrings"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
|
||||
"github.com/disintegration/gift"
|
||||
|
||||
@@ -40,7 +40,6 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/resources/images"
|
||||
|
||||
// Blind import for image.Decode
|
||||
@@ -82,8 +81,9 @@ func (i *imageResource) Exif() *exif.ExifInfo {
|
||||
|
||||
func (i *imageResource) getExif() *exif.ExifInfo {
|
||||
i.metaInit.Do(func() {
|
||||
supportsExif := i.Format == images.JPEG || i.Format == images.TIFF
|
||||
if !supportsExif {
|
||||
mf := i.Format.ToImageMetaImageFormatFormat()
|
||||
if mf == -1 {
|
||||
// No Exif support for this format.
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,7 +114,8 @@ func (i *imageResource) getExif() *exif.ExifInfo {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
x, err := i.getSpec().imaging.DecodeExif(f)
|
||||
filename := i.getResourcePaths().Path()
|
||||
x, err := i.getSpec().imaging.DecodeExif(filename, mf, f)
|
||||
if err != nil {
|
||||
i.getSpec().Logger.Warnf("Unable to decode Exif metadata from image: %s", i.Key())
|
||||
return nil
|
||||
@@ -272,7 +273,7 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) {
|
||||
}
|
||||
|
||||
conf.Action = "filter"
|
||||
conf.Key = identity.HashString(gfilters)
|
||||
conf.Key = hashing.HashString(gfilters)
|
||||
conf.TargetFormat = targetFormat
|
||||
if conf.TargetFormat == 0 {
|
||||
conf.TargetFormat = i.Format
|
||||
@@ -471,13 +472,15 @@ func (i *imageResource) clone(img image.Image) *imageResource {
|
||||
}
|
||||
|
||||
func (i *imageResource) getImageMetaCacheTargetPath() string {
|
||||
const imageMetaVersionNumber = 1 // Increment to invalidate the meta cache
|
||||
// Increment to invalidate the meta cache
|
||||
// Last increment: v0.130.0 when change to the new imagemeta library for Exif.
|
||||
const imageMetaVersionNumber = 2
|
||||
|
||||
cfgHash := i.getSpec().imaging.Cfg.SourceHash
|
||||
df := i.getResourcePaths()
|
||||
p1, _ := paths.FileAndExt(df.File)
|
||||
h := i.hash()
|
||||
idStr := identity.HashString(h, i.size(), imageMetaVersionNumber, cfgHash)
|
||||
idStr := hashing.HashString(h, i.size(), imageMetaVersionNumber, cfgHash)
|
||||
df.File = fmt.Sprintf("%s_%s.json", p1, idStr)
|
||||
return df.TargetPath()
|
||||
}
|
||||
@@ -487,36 +490,16 @@ func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) interna
|
||||
if conf.TargetFormat != i.Format {
|
||||
p2 = conf.TargetFormat.DefaultExtension()
|
||||
}
|
||||
|
||||
h := i.hash()
|
||||
idStr := fmt.Sprintf("_hu%s_%d", h, i.size())
|
||||
|
||||
// Do not change for no good reason.
|
||||
const md5Threshold = 100
|
||||
|
||||
key := conf.GetKey(i.Format)
|
||||
|
||||
// It is useful to have the key in clear text, but when nesting transforms, it
|
||||
// can easily be too long to read, and maybe even too long
|
||||
// for the different OSes to handle.
|
||||
if len(p1)+len(idStr)+len(p2) > md5Threshold {
|
||||
key = helpers.MD5String(p1 + key + p2)
|
||||
huIdx := strings.Index(p1, "_hu")
|
||||
if huIdx != -1 {
|
||||
p1 = p1[:huIdx]
|
||||
} else {
|
||||
// This started out as a very long file name. Making it even longer
|
||||
// could melt ice in the Arctic.
|
||||
p1 = ""
|
||||
}
|
||||
} else if strings.Contains(p1, idStr) {
|
||||
// On scaling an already scaled image, we get the file info from the original.
|
||||
// Repeating the same info in the filename makes it stuttery for no good reason.
|
||||
idStr = ""
|
||||
const prefix = "_hu"
|
||||
huIdx := strings.LastIndex(p1, prefix)
|
||||
incomingID := "i"
|
||||
if huIdx > -1 {
|
||||
incomingID = p1[huIdx+len(prefix):]
|
||||
p1 = p1[:huIdx]
|
||||
}
|
||||
|
||||
hash := hashing.HashUint64(incomingID, i.hash(), conf.GetKey(i.Format))
|
||||
rp := i.getResourcePaths()
|
||||
rp.File = fmt.Sprintf("%s%s_%s%s", p1, idStr, key, p2)
|
||||
rp.File = fmt.Sprintf("%s%s%d%s", p1, prefix, hash, p2)
|
||||
|
||||
return rp
|
||||
}
|
||||
|
||||
@@ -20,22 +20,28 @@ import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
)
|
||||
|
||||
func TestImageResizeWebP(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
_, image := fetchImage(c, "sunset.webp")
|
||||
_, image := fetchImage(c, "sunrise.webp")
|
||||
|
||||
c.Assert(image.MediaType(), qt.Equals, media.Builtin.WEBPType)
|
||||
c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.webp")
|
||||
c.Assert(image.RelPermalink(), qt.Equals, "/a/sunrise.webp")
|
||||
c.Assert(image.ResourceType(), qt.Equals, "image")
|
||||
c.Assert(image.Exif(), qt.IsNil)
|
||||
exif := image.Exif()
|
||||
c.Assert(exif, qt.Not(qt.IsNil))
|
||||
c.Assert(exif.Tags["Copyright"], qt.Equals, "Bjørn Erik Pedersen")
|
||||
c.Assert(exif.Lat, hqt.IsSameFloat64, 36.59744166666667)
|
||||
c.Assert(exif.Long, hqt.IsSameFloat64, -4.50846)
|
||||
c.Assert(exif.Date.IsZero(), qt.Equals, false)
|
||||
|
||||
resized, err := image.Resize("123x")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(image.MediaType(), qt.Equals, media.Builtin.WEBPType)
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu36ee0b61ba924719ad36da960c273f96_59826_123x0_resize_q68_h2_linear_2.webp")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunrise_hu544374262273649331.webp")
|
||||
c.Assert(resized.Width(), qt.Equals, 123)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"image"
|
||||
"image/gif"
|
||||
"io/fs"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -30,17 +29,17 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/bep/imagemeta"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/gohugoio/hugo/resources/images/webp"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/disintegration/gift"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/resources/images"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -67,8 +66,13 @@ var eq = qt.CmpEquals(
|
||||
return m1.Type == m2.Type
|
||||
}),
|
||||
cmp.Comparer(
|
||||
func(v1, v2 *big.Rat) bool {
|
||||
return v1.RatString() == v2.RatString()
|
||||
func(v1, v2 imagemeta.Rat[uint32]) bool {
|
||||
return v1.String() == v2.String()
|
||||
},
|
||||
),
|
||||
cmp.Comparer(
|
||||
func(v1, v2 imagemeta.Rat[int32]) bool {
|
||||
return v1.String() == v2.String()
|
||||
},
|
||||
),
|
||||
cmp.Comparer(func(v1, v2 time.Time) bool {
|
||||
@@ -119,28 +123,28 @@ func TestImageTransformBasic(t *testing.T) {
|
||||
assertWidthHeight(resizedAndRotated, 125, 200)
|
||||
|
||||
assertWidthHeight(resized, 300, 200)
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x200_resize_q68_linear.jpg")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu2082030801149749592.jpg")
|
||||
|
||||
fitted, err := resized.Fit("50x50")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_625708021e2bb281c9f1002f88e4753f.jpg")
|
||||
c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu16263619592447877226.jpg")
|
||||
assertWidthHeight(fitted, 50, 33)
|
||||
|
||||
// Check the MD5 key threshold
|
||||
fittedAgain, _ := fitted.Fit("10x20")
|
||||
fittedAgain, err = fittedAgain.Fit("10x20")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_3f65ba24dc2b7fba0f56d7f104519157.jpg")
|
||||
c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu847809310637164306.jpg")
|
||||
assertWidthHeight(fittedAgain, 10, 7)
|
||||
|
||||
filled, err := image.Fill("200x100 bottomLeft")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x100_fill_q68_linear_bottomleft.jpg")
|
||||
c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu18289448341423092707.jpg")
|
||||
assertWidthHeight(filled, 200, 100)
|
||||
|
||||
smart, err := image.Fill("200x100 smart")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(smart.RelPermalink(), qt.Equals, fmt.Sprintf("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x100_fill_q68_linear_smart%d.jpg", 1))
|
||||
c.Assert(smart.RelPermalink(), qt.Equals, "/a/sunset_hu11649371610839769766.jpg")
|
||||
assertWidthHeight(smart, 200, 100)
|
||||
|
||||
// Check cache
|
||||
@@ -150,12 +154,12 @@ func TestImageTransformBasic(t *testing.T) {
|
||||
|
||||
cropped, err := image.Crop("300x300 topRight")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x300_crop_q68_linear_topright.jpg")
|
||||
c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu2242042514052853140.jpg")
|
||||
assertWidthHeight(cropped, 300, 300)
|
||||
|
||||
smartcropped, err := image.Crop("200x200 smart")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(smartcropped.RelPermalink(), qt.Equals, fmt.Sprintf("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_crop_q68_linear_smart%d.jpg", 1))
|
||||
c.Assert(smartcropped.RelPermalink(), qt.Equals, "/a/sunset_hu12983255101170993571.jpg")
|
||||
assertWidthHeight(smartcropped, 200, 200)
|
||||
|
||||
// Check cache
|
||||
@@ -222,7 +226,7 @@ func TestImageTransformFormat(t *testing.T) {
|
||||
|
||||
imagePng, err := image.Resize("450x png")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_450x0_resize_linear.png")
|
||||
c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu11737890885216583918.png")
|
||||
c.Assert(imagePng.ResourceType(), qt.Equals, "image")
|
||||
assertExtWidthHeight(imagePng, ".png", 450, 281)
|
||||
c.Assert(imagePng.Name(), qt.Equals, "sunset.jpg")
|
||||
@@ -230,7 +234,7 @@ func TestImageTransformFormat(t *testing.T) {
|
||||
|
||||
imageGif, err := image.Resize("225x gif")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_225x0_resize_linear.gif")
|
||||
c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu1431827106749674475.gif")
|
||||
c.Assert(imageGif.ResourceType(), qt.Equals, "image")
|
||||
assertExtWidthHeight(imageGif, ".gif", 225, 141)
|
||||
c.Assert(imageGif.Name(), qt.Equals, "sunset.jpg")
|
||||
@@ -253,7 +257,7 @@ func TestImagePermalinkPublishOrder(t *testing.T) {
|
||||
}()
|
||||
|
||||
check1 := func(img images.ImageResource) {
|
||||
resizedLink := "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_100x50_resize_q75_box.jpg"
|
||||
resizedLink := "/a/sunset_hu7919355342577096259.jpg"
|
||||
c.Assert(img.RelPermalink(), qt.Equals, resizedLink)
|
||||
assertImageFile(c, spec.PublishFs, resizedLink, 100, 50)
|
||||
}
|
||||
@@ -294,12 +298,12 @@ func TestImageBugs(t *testing.T) {
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resized, qt.Not(qt.IsNil))
|
||||
c.Assert(resized.Width(), qt.Equals, 200)
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_65b757a6e14debeae720fe8831f0a9bc.jpg")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu9514381480012510326.jpg")
|
||||
resized, err = resized.Resize("100x")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resized, qt.Not(qt.IsNil))
|
||||
c.Assert(resized.Width(), qt.Equals, 100)
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_c876768085288f41211f768147ba2647.jpg")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu1776700126481066216.jpg")
|
||||
})
|
||||
|
||||
// Issue #6137
|
||||
@@ -392,12 +396,12 @@ func TestImageResize8BitPNG(t *testing.T) {
|
||||
c.Assert(image.MediaType().Type, qt.Equals, "image/png")
|
||||
c.Assert(image.RelPermalink(), qt.Equals, "/a/gohugoio.png")
|
||||
c.Assert(image.ResourceType(), qt.Equals, "image")
|
||||
c.Assert(image.Exif(), qt.IsNil)
|
||||
c.Assert(image.Exif(), qt.IsNotNil)
|
||||
|
||||
resized, err := image.Resize("800x")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resized.MediaType().Type, qt.Equals, "image/png")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu0e1b9e4a4be4d6f86c7b37b9ccce3fbc_73886_800x0_resize_linear_3.png")
|
||||
c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu8582372628235034388.png")
|
||||
c.Assert(resized.Width(), qt.Equals, 800)
|
||||
}
|
||||
|
||||
@@ -443,6 +447,7 @@ func TestImageExif(t *testing.T) {
|
||||
c.Assert(lensModel, qt.Equals, "smc PENTAX-DA* 16-50mm F2.8 ED AL [IF] SDM")
|
||||
resized, _ := image.Resize("300x200")
|
||||
x2 := resized.Exif()
|
||||
|
||||
c.Assert(x2, eq, x)
|
||||
}
|
||||
|
||||
@@ -809,10 +814,10 @@ func assetGoldenDirs(c *qt.C, dir1, dir2 string) {
|
||||
|
||||
if !goldenEqual(nrgba1, nrgba2) {
|
||||
switch fi1.Name() {
|
||||
case "gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_73c19c5f80881858a85aa23cd0ca400d.png",
|
||||
"gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_ae631e5252bb5d7b92bc766ad1a89069.png",
|
||||
"gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_d1bbfa2629bffb90118cacce3fcfb924.png",
|
||||
"giphy_hu3eafc418e52414ace6236bf1d31f82e1_52213_200x0_resize_box_1.gif":
|
||||
case "giphy_hu13007323561585908901.gif",
|
||||
"gohugoio8_hu12690451569630232821.png",
|
||||
"gohugoio8_hu1619987041333606118.png",
|
||||
"gohugoio8_hu18164141965527013334.png":
|
||||
c.Log("expectedly differs from golden due to dithering:", fi1.Name())
|
||||
default:
|
||||
c.Errorf("resulting image differs from golden: %s", fi1.Name())
|
||||
@@ -829,9 +834,9 @@ func assetGoldenDirs(c *qt.C, dir1, dir2 string) {
|
||||
_, err = f2.Seek(0, 0)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
hash1, err := helpers.MD5FromReader(f1)
|
||||
hash1, _, err := hashing.XXHashFromReader(f1)
|
||||
c.Assert(err, qt.IsNil)
|
||||
hash2, err := helpers.MD5FromReader(f2)
|
||||
hash2, _, err := hashing.XXHashFromReader(f2)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
c.Assert(hash1, qt.Equals, hash2)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/disintegration/gift"
|
||||
"github.com/gohugoio/hugo/resources/images/exif"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
var _ gift.Filter = (*autoOrientFilter)(nil)
|
||||
@@ -49,7 +50,8 @@ func (f autoOrientFilter) Bounds(srcBounds image.Rectangle) image.Rectangle {
|
||||
|
||||
func (f autoOrientFilter) AutoOrient(exifInfo *exif.ExifInfo) gift.Filter {
|
||||
if exifInfo != nil {
|
||||
if orientation, ok := exifInfo.Tags["Orientation"].(int); ok {
|
||||
if v, ok := exifInfo.Tags["Orientation"]; ok {
|
||||
orientation := cast.ToInt(v)
|
||||
if filter, ok := transformationFilters[orientation]; ok {
|
||||
return filter
|
||||
}
|
||||
|
||||
@@ -14,25 +14,18 @@
|
||||
package exif
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/bep/imagemeta"
|
||||
"github.com/bep/logg"
|
||||
"github.com/bep/tmc"
|
||||
|
||||
_exif "github.com/rwcarlsen/goexif/exif"
|
||||
"github.com/rwcarlsen/goexif/tiff"
|
||||
)
|
||||
|
||||
const exifTimeLayout = "2006:01:02 15:04:05"
|
||||
|
||||
// ExifInfo holds the decoded Exif data for an Image.
|
||||
type ExifInfo struct {
|
||||
// GPS latitude in degrees.
|
||||
@@ -53,6 +46,15 @@ type Decoder struct {
|
||||
excludeFieldsrRe *regexp.Regexp
|
||||
noDate bool
|
||||
noLatLong bool
|
||||
warnl logg.LevelLogger
|
||||
}
|
||||
|
||||
func (d *Decoder) shouldInclude(s string) bool {
|
||||
return (d.includeFieldsRe == nil || d.includeFieldsRe.MatchString(s))
|
||||
}
|
||||
|
||||
func (d *Decoder) shouldExclude(s string) bool {
|
||||
return d.excludeFieldsrRe != nil && d.excludeFieldsrRe.MatchString(s)
|
||||
}
|
||||
|
||||
func IncludeFields(expression string) func(*Decoder) error {
|
||||
@@ -91,6 +93,13 @@ func WithDateDisabled(disabled bool) func(*Decoder) error {
|
||||
}
|
||||
}
|
||||
|
||||
func WithWarnLogger(warnl logg.LevelLogger) func(*Decoder) error {
|
||||
return func(d *Decoder) error {
|
||||
d.warnl = warnl
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func compileRegexp(expression string) (*regexp.Regexp, error) {
|
||||
expression = strings.TrimSpace(expression)
|
||||
if expression == "" {
|
||||
@@ -115,148 +124,222 @@ func NewDecoder(options ...func(*Decoder) error) (*Decoder, error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *Decoder) Decode(r io.Reader) (ex *ExifInfo, err error) {
|
||||
var (
|
||||
isTimeTag = func(s string) bool {
|
||||
return strings.Contains(s, "Time")
|
||||
}
|
||||
isGPSTag = func(s string) bool {
|
||||
return strings.HasPrefix(s, "GPS")
|
||||
}
|
||||
)
|
||||
|
||||
// Filename is only used for logging.
|
||||
func (d *Decoder) Decode(filename string, format imagemeta.ImageFormat, r io.Reader) (ex *ExifInfo, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("exif failed: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
var x *_exif.Exif
|
||||
x, err = _exif.Decode(r)
|
||||
if err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
// Found no Exif
|
||||
return nil, nil
|
||||
}
|
||||
return
|
||||
var tagInfos imagemeta.Tags
|
||||
handleTag := func(ti imagemeta.TagInfo) error {
|
||||
tagInfos.Add(ti)
|
||||
return nil
|
||||
}
|
||||
|
||||
shouldInclude := func(ti imagemeta.TagInfo) bool {
|
||||
if ti.Source == imagemeta.EXIF {
|
||||
if !d.noDate {
|
||||
// We need the time tags to calculate the date.
|
||||
if isTimeTag(ti.Tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !d.noLatLong {
|
||||
// We need to GPS tags to calculate the lat/long.
|
||||
if isGPSTag(ti.Tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(ti.Namespace, "IFD0") {
|
||||
// Drop thumbnail tags.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if d.shouldExclude(ti.Tag) {
|
||||
return false
|
||||
}
|
||||
|
||||
return d.shouldInclude(ti.Tag)
|
||||
}
|
||||
|
||||
var warnf func(string, ...any)
|
||||
if d.warnl != nil {
|
||||
// There should be very little warnings (fingers crossed!),
|
||||
// but this will typically be unrecognized formats.
|
||||
// To be able to possibly get rid of these warnings,
|
||||
// we need to know what images are causing them.
|
||||
warnf = func(format string, args ...any) {
|
||||
format = fmt.Sprintf("%q: %s: ", filename, format)
|
||||
d.warnl.Logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
err = imagemeta.Decode(
|
||||
imagemeta.Options{
|
||||
R: r.(io.ReadSeeker),
|
||||
ImageFormat: format,
|
||||
ShouldHandleTag: shouldInclude,
|
||||
HandleTag: handleTag,
|
||||
Sources: imagemeta.EXIF, // For now. TODO(bep)
|
||||
Warnf: warnf,
|
||||
},
|
||||
)
|
||||
|
||||
var tm time.Time
|
||||
var lat, long float64
|
||||
|
||||
if !d.noDate {
|
||||
tm, _ = x.DateTime()
|
||||
tm, _ = tagInfos.GetDateTime()
|
||||
}
|
||||
|
||||
if !d.noLatLong {
|
||||
lat, long, _ = x.LatLong()
|
||||
if math.IsNaN(lat) {
|
||||
lat = 0
|
||||
}
|
||||
if math.IsNaN(long) {
|
||||
long = 0
|
||||
}
|
||||
lat, long, _ = tagInfos.GetLatLong()
|
||||
}
|
||||
|
||||
walker := &exifWalker{x: x, vals: make(map[string]any), includeMatcher: d.includeFieldsRe, excludeMatcher: d.excludeFieldsrRe}
|
||||
if err = x.Walk(walker); err != nil {
|
||||
return
|
||||
tags := make(map[string]any)
|
||||
for k, v := range tagInfos.All() {
|
||||
if d.shouldExclude(k) {
|
||||
continue
|
||||
}
|
||||
if !d.shouldInclude(k) {
|
||||
continue
|
||||
}
|
||||
tags[k] = v.Value
|
||||
}
|
||||
|
||||
ex = &ExifInfo{Lat: lat, Long: long, Date: tm, Tags: walker.vals}
|
||||
ex = &ExifInfo{Lat: lat, Long: long, Date: tm, Tags: tags}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func decodeTag(x *_exif.Exif, f _exif.FieldName, t *tiff.Tag) (any, error) {
|
||||
switch t.Format() {
|
||||
case tiff.StringVal, tiff.UndefVal:
|
||||
s := nullString(t.Val)
|
||||
if strings.Contains(string(f), "DateTime") {
|
||||
if d, err := tryParseDate(x, s); err == nil {
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
case tiff.OtherVal:
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
var rv []any
|
||||
|
||||
for i := 0; i < int(t.Count); i++ {
|
||||
switch t.Format() {
|
||||
case tiff.RatVal:
|
||||
n, d, _ := t.Rat2(i)
|
||||
rat := big.NewRat(n, d)
|
||||
// if t is int or t > 1, use float64
|
||||
if rat.IsInt() || rat.Cmp(big.NewRat(1, 1)) == 1 {
|
||||
f, _ := rat.Float64()
|
||||
rv = append(rv, f)
|
||||
} else {
|
||||
rv = append(rv, rat)
|
||||
}
|
||||
|
||||
case tiff.FloatVal:
|
||||
v, _ := t.Float(i)
|
||||
rv = append(rv, v)
|
||||
case tiff.IntVal:
|
||||
v, _ := t.Int(i)
|
||||
rv = append(rv, v)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Count == 1 {
|
||||
if len(rv) == 1 {
|
||||
return rv[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// Code borrowed from exif.DateTime and adjusted.
|
||||
func tryParseDate(x *_exif.Exif, s string) (time.Time, error) {
|
||||
dateStr := strings.TrimRight(s, "\x00")
|
||||
// TODO(bep): look for timezone offset, GPS time, etc.
|
||||
timeZone := time.Local
|
||||
if tz, _ := x.TimeZone(); tz != nil {
|
||||
timeZone = tz
|
||||
}
|
||||
return time.ParseInLocation(exifTimeLayout, dateStr, timeZone)
|
||||
}
|
||||
|
||||
type exifWalker struct {
|
||||
x *_exif.Exif
|
||||
vals map[string]any
|
||||
includeMatcher *regexp.Regexp
|
||||
excludeMatcher *regexp.Regexp
|
||||
}
|
||||
|
||||
func (e *exifWalker) Walk(f _exif.FieldName, tag *tiff.Tag) error {
|
||||
name := string(f)
|
||||
if e.excludeMatcher != nil && e.excludeMatcher.MatchString(name) {
|
||||
return nil
|
||||
}
|
||||
if e.includeMatcher != nil && !e.includeMatcher.MatchString(name) {
|
||||
return nil
|
||||
}
|
||||
val, err := decodeTag(e.x, f, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.vals[name] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func nullString(in []byte) string {
|
||||
var rv bytes.Buffer
|
||||
for len(in) > 0 {
|
||||
r, size := utf8.DecodeRune(in)
|
||||
if unicode.IsGraphic(r) {
|
||||
rv.WriteRune(r)
|
||||
}
|
||||
in = in[size:]
|
||||
}
|
||||
return rv.String()
|
||||
}
|
||||
|
||||
var tcodec *tmc.Codec
|
||||
|
||||
func init() {
|
||||
newIntadapter := func(target any) tmc.Adapter {
|
||||
var bitSize int
|
||||
var isSigned bool
|
||||
|
||||
switch target.(type) {
|
||||
case int:
|
||||
bitSize = 0
|
||||
isSigned = true
|
||||
case int8:
|
||||
bitSize = 8
|
||||
isSigned = true
|
||||
case int16:
|
||||
bitSize = 16
|
||||
isSigned = true
|
||||
case int32:
|
||||
bitSize = 32
|
||||
isSigned = true
|
||||
case int64:
|
||||
bitSize = 64
|
||||
isSigned = true
|
||||
case uint:
|
||||
bitSize = 0
|
||||
case uint8:
|
||||
bitSize = 8
|
||||
case uint16:
|
||||
bitSize = 16
|
||||
case uint32:
|
||||
bitSize = 32
|
||||
case uint64:
|
||||
bitSize = 64
|
||||
}
|
||||
|
||||
intFromString := func(s string) (any, error) {
|
||||
if bitSize == 0 {
|
||||
return strconv.Atoi(s)
|
||||
}
|
||||
|
||||
var v any
|
||||
var err error
|
||||
|
||||
if isSigned {
|
||||
v, err = strconv.ParseInt(s, 10, bitSize)
|
||||
} else {
|
||||
v, err = strconv.ParseUint(s, 10, bitSize)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if isSigned {
|
||||
i := v.(int64)
|
||||
switch target.(type) {
|
||||
case int:
|
||||
return int(i), nil
|
||||
case int8:
|
||||
return int8(i), nil
|
||||
case int16:
|
||||
return int16(i), nil
|
||||
case int32:
|
||||
return int32(i), nil
|
||||
case int64:
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
|
||||
i := v.(uint64)
|
||||
switch target.(type) {
|
||||
case uint:
|
||||
return uint(i), nil
|
||||
case uint8:
|
||||
return uint8(i), nil
|
||||
case uint16:
|
||||
return uint16(i), nil
|
||||
case uint32:
|
||||
return uint32(i), nil
|
||||
case uint64:
|
||||
return i, nil
|
||||
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unsupported target type %T", target)
|
||||
}
|
||||
|
||||
intToString := func(v any) (string, error) {
|
||||
return fmt.Sprintf("%d", v), nil
|
||||
}
|
||||
|
||||
return tmc.NewAdapter(target, intFromString, intToString)
|
||||
}
|
||||
|
||||
ru, _ := imagemeta.NewRat[uint32](1, 2)
|
||||
ri, _ := imagemeta.NewRat[int32](1, 2)
|
||||
tmcAdapters := []tmc.Adapter{
|
||||
tmc.NewAdapter(ru, nil, nil),
|
||||
tmc.NewAdapter(ri, nil, nil),
|
||||
newIntadapter(int(1)),
|
||||
newIntadapter(int8(1)),
|
||||
newIntadapter(int16(1)),
|
||||
newIntadapter(int32(1)),
|
||||
newIntadapter(int64(1)),
|
||||
newIntadapter(uint(1)),
|
||||
newIntadapter(uint8(1)),
|
||||
newIntadapter(uint16(1)),
|
||||
newIntadapter(uint32(1)),
|
||||
newIntadapter(uint64(1)),
|
||||
}
|
||||
|
||||
tmcAdapters = append(tmc.DefaultTypeAdapters, tmcAdapters...)
|
||||
|
||||
var err error
|
||||
tcodec, err = tmc.New()
|
||||
tcodec, err = tmc.New(tmc.WithTypeAdapters(tmcAdapters))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -15,13 +15,12 @@ package exif
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/bep/imagemeta"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -35,11 +34,12 @@ func TestExif(t *testing.T) {
|
||||
|
||||
d, err := NewDecoder(IncludeFields("Lens|Date"))
|
||||
c.Assert(err, qt.IsNil)
|
||||
x, err := d.Decode(f)
|
||||
x, err := d.Decode("", imagemeta.JPEG, f)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(x.Date.Format("2006-01-02"), qt.Equals, "2017-10-27")
|
||||
|
||||
// Malaga: https://goo.gl/taazZy
|
||||
|
||||
c.Assert(x.Lat, qt.Equals, float64(36.59744166666667))
|
||||
c.Assert(x.Long, qt.Equals, float64(-4.50846))
|
||||
|
||||
@@ -49,9 +49,9 @@ func TestExif(t *testing.T) {
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
c.Assert(lensModel, qt.Equals, "smc PENTAX-DA* 16-50mm F2.8 ED AL [IF] SDM")
|
||||
|
||||
v, found = x.Tags["DateTime"]
|
||||
v, found = x.Tags["ModifyDate"]
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(v, hqt.IsSameType, time.Time{})
|
||||
c.Assert(v, qt.Equals, "2017:11:23 09:56:54")
|
||||
|
||||
// Verify that it survives a round-trip to JSON and back.
|
||||
data, err := json.Marshal(x)
|
||||
@@ -72,8 +72,8 @@ func TestExifPNG(t *testing.T) {
|
||||
|
||||
d, err := NewDecoder()
|
||||
c.Assert(err, qt.IsNil)
|
||||
_, err = d.Decode(f)
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
_, err = d.Decode("", imagemeta.PNG, f)
|
||||
c.Assert(err, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestIssue8079(t *testing.T) {
|
||||
@@ -85,28 +85,11 @@ func TestIssue8079(t *testing.T) {
|
||||
|
||||
d, err := NewDecoder()
|
||||
c.Assert(err, qt.IsNil)
|
||||
x, err := d.Decode(f)
|
||||
x, err := d.Decode("", imagemeta.JPEG, f)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(x.Tags["ImageDescription"], qt.Equals, "Città del Vaticano #nanoblock #vatican #vaticancity")
|
||||
}
|
||||
|
||||
func TestNullString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
expect string
|
||||
}{
|
||||
{"foo", "foo"},
|
||||
{"\x20", "\x20"},
|
||||
{"\xc4\x81", "\xc4\x81"}, // \u0101
|
||||
{"\u0160", "\u0160"}, // non-breaking space
|
||||
} {
|
||||
res := nullString([]byte(test.in))
|
||||
c.Assert(res, qt.Equals, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeExif(b *testing.B) {
|
||||
c := qt.New(b)
|
||||
f, err := os.Open(filepath.FromSlash("../../testdata/sunset.jpg"))
|
||||
@@ -118,7 +101,7 @@ func BenchmarkDecodeExif(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = d.Decode(f)
|
||||
_, err = d.Decode("", imagemeta.JPEG, f)
|
||||
c.Assert(err, qt.IsNil)
|
||||
f.Seek(0, 0)
|
||||
}
|
||||
@@ -126,8 +109,13 @@ func BenchmarkDecodeExif(b *testing.B) {
|
||||
|
||||
var eq = qt.CmpEquals(
|
||||
cmp.Comparer(
|
||||
func(v1, v2 *big.Rat) bool {
|
||||
return v1.RatString() == v2.RatString()
|
||||
func(v1, v2 imagemeta.Rat[uint32]) bool {
|
||||
return v1.String() == v2.String()
|
||||
},
|
||||
),
|
||||
cmp.Comparer(
|
||||
func(v1, v2 imagemeta.Rat[int32]) bool {
|
||||
return v1.String() == v2.String()
|
||||
},
|
||||
),
|
||||
cmp.Comparer(func(v1, v2 time.Time) bool {
|
||||
@@ -138,14 +126,15 @@ var eq = qt.CmpEquals(
|
||||
func TestIssue10738(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
testFunc := func(path, include string) any {
|
||||
testFunc := func(c *qt.C, path, include string) any {
|
||||
c.Helper()
|
||||
f, err := os.Open(filepath.FromSlash(path))
|
||||
c.Assert(err, qt.IsNil)
|
||||
defer f.Close()
|
||||
|
||||
d, err := NewDecoder(IncludeFields(include))
|
||||
c.Assert(err, qt.IsNil)
|
||||
x, err := d.Decode(f)
|
||||
x, err := d.Decode("", imagemeta.JPEG, f)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
// Verify that it survives a round-trip to JSON and back.
|
||||
@@ -194,7 +183,7 @@ func TestIssue10738(t *testing.T) {
|
||||
include: "Lens|Date|ExposureTime",
|
||||
}, want{
|
||||
10,
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -221,7 +210,7 @@ func TestIssue10738(t *testing.T) {
|
||||
include: "Lens|Date|ExposureTime",
|
||||
}, want{
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -266,7 +255,7 @@ func TestIssue10738(t *testing.T) {
|
||||
include: "Lens|Date|ExposureTime",
|
||||
}, want{
|
||||
30,
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -293,19 +282,21 @@ func TestIssue10738(t *testing.T) {
|
||||
include: "Lens|Date|ExposureTime",
|
||||
}, want{
|
||||
4,
|
||||
0,
|
||||
1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
c.Run(tt.name, func(c *qt.C) {
|
||||
got := testFunc(tt.args.path, tt.args.include)
|
||||
got := testFunc(c, tt.args.path, tt.args.include)
|
||||
switch v := got.(type) {
|
||||
case float64:
|
||||
c.Assert(v, qt.Equals, float64(tt.want.vN))
|
||||
case *big.Rat:
|
||||
c.Assert(v, eq, big.NewRat(tt.want.vN, tt.want.vD))
|
||||
case imagemeta.Rat[uint32]:
|
||||
r, err := imagemeta.NewRat[uint32](uint32(tt.want.vN), uint32(tt.want.vD))
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v, eq, r)
|
||||
default:
|
||||
c.Fatalf("unexpected type: %T", got)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
)
|
||||
|
||||
func TestFilterHash(t *testing.T) {
|
||||
@@ -25,8 +25,8 @@ func TestFilterHash(t *testing.T) {
|
||||
|
||||
f := &Filters{}
|
||||
|
||||
c.Assert(identity.HashString(f.Grayscale()), qt.Equals, identity.HashString(f.Grayscale()))
|
||||
c.Assert(identity.HashString(f.Grayscale()), qt.Not(qt.Equals), identity.HashString(f.Invert()))
|
||||
c.Assert(identity.HashString(f.Gamma(32)), qt.Not(qt.Equals), identity.HashString(f.Gamma(33)))
|
||||
c.Assert(identity.HashString(f.Gamma(32)), qt.Equals, identity.HashString(f.Gamma(32)))
|
||||
c.Assert(hashing.HashString(f.Grayscale()), qt.Equals, hashing.HashString(f.Grayscale()))
|
||||
c.Assert(hashing.HashString(f.Grayscale()), qt.Not(qt.Equals), hashing.HashString(f.Invert()))
|
||||
c.Assert(hashing.HashString(f.Gamma(32)), qt.Not(qt.Equals), hashing.HashString(f.Gamma(33)))
|
||||
c.Assert(hashing.HashString(f.Gamma(32)), qt.Equals, hashing.HashString(f.Gamma(32)))
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/bep/gowebp/libwebp/webpoptions"
|
||||
"github.com/bep/imagemeta"
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/resources/images/webp"
|
||||
|
||||
@@ -174,13 +176,14 @@ func (i *Image) initConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewImageProcessor(cfg *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) (*ImageProcessor, error) {
|
||||
func NewImageProcessor(warnl logg.LevelLogger, cfg *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) (*ImageProcessor, error) {
|
||||
e := cfg.Config.Imaging.Exif
|
||||
exifDecoder, err := exif.NewDecoder(
|
||||
exif.WithDateDisabled(e.DisableDate),
|
||||
exif.WithLatLongDisabled(e.DisableLatLong),
|
||||
exif.ExcludeFields(e.ExcludeFields),
|
||||
exif.IncludeFields(e.IncludeFields),
|
||||
exif.WithWarnLogger(warnl),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -197,8 +200,9 @@ type ImageProcessor struct {
|
||||
exifDecoder *exif.Decoder
|
||||
}
|
||||
|
||||
func (p *ImageProcessor) DecodeExif(r io.Reader) (*exif.ExifInfo, error) {
|
||||
return p.exifDecoder.Decode(r)
|
||||
// Filename is only used for logging.
|
||||
func (p *ImageProcessor) DecodeExif(filename string, format imagemeta.ImageFormat, r io.Reader) (*exif.ExifInfo, error) {
|
||||
return p.exifDecoder.Decode(filename, format, r)
|
||||
}
|
||||
|
||||
func (p *ImageProcessor) FiltersFromConfig(src image.Image, conf ImageConfig) ([]gift.Filter, error) {
|
||||
@@ -353,6 +357,21 @@ const (
|
||||
WEBP
|
||||
)
|
||||
|
||||
func (f Format) ToImageMetaImageFormatFormat() imagemeta.ImageFormat {
|
||||
switch f {
|
||||
case JPEG:
|
||||
return imagemeta.JPEG
|
||||
case PNG:
|
||||
return imagemeta.PNG
|
||||
case TIFF:
|
||||
return imagemeta.TIFF
|
||||
case WEBP:
|
||||
return imagemeta.WebP
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// RequiresDefaultQuality returns if the default quality needs to be applied to
|
||||
// images of this format.
|
||||
func (f Format) RequiresDefaultQuality() bool {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package images_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestAutoOrient(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- assets/rotate270.jpg --
|
||||
sourcefilename: ../testdata/exif/orientation6.jpg
|
||||
-- layouts/index.html --
|
||||
{{ $img := resources.Get "rotate270.jpg" }}
|
||||
W/H original: {{ $img.Width }}/{{ $img.Height }}
|
||||
{{ $rotated := $img.Filter images.AutoOrient }}
|
||||
W/H rotated: {{ $rotated.Width }}/{{ $rotated.Height }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "W/H original: 80/40\n\nW/H rotated: 40/80")
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
package internal
|
||||
|
||||
import "github.com/gohugoio/hugo/identity"
|
||||
import "github.com/gohugoio/hugo/common/hashing"
|
||||
|
||||
// ResourceTransformationKey are provided by the different transformation implementations.
|
||||
// It identifies the transformation (name) and its configuration (elements).
|
||||
@@ -38,5 +38,5 @@ func (k ResourceTransformationKey) Value() string {
|
||||
return k.Name
|
||||
}
|
||||
|
||||
return k.Name + "_" + identity.HashString(k.elements...)
|
||||
return k.Name + "_" + hashing.HashString(k.elements...)
|
||||
}
|
||||
|
||||
@@ -32,5 +32,5 @@ func TestResourceTransformationKey(t *testing.T) {
|
||||
key := NewResourceTransformationKey("testing",
|
||||
testStruct{Name: "test", V1: int64(10), V2: int32(20), V3: 30, V4: uint64(40)})
|
||||
c := qt.New(t)
|
||||
c.Assert(key.Value(), qt.Equals, "testing_518996646957295636")
|
||||
c.Assert(key.Value(), qt.Equals, "testing_4231238781487357822")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ package page
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -55,12 +55,23 @@ func preparePageGroupTestPages(t *testing.T) Pages {
|
||||
p.params["custom_param"] = src.param
|
||||
p.params["custom_date"] = cast.ToTime(src.date)
|
||||
p.params["custom_string_date"] = src.date
|
||||
p.params["custom_object"] = map[string]any{
|
||||
"param": src.param,
|
||||
"date": cast.ToTime(src.date),
|
||||
"string_date": src.date,
|
||||
}
|
||||
pages = append(pages, p)
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
var comparePageGroup = qt.CmpEquals(cmp.Comparer(func(a, b Page) bool {
|
||||
return a == b
|
||||
}))
|
||||
|
||||
func TestGroupByWithFieldNameArg(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -70,15 +81,13 @@ func TestGroupByWithFieldNameArg(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupBy(context.Background(), "Weight")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByWithMethodNameArg(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -87,15 +96,13 @@ func TestGroupByWithMethodNameArg(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupBy(context.Background(), "Type")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByWithSectionArg(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -104,15 +111,13 @@ func TestGroupByWithSectionArg(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupBy(context.Background(), "Section")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be\n%#v, got\n%#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -122,47 +127,39 @@ func TestGroupByInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupBy(context.Background(), "Weight", "desc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByCalledWithEmptyPages(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
var pages Pages
|
||||
groups, err := pages.GroupBy(context.Background(), "Weight")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if groups != nil {
|
||||
t.Errorf("PagesGroup isn't empty. It should be %#v, got %#v", nil, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
|
||||
groups1, err := pages.GroupBy(context.Background(), "Weight", "desc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
groups2, err := pages.GroupBy(context.Background(), "Weight")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
groups2 = groups2.Reverse()
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
if !reflect.DeepEqual(groups2, groups1) {
|
||||
t.Errorf("PagesGroup is sorted in unexpected order. It should be %#v, got %#v", groups2, groups1)
|
||||
}
|
||||
groups2 = groups2.Reverse()
|
||||
c.Assert(groups2, comparePageGroup, groups1)
|
||||
}
|
||||
|
||||
func TestGroupByParam(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -172,15 +169,13 @@ func TestGroupByParam(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParam("custom_param")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -190,12 +185,8 @@ func TestGroupByParamInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParam("custom_param", "desc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamCalledWithCapitalLetterString(t *testing.T) {
|
||||
@@ -208,10 +199,12 @@ func TestGroupByParamCalledWithCapitalLetterString(t *testing.T) {
|
||||
groups, err := pages.GroupByParam("custom_param")
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups[0].Key, qt.Equals, testStr)
|
||||
c.Assert(groups[0].Key, qt.DeepEquals, testStr)
|
||||
}
|
||||
|
||||
func TestGroupByParamCalledWithSomeUnavailableParams(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
delete(pages[1].Params(), "custom_param")
|
||||
@@ -223,36 +216,49 @@ func TestGroupByParamCalledWithSomeUnavailableParams(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParam("custom_param")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamCalledWithEmptyPages(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
var pages Pages
|
||||
groups, err := pages.GroupByParam("custom_param")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if groups != nil {
|
||||
t.Errorf("PagesGroup isn't empty. It should be %#v, got %#v", nil, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestGroupByParamCalledWithUnavailableParam(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
_, err := pages.GroupByParam("unavailable_param")
|
||||
if err != nil {
|
||||
t.Errorf("GroupByParam returned an error when it shouldn't")
|
||||
c.Assert(err, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestGroupByParamNested(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
|
||||
expect := PagesGroup{
|
||||
{Key: "bar", Pages: Pages{pages[1], pages[3]}},
|
||||
{Key: "baz", Pages: Pages{pages[4]}},
|
||||
{Key: "foo", Pages: Pages{pages[0], pages[2]}},
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParam("custom_object.param")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByDate(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -262,15 +268,13 @@ func TestGroupByDate(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByDate("2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByDateInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -280,15 +284,13 @@ func TestGroupByDateInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByDate("2006-01", "asc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByPublishDate(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -298,15 +300,13 @@ func TestGroupByPublishDate(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByPublishDate("2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByPublishDateInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -316,27 +316,23 @@ func TestGroupByPublishDateInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByDate("2006-01", "asc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByPublishDateWithEmptyPages(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
var pages Pages
|
||||
groups, err := pages.GroupByPublishDate("2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if groups != nil {
|
||||
t.Errorf("PagesGroup isn't empty. It should be %#v, got %#v", nil, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestGroupByExpiryDate(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -346,15 +342,13 @@ func TestGroupByExpiryDate(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByExpiryDate("2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamDate(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -364,16 +358,30 @@ func TestGroupByParamDate(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParamDate("custom_date", "2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamDateNested(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
|
||||
{Key: "2012-03", Pages: Pages{pages[3]}},
|
||||
{Key: "2012-01", Pages: Pages{pages[1]}},
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParamDate("custom_object.date", "2006-01")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
// https://github.com/gohugoio/hugo/issues/3983
|
||||
func TestGroupByParamDateWithStringParams(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -383,15 +391,29 @@ func TestGroupByParamDateWithStringParams(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParamDate("custom_string_date", "2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamDateNestedWithStringParams(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
{Key: "2012-04", Pages: Pages{pages[4], pages[2], pages[0]}},
|
||||
{Key: "2012-03", Pages: Pages{pages[3]}},
|
||||
{Key: "2012-01", Pages: Pages{pages[1]}},
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParamDate("custom_object.string_date", "2006-01")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByLastmod(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -401,15 +423,13 @@ func TestGroupByLastmod(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByLastmod("2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByLastmodInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -419,15 +439,13 @@ func TestGroupByLastmodInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByLastmod("2006-01", "asc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be\n%#v, got\n%#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamDateInReverseOrder(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
pages := preparePageGroupTestPages(t)
|
||||
expect := PagesGroup{
|
||||
@@ -437,22 +455,16 @@ func TestGroupByParamDateInReverseOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
groups, err := pages.GroupByParamDate("custom_date", "2006-01", "asc")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(groups, expect) {
|
||||
t.Errorf("PagesGroup has unexpected groups. It should be %#v, got %#v", expect, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, comparePageGroup, expect)
|
||||
}
|
||||
|
||||
func TestGroupByParamDateWithEmptyPages(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
t.Parallel()
|
||||
var pages Pages
|
||||
groups, err := pages.GroupByParamDate("custom_date", "2006-01")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make PagesGroup array: %s", err)
|
||||
}
|
||||
if groups != nil {
|
||||
t.Errorf("PagesGroup isn't empty. It should be %#v, got %#v", nil, groups)
|
||||
}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(groups, qt.IsNil)
|
||||
}
|
||||
|
||||
@@ -57,13 +57,17 @@ func (d Dates) IsDateOrLastModAfter(in Dates) bool {
|
||||
return d.Date.After(in.Date) || d.Lastmod.After(in.Lastmod)
|
||||
}
|
||||
|
||||
func (d *Dates) UpdateDateAndLastmodIfAfter(in Dates) {
|
||||
func (d *Dates) UpdateDateAndLastmodAndPublishDateIfAfter(in Dates) {
|
||||
if in.Date.After(d.Date) {
|
||||
d.Date = in.Date
|
||||
}
|
||||
if in.Lastmod.After(d.Lastmod) {
|
||||
d.Lastmod = in.Lastmod
|
||||
}
|
||||
|
||||
if in.PublishDate.After(d.PublishDate) && in.PublishDate.Before(htime.Now()) {
|
||||
d.PublishDate = in.PublishDate
|
||||
}
|
||||
}
|
||||
|
||||
func (d Dates) IsAllDatesZero() bool {
|
||||
|
||||
@@ -40,6 +40,8 @@ type PermalinkExpander struct {
|
||||
expanders map[string]map[string]func(Page) (string, error)
|
||||
|
||||
urlize func(uri string) string
|
||||
|
||||
patternCache *maps.Cache[string, func(Page) (string, error)]
|
||||
}
|
||||
|
||||
// Time for checking date formats. Every field is different than the
|
||||
@@ -71,7 +73,10 @@ func (p PermalinkExpander) callback(attr string) (pageToPermaAttribute, bool) {
|
||||
// NewPermalinkExpander creates a new PermalinkExpander configured by the given
|
||||
// urlize func.
|
||||
func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]map[string]string) (PermalinkExpander, error) {
|
||||
p := PermalinkExpander{urlize: urlize}
|
||||
p := PermalinkExpander{
|
||||
urlize: urlize,
|
||||
patternCache: maps.NewCache[string, func(Page) (string, error)](),
|
||||
}
|
||||
|
||||
p.knownPermalinkAttributes = map[string]pageToPermaAttribute{
|
||||
"year": p.pageToPermalinkDate,
|
||||
@@ -102,6 +107,16 @@ func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]ma
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ExpandPattern expands the path in p with the specified expand pattern.
|
||||
func (l PermalinkExpander) ExpandPattern(pattern string, p Page) (string, error) {
|
||||
expander, err := l.getOrParsePattern(pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return expander(p)
|
||||
}
|
||||
|
||||
// Expand expands the path in p according to the rules defined for the given key.
|
||||
// If no rules are found for the given key, an empty string is returned.
|
||||
func (l PermalinkExpander) Expand(key string, p Page) (string, error) {
|
||||
@@ -129,17 +144,11 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Page) (string, error), error) {
|
||||
expanders := make(map[string]func(Page) (string, error))
|
||||
|
||||
for k, pattern := range patterns {
|
||||
k = strings.Trim(k, sectionCutSet)
|
||||
|
||||
func (l PermalinkExpander) getOrParsePattern(pattern string) (func(Page) (string, error), error) {
|
||||
return l.patternCache.GetOrCreate(pattern, func() (func(Page) (string, error), error) {
|
||||
if !l.validate(pattern) {
|
||||
return nil, &permalinkExpandError{pattern: pattern, err: errPermalinkIllFormed}
|
||||
}
|
||||
|
||||
pattern := pattern
|
||||
matches := attributeRegexp.FindAllStringSubmatch(pattern, -1)
|
||||
|
||||
callbacks := make([]pageToPermaAttribute, len(matches))
|
||||
@@ -157,7 +166,7 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa
|
||||
callbacks[i] = callback
|
||||
}
|
||||
|
||||
expanders[k] = func(p Page) (string, error) {
|
||||
return func(p Page) (string, error) {
|
||||
if matches == nil {
|
||||
return pattern, nil
|
||||
}
|
||||
@@ -173,12 +182,25 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa
|
||||
}
|
||||
|
||||
newField = strings.Replace(newField, replacement, newAttr, 1)
|
||||
|
||||
}
|
||||
|
||||
return newField, nil
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Page) (string, error), error) {
|
||||
expanders := make(map[string]func(Page) (string, error))
|
||||
|
||||
for k, pattern := range patterns {
|
||||
k = strings.Trim(k, sectionCutSet)
|
||||
|
||||
expander, err := l.getOrParsePattern(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expanders[k] = expander
|
||||
}
|
||||
|
||||
return expanders, nil
|
||||
|
||||
@@ -193,3 +193,42 @@ List.
|
||||
b.AssertFileContent("public/libros/fiction/index.html", "List.")
|
||||
b.AssertFileContent("public/libros/fiction/2023/book1/index.html", "Single.")
|
||||
}
|
||||
|
||||
func TestPermalinksUrlCascade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- layouts/_default/list.html --
|
||||
List|{{ .Kind }}|{{ .RelPermalink }}|
|
||||
-- layouts/_default/single.html --
|
||||
Single|{{ .Kind }}|{{ .RelPermalink }}|
|
||||
-- hugo.toml --
|
||||
-- content/cooking/delicious-recipes/_index.md --
|
||||
---
|
||||
url: /delicious-recipe/
|
||||
cascade:
|
||||
url: /delicious-recipe/:slug/
|
||||
---
|
||||
-- content/cooking/delicious-recipes/example1.md --
|
||||
---
|
||||
title: Recipe 1
|
||||
---
|
||||
-- content/cooking/delicious-recipes/example2.md --
|
||||
---
|
||||
title: Recipe 2
|
||||
slug: custom-recipe-2
|
||||
---
|
||||
`
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
LogLevel: logg.LevelWarn,
|
||||
}).Build()
|
||||
|
||||
t.Log(b.LogString())
|
||||
b.Assert(b.H.Log.LoggCount(logg.LevelWarn), qt.Equals, 0)
|
||||
b.AssertFileContent("public/delicious-recipe/index.html", "List|section|/delicious-recipe/")
|
||||
b.AssertFileContent("public/delicious-recipe/recipe-1/index.html", "Single|page|/delicious-recipe/recipe-1/")
|
||||
b.AssertFileContent("public/delicious-recipe/custom-recipe-2/index.html", "Single|page|/delicious-recipe/custom-recipe-2/")
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
|
||||
@@ -307,7 +308,7 @@ type fileInfo interface {
|
||||
}
|
||||
|
||||
type hashProvider interface {
|
||||
hash() string
|
||||
hash() uint64
|
||||
}
|
||||
|
||||
var _ resource.StaleInfo = (*StaleValue[any])(nil)
|
||||
@@ -403,7 +404,7 @@ func (l *genericResource) size() int64 {
|
||||
return l.h.size
|
||||
}
|
||||
|
||||
func (l *genericResource) hash() string {
|
||||
func (l *genericResource) hash() uint64 {
|
||||
if err := l.h.init(l); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -628,7 +629,7 @@ type targetPather interface {
|
||||
}
|
||||
|
||||
type resourceHash struct {
|
||||
value string
|
||||
value uint64
|
||||
size int64
|
||||
initOnce sync.Once
|
||||
}
|
||||
@@ -636,7 +637,7 @@ type resourceHash struct {
|
||||
func (r *resourceHash) init(l hugio.ReadSeekCloserProvider) error {
|
||||
var initErr error
|
||||
r.initOnce.Do(func() {
|
||||
var hash string
|
||||
var hash uint64
|
||||
var size int64
|
||||
f, err := l.ReadSeekCloser()
|
||||
if err != nil {
|
||||
@@ -644,7 +645,7 @@ func (r *resourceHash) init(l hugio.ReadSeekCloserProvider) error {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
hash, size, err = helpers.MD5FromReaderFast(f)
|
||||
hash, size, err = hashImage(f)
|
||||
if err != nil {
|
||||
initErr = fmt.Errorf("failed to calculate hash: %w", err)
|
||||
return
|
||||
@@ -655,3 +656,7 @@ func (r *resourceHash) init(l hugio.ReadSeekCloserProvider) error {
|
||||
|
||||
return initErr
|
||||
}
|
||||
|
||||
func hashImage(r io.ReadSeeker) (uint64, int64, error) {
|
||||
return hashing.XXHashFromReader(r)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -36,9 +37,9 @@ func GetParamToLower(r Resource, key string) any {
|
||||
}
|
||||
|
||||
func getParam(r Resource, key string, stringToLower bool) any {
|
||||
v := r.Params()[strings.ToLower(key)]
|
||||
v, err := maps.GetNestedParam(key, ".", r.Params())
|
||||
|
||||
if v == nil {
|
||||
if v == nil || err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/httpcache"
|
||||
hhttpcache "github.com/gohugoio/hugo/cache/httpcache"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
|
||||
@@ -34,6 +33,7 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/cache/dynacache"
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/hcontext"
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
"github.com/gohugoio/hugo/common/tasks"
|
||||
@@ -226,7 +226,7 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
|
||||
// TODO(bep) see #10912; we currently emit a warning for this config scenario.
|
||||
func (c *Client) FromString(targetPath, content string) (resource.Resource, error) {
|
||||
targetPath = path.Clean(targetPath)
|
||||
key := dynacache.CleanKey(targetPath) + helpers.MD5String(content)
|
||||
key := dynacache.CleanKey(targetPath) + hashing.MD5FromStringHexEncoded(content)
|
||||
r, err := c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
|
||||
return c.rs.NewResource(
|
||||
resources.ResourceSourceDescriptor{
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
gmaps "maps"
|
||||
|
||||
"github.com/gohugoio/httpcache"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
@@ -310,10 +311,10 @@ func (c *Client) validateFromRemoteArgs(uri string, options fromRemoteOptions) e
|
||||
func remoteResourceKeys(uri string, optionsm map[string]any) (string, string) {
|
||||
var userKey string
|
||||
if key, k, found := maps.LookupEqualFold(optionsm, "key"); found {
|
||||
userKey = identity.HashString(key)
|
||||
userKey = hashing.HashString(key)
|
||||
delete(optionsm, k)
|
||||
}
|
||||
optionsKey := identity.HashString(uri, optionsm)
|
||||
optionsKey := hashing.HashString(uri, optionsm)
|
||||
if userKey == "" {
|
||||
userKey = optionsKey
|
||||
}
|
||||
|
||||
@@ -121,15 +121,16 @@ func TestRemoteResourceKeys(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
check := func(uri string, optionsm map[string]any, expect1, expect2 string) {
|
||||
c.Helper()
|
||||
got1, got2 := remoteResourceKeys(uri, optionsm)
|
||||
c.Assert(got1, qt.Equals, expect1)
|
||||
c.Assert(got2, qt.Equals, expect2)
|
||||
}
|
||||
|
||||
check("foo", nil, "5917621528921068675", "5917621528921068675")
|
||||
check("foo", map[string]any{"bar": "baz"}, "7294498335241413323", "7294498335241413323")
|
||||
check("foo", map[string]any{"key": "1234", "bar": "baz"}, "14904296279238663669", "7294498335241413323")
|
||||
check("foo", map[string]any{"key": "12345", "bar": "baz"}, "12191037851845371770", "7294498335241413323")
|
||||
check("asdf", map[string]any{"key": "1234", "bar": "asdf"}, "14904296279238663669", "3787889110563790121")
|
||||
check("asdf", map[string]any{"key": "12345", "bar": "asdf"}, "12191037851845371770", "3787889110563790121")
|
||||
check("foo", nil, "7763396052142361238", "7763396052142361238")
|
||||
check("foo", map[string]any{"bar": "baz"}, "5783339285578751849", "5783339285578751849")
|
||||
check("foo", map[string]any{"key": "1234", "bar": "baz"}, "15578353952571222948", "5783339285578751849")
|
||||
check("foo", map[string]any{"key": "12345", "bar": "baz"}, "14335752410685132726", "5783339285578751849")
|
||||
check("asdf", map[string]any{"key": "1234", "bar": "asdf"}, "15578353952571222948", "15615023578599429261")
|
||||
check("asdf", map[string]any{"key": "12345", "bar": "asdf"}, "14335752410685132726", "15615023578599429261")
|
||||
}
|
||||
|
||||
@@ -60,7 +60,9 @@ func NewSpec(
|
||||
conf := s.Cfg.GetConfig().(*allconfig.Config)
|
||||
imgConfig := conf.Imaging
|
||||
|
||||
imaging, err := images.NewImageProcessor(imgConfig)
|
||||
imagesWarnl := logger.WarnCommand("images")
|
||||
|
||||
imaging, err := images.NewImageProcessor(imagesWarnl, imgConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkHashImage(b *testing.B) {
|
||||
f, err := os.Open("testdata/sunset.jpg")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, err := hashImage(f)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
f.Seek(0, 0)
|
||||
}
|
||||
}
|
||||
@@ -99,11 +99,6 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
|
||||
|
||||
cmdArgs = append(cmdArgs, options.toArgs()...)
|
||||
|
||||
// TODO1
|
||||
// npm i tailwindcss @tailwindcss/cli
|
||||
// npm i tailwindcss@next @tailwindcss/cli@next
|
||||
// npx tailwindcss -h
|
||||
|
||||
var errBuf bytes.Buffer
|
||||
|
||||
stderr := io.MultiWriter(infow, &errBuf)
|
||||
@@ -134,7 +129,6 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
|
||||
t.rs.Assets.Fs, t.rs.Logger, ctx.DependencyManager,
|
||||
)
|
||||
|
||||
// TODO1 option {
|
||||
src, err = imp.resolve()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestOptionKey(t *testing.T) {
|
||||
|
||||
key := (&buildTransformation{optsm: opts}).Key()
|
||||
|
||||
c.Assert(key.Value(), qt.Equals, "jsbuild_7891849149754191852")
|
||||
c.Assert(key.Value(), qt.Equals, "jsbuild_1533819657654811600")
|
||||
}
|
||||
|
||||
func TestToBuildOptions(t *testing.T) {
|
||||
|
||||
@@ -27,6 +27,7 @@ func TestImageCache(t *testing.T) {
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
disableLiveReload = true
|
||||
baseURL = "https://example.org"
|
||||
-- content/mybundle/index.md --
|
||||
---
|
||||
@@ -61,9 +62,9 @@ anigif: {{ $anigif.RelPermalink }}|{{ $anigif.Width }}|{{ $anigif.Height }}|{{ $
|
||||
|
||||
assertImages := func() {
|
||||
b.AssertFileContent("public/index.html", `
|
||||
gif: /mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_1x2_resize_box_3.gif|}|1|2|image/gif|
|
||||
bmp: /mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_2x3_resize_box_3.bmp|}|2|3|image/bmp|
|
||||
anigif: /mybundle/giphy_hu3eafc418e52414ace6236bf1d31f82e1_52213_4x5_resize_box_1.gif|4|5|image/gif|
|
||||
gif: /mybundle/pixel_hu14657638653019978294.gif|}|1|2|image/gif|
|
||||
bmp: /mybundle/pixel_hu14705577916774115224.bmp|}|2|3|image/bmp|
|
||||
anigif: /mybundle/giphy_hu3665406585348417395.gif|4|5|image/gif|
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -159,9 +160,9 @@ resize 2|RelPermalink: {{ $image.RelPermalink }}|MediaType: {{ $image.MediaType
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"jpg|RelPermalink: /images/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_filter_17010532266664966692.jpg|MediaType: image/jpeg|Width: 1|Height: 1|",
|
||||
"resize 1|RelPermalink: /images/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_filter_6707036659822075562.jpg|MediaType: image/jpeg|Width: 20|Height: 30|",
|
||||
"resize 2|RelPermalink: /images/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_filter_6707036659822075562.jpg|MediaType: image/jpeg|Width: 20|Height: 30|",
|
||||
"jpg|RelPermalink: /images/pixel_hu13683954895608450100.jpg|MediaType: image/jpeg|Width: 1|Height: 1|",
|
||||
"resize 1|RelPermalink: /images/pixel_hu3453403302435331853.jpg|MediaType: image/jpeg|Width: 20|Height: 30|",
|
||||
"resize 2|RelPermalink: /images/pixel_hu3453403302435331853.jpg|MediaType: image/jpeg|Width: 20|Height: 30|",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 304 KiB After Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |