Compare commits

..

59 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen cff26309e5 github: Update image actions versions and some adjustments
Co-authored-by: morre <morre@mor.re>
2024-08-09 16:25:58 +02:00
Morre 6206b44ba6 github: Build and publish Docker image on release
Fixes #10760
2024-08-09 09:12:11 +02:00
Bjørn Erik Pedersen 665ac949bd markup: Add blockquote render hooks
Closes #12590
2024-08-07 18:28:23 +02:00
Bjørn Erik Pedersen 4c162deb03 markup/goldmark/codeblocks: Simplify codeblcok hook code 2024-08-07 18:28:23 +02:00
Bjørn Erik Pedersen c6227f1d85 Add render hooks for inline and block passthrough snippets
Fixes #11927
2024-08-07 18:28:23 +02:00
dependabot[bot] 1781b18427 build(deps): bump golang.org/x/net from 0.27.0 to 0.28.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/net/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 12:26:01 +02:00
dependabot[bot] d6d9dddff3 build(deps): bump golang.org/x/image from 0.18.0 to 0.19.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/image/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 11:14:31 +02:00
Bjørn Erik Pedersen 10891f305e tpl/cast: Improve float
Make it support the new upstream rational number type in Exif.

See #12718
2024-08-06 22:26:43 +02:00
Bjørn Erik Pedersen c07e94fa6d Skip TestEchoParam
Is deprecated, will be remvoved soon.
2024-08-06 19:13:35 +02:00
dependabot[bot] 8f8d12ab2f build(deps): bump github.com/bep/imagemeta from 0.7.5 to 0.7.6 (#12720)
Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.7.5 to 0.7.6.
- [Release notes](https://github.com/bep/imagemeta/releases)
- [Commits](https://github.com/bep/imagemeta/compare/v0.7.5...v0.7.6)

---
updated-dependencies:
- dependency-name: github.com/bep/imagemeta
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Closes #12718
2024-08-06 12:05:52 +02:00
Bjørn Erik Pedersen 69455fa422 Fix deprecation errors 2024-08-05 19:23:53 +02:00
hugoreleaser 2fcc53780f releaser: Prepare repository for 0.132.0-DEV
[ci skip]
2024-08-02 09:18:21 +00:00
hugoreleaser bfbee17932 releaser: Bump versions for release of 0.131.0
[ci skip]
2024-08-02 09:03:48 +00:00
Bjørn Erik Pedersen 944fcc216f Fix images.AutoOrient regression
Broken in v0.130.0. Added a integration test to avoid future breakage.

Fixes #12712
2024-08-02 10:36:06 +02:00
n1xx1 914fa13efd revamp pagegroup tests with quicktest 2024-08-02 10:03:20 +02:00
n1xx1 51f09b17fd allow nested params when using Pages.GroupByParam and Pages.GroupByParamDate 2024-08-02 10:03:20 +02:00
dependabot[bot] be643580dd build(deps): bump github.com/bep/godartsass/v2 from 2.0.0 to 2.1.0
Bumps [github.com/bep/godartsass/v2](https://github.com/bep/godartsass) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/bep/godartsass/releases)
- [Commits](https://github.com/bep/godartsass/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: github.com/bep/godartsass/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 00:36:13 +02:00
dependabot[bot] 20e4dd2ee3 build(deps): bump google.golang.org/api from 0.152.0 to 0.189.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.152.0 to 0.189.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.152.0...v0.189.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 19:57:38 +02:00
dependabot[bot] f41149979a build(deps): bump github.com/spf13/cobra from 1.8.0 to 1.8.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 16:34:22 +02:00
dependabot[bot] 47fee839f6 build(deps): bump gocloud.dev from 0.36.0 to 0.38.0
Bumps [gocloud.dev](https://github.com/google/go-cloud) from 0.36.0 to 0.38.0.
- [Release notes](https://github.com/google/go-cloud/releases)
- [Commits](https://github.com/google/go-cloud/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: gocloud.dev
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 16:33:35 +02:00
dependabot[bot] 99665ef227 build(deps): bump github.com/gorilla/websocket from 1.5.1 to 1.5.3
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.1...v1.5.3)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 16:32:47 +02:00
dependabot[bot] eb2eeee687 build(deps): bump github.com/kyokomi/emoji/v2 from 2.2.12 to 2.2.13
Bumps [github.com/kyokomi/emoji/v2](https://github.com/kyokomi/emoji) from 2.2.12 to 2.2.13.
- [Release notes](https://github.com/kyokomi/emoji/releases)
- [Commits](https://github.com/kyokomi/emoji/compare/v2.2.12...v2.2.13)

---
updated-dependencies:
- dependency-name: github.com/kyokomi/emoji/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 16:31:38 +02:00
Bjørn Erik Pedersen bea5a46de4 deps: Upgrade github.com/hairyhenderson/go-codeowners v0.4.0 => v0.5.0 2024-08-01 14:46:54 +02:00
dependabot[bot] f1a7f3d01a build(deps): bump github.com/fatih/color from 1.16.0 to 1.17.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 14:46:13 +02:00
dependabot[bot] 08243be1d2 build(deps): bump github.com/evanw/esbuild from 0.21.4 to 0.23.0
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.21.4 to 0.23.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.21.4...v0.23.0)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 13:43:28 +02:00
dependabot[bot] 0490a8a162 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.36 to 2.20.37
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.36 to 2.20.37.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.36...v2.20.37)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 13:14:38 +02:00
Bjørn Erik Pedersen d795cacd71 Revert "deps: Set toolchain go1.21.0 in go.mod"
This reverts commit 78c9a18b0c.
2024-08-01 13:04:29 +02:00
Bjørn Erik Pedersen a5db76fe62 Run go mod tidy 2024-08-01 12:22:27 +02:00
n1xx1 566fe7ba12 resources/page: Expand parmalinks tokens in url
This change allows to use permalink tokens in url front matter fields. This should be useful to target more specific pages instead of using a global permalink configuration. It's expected to be used with cascade.

Fixes #9714
2024-08-01 12:14:29 +02:00
Bjørn Erik Pedersen 92573012e8 Bump to go 1.21 in go.mod 2024-08-01 12:07:27 +02:00
Bjørn Erik Pedersen 071f8b4466 tpl: Sync Go template packages with the Go 1.22.5 source 2024-08-01 12:07:27 +02:00
Bjørn Erik Pedersen ae9d5e7d83 Upgrade to Go 1.22.5 2024-08-01 12:07:27 +02:00
Bjørn Erik Pedersen 78c9a18b0c deps: Set toolchain go1.21.0 in go.mod
To hopefully resolve dependabot issues.
2024-07-31 19:47:41 +02:00
Bjørn Erik Pedersen 79aa2ab618 deps: Upgrade golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d => v0.23.0
Main motivation is to try to get dependabot running again.
2024-07-31 18:52:13 +02:00
Bjørn Erik Pedersen 216a69a1ef Shorten processed image filenames
Fixes #12688
Fixes #12656
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen e67886c038 Consolidate all hashing to the common/hashing package
And remove now unsued hashing funcs.
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen d5eda13cb2 Replace the MD5 hashing of images with xxHash
Note that we only use this for change detection.

The previous implementation invoked `MD5FromReaderFast` that created a MD5 has from 8 64 bytes chunks in the file, which is obviously very fast. The new implementation creates the hash from the entire file and ... seems to be even more effective:

```
name          old time/op    new time/op    delta
HashImage-10    9.45µs ±21%   10.89µs ± 1%     ~     (p=0.343 n=4+4)

name          old alloc/op   new alloc/op   delta
HashImage-10      144B ± 0%        8B ± 0%  -94.44%  (p=0.029 n=4+4)

name          old allocs/op  new allocs/op  delta
HashImage-10      4.00 ± 0%      1.00 ± 0%  -75.00%  (p=0.029 n=4+4)
```
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen 8b5d796989 resources: Add BenchmarkHashImage 2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen c9d7577cb4 deps: Replace github.com/mitchellh/hashstructure/v2 with github.com/gohugoio/hashstructure
The old repo is archived and no longer maintained.

See #11644
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen 2babd6404e identity: Use xxHash in hashstructure (note)
```
name                 old time/op    new time/op    delta
HashString/n28-10       133ns ± 9%     107ns ±10%  -19.58%  (p=0.029 n=4+4)
HashString/n112-10      243ns ± 5%     121ns ± 4%  -50.08%  (p=0.029 n=4+4)
HashString/n448-10      698ns ± 3%     174ns ± 5%  -75.02%  (p=0.029 n=4+4)
HashString/n1792-10    2.58µs ± 4%    0.38µs ± 4%  -85.11%  (p=0.029 n=4+4)
HashString/n7168-10    10.0µs ± 3%     1.3µs ± 4%  -86.91%  (p=0.029 n=4+4)

name                 old alloc/op   new alloc/op   delta
HashString/n28-10       80.0B ± 0%     72.0B ± 0%  -10.00%  (p=0.029 n=4+4)
HashString/n112-10       160B ± 0%      152B ± 0%   -5.00%  (p=0.029 n=4+4)
HashString/n448-10       496B ± 0%      488B ± 0%   -1.61%  (p=0.029 n=4+4)
HashString/n1792-10    1.84kB ± 0%    1.83kB ± 0%   -0.43%  (p=0.029 n=4+4)
HashString/n7168-10    8.24kB ± 0%    8.23kB ± 0%   -0.06%  (p=0.029 n=4+4)

name                 old allocs/op  new allocs/op  delta
HashString/n28-10        4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.029 n=4+4)
HashString/n112-10       4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.029 n=4+4)
HashString/n448-10       4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.029 n=4+4)
HashString/n1792-10      4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.029 n=4+4)
HashString/n7168-10      4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.029 n=4+4)
```
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen 78db8aebca identity: Upgrade to github.com/mitchellh/hashstructure/v2 v2.0.2
```
name                 old time/op    new time/op    delta
HashString/n28-10       131ns ± 7%     133ns ±19%   ~     (p=0.343 n=4+4)
HashString/n112-10      237ns ± 5%     241ns ± 3%   ~     (p=0.229 n=4+4)
HashString/n448-10      688ns ± 3%     687ns ± 1%   ~     (p=0.886 n=4+4)
HashString/n1792-10    2.51µs ± 3%    2.56µs ± 3%   ~     (p=0.200 n=4+4)
HashString/n7168-10    10.1µs ± 2%    10.0µs ± 4%   ~     (p=0.686 n=4+4)

name                 old alloc/op   new alloc/op   delta
HashString/n28-10       80.0B ± 0%     80.0B ± 0%   ~     (all equal)
HashString/n112-10       160B ± 0%      160B ± 0%   ~     (all equal)
HashString/n448-10       496B ± 0%      496B ± 0%   ~     (all equal)
HashString/n1792-10    1.84kB ± 0%    1.84kB ± 0%   ~     (all equal)
HashString/n7168-10    8.24kB ± 0%    8.24kB ± 0%   ~     (all equal)

name                 old allocs/op  new allocs/op  delta
HashString/n28-10        4.00 ± 0%      4.00 ± 0%   ~     (all equal)
HashString/n112-10       4.00 ± 0%      4.00 ± 0%   ~     (all equal)
HashString/n448-10       4.00 ± 0%      4.00 ± 0%   ~     (all equal)
HashString/n1792-10      4.00 ± 0%      4.00 ± 0%   ~     (all equal)
HashString/n7168-10      4.00 ± 0%      4.00 ± 0%   ~     (all equal)
```

Closes #11644
2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen 3140e0b994 identity: Add BenchmarkHashString 2024-07-31 16:44:06 +02:00
Bjørn Erik Pedersen 9989404d97 deps: Upgrade github.com/bep/imagemeta v0.7.4 => v0.7.5 2024-07-31 15:35:18 +02:00
Bjørn Erik Pedersen b81ba2a0f0 deps: Upgrade github.com/aws/aws-sdk-go-v2 v1.26.1 => v1.30.3 2024-07-31 14:22:20 +02:00
Bjørn Erik Pedersen afdd87db59 deps: Upgrade to github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 2024-07-31 12:53:27 +02:00
hugoreleaser e45eae4d67 releaser: Prepare repository for 0.131.0-DEV
[ci skip]
2024-07-29 14:06:20 +00:00
hugoreleaser 9b1b11c8a5 releaser: Bump versions for release of 0.130.0
[ci skip]
2024-07-29 13:51:56 +00:00
raoulb 9d2b5f98d0 math: Add trigonometric functions and some angle helper functions
This commit adds these new template functions in the `math` namespace:

math.Acos
math.Asin
math.Atan
math.Atan2
math.Cos
math.Pi
math.Sin
math.Tan
math.ToDegrees
math.ToRadians

Co-authored-by: Joe Mooring <joe@mooring.com>
2024-07-29 11:05:36 +02:00
Bjørn Erik Pedersen 0e00561620 deps: Upgrade github.com/bep/imagemeta v0.7.3 => v0.7.4 2024-07-23 20:46:05 +02:00
Bjørn Erik Pedersen 71842140d0 deps: Upgrade github.com/bep/imagemeta v0.7.1 => v0.7.3 2024-07-21 21:44:48 +02:00
Bjørn Erik Pedersen cb95a033c1 readme: Pull the star history down a little 2024-07-20 17:46:48 +02:00
Bjørn Erik Pedersen 3240511153 deps: Upgraded github.com/bep/imagemeta v0.7.0 => v0.7.1
Fixes #12669
2024-07-20 17:28:47 +02:00
Bjørn Erik Pedersen 72ff937e11 Switch EXIF library
Closes #10855
Closes #8586
Closes #8996
2024-07-20 15:56:10 +02:00
Bjørn Erik Pedersen a28bed0817 readme: Add Star History 2024-07-20 15:37:22 +02:00
hugoreleaser 979423f4d5 releaser: Prepare repository for 0.130.0-DEV
[ci skip]
2024-07-17 13:42:58 +00:00
hugoreleaser e85be29867 releaser: Bump versions for release of 0.129.0
[ci skip]
2024-07-17 13:29:16 +00:00
Bjørn Erik Pedersen 4d8bfa7f1c tpl: Use xxHash instead of MD5 to hash the deferred templates
Motivation is performance. These templates are typically very small, so the win is minor, I guess.
2024-07-17 12:51:49 +02:00
Bjørn Erik Pedersen f0ed91caba Throw error if resources.PostProcess is used in a deferred template
That just doesn't work.

See #12655
2024-07-17 12:47:25 +02:00
Bjørn Erik Pedersen 7be0377505 deps: Upgrade github.com/bep/gitmap v1.4.0 => v1.6.0 (note)
Closes #8627
2024-07-17 10:54:28 +02:00
216 changed files with 3245 additions and 1213 deletions
+2 -2
View File
@@ -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
+120
View File
@@ -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 }}
+3
View File
@@ -102,6 +102,9 @@ Build the extended edition:
```text
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
```
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline)
## Documentation
+166
View File
@@ -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)
}
+144
View File
@@ -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)
}
})
}
}
+3 -2
View File
@@ -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
}
+1 -1
View File
@@ -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",
}
+2 -2
View File
@@ -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
}
+8 -5
View File
@@ -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.
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -43,7 +43,7 @@ func TestNamespace(t *testing.T) {
c.Assert(err, qt.IsNil)
c.Assert(ns, qt.Not(qt.IsNil))
c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"})
c.Assert(ns.SourceHash, qt.Equals, "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))
}
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+24
View File
@@ -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
```
+193 -38
View 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:
+76 -68
View File
@@ -4,17 +4,18 @@ 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.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
@@ -26,8 +27,8 @@ require (
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
@@ -36,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
@@ -60,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.36
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.18.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.16.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
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
@@ -141,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
+196 -145
View File
@@ -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,99 +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.4.0 h1:GeWbPb2QDTfcZLBQmCB693N3sJmPQfeu81fDrD5r8x8=
github.com/bep/gitmap v1.4.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw=
github.com/bep/gitmap v1.5.0 h1:ExDl7HeDaRDG8FXFRTnv20qzbyJlC6ivdOboMYFvrms=
github.com/bep/gitmap v1.5.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw=
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=
@@ -165,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=
@@ -176,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=
@@ -185,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=
@@ -206,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=
@@ -258,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=
@@ -280,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=
@@ -299,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=
@@ -351,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=
@@ -368,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=
@@ -390,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=
@@ -400,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=
@@ -416,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=
@@ -439,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.36 h1:uhbCO5NNS0UgJfEyE/ZR+xU5DL9Dz0ngrJ8W9A6coCQ=
github.com/tdewolff/minify/v2 v2.20.36/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU=
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=
@@ -465,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=
@@ -478,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=
@@ -495,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.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
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=
@@ -520,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=
@@ -557,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=
@@ -568,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=
@@ -582,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=
@@ -621,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=
@@ -639,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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
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=
@@ -653,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=
@@ -697,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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
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=
@@ -724,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=
@@ -733,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=
@@ -771,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=
@@ -793,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=
@@ -805,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=
+2 -62
View File
@@ -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'
-89
View File
@@ -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"}
+2 -2
View File
@@ -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) {
+10 -11
View File
@@ -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()
}
+1 -5
View File
@@ -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
`)
}
+3 -3
View File
@@ -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.
+13
View File
@@ -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
+12 -2
View File
@@ -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 {
+2
View File
@@ -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 {
+3 -3
View File
@@ -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
+3 -4
View File
@@ -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,
+2 -1
View File
@@ -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
+4 -4
View File
@@ -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&#43;8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css
CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03&#43;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}")
+8
View File
@@ -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)
}
+5 -2
View File
@@ -1,7 +1,10 @@
# Release env.
# These will be replaced by script before release.
HUGORELEASER_TAG=v0.128.2
HUGORELEASER_COMMITISH=de36c1a95d28595d8243fd8b891665b069ed0850
HUGORELEASER_TAG=v0.131.0
HUGORELEASER_COMMITISH=bfbee17932ff24009008aa94cdd75c0c41f59279
-69
View File
@@ -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
}
}
+53
View File
@@ -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
+248
View File
@@ -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)
}
}
+18 -16
View File
@@ -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, "{")
-54
View File
@@ -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)
}
}
}
+6 -30
View File
@@ -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() {
+12 -1
View File
@@ -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) {
+226
View File
@@ -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
`)
}
+2 -2
View File
@@ -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
}
+19 -36
View File
@@ -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
}
+10 -4
View File
@@ -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)
}
+30 -25
View File
@@ -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)
+3 -1
View File
@@ -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
}
+208 -125
View File
@@ -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)
}
+28 -37
View File
@@ -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)
}
+5 -5
View File
@@ -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)))
}
+22 -3
View File
@@ -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")
}
+2 -2
View File
@@ -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...)
}
+1 -1
View File
@@ -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")
}
+150 -138
View File
@@ -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)
}
+33 -11
View File
@@ -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/")
}
+10 -5
View File
@@ -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)
}
+3 -2
View File
@@ -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")
}
+3 -1
View File
@@ -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
}
+36
View File
@@ -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) {
+7 -6
View File
@@ -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|",
)
}
Binary file not shown.

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: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

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