Compare commits

...

81 commits

Author SHA1 Message Date
David Jones
dd6e2c8724
deploy: walkLocal worker pool for performance
Some checks are pending
Test / test (1.23.x, ubuntu-latest) (push) Waiting to run
Test / test (1.23.x, windows-latest) (push) Waiting to run
Test / test (1.24.x, ubuntu-latest) (push) Waiting to run
Test / test (1.24.x, windows-latest) (push) Waiting to run
2025-06-29 16:41:56 +02:00
hugoreleaser
762417617c releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-06-23 08:38:21 +00:00
hugoreleaser
29bdbde19c releaser: Bump versions for release of 0.147.9
[ci skip]
2025-06-23 08:22:20 +00:00
Bjørn Erik Pedersen
6a4a3ab8f8 Remove WARN with false negatives
Fixes #13806
2025-06-22 16:55:43 +02:00
Bjørn Erik Pedersen
36f6f987a9 resources/page: Make sure a map is always initialized
Fixes #13810
2025-06-21 14:38:06 +02:00
Joe Mooring
18a9ca7d7a tpl/tplimpl: Copy embedded HTML table render hook to each output format
Closes #13351
2025-06-21 14:37:47 +02:00
Joe Mooring
b6c8dfa9dc
tpl/tplimpl: Change resources.GetRemote errors to suppressible warnings
Closes #13803
2025-06-17 07:35:14 -07:00
dependabot[bot]
621ea42f3c build(deps): bump google.golang.org/api from 0.221.0 to 0.237.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.221.0 to 0.237.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.221.0...v0.237.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 13:53:25 +02:00
Bjørn Erik Pedersen
4ef5720141
hugolib: Remove test for deprecated future 2025-06-13 12:17:06 +02:00
dependabot[bot]
34e83789f7 build(deps): bump github.com/aws/aws-sdk-go-v2 from 1.36.1 to 1.36.4
Bumps [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) from 1.36.1 to 1.36.4.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/changelog-template.json)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.36.1...v1.36.4)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2
  dependency-version: 1.36.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 11:25:11 +02:00
dependabot[bot]
4d3ebe4d21 build(deps): bump golang.org/x/image from 0.27.0 to 0.28.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/image/compare/v0.27.0...v0.28.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-12 14:17:16 +02:00
Joe Mooring
b5c0383bda deps: Upgrade github.com/spf13/cast v1.8.0 => v1.9.2 2025-06-12 10:07:09 +02:00
Joe Mooring
4217fee4b0 common/terminal: Enable color output on windows
Closes #8209
2025-06-09 11:44:13 +02:00
hugoreleaser
fad57964aa releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-06-07 13:13:56 +00:00
hugoreleaser
10da2bd765 releaser: Bump versions for release of 0.147.8
[ci skip]
2025-06-07 12:59:52 +00:00
Joe Mooring
01241d5dc9 hugolib: Emit ignorable warning when home page is a leaf bundle
Closes #13538
2025-06-07 13:02:28 +02:00
Bjørn Erik Pedersen
8e61f1fe12
dockerfile: Update Alpine
Closes #13783
2025-06-06 08:16:30 +02:00
Bjørn Erik Pedersen
f37412a575
dockerfile: Update Go version
FIxes #13780
2025-06-04 16:52:17 +02:00
dependabot[bot]
21a4a9acd7 build(deps): bump github.com/evanw/esbuild from 0.25.3 to 0.25.5
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.25.3 to 0.25.5.
- [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.25.3...v0.25.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 11:27:57 +02:00
dependabot[bot]
7a4a4790e5 build(deps): bump github.com/niklasfasching/go-org from 1.7.0 to 1.8.0
Bumps [github.com/niklasfasching/go-org](https://github.com/niklasfasching/go-org) from 1.7.0 to 1.8.0.
- [Commits](https://github.com/niklasfasching/go-org/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/niklasfasching/go-org
  dependency-version: 1.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 11:26:51 +02:00
dependabot[bot]
54065b7ef8 build(deps): bump golang.org/x/net from 0.39.0 to 0.40.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/net/compare/v0.39.0...v0.40.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 09:01:37 +02:00
dependabot[bot]
e333836f49 build(deps): bump github.com/yuin/goldmark from 1.7.11 to 1.7.12
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.11 to 1.7.12.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.11...v1.7.12)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-version: 1.7.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 18:18:44 +02:00
dependabot[bot]
cc7bfeea32 build(deps): bump github.com/tdewolff/minify/v2 from 2.23.5 to 2.23.8
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.23.5 to 2.23.8.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.23.5...v2.23.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 18:18:24 +02:00
Bjørn Erik Pedersen
32eb1a8ad4 all: Replace _build with build in tests
_build is deprecated and now shows up as warning.
2025-06-02 16:35:37 +02:00
hugoreleaser
32af02cd3e releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-31 12:55:24 +00:00
hugoreleaser
189453612e releaser: Bump versions for release of 0.147.7
[ci skip]
2025-05-31 12:41:12 +00:00
Bjørn Erik Pedersen
5273a884d4 Fix language handling in shortcode templates
Fixes #13767
2025-05-31 13:57:00 +02:00
Bjørn Erik Pedersen
6334948515
Handle KaTeX warnings (#13760)
Co-authored-by: Joe Mooring <joe.mooring@veriphor.com>

Fixes #13735
2025-05-30 20:57:54 +02:00
dependabot[bot]
75259636c8 build(deps): bump golang.org/x/image from 0.26.0 to 0.27.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/image/compare/v0.26.0...v0.27.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 15:13:21 +02:00
dependabot[bot]
0df9f3510f build(deps): bump golang.org/x/text from 0.24.0 to 0.25.0
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.24.0 to 0.25.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.24.0...v0.25.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 12:50:09 +02:00
dependabot[bot]
302e6a726b build(deps): bump github.com/spf13/cast from 1.7.1 to 1.8.0
Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.7.1 to 1.8.0.
- [Release notes](https://github.com/spf13/cast/releases)
- [Commits](https://github.com/spf13/cast/compare/v1.7.1...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cast
  dependency-version: 1.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 08:34:09 +02:00
dependabot[bot]
202fe0d45c build(deps): bump github.com/alecthomas/chroma/v2 from 2.17.2 to 2.18.0
Bumps [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) from 2.17.2 to 2.18.0.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.17.2...v2.18.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-29 22:12:58 +02:00
Joe Mooring
843ffeb48d resources/page: Respect disablePathToLower for permalink tokens
Fixes #13755
2025-05-29 22:10:03 +02:00
Ruslan Semagin
bff5d19121 common/collections: Increase test coverage 2025-05-29 09:17:13 +02:00
Ruslan Semagin
da370d30de parser/pageparser: Add coverage for all IsX methods of Item
Added tests for all boolean methods on Item, increasing overall code coverage.
2025-05-28 21:53:17 +02:00
Bjørn Erik Pedersen
6bd328c584 resources: Remove unused interface 2025-05-28 19:32:28 +02:00
Bjørn Erik Pedersen
766a2e7868 Make sure that unreferenced but changed bundle resources gets republished
Fixes #13748
2025-05-28 19:32:28 +02:00
Bjørn Erik Pedersen
13e1617557 deps: Upgrade github.com/olekukonko/tablewriter v0.0.5 => v1.0.7 2025-05-28 18:07:14 +02:00
hugoreleaser
463e440c7a releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-27 11:31:53 +00:00
hugoreleaser
0a5fd8ebb8 releaser: Bump versions for release of 0.147.6
[ci skip]
2025-05-27 11:17:16 +00:00
Andreas Deininger
e57dcd3795 Improve warning message on superfluous prefix when using function 'partials.Include' 2025-05-27 12:16:26 +02:00
Bjørn Erik Pedersen
eaf5ace30d Fix recent regression with cascading of params to content adapters
Fixes #13743
2025-05-26 21:26:19 +02:00
Bjørn Erik Pedersen
9ad26b69ad Fix it so e.g. de in layouts/_shortcodes/de.html is not interpreted as a language code
Fixes #13740
2025-05-26 20:26:56 +02:00
Bjørn Erik Pedersen
f47193669d commands: Make sure the browser gets refreshed on changes when --disableFastRender is set
Fixes #13727
2025-05-23 19:46:16 +02:00
Joe Mooring
013c8cfb25 tpl/transform: Expose the KaTeX strict option
Closes #13729
2025-05-23 19:21:38 +02:00
hugoreleaser
e25db38467 releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-22 11:51:47 +00:00
hugoreleaser
7766fc6241 releaser: Bump versions for release of 0.147.5
[ci skip]
2025-05-22 11:37:19 +00:00
Bjørn Erik Pedersen
4a48facef4
Merge branch 'release-0.147.4' 2025-05-22 13:19:34 +02:00
Bjørn Erik Pedersen
0c7b1a3f26 Fix live reload when editing inline partials
Fixes #13723
2025-05-22 13:15:41 +02:00
dependabot[bot]
970b887ba1 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.37 to 2.23.5
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.37 to 2.23.5.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.37...v2.23.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-20 14:49:06 +02:00
hugoreleaser
b9b95e5aec releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-20 10:55:43 +00:00
hugoreleaser
84c8426f32 releaser: Bump versions for release of 0.147.4
[ci skip]
2025-05-20 10:41:19 +00:00
Bjørn Erik Pedersen
a03a245f0c Fix it so css.TailwindCSS inlineImports options isn't always enabled
To avoid breaking existing setup and to make a better default option, the option is now `disableInlineImports` (default false).
Fixes #13719
2025-05-19 19:36:48 +02:00
Bjørn Erik Pedersen
5a81a3a4cf tpl: Add a test case
I'm not able to reproduce this, but leaving it in.

Closes #13699
2025-05-18 12:48:24 +02:00
Bjørn Erik Pedersen
61317821e4 tpl: Narrow down the usage of plain text shortcodes when rendering HTML
After this commit, if you want to resolve `layouts/_shortcodes/myshortcode.txt` when rendering HTML content, you need to use the `{{%` shortcode delimiter:

```
{{% myshortcode %}}
```

This should be what people would do anyway, but we have also as part of this improved the error message to inform about what needs to be done.

Note that this is not relevant for partials.

Fixes #13698
2025-05-18 12:48:24 +02:00
Bjørn Erik Pedersen
6142bc701c tpl: Fix theme overrides when theme has old layout setup (e.g. _default)
Fixes #13715
2025-05-18 12:48:24 +02:00
hugoreleaser
e6574cf7a7 releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-12 12:40:04 +00:00
hugoreleaser
05417512bd releaser: Bump versions for release of 0.147.3
[ci skip]
2025-05-12 12:25:03 +00:00
Joe Mooring
81426998b8 tpl/tplimpl: Change calls to simple versions of embedded shortcodes
Closes #13700
2025-05-12 14:19:11 +02:00
Bjørn Erik Pedersen
6def5a1ba9
Update README.md 2025-05-12 12:10:27 +02:00
Bjørn Erik Pedersen
bc98e7a80d config: Fix env override of slices
Fixes #13707
2025-05-11 19:24:24 +02:00
Bjørn Erik Pedersen
c745a3e108 Fix/implement cascade for content adapters
Fixes #13692
2025-05-10 15:12:24 +02:00
Joe Mooring
9d1d8c8899 commands: Fix description of new theme commands
Fixes #13701
2025-05-09 20:03:21 +02:00
Joe Mooring
84d7a108e8
tpl/tplimpl: Fix vimeo shortcode test to accommodate API changes
Fixes #13687
2025-05-09 10:02:00 -07:00
Joe Mooring
325a0dba63 tpl/math: Add MaxInt64 function
Closes #13693
2025-05-07 19:40:29 +02:00
hugoreleaser
d70f828e2b releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-06 11:33:36 +00:00
hugoreleaser
c7feb15d10 releaser: Bump versions for release of 0.147.2
[ci skip]
2025-05-06 11:18:55 +00:00
Bjørn Erik Pedersen
363ab48a24 Fix handling of "outputs" from content adapter pages
Fixes #13689
2025-05-06 11:40:46 +02:00
Bjørn Erik Pedersen
80f0595311 tpl: Fix case issue in templates.Exists
Fixes #13684
2025-05-05 11:23:52 +02:00
Bjørn Erik Pedersen
b39b249623 config: Add some more merge tests
See #13681
2025-05-02 11:15:38 +02:00
hugoreleaser
d799c045fd releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-05-01 14:04:58 +00:00
hugoreleaser
95666fc5a4 releaser: Bump versions for release of 0.147.1
[ci skip]
2025-05-01 13:50:04 +00:00
dependabot[bot]
620fc87b56 build(deps): bump github.com/alecthomas/chroma/v2 from 2.17.0 to 2.17.2
Bumps [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) from 2.17.0 to 2.17.2.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.17.0...v2.17.2)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.17.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 15:44:26 +02:00
dependabot[bot]
8b2124e7c3 build(deps): bump github.com/getkin/kin-openapi from 0.131.0 to 0.132.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.131.0 to 0.132.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.131.0...v0.132.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-version: 0.132.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 13:19:22 +02:00
Bjørn Erik Pedersen
5fec7829b1 tpl: Add some more test cases
See #13672
See #13668
2025-05-01 12:55:11 +02:00
dependabot[bot]
927d1ec6c1 build(deps): bump github.com/yuin/goldmark from 1.7.10 to 1.7.11
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.10 to 1.7.11.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.10...v1.7.11)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-version: 1.7.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-30 22:09:10 +02:00
Bjørn Erik Pedersen
be93d5218b tpl: Fix overlapping layout sections
Fixes #13672
2025-04-30 21:04:03 +02:00
Bjørn Erik Pedersen
a1cb15e1cf Fix it so the owning taxonomy gets rerendered in server when new tags are added
Updates #13648
2025-04-28 21:42:16 +02:00
Joe Mooring
673a4d00eb commands/server: Display correct multihost language in console
Fixes #12564
2025-04-28 20:00:15 +02:00
Joe Mooring
31db7edf6d hugolib: Use new build key in content placeholder
Fixes #13655
2025-04-27 13:18:43 +02:00
hugoreleaser
5857b60cbc releaser: Prepare repository for 0.148.0-DEV
[ci skip]
2025-04-25 15:41:01 +00:00
90 changed files with 2331 additions and 687 deletions

View file

@ -2,8 +2,8 @@
# Twitter: https://twitter.com/gohugoio
# Website: https://gohugo.io/
ARG GO_VERSION="1.23.2"
ARG ALPINE_VERSION="3.20"
ARG GO_VERSION="1.24"
ARG ALPINE_VERSION="3.22"
ARG DART_SASS_VERSION="1.79.3"
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
@ -19,7 +19,7 @@ RUN apk add clang lld
COPY --from=xx / /
ARG TARGETPLATFORM
RUN xx-apk add musl-dev gcc g++
RUN xx-apk add musl-dev gcc g++
# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so:
# docker build --build-arg HUGO_BUILD_TAGS=withdeploy .
@ -72,7 +72,7 @@ RUN mkdir -p /var/hugo/bin /cache && \
adduser -Sg hugo -u 1000 -h /var/hugo hugo && \
chown -R hugo: /var/hugo /cache && \
# For the Hugo's Git integration to work.
runuser -u hugo -- git config --global --add safe.directory /project && \
runuser -u hugo -- git config --global --add safe.directory /project && \
# See https://github.com/gohugoio/hugo/issues/9810
runuser -u hugo -- git config --global core.quotepath false

View file

@ -65,9 +65,11 @@ See the [features] section of the documentation for a comprehensive summary of H
<p>&nbsp;</p>
<p float="left">
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
&nbsp;&nbsp;&nbsp;
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
&nbsp;&nbsp;&nbsp;
<a href="https://pinme.eth.limo/?s=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/logo-pinme.svg" width="200" alt="PinMe."></a>
</p>
## Editions

View file

@ -972,6 +972,9 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
lrl.Logf("force refresh")
livereload.ForceRefresh()
}
} else {
lrl.Logf("force refresh")
livereload.ForceRefresh()
}
if len(cssChanges) > 0 {

View file

@ -76,10 +76,8 @@ Ensure you run this within the root directory of your site.`,
&simpleCommand{
name: "site",
use: "site [path]",
short: "Create a new site (skeleton)",
long: `Create a new site in the provided directory.
The new site will have the correct structure, but no content or theme yet.
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
short: "Create a new site",
long: `Create a new site at the specified path.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
if len(args) < 1 {
return newUserError("path needs to be provided")
@ -124,11 +122,9 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
&simpleCommand{
name: "theme",
use: "theme [name]",
short: "Create a new theme (skeleton)",
long: `Create a new theme (skeleton) called [name] in ./themes.
New theme is a skeleton. Please add content to the touched files. Add your
name to the copyright line in the license and adjust the theme.toml file
according to your needs.`,
short: "Create a new theme",
long: `Create a new theme with the specified name in the ./themes directory.
This generates a functional theme including template examples and sample content.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
if len(args) < 1 {
return newUserError("theme name needs to be provided")

View file

@ -627,7 +627,7 @@ func (c *serverCommand) setServerInfoInConfig() error {
panic("no server ports set")
}
return c.withConfE(func(conf *commonConfig) error {
for i, language := range conf.configs.Languages {
for i, language := range conf.configs.LanguagesDefaultFirst {
isMultihost := conf.configs.IsMultihost
var serverPort int
if isMultihost {

View file

@ -15,6 +15,7 @@ package collections
import (
"html/template"
"reflect"
"testing"
qt "github.com/frankban/quicktest"
@ -77,6 +78,7 @@ func TestAppend(t *testing.T) {
{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
{[]string{"a", "b"}, []any{}, []string{"a", "b"}},
} {
result, err := Append(test.start, test.addend...)
@ -146,3 +148,66 @@ func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) {
c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"})
c.Assert(slice, qt.DeepEquals, []string{"d", "b"})
}
func TestIndirect(t *testing.T) {
t.Parallel()
c := qt.New(t)
type testStruct struct {
Field string
}
var (
nilPtr *testStruct
nilIface interface{} = nil
nonNilIface interface{} = &testStruct{Field: "hello"}
)
tests := []struct {
name string
input any
wantKind reflect.Kind
wantNil bool
}{
{
name: "nil pointer",
input: nilPtr,
wantKind: reflect.Ptr,
wantNil: true,
},
{
name: "nil interface",
input: nilIface,
wantKind: reflect.Invalid,
wantNil: false,
},
{
name: "non-nil pointer to struct",
input: &testStruct{Field: "abc"},
wantKind: reflect.Struct,
wantNil: false,
},
{
name: "non-nil interface holding pointer",
input: nonNilIface,
wantKind: reflect.Struct,
wantNil: false,
},
{
name: "plain value",
input: testStruct{Field: "xyz"},
wantKind: reflect.Struct,
wantNil: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := reflect.ValueOf(tt.input)
got, isNil := indirect(v)
c.Assert(got.Kind(), qt.Equals, tt.wantKind)
c.Assert(isNil, qt.Equals, tt.wantNil)
})
}
}

View file

@ -136,3 +136,37 @@ func TestSortedStringSlice(t *testing.T) {
c.Assert(s.Count("z"), qt.Equals, 0)
c.Assert(s.Count("a"), qt.Equals, 1)
}
func TestStringSliceToInterfaceSlice(t *testing.T) {
t.Parallel()
c := qt.New(t)
tests := []struct {
name string
in []string
want []any
}{
{
name: "empty slice",
in: []string{},
want: []any{},
},
{
name: "single element",
in: []string{"hello"},
want: []any{"hello"},
},
{
name: "multiple elements",
in: []string{"a", "b", "c"},
want: []any{"a", "b", "c"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := StringSliceToInterfaceSlice(tt.in)
c.Assert(got, qt.DeepEquals, tt.want)
})
}
}

View file

@ -0,0 +1,77 @@
package collections
import (
"testing"
qt "github.com/frankban/quicktest"
)
func TestNewStack(t *testing.T) {
t.Parallel()
c := qt.New(t)
s := NewStack[int]()
c.Assert(s, qt.IsNotNil)
}
func TestStackBasic(t *testing.T) {
t.Parallel()
c := qt.New(t)
s := NewStack[int]()
c.Assert(s.Len(), qt.Equals, 0)
s.Push(1)
s.Push(2)
s.Push(3)
c.Assert(s.Len(), qt.Equals, 3)
top, ok := s.Peek()
c.Assert(ok, qt.Equals, true)
c.Assert(top, qt.Equals, 3)
popped, ok := s.Pop()
c.Assert(ok, qt.Equals, true)
c.Assert(popped, qt.Equals, 3)
c.Assert(s.Len(), qt.Equals, 2)
_, _ = s.Pop()
_, _ = s.Pop()
_, ok = s.Pop()
c.Assert(ok, qt.Equals, false)
}
func TestStackDrain(t *testing.T) {
t.Parallel()
c := qt.New(t)
s := NewStack[string]()
s.Push("a")
s.Push("b")
got := s.Drain()
c.Assert(got, qt.DeepEquals, []string{"a", "b"})
c.Assert(s.Len(), qt.Equals, 0)
}
func TestStackDrainMatching(t *testing.T) {
t.Parallel()
c := qt.New(t)
s := NewStack[int]()
s.Push(1)
s.Push(2)
s.Push(3)
s.Push(4)
got := s.DrainMatching(func(v int) bool { return v%2 == 0 })
c.Assert(got, qt.DeepEquals, []int{4, 2})
c.Assert(s.Drain(), qt.DeepEquals, []int{1, 3})
}

View file

@ -24,6 +24,7 @@ const (
WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html"
WarnGoldmarkRawHTML = "warning-goldmark-raw-html"
WarnPartialSuperfluousPrefix = "warning-partial-superfluous-prefix"
WarnHomePageIsLeafBundle = "warning-home-page-is-leaf-bundle"
)
// Field/method names with special meaning.

View file

@ -17,7 +17,7 @@ package hugo
// This should be the only one.
var CurrentVersion = Version{
Major: 0,
Minor: 147,
Minor: 148,
PatchLevel: 0,
Suffix: "",
Suffix: "-DEV",
}

View file

@ -120,15 +120,20 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
return p, nil
}
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot int) {
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot, numDots int, isLast bool) {
if p.posContainerHigh != -1 {
return
}
mayHaveLang := p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
mayHaveLang := numDots > 1 && p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
mayHaveLang = mayHaveLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
mayHaveOutputFormat := component == files.ComponentFolderLayouts
mayHaveKind := p.posIdentifierKind == -1 && mayHaveOutputFormat
mayHaveLayout := component == files.ComponentFolderLayouts
var mayHaveLayout bool
if p.pathType == TypeShortcode {
mayHaveLayout = !isLast && component == files.ComponentFolderLayouts
} else {
mayHaveLayout = component == files.ComponentFolderLayouts
}
var found bool
var high int
@ -167,7 +172,6 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
if langFound {
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierLanguage = len(p.identifiersKnown) - 1
}
}
@ -234,19 +238,24 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
p.s = s
slashCount := 0
lastDot := 0
lastSlashIdx := strings.LastIndex(s, "/")
numDots := strings.Count(s[lastSlashIdx+1:], ".")
if strings.Contains(s, "/_shortcodes/") {
p.pathType = TypeShortcode
}
for i := len(s) - 1; i >= 0; i-- {
c := s[i]
switch c {
case '.':
pp.parseIdentifier(component, s, p, i, lastDot)
pp.parseIdentifier(component, s, p, i, lastDot, numDots, false)
lastDot = i
case '/':
slashCount++
if p.posContainerHigh == -1 {
if lastDot > 0 {
pp.parseIdentifier(component, s, p, i, lastDot)
pp.parseIdentifier(component, s, p, i, lastDot, numDots, true)
}
p.posContainerHigh = i + 1
} else if p.posContainerLow == -1 {
@ -282,10 +291,9 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
p.pathType = TypeContentData
}
}
}
if component == files.ComponentFolderLayouts {
if p.pathType < TypeMarkup && component == files.ComponentFolderLayouts {
if p.posIdentifierBaseof != -1 {
p.pathType = TypeBaseof
} else {
@ -301,12 +309,10 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
}
if p.pathType == TypeShortcode && p.posIdentifierLayout != -1 {
// myshortcode or myshortcode.html, no layout.
if len(p.identifiersKnown) <= 2 {
id := p.identifiersKnown[p.posIdentifierLayout]
if id.Low == p.posContainerHigh {
// First identifier is shortcode name.
p.posIdentifierLayout = -1
} else {
// First is always the name.
p.posIdentifierLayout--
}
}

View file

@ -434,12 +434,12 @@ func TestParseLayouts(t *testing.T) {
},
{
"Layout multiple",
"/maylayout.list.section.no.html",
"/mylayout.list.section.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Layout(), qt.Equals, "maylayout")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "maylayout"})
c.Assert(p.Layout(), qt.Equals, "mylayout")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "mylayout"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
c.Assert(p.Base(), qt.Equals, "/maylayout.html")
c.Assert(p.Base(), qt.Equals, "/mylayout.html")
c.Assert(p.Lang(), qt.Equals, "no")
},
},
@ -487,7 +487,8 @@ func TestParseLayouts(t *testing.T) {
func(c *qt.C, p *Path) {
c.Assert(p.Base(), qt.Equals, "/_shortcodes/myshortcode.html")
c.Assert(p.Type(), qt.Equals, TypeShortcode)
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list", "myshortcode"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list"})
c.Assert(p.Layout(), qt.Equals, "list")
c.Assert(p.PathNoIdentifier(), qt.Equals, "/_shortcodes/myshortcode")
c.Assert(p.PathBeforeLangAndOutputFormatAndExt(), qt.Equals, "/_shortcodes/myshortcode.list")
c.Assert(p.Lang(), qt.Equals, "")
@ -563,11 +564,30 @@ func TestParseLayouts(t *testing.T) {
c.Assert(p.Type(), qt.Equals, TypePartial)
},
},
{
"Shortcode lang in root",
"/_shortcodes/no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Type(), qt.Equals, TypeShortcode)
c.Assert(p.Lang(), qt.Equals, "")
c.Assert(p.NameNoIdentifier(), qt.Equals, "no")
},
},
{
"Shortcode lang layout",
"/_shortcodes/myshortcode.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Type(), qt.Equals, TypeShortcode)
c.Assert(p.Lang(), qt.Equals, "no")
c.Assert(p.Layout(), qt.Equals, "")
c.Assert(p.NameNoIdentifier(), qt.Equals, "myshortcode")
},
},
}
for _, test := range tests {
c.Run(test.name, func(c *qt.C) {
if test.name != "Baseof" {
if test.name != "Shortcode lang layout" {
// return
}
test.assert(c, testParser.Parse(files.ComponentFolderLayouts, test.path))

View file

@ -17,7 +17,6 @@ package terminal
import (
"fmt"
"os"
"runtime"
"strings"
isatty "github.com/mattn/go-isatty"
@ -41,10 +40,6 @@ func PrintANSIColors(f *os.File) bool {
// IsTerminal return true if the file descriptor is terminal and the TERM
// environment variable isn't a dumb one.
func IsTerminal(f *os.File) bool {
if runtime.GOOS == "windows" {
return false
}
fd := f.Fd()
return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd))
}

View file

@ -146,7 +146,7 @@ type Config struct {
// The cascade configuration section contains the top level front matter cascade configuration options,
// a slice of page matcher and params to apply to those pages.
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]] `mapstructure:"-"`
// The segments defines segments for the site. Used for partial/segmented builds.
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
@ -776,7 +776,7 @@ type Configs struct {
}
func (c *Configs) Validate(logger loggers.Logger) error {
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
c.Base.Cascade.Config.Range(func(p page.PageMatcher, cfg page.PageMatcherParamsConfig) bool {
page.CheckCascadePattern(logger, p)
return true
})

View file

@ -273,6 +273,69 @@ GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
b.AssertFileContent("public/index.html", "GA ID: foo bar.")
}
func TestMergeDeepBuildStats(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
title = "Theme 1"
_merge = "deep"
[module]
[module.hugoVersion]
[[module.imports]]
path = "theme1"
-- themes/theme1/hugo.toml --
[build]
[build.buildStats]
disableIDs = true
enable = true
-- layouts/home.html --
Home.
`
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
conf := b.H.Configs
base := conf.Base
b.Assert(base.Title, qt.Equals, "Theme 1")
b.Assert(len(base.Module.Imports), qt.Equals, 1)
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
b.AssertFileExists("/hugo_stats.json", true)
}
func TestMergeDeepBuildStatsTheme(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
_merge = "deep"
theme = ["theme1"]
-- themes/theme1/hugo.toml --
title = "Theme 1"
[build]
[build.buildStats]
disableIDs = true
enable = true
-- layouts/home.html --
Home.
`
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
conf := b.H.Configs
base := conf.Base
b.Assert(base.Title, qt.Equals, "Theme 1")
b.Assert(len(base.Module.Imports), qt.Equals, 1)
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
b.AssertFileExists("/hugo_stats.json", true)
}
func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
t.Parallel()
@ -294,3 +357,25 @@ All.
b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
}
func TestDefaultConfigEnvDisableLanguagesIssue13707(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableLanguages = []
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
[languages.sv]
weight = 3
`
b := hugolib.Test(t, files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) {
conf.Environ = []string{`HUGO_DISABLELANGUAGES=sv nn`}
}))
b.Assert(len(b.H.Sites), qt.Equals, 1)
}

View file

@ -330,7 +330,7 @@ var allDecoderSetups = map[string]decodeWeight{
key: "cascade",
decode: func(d decodeWeight, p decodeConfig) error {
var err error
p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key))
p.c.Cascade, err = page.DecodeCascadeConfig(nil, true, p.p.Get(d.key))
return err
},
},

View file

@ -233,40 +233,51 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
if existing != nil {
val, err := metadecoders.Default.UnmarshalStringTo(env.Value, existing)
if err != nil {
if err == nil {
val = l.envValToVal(env.Key, val)
if owner != nil {
owner[nestedKey] = val
} else {
l.cfg.Set(env.Key, val)
}
continue
}
if owner != nil {
owner[nestedKey] = val
} else {
l.cfg.Set(env.Key, val)
}
} else {
if nestedKey != "" {
owner[nestedKey] = env.Value
} else {
var val any
key := strings.ReplaceAll(env.Key, delim, ".")
_, ok := allDecoderSetups[key]
if ok {
// A map.
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
val = v
}
}
if val == nil {
// A string.
val = l.envStringToVal(key, env.Value)
}
l.cfg.Set(key, val)
}
}
if owner != nil && nestedKey != "" {
owner[nestedKey] = env.Value
} else {
var val any
key := strings.ReplaceAll(env.Key, delim, ".")
_, ok := allDecoderSetups[key]
if ok {
// A map.
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
val = v
}
}
if val == nil {
// A string.
val = l.envStringToVal(key, env.Value)
}
l.cfg.Set(key, val)
}
}
return nil
}
func (l *configLoader) envValToVal(k string, v any) any {
switch v := v.(type) {
case string:
return l.envStringToVal(k, v)
default:
return v
}
}
func (l *configLoader) envStringToVal(k, v string) any {
switch k {
case "disablekinds", "disablelanguages":

View file

@ -36,6 +36,7 @@ import (
"github.com/dustin/go-humanize"
"github.com/gobwas/glob"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/para"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deploy/deployconfig"
"github.com/gohugoio/hugo/media"
@ -487,7 +488,12 @@ func knownHiddenDirectory(name string) bool {
// walkLocal walks the source directory and returns a flat list of files,
// using localFile.SlashPath as the map keys.
func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, include, exclude glob.Glob, mediaTypes media.Types, mappath func(string) string) (map[string]*localFile, error) {
retval := map[string]*localFile{}
retval := make(map[string]*localFile)
var mu sync.Mutex
workers := para.New(d.cfg.Workers)
g, _ := workers.Start(context.Background())
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
@ -508,45 +514,54 @@ func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, incl
return nil
}
// When a file system is HFS+, its filepath is in NFD form.
if runtime.GOOS == "darwin" {
path = norm.NFC.String(path)
}
// Check include/exclude matchers.
slashpath := filepath.ToSlash(path)
if include != nil && !include.Match(slashpath) {
d.logger.Infof(" dropping %q due to include\n", slashpath)
return nil
}
if exclude != nil && exclude.Match(slashpath) {
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
return nil
}
// Find the first matching matcher (if any).
var m *deployconfig.Matcher
for _, cur := range matchers {
if cur.Matches(slashpath) {
m = cur
break
// Process each file in a worker
g.Run(func() error {
// When a file system is HFS+, its filepath is in NFD form.
if runtime.GOOS == "darwin" {
path = norm.NFC.String(path)
}
}
// Apply any additional modifications to the local path, to map it to
// the remote path.
if mappath != nil {
slashpath = mappath(slashpath)
}
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
if err != nil {
return err
}
retval[lf.SlashPath] = lf
// Check include/exclude matchers.
slashpath := filepath.ToSlash(path)
if include != nil && !include.Match(slashpath) {
d.logger.Infof(" dropping %q due to include\n", slashpath)
return nil
}
if exclude != nil && exclude.Match(slashpath) {
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
return nil
}
// Find the first matching matcher (if any).
var m *deployconfig.Matcher
for _, cur := range matchers {
if cur.Matches(slashpath) {
m = cur
break
}
}
// Apply any additional modifications to the local path, to map it to
// the remote path.
if mappath != nil {
slashpath = mappath(slashpath)
}
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
if err != nil {
return err
}
mu.Lock()
retval[lf.SlashPath] = lf
mu.Unlock()
return nil
})
return nil
})
if err != nil {
return nil, err
}
if err := g.Wait(); err != nil {
return nil, err
}
return retval, nil
}

View file

@ -623,7 +623,7 @@ func TestEndToEndSync(t *testing.T) {
localFs: test.fs,
bucket: test.bucket,
mediaTypes: media.DefaultTypes,
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
}
// Initial deployment should sync remote with local.
@ -706,7 +706,7 @@ func TestMaxDeletes(t *testing.T) {
localFs: test.fs,
bucket: test.bucket,
mediaTypes: media.DefaultTypes,
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
}
// Sync remote with local.
@ -836,7 +836,7 @@ func TestIncludeExclude(t *testing.T) {
}
deployer := &Deployer{
localFs: fsTest.fs,
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
target: tgt,
mediaTypes: media.DefaultTypes,
}
@ -893,7 +893,7 @@ func TestIncludeExcludeRemoteDelete(t *testing.T) {
}
deployer := &Deployer{
localFs: fsTest.fs,
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
mediaTypes: media.DefaultTypes,
}
@ -945,7 +945,7 @@ func TestCompression(t *testing.T) {
deployer := &Deployer{
localFs: test.fs,
bucket: test.bucket,
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
mediaTypes: media.DefaultTypes,
}
@ -1000,7 +1000,7 @@ func TestMatching(t *testing.T) {
deployer := &Deployer{
localFs: test.fs,
bucket: test.bucket,
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
mediaTypes: media.DefaultTypes,
}
@ -1097,5 +1097,6 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
func newDeployer() *Deployer {
return &Deployer{
logger: loggers.NewDefault(),
cfg: deployconfig.DeployConfig{Workers: 2},
}
}

98
go.mod
View file

@ -2,9 +2,9 @@ module github.com/gohugoio/hugo
require (
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
github.com/alecthomas/chroma/v2 v2.17.0
github.com/alecthomas/chroma/v2 v2.18.0
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
github.com/aws/aws-sdk-go-v2 v1.36.1
github.com/aws/aws-sdk-go-v2 v1.36.4
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10
github.com/bep/clocks v0.5.0
github.com/bep/debounce v1.2.0
@ -26,12 +26,12 @@ require (
github.com/clbanning/mxj/v2 v2.7.0
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.1
github.com/evanw/esbuild v0.25.3
github.com/evanw/esbuild v0.25.5
github.com/fatih/color v1.18.0
github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.14.6
github.com/fsnotify/fsnotify v1.9.0
github.com/getkin/kin-openapi v0.131.0
github.com/getkin/kin-openapi v0.132.0
github.com/ghodss/yaml v1.0.0
github.com/gobuffalo/flect v1.0.3
github.com/gobwas/glob v0.2.3
@ -54,44 +54,46 @@ require (
github.com/mattn/go-isatty v0.0.20
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
github.com/olekukonko/tablewriter v0.0.5
github.com/niklasfasching/go-org v1.8.0
github.com/olekukonko/tablewriter v1.0.7
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/pelletier/go-toml/v2 v2.2.4
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/rogpeppe/go-internal v1.14.1
github.com/sanity-io/litter v1.5.8
github.com/spf13/afero v1.14.0
github.com/spf13/cast v1.7.1
github.com/spf13/cast v1.9.2
github.com/spf13/cobra v1.9.1
github.com/spf13/fsync v0.10.1
github.com/spf13/pflag v1.0.6
github.com/tdewolff/minify/v2 v2.20.37
github.com/tdewolff/parse/v2 v2.7.15
github.com/tdewolff/minify/v2 v2.23.8
github.com/tdewolff/parse/v2 v2.8.1
github.com/tetratelabs/wazero v1.9.0
github.com/yuin/goldmark v1.7.10
github.com/yuin/goldmark v1.7.12
github.com/yuin/goldmark-emoji v1.0.6
go.uber.org/automaxprocs v1.5.3
gocloud.dev v0.40.0
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
golang.org/x/image v0.26.0
golang.org/x/mod v0.24.0
golang.org/x/net v0.39.0
golang.org/x/sync v0.13.0
golang.org/x/text v0.24.0
golang.org/x/tools v0.32.0
google.golang.org/api v0.221.0
golang.org/x/image v0.28.0
golang.org/x/mod v0.25.0
golang.org/x/net v0.41.0
golang.org/x/sync v0.15.0
golang.org/x/text v0.26.0
golang.org/x/tools v0.33.0
google.golang.org/api v0.237.0
gopkg.in/yaml.v2 v2.4.0
rsc.io/qr v0.2.0
)
require (
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.14.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.2.2 // indirect
cloud.google.com/go/storage v1.43.0 // indirect
cel.dev/expr v0.23.0 // indirect
cloud.google.com/go v0.120.0 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
cloud.google.com/go/storage v1.50.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.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.10.0 // indirect
@ -99,6 +101,9 @@ require (
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.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
@ -118,9 +123,13 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // 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.21.0 // indirect
@ -130,8 +139,8 @@ require (
github.com/google/s2a-go v0.1.9 // 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.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@ -141,31 +150,40 @@ require (
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 // indirect
github.com/olekukonko/ll v0.0.8 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/oauth2 v0.26.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/time v0.10.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // 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

221
go.sum
View file

@ -1,3 +1,5 @@
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -17,26 +19,30 @@ 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.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
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/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
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.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
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=
@ -47,8 +53,10 @@ 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.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
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.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
@ -71,18 +79,26 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd
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/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0 h1:nNMpRpnkWDAaqcpxMJvxa/Ud98gjbYwayJY4/9bdjiU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
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.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
@ -167,6 +183,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/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -185,9 +203,17 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
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/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U=
github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/evanw/esbuild v0.25.5 h1:E+JpeY5S/1LFmnX1vtuZqUKT7qDVcfXdhzMhM3uIKFs=
github.com/evanw/esbuild v0.25.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@ -201,13 +227,15 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
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=
@ -317,12 +345,12 @@ 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.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
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.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -376,8 +404,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
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/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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=
@ -388,14 +416,18 @@ github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78Rwc
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 h1:r3FaAI0NZK3hSmtTDrBVREhKULp8oUeqLT5Eyl2mSPo=
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.8 h1:sbGZ1Fx4QxJXEqL/6IG8GEFnYojUSQ45dJVwN2FH2fc=
github.com/olekukonko/ll v0.0.8/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
@ -408,12 +440,16 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@ -428,14 +464,16 @@ 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.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
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.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -449,13 +487,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/tdewolff/minify/v2 v2.23.8 h1:tvjHzRer46kwOfpdCBCWsDblCw3QtnLJRd61pTVkyZ8=
github.com/tdewolff/minify/v2 v2.23.8/go.mod h1:VW3ISUd3gDOZuQ/jwZr4sCzsuX+Qvsx87FDMjk6Rvno=
github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po=
github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
@ -466,10 +503,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -480,20 +519,24 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
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.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng=
@ -509,8 +552,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
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=
@ -526,8 +569,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.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
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=
@ -554,8 +597,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
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=
@ -595,8 +638,8 @@ 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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
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=
@ -606,8 +649,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.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
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=
@ -623,8 +666,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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=
@ -672,8 +715,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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=
@ -692,13 +735,13 @@ 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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
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=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -751,8 +794,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
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.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
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=
@ -778,8 +821,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.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
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=
@ -823,12 +866,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-20241104194629-dd2ea8efbc28 h1:KJjNNclfpIkVqrZlTWcgOOaVQ00LdBnoEaRfkUx760s=
google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:mt9/MofW7AWQ+Gy179ChOnvmJatV8YHUmrcedo9CIFI=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
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=
@ -845,8 +888,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.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
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=
@ -857,8 +900,8 @@ 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.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
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=

View file

@ -19,6 +19,8 @@ import (
"sync/atomic"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/renderer"
"github.com/olekukonko/tablewriter/tw"
)
// ProcessingStats represents statistics about a site build.
@ -66,23 +68,6 @@ func (s *ProcessingStats) Add(counter *uint64, amount int) {
atomic.AddUint64(counter, uint64(amount))
}
// Table writes a table-formatted representation of the stats in a
// ProcessingStats instance to w.
func (s *ProcessingStats) Table(w io.Writer) {
titleVals := s.toVals()
data := make([][]string, len(titleVals))
for i, tv := range titleVals {
data[i] = []string{tv.name, strconv.Itoa(int(tv.val))}
}
table := tablewriter.NewWriter(w)
table.AppendBulk(data)
table.SetHeader([]string{"", s.Name})
table.SetBorder(false)
table.Render()
}
// ProcessingStatsTable writes a table-formatted representation of stats to w.
func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) {
names := make([]string, len(stats)+1)
@ -106,13 +91,26 @@ func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) {
data[j] = append(data[j], strconv.Itoa(int(tv.val)))
}
}
}
table := tablewriter.NewWriter(w)
table := tablewriter.NewTable(
w,
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
Borders: tw.BorderNone,
Symbols: tw.NewSymbols(tw.StyleLight),
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.Off},
Lines: tw.Lines{ShowFooterLine: tw.On},
},
})),
tablewriter.WithConfig(
tablewriter.Config{
MaxWidth: 70,
Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight, PerColumn: []tw.Align{tw.AlignLeft}}},
}),
)
table.AppendBulk(data)
table.SetHeader(names)
table.SetBorder(false)
table.Bulk(data)
table.Header(names)
table.Render()
}

View file

@ -892,7 +892,7 @@ disableKinds = ['home','rss','sitemap','taxonomy','term']
---
title: s
cascade:
_build:
build:
render: never
---
-- content/s/p1.md --
@ -917,3 +917,51 @@ title: p2
b.AssertFileExists("public/sx/index.html", true) // failing
b.AssertFileExists("public/sx/p2/index.html", true) // failing
}
func TestCascadeGotmplIssue13743(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
[cascade.params]
foo = 'bar'
[cascade.target]
path = '/p1'
-- content/_content.gotmpl --
{{ .AddPage (dict "title" "p1" "path" "p1") }}
-- layouts/all.html --
{{ .Title }}|{{ .Params.foo }}
`
b := Test(t, files)
b.AssertFileContent("public/p1/index.html", "p1|bar") // actual content is "p1|"
}
func TestCascadeWarnOverrideIssue13806(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
[[cascade]]
[cascade.params]
searchable = true
[cascade.target]
kind = 'page'
-- content/something.md --
---
title: Something
params:
searchable: false
---
-- layouts/all.html --
All.
`
b := Test(t, files, TestOptWarn())
b.AssertLogContains("! WARN")
}

View file

@ -1406,7 +1406,7 @@ home = ["html"]
"home": {"html"},
"page": {"html"},
"rss": {"rss"},
"section": nil,
"section": {},
"taxonomy": {"html", "rss"},
"term": {"html", "rss"},
})

View file

@ -114,7 +114,7 @@ func (f ContentFactory) CreateContentPlaceHolder(filename string, force bool) (s
// the paths correct.
placeholder := `---
title: "Content Placeholder"
_build:
build:
render: never
list: never
publishResources: false

View file

@ -356,7 +356,7 @@ func (m *pageMap) addPagesFromGoTmplFi(fi hugofs.FileMetaInfo, buildConfig *Buil
Watching: s.Conf.Watching(),
HandlePage: func(pt *pagesfromdata.PagesFromTemplate, pc *pagemeta.PageConfig) error {
s := pt.Site.(*Site)
if err := pc.Compile(pt.GoTmplFi.Meta().PathInfo.Base(), true, "", s.Log, s.conf.MediaTypes.Config); err != nil {
if err := pc.CompileForPagesFromDataPre(pt.GoTmplFi.Meta().PathInfo.Base(), m.s.Log, s.conf.MediaTypes.Config); err != nil {
return err
}

View file

@ -21,7 +21,6 @@ import (
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@ -1310,7 +1309,6 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
}
po.renderState = 0
po.p.resourcesPublishInit = &sync.Once{}
if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatus404HTMLFormat.Name {
// Will force a re-render even in fast render mode.
po.renderOnce = false
@ -1330,6 +1328,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
// This needs no reset, so no need to check it.
return nil
}
// First check the top level dependency manager.
for _, id := range changes {
checkedCounter.Add(1)
@ -1411,7 +1410,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
}
// Handle cascades first to get any default dates set.
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
if keyPage == "" {
// Home page gets it's cascade from the site config.
cascade = sa.conf.Cascade.Config
@ -1423,7 +1422,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
} else {
_, data := pw.WalkContext.Data().LongestPrefix(paths.Dir(keyPage))
if data != nil {
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
}
}
@ -1505,11 +1504,11 @@ func (sa *sitePagesAssembler) applyAggregates() error {
pageResource := rs.r.(*pageState)
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
pageResource.m.resourcePath = relPath
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
// Apply cascade (if set) to the page.
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
if data != nil {
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
}
if err := pageResource.setMetaPost(cascade); err != nil {
return false, err
@ -1573,10 +1572,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
const eventName = "dates"
if p.Kind() == kinds.KindTerm {
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
_, data := pw.WalkContext.Data().LongestPrefix(s)
if data != nil {
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
}
if err := p.setMetaPost(cascade); err != nil {
return false, err
@ -1645,6 +1644,8 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
views = sa.pageMap.cfg.taxonomyConfig.views
)
rebuild := sa.s.h.isRebuild()
lockType := doctree.LockTypeWrite
w := &doctree.NodeShiftTreeWalker[contentNodeI]{
Tree: pages,
@ -1677,6 +1678,14 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
pi := sa.Site.Conf.PathParser().Parse(files.ComponentFolderContent, viewTermKey+"/_index.md")
term := pages.Get(pi.Base())
if term == nil {
if rebuild {
// A new tag was added in server mode.
taxonomy := pages.Get(viewName.pluralTreeKey)
if taxonomy != nil {
sa.assembleChanges.Add(taxonomy.GetIdentity())
}
}
m := &pageMeta{
term: v,
singular: viewName.singular,
@ -1684,7 +1693,9 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
pathInfo: pi,
pageMetaParams: &pageMetaParams{
pageConfig: &pagemeta.PageConfig{
Kind: kinds.KindTerm,
PageConfigEarly: pagemeta.PageConfigEarly{
Kind: kinds.KindTerm,
},
},
},
}
@ -1945,7 +1956,9 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
pageMetaParams: &pageMetaParams{
pageConfig: &pagemeta.PageConfig{
Kind: kind,
PageConfigEarly: pagemeta.PageConfigEarly{
Kind: kind,
},
},
},
standaloneOutputFormat: f,
@ -2071,7 +2084,9 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
pathInfo: p,
pageMetaParams: &pageMetaParams{
pageConfig: &pagemeta.PageConfig{
Kind: kinds.KindHome,
PageConfigEarly: pagemeta.PageConfigEarly{
Kind: kinds.KindHome,
},
},
},
}
@ -2104,7 +2119,9 @@ func (sa *sitePagesAssembler) addMissingTaxonomies() error {
pathInfo: sa.Conf.PathParser().Parse(files.ComponentFolderContent, key+"/_index.md"),
pageMetaParams: &pageMetaParams{
pageConfig: &pagemeta.PageConfig{
Kind: kinds.KindTaxonomy,
PageConfigEarly: pagemeta.PageConfigEarly{
Kind: kinds.KindTaxonomy,
},
},
},
singular: viewName.singular,

View file

@ -44,14 +44,14 @@ tags: ["mytag"]
`, "sect/no-list.md", `
---
title: No List
_build:
build:
list: false
---
`, "sect/no-render.md", `
---
title: No List
_build:
build:
render: false
---
`,
@ -59,14 +59,14 @@ _build:
---
title: No Render Link
aliases: ["/link-alias"]
_build:
build:
render: link
---
`,
"sect/no-publishresources/index.md", `
---
title: No Publish Resources
_build:
build:
publishResources: false
---
@ -81,7 +81,7 @@ headless: true
---
title: Headless Local Lists
cascade:
_build:
build:
render: false
list: local
publishResources: false
@ -365,7 +365,7 @@ Data1: {{ $data1.RelPermalink }}
`)
b.WithContent("section/bundle-false/index.md", `---\ntitle: BundleFalse
_build:
build:
publishResources: false
---`,
"section/bundle-false/data1.json", "Some data1",
@ -388,7 +388,7 @@ func TestNoRenderAndNoPublishResources(t *testing.T) {
noRenderPage := `
---
title: %s
_build:
build:
render: false
publishResources: false
---

View file

@ -324,6 +324,14 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
}
}
// Handle new terms from assemblePagesStep2.
changes = bcfg.WhatChanged.Drain()
if len(changes) > 0 {
if err := h.resolveAndClearStateForIdentities(ctx, l, nil, changes); err != nil {
return err
}
}
h.renderFormats = output.Formats{}
for _, s := range h.Sites {
s.s.initRenderFormats()

View file

@ -263,7 +263,7 @@ func (s *IntegrationTestBuilder) AssertLogContains(els ...string) {
}
}
// AssertLogNotContains asserts that the last build log does matches the given regular expressions.
// AssertLogMatches asserts that the last build log matches the given regular expressions.
// The regular expressions can be negated with a "! " prefix.
func (s *IntegrationTestBuilder) AssertLogMatches(expression string) {
s.Helper()

View file

@ -19,7 +19,6 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"github.com/gohugoio/hugo/hugofs"
@ -29,6 +28,7 @@ import (
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/related"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/tpl/tplimpl"
"github.com/spf13/afero"
@ -110,8 +110,7 @@ type pageState struct {
*pageCommon
resource.Staler
dependencyManager identity.Manager
resourcesPublishInit *sync.Once
dependencyManager identity.Manager
}
func (p *pageState) incrPageOutputTemplateVariation() {
@ -522,39 +521,35 @@ func (p *pageState) initPage() error {
}
func (p *pageState) renderResources() error {
var initErr error
for _, r := range p.Resources() {
p.resourcesPublishInit.Do(func() {
for _, r := range p.Resources() {
if _, ok := r.(page.Page); ok {
if _, ok := r.(page.Page); ok {
if p.s.h.buildCounter.Load() == 0 {
// Pages gets rendered with the owning page but we count them here.
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Pages)
continue
}
if _, isWrapper := r.(resource.ResourceWrapper); isWrapper {
// Skip resources that are wrapped.
// These gets published on its own.
continue
}
src, ok := r.(resource.Source)
if !ok {
initErr = fmt.Errorf("resource %T does not support resource.Source", r)
return
}
if err := src.Publish(); err != nil {
if !herrors.IsNotExist(err) {
p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
}
} else {
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
}
continue
}
})
return initErr
if resources.IsPublished(r) {
continue
}
src, ok := r.(resource.Source)
if !ok {
return fmt.Errorf("resource %T does not support resource.Source", r)
}
if err := src.Publish(); err != nil {
if !herrors.IsNotExist(err) {
p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
}
} else {
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
}
}
return nil
}
func (p *pageState) AlternativeOutputFormats() page.OutputFormats {

View file

@ -29,7 +29,6 @@ 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"
@ -54,7 +53,6 @@ type pageMeta struct {
resource.Staler
*pageMetaParams
pageMetaFrontMatter
// Set for standalone pages, e.g. robotsTXT.
standaloneOutputFormat output.Format
@ -73,13 +71,7 @@ type pageMeta struct {
// Prepare for a rebuild of the data passed in from front matter.
func (m *pageMeta) setMetaPostPrepareRebuild() {
params := xmaps.Clone(m.paramsOriginal)
m.pageMetaParams.pageConfig = &pagemeta.PageConfig{
Kind: m.pageConfig.Kind,
Lang: m.pageConfig.Lang,
Path: m.pageConfig.Path,
Params: params,
}
m.pageMetaFrontMatter = pageMetaFrontMatter{}
m.pageMetaParams.pageConfig = pagemeta.ClonePageConfigForRebuild(m.pageMetaParams.pageConfig, params)
}
type pageMetaParams struct {
@ -90,18 +82,17 @@ type pageMetaParams struct {
// These are only set in watch mode.
datesOriginal pagemeta.Dates
paramsOriginal map[string]any // contains the original params as defined in the front matter.
cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter.
}
// From page front matter.
type pageMetaFrontMatter struct {
configuredOutputFormats output.Formats // outputs defined in front matter.
paramsOriginal map[string]any // contains the original params as defined in the front matter.
cascadeOriginal *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // contains the original cascade as defined in the front matter.
}
func (m *pageMetaParams) init(preserveOriginal bool) {
if preserveOriginal {
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
if m.pageConfig.IsFromContentAdapter {
m.paramsOriginal = xmaps.Clone(m.pageConfig.ContentAdapterData)
} else {
m.paramsOriginal = xmaps.Clone(m.pageConfig.Params)
}
m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone()
}
}
@ -261,7 +252,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
// Check for any cascade define on itself.
if cv, found := frontmatter["cascade"]; found {
var err error
cascade, err := page.DecodeCascade(logger, cv)
cascade, err := page.DecodeCascade(logger, true, cv)
if err != nil {
return err
}
@ -299,7 +290,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
return nil
}
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error {
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) error {
ps.m.setMetaPostCount++
var cascadeHashPre uint64
if ps.m.setMetaPostCount > 1 {
@ -311,15 +302,20 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
// Apply cascades first so they can be overridden later.
if cascade != nil {
if ps.m.pageConfig.CascadeCompiled != nil {
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
if !found {
ps.m.pageConfig.CascadeCompiled.Set(k, v)
} else {
// Merge
for ck, cv := range v {
if _, found := vv[ck]; !found {
vv[ck] = cv
for ck, cv := range v.Params {
if _, found := vv.Params[ck]; !found {
vv.Params[ck] = cv
}
}
for ck, cv := range v.Fields {
if _, found := vv.Fields[ck]; !found {
vv.Fields[ck] = cv
}
}
}
@ -348,18 +344,35 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
}
// Cascade is also applied to itself.
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
var err error
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
if !k.Matches(ps) {
return true
}
for kk, vv := range v {
for kk, vv := range v.Params {
if _, found := ps.m.pageConfig.Params[kk]; !found {
ps.m.pageConfig.Params[kk] = vv
}
}
for kk, vv := range v.Fields {
if ps.m.pageConfig.IsFromContentAdapter {
if _, found := ps.m.pageConfig.ContentAdapterData[kk]; !found {
ps.m.pageConfig.ContentAdapterData[kk] = vv
}
} else {
if _, found := ps.m.pageConfig.Params[kk]; !found {
ps.m.pageConfig.Params[kk] = vv
}
}
}
return true
})
if err != nil {
return err
}
if err := ps.setMetaPostParams(); err != nil {
return err
}
@ -405,6 +418,12 @@ func (p *pageState) setMetaPostParams() error {
PathOrTitle: p.pathOrTitle(),
}
if isContentAdapter {
if err := pm.pageConfig.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
return err
}
}
// Handle the date separately
// TODO(bep) we need to "do more" in this area so this can be split up and
// more easily tested without the Page, but the coupling is strong.
@ -531,16 +550,7 @@ params:
for i, s := range o {
o[i] = strings.ToLower(s)
}
if len(o) > 0 {
// Output formats are explicitly set in front matter, use those.
outFormats, err := p.s.conf.OutputFormats.Config.GetByNames(o...)
if err != nil {
p.s.Log.Errorf("Failed to resolve output formats: %s", err)
} else {
pm.configuredOutputFormats = outFormats
params[loki] = outFormats
}
}
pm.pageConfig.Outputs = o
case "draft":
draft = new(bool)
*draft = cast.ToBool(v)
@ -636,9 +646,6 @@ params:
}
for k, v := range userParams {
if _, found := params[k]; found {
p.s.Log.Warnidf(constants.WarnFrontMatterParamsOverrides, "Hugo front matter key %q is overridden in params section.", k)
}
params[strings.ToLower(k)] = v
}
@ -672,7 +679,7 @@ params:
return err
}
if err := pcfg.Compile("", false, ext, p.s.Log, p.s.conf.MediaTypes.Config); err != nil {
if err := pcfg.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
return err
}
@ -829,8 +836,8 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.
// The output formats this page will be rendered to.
func (m *pageMeta) outputFormats() output.Formats {
if len(m.configuredOutputFormats) > 0 {
return m.configuredOutputFormats
if len(m.pageConfig.ConfiguredOutputFormats) > 0 {
return m.pageConfig.ConfiguredOutputFormats
}
return m.s.conf.C.KindOutputFormats[m.Kind()]
}

View file

@ -16,12 +16,12 @@ package hugolib
import (
"fmt"
"strings"
"sync"
"sync/atomic"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/common/constants"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/paths"
@ -40,6 +40,14 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
// Make sure that any partially created page part is marked as stale.
m.MarkStale()
}
if p != nil && pth != nil && p.IsHome() && pth.IsLeafBundle() {
msg := "Using %s in your content's root directory is usually incorrect for your home page. "
msg += "You should use %s instead. If you don't rename this file, your home page will be "
msg += "treated as a leaf bundle, meaning it won't be able to have any child pages or sections."
h.Log.Warnidf(constants.WarnHomePageIsLeafBundle, msg, pth.PathNoLeadingSlash(), strings.ReplaceAll(pth.PathNoLeadingSlash(), "index", "_index"))
}
return p, pth, err
}
@ -190,7 +198,6 @@ func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
pid: pid,
pageOutput: nopPageOutput,
pageOutputTemplateVariationsState: &atomic.Uint32{},
resourcesPublishInit: &sync.Once{},
Staler: m,
dependencyManager: m.s.Conf.NewIdentityManager(m.Path()),
pageCommon: &pageCommon{

View file

@ -1968,3 +1968,35 @@ Title: {{ .Title }}
"deprecated: path in front matter was deprecated",
)
}
// Issue 13538
func TestHomePageIsLeafBundle(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
defaultContentLanguage = 'de'
defaultContentLanguageInSubdir = true
[languages.de]
weight = 1
[languages.en]
weight = 2
-- layouts/all.html --
{{ .Title }}
-- content/index.de.md --
---
title: home de
---
-- content/index.en.org --
---
title: home en
---
`
b := Test(t, files, TestOptWarn())
b.AssertFileContent("public/de/index.html", "home de")
b.AssertFileContent("public/en/index.html", "home en")
b.AssertLogContains("Using index.de.md in your content's root directory is usually incorrect for your home page. You should use _index.de.md instead.")
b.AssertLogContains("Using index.en.org in your content's root directory is usually incorrect for your home page. You should use _index.en.org instead.")
}

View file

@ -91,17 +91,20 @@ func (p *pagesFromDataTemplateContext) AddPage(v any) (string, error) {
pd := pagemeta.DefaultPageConfig
pd.IsFromContentAdapter = true
pd.ContentAdapterData = m
if err := mapstructure.WeakDecode(m, &pd); err != nil {
return "", fmt.Errorf("failed to decode page map: %w", err)
// The rest will be handled after the cascade is calculated and applied.
if err := mapstructure.WeakDecode(pd.ContentAdapterData, &pd.PageConfigEarly); err != nil {
err = fmt.Errorf("failed to decode page map: %w", err)
return "", err
}
p.p.buildState.NumPagesAdded++
if err := pd.Validate(true); err != nil {
return "", err
}
p.p.buildState.NumPagesAdded++
return "", p.p.HandlePage(p.p, &pd)
}

View file

@ -779,3 +779,139 @@ Single.
b.AssertFileContent("public/tags/index.html", "Terms: mytag: 1|§s")
}
func TestContentAdapterOutputsIssue13689(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
[outputs]
page = ['html','json']
-- layouts/page.html --
html: {{ .Title }}
-- layouts/page.json --
json: {{ .Title }}
-- content/p1.md --
---
title: p1
---
-- content/p2.md --
---
title: p2
outputs:
- html
---
-- content/_content.gotmpl --
{{ $page := dict "path" "p3" "title" "p3" }}
{{ $.AddPage $page }}
{{ $page := dict "path" "p4" "title" "p4" "outputs" (slice "html") }}
{{ $.AddPage $page }}
`
b := hugolib.Test(t, files)
b.AssertFileExists("public/p1/index.html", true)
b.AssertFileExists("public/p1/index.json", true)
b.AssertFileExists("public/p2/index.html", true)
b.AssertFileExists("public/p2/index.json", false)
b.AssertFileExists("public/p3/index.html", true)
b.AssertFileExists("public/p3/index.json", true)
b.AssertFileExists("public/p4/index.html", true)
b.AssertFileExists("public/p4/index.json", false) // currently returns true
}
func TestContentAdapterOutputsIssue13692(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','home','sitemap','taxonomy','term']
[[cascade]]
outputs = ['html','json']
[cascade.target]
path = '{/s2,/s4}'
-- layouts/section.html --
html: {{ .Title }}
-- layouts/section.json --
json: {{ .Title }}
-- content/s1/_index.md --
---
title: s1
---
-- content/s2/_index.md --
---
title: s2
---
-- content/_content.gotmpl --
{{ $page := dict "path" "s3" "title" "s3" "kind" "section" }}
{{ $.AddPage $page }}
{{ $page := dict "path" "s4" "title" "s4" "kind" "section" }}
{{ $.AddPage $page }}
{{ $page := dict "path" "s5" "title" "s5" "kind" "section" "outputs" (slice "html") }}
{{ $.AddPage $page }}
`
b := hugolib.Test(t, files)
b.AssertFileExists("public/s1/index.html", true)
b.AssertFileExists("public/s1/index.json", false)
b.AssertFileExists("public/s1/index.xml", true)
b.AssertFileExists("public/s2/index.html", true)
b.AssertFileExists("public/s2/index.json", true)
b.AssertFileExists("public/s2/index.xml", false)
b.AssertFileExists("public/s3/index.html", true)
b.AssertFileExists("public/s3/index.json", false)
b.AssertFileExists("public/s3/index.xml", true)
b.AssertFileExists("public/s4/index.html", true)
b.AssertFileExists("public/s4/index.json", true)
b.AssertFileExists("public/s4/index.xml", false)
b.AssertFileExists("public/s5/index.html", true)
b.AssertFileExists("public/s5/index.json", false)
b.AssertFileExists("public/s5/index.xml", false)
}
func TestContentAdapterCascadeBasic(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableLiveReload = true
-- content/_index.md --
---
cascade:
- title: foo
target:
path: "**"
---
-- layouts/all.html --
Title: {{ .Title }}|Content: {{ .Content }}|
-- content/_content.gotmpl --
{{ $content := dict
"mediaType" "text/markdown"
"value" "The _Hunchback of Notre Dame_ was written by Victor Hugo."
}}
{{ $page := dict "path" "s1" "kind" "page" }}
{{ $.AddPage $page }}
{{ $page := dict "path" "s2" "kind" "page" "title" "bar" "content" $content }}
{{ $.AddPage $page }}
`
b := hugolib.TestRunning(t, files)
b.AssertFileContent("public/s1/index.html", "Title: foo|")
b.AssertFileContent("public/s2/index.html", "Title: bar|", "Content: <p>The <em>Hunchback of Notre Dame</em> was written by Victor Hugo.</p>")
b.EditFileReplaceAll("content/_index.md", "foo", "baz").Build()
b.AssertFileContent("public/s1/index.html", "Title: baz|")
}

View file

@ -57,7 +57,7 @@ Summary: {{ .Summary }}|
)
}
func TestFrontMatterParamsKindPath(t *testing.T) {
func TestFrontMatterParamsPath(t *testing.T) {
t.Parallel()
files := `
@ -72,10 +72,9 @@ date: 2019-08-07
path: "/a/b/c"
slug: "s1"
---
-- content/mysection.md --
-- content/mysection/_index.md --
---
title: "My Section"
kind: "section"
date: 2022-08-07
path: "/a/b"
---
@ -95,66 +94,6 @@ a/b pages: {{ range $ab.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ end }}
)
}
func TestFrontMatterParamsLang(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org/"
disableKinds = ["taxonomy", "term"]
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
-- content/p1.md --
---
title: "P1 nn"
lang: "nn"
---
-- content/p2.md --
---
title: "P2"
---
-- layouts/index.html --
RegularPages: {{ range site.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ end }}$
`
b := Test(t, files)
b.AssertFileContent("public/en/index.html",
"RegularPages: /p2|/en/p2/|P2|$",
)
b.AssertFileContent("public/nn/index.html",
"RegularPages: /p1|/nn/p1/|P1 nn|$",
)
}
func TestFrontMatterTitleOverrideWarn(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org/"
disableKinds = ["taxonomy", "term"]
-- content/p1.md --
---
title: "My title"
params:
title: "My title from params"
---
`
b := Test(t, files, TestOptWarn())
b.AssertLogContains("ARN Hugo front matter key \"title\" is overridden in params section", "You can suppress this warning")
}
func TestFrontMatterParamsLangNoCascade(t *testing.T) {
t.Parallel()

View file

@ -1766,6 +1766,60 @@ MyTemplate: {{ partial "MyTemplate.html" . }}|
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited")
}
func TestRebuildEditInlinePartial13723(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
title = "Foo"
-- layouts/baseof.html --
{{ block "main" . }}Main.{{ end }}
{{ partial "myinlinepartialinbaseof.html" . }}|
{{- define "_partials/myinlinepartialinbaseof.html" }}
My inline partial in baseof.
{{ end }}
-- layouts/_partials/mypartial.html --
Mypartial.
{{ partial "myinlinepartial.html" . }}|
{{- define "_partials/myinlinepartial.html" }}
Mypartial Inline.|{{ .Title }}|
{{ end }}
-- layouts/_partials/myotherpartial.html --
Myotherpartial.
{{ partial "myotherinlinepartial.html" . }}|
{{- define "_partials/myotherinlinepartial.html" }}
Myotherpartial Inline.|{{ .Title }}|
{{ return "myotherinlinepartial" }}
{{ end }}
-- layouts/all.html --
{{ define "main" }}
{{ partial "mypartial.html" . }}|
{{ partial "myotherpartial.html" . }}|
{{ partial "myinlinepartialinall.html" . }}|
{{ end }}
{{- define "_partials/myinlinepartialinall.html" }}
My inline partial in all.
{{ end }}
`
b := TestRunning(t, files)
b.AssertFileContent("public/index.html", "Mypartial.", "Mypartial Inline.|Foo")
// Edit inline partial in partial.
b.EditFileReplaceAll("layouts/_partials/mypartial.html", "Mypartial Inline.", "Mypartial Inline Edited.").Build()
b.AssertFileContent("public/index.html", "Mypartial Inline Edited.|Foo")
// Edit inline partial in baseof.
b.EditFileReplaceAll("layouts/baseof.html", "My inline partial in baseof.", "My inline partial in baseof Edited.").Build()
b.AssertFileContent("public/index.html", "My inline partial in baseof Edited.")
// Edit inline partial in all.
b.EditFileReplaceAll("layouts/all.html", "My inline partial in all.", "My inline partial in all Edited.").Build()
b.AssertFileContent("public/index.html", "My inline partial in all Edited.")
}
func TestRebuildEditAsciidocContentFile(t *testing.T) {
if !asciidocext.Supports() {
t.Skip("skip asciidoc")
@ -1865,3 +1919,50 @@ p1-content|
b.EditFileReplaceAll("content/p1/index.md", "p1-content", "p1-content-foo").Build()
b.AssertFileContent("public/p1/index.html", "p1-content-foo")
}
func TestRebuildEditTagIssue13648(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- layouts/all.html --
All. {{ range .Pages }}{{ .Title }}|{{ end }}
-- content/p1.md --
---
title: "P1"
tags: ["tag1"]
---
`
b := TestRunning(t, files)
b.AssertFileContent("public/tags/index.html", "All. Tag1|")
b.EditFileReplaceAll("content/p1.md", "tag1", "tag2").Build()
// Note that the below is still not correct, as this is effectively a rename, and
// Tag2 should be removed from the list.
// But that is a harder problem to tackle.
b.AssertFileContent("public/tags/index.html", "All. Tag1|Tag2|")
}
func TestRebuildEditNonReferencedResourceIssue13748(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- content/mybundle/index.md --
-- content/mybundle/resource.txt --
This is a resource file.
-- layouts/all.html --
All.
`
b := TestRunning(t, files)
b.AssertFileContent("public/mybundle/resource.txt", "This is a resource file.")
b.EditFileReplaceAll("content/mybundle/resource.txt", "This is a resource file.", "This is an edited resource file.").Build()
b.AssertFileContent("public/mybundle/resource.txt", "This is an edited resource file.")
}

View file

@ -398,6 +398,10 @@ func doRenderShortcode(
return true
}
base, layoutDescriptor := po.GetInternalTemplateBasePathAndDescriptor()
// With shortcodes/mymarkdown.md (only), this allows {{% mymarkdown %}} when rendering HTML,
// but will not resolve any template when doing {{< mymarkdown >}}.
layoutDescriptor.AlwaysAllowPlainText = sc.doMarkup
q := tplimpl.TemplateQuery{
Path: base,
Name: sc.name,
@ -405,10 +409,9 @@ func doRenderShortcode(
Desc: layoutDescriptor,
Consider: include,
}
v := s.TemplateStore.LookupShortcode(q)
v, err := s.TemplateStore.LookupShortcode(q)
if v == nil {
s.Log.Errorf("Unable to locate template for shortcode %q in page %q", sc.name, p.File().Path())
return zeroShortcode, nil
return zeroShortcode, err
}
tmpl = v
hasVariants = hasVariants || len(ofCount) > 1

View file

@ -918,7 +918,7 @@ func TestShortcodeMarkdownOutputFormat(t *testing.T) {
---
title: "p1"
---
{{< foo >}}
{{% foo %}}
# The below would have failed using the HTML template parser.
-- layouts/shortcodes/foo.md --
§§§
@ -930,9 +930,7 @@ title: "p1"
b := Test(t, files)
b.AssertFileContent("public/p1/index.html", `
<x
`)
b.AssertFileContent("public/p1/index.html", "<code>&lt;x")
}
func TestShortcodePreserveIndentation(t *testing.T) {

View file

@ -204,6 +204,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
// Katex is relatively slow.
PoolSize: 8,
Infof: logger.InfoCommand("wasm").Logf,
Warnf: logger.WarnCommand("wasm").Logf,
},
),
}
@ -804,7 +805,7 @@ func (s *Site) initRenderFormats() {
Tree: s.pageMap.treePages,
Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
if p, ok := n.(*pageState); ok {
for _, f := range p.m.configuredOutputFormats {
for _, f := range p.m.pageConfig.ConfiguredOutputFormats {
if !formatSet[f.Name] {
formats = append(formats, f)
formatSet[f.Name] = true

View file

@ -16,7 +16,6 @@ package hugolib
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/gohugoio/hugo/helpers"
@ -89,14 +88,11 @@ aliases: [/Ali%d]
h.Sites[1].PathSpec.ProcessingStats,
}
stats[0].Table(io.Discard)
stats[1].Table(io.Discard)
var buff bytes.Buffer
helpers.ProcessingStatsTable(&buff, stats...)
c.Assert(buff.String(), qt.Contains, "Pages | 21 | 7")
c.Assert(buff.String(), qt.Contains, "Pages │ 21 │ 7")
}
func TestSiteLastmod(t *testing.T) {

View file

@ -615,7 +615,7 @@ var weightedPage5 = `+++
weight = "5"
title = "Five"
[_build]
[build]
render = "never"
+++
Front Matter with Ordered Pages 5`

View file

@ -1,7 +1,17 @@
# Release env.
# These will be replaced by script before release.
HUGORELEASER_TAG=v0.146.7
HUGORELEASER_COMMITISH=1ad3d39dc4693434505fc81f91eed57333017e93
HUGORELEASER_TAG=v0.147.9
HUGORELEASER_COMMITISH=29bdbde19c288d190e889294a862103c6efb70bf

View file

@ -4,6 +4,15 @@ export function readInput(handle) {
let currentLine = [];
const buffer = new Uint8Array(buffSize);
// These are not implemented by QuickJS.
console.warn = (value) => {
console.log(value);
};
console.error = (value) => {
throw new Error(value);
};
// Read all the available bytes
while (true) {
// Stdin file descriptor

View file

@ -1,2 +1,2 @@
(()=>{function l(r){let e=[],a=new Uint8Array(1024);for(;;){let n=0;try{n=Javy.IO.readSync(0,a)}catch(o){if(o.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(n<0)throw new Error("Error reading from stdin");if(n===0)break;if(e=[...e,...a.subarray(0,n)],!e.includes(10))continue;let t=0;for(let o=0;t<e.length;t++)if(e[t]===10){let w=e.splice(o,t+1),f=new Uint8Array(w),c;try{c=JSON.parse(new TextDecoder().decode(f))}catch(d){throw new Error(`Error parsing JSON '${new TextDecoder().decode(f)}' from stdin: ${d.message}`)}try{r(c)}catch(d){let u=c.header;u.err=d.message,i({header:u})}o=t+1}e=e.slice(t)}}function i(r){let s=new TextEncoder().encode(JSON.stringify(r)+`
`),e=new Uint8Array(s);Javy.IO.writeSync(1,e)}var h=function(r){i({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");l(h);})();
(()=>{function w(r){let e=[],c=new Uint8Array(1024);for(console.warn=n=>{console.log(n)},console.error=n=>{throw new Error(n)};;){let o=0;try{o=Javy.IO.readSync(0,c)}catch(a){if(a.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(o<0)throw new Error("Error reading from stdin");if(o===0)break;if(e=[...e,...c.subarray(0,o)],!e.includes(10))continue;let t=0;for(let a=0;t<e.length;t++)if(e[t]===10){let h=e.splice(a,t+1),l=new Uint8Array(h),d;try{d=JSON.parse(new TextDecoder().decode(l))}catch(s){throw new Error(`Error parsing JSON '${new TextDecoder().decode(l)}' from stdin: ${s.message}`)}try{r(d)}catch(s){let u=d.header;u.err=s.message,i({header:u})}a=t+1}e=e.slice(t)}}function i(r){let f=new TextEncoder().encode(JSON.stringify(r)+`
`),e=new Uint8Array(f);Javy.IO.writeSync(1,e)}var g=function(r){i({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");w(g);})();

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,16 @@ const render = function (input) {
const expression = data.expression;
const options = data.options;
const header = input.header;
header.warnings = [];
if (options.strict == 'warn') {
// By default, KaTeX will write to console.warn, that's a little hard to handle.
options.strict = (errorCode, errorMsg) => {
header.warnings.push(
`katex: LaTeX-incompatible input and strict mode is set to 'warn': ${errorMsg} [${errorCode}]`,
);
};
}
// Any error thrown here will be caught by the common.js readInput function.
const output = katex.renderToString(expression, options);
writeOutput({ header: header, data: { output: output } });

View file

@ -45,7 +45,7 @@ type KatexOptions struct {
// A color string given in the format "#XXX" or "#XXXXXX"
ErrorColor string `json:"errorColor"`
// A collection of custom macros.
// A collection of custom macros.
Macros map[string]string `json:"macros,omitempty"`
// Specifies a minimum thickness, in ems, for fraction lines.
@ -53,6 +53,22 @@ type KatexOptions struct {
// If true, KaTeX will throw a ParseError when it encounters an unsupported command.
ThrowOnError bool `json:"throwOnError"`
// Controls how KaTeX handles LaTeX features that offer convenience but
// aren't officially supported, one of error (default), ignore, or warn.
//
// - error: Throws an error when convenient, unsupported LaTeX features
// are encountered.
// - ignore: Allows convenient, unsupported LaTeX features without any
// feedback.
// - warn: Emits a warning when convenient, unsupported LaTeX features are
// encountered.
//
// The "newLineInDisplayMode" error code, which flags the use of \\
// or \newline in display mode outside an array or tabular environment, is
// intentionally designed not to throw an error, despite this behavior
// being questionable.
Strict string `json:"strict"`
}
type KatexOutput struct {

View file

@ -51,6 +51,9 @@ type Header struct {
// Set in the response if there was an error.
Err string `json:"err"`
// Warnings is a list of warnings that may be returned in the response.
Warnings []string `json:"warnings,omitempty"`
}
type Message[T any] struct {
@ -155,6 +158,7 @@ func (p *dispatcherPool[Q, R]) Execute(ctx context.Context, q Message[Q]) (Messa
}
resp, err := call.response, p.Err()
if err == nil && resp.Header.Err != "" {
err = errors.New(resp.Header.Err)
}
@ -270,6 +274,8 @@ type Options struct {
Infof func(format string, v ...any)
Warnf func(format string, v ...any)
// E.g. quickjs wasm. May be omitted if not needed.
Runtime Binary
@ -325,6 +331,7 @@ type dispatcherPool[Q, R any] struct {
counter atomic.Uint32
dispatchers []*dispatcher[Q, R]
close func() error
opts Options
errc chan error
donec chan struct{}
@ -355,6 +362,11 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
// noop
}
}
if opts.Warnf == nil {
opts.Warnf = func(format string, v ...any) {
// noop
}
}
if opts.Memory <= 0 {
// 32 MiB
@ -466,6 +478,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
dp := &dispatcherPool[Q, R]{
dispatchers: make([]*dispatcher[Q, R], len(inOuts)),
opts: opts,
errc: make(chan error, 10),
donec: make(chan struct{}),

Binary file not shown.

Binary file not shown.

View file

@ -104,7 +104,7 @@ func (i Item) ValTyped(source []byte) any {
}
func (i Item) IsText() bool {
return i.Type == tText || i.Type == tIndentation
return i.Type == tText || i.IsIndentation()
}
func (i Item) IsIndentation() bool {
@ -152,7 +152,7 @@ func (i Item) IsFrontMatter() bool {
}
func (i Item) IsDone() bool {
return i.Type == tError || i.Type == tEOF
return i.IsError() || i.IsEOF()
}
func (i Item) IsEOF() bool {
@ -166,18 +166,19 @@ func (i Item) IsError() bool {
func (i Item) ToString(source []byte) string {
val := i.Val(source)
switch {
case i.Type == tEOF:
case i.IsEOF():
return "EOF"
case i.Type == tError:
case i.IsError():
return string(val)
case i.Type == tIndentation:
case i.IsIndentation():
return fmt.Sprintf("%s:[%s]", i.Type, util.VisualizeSpaces(val))
case i.Type > tKeywordMarker:
return fmt.Sprintf("<%s>", val)
case len(val) > 50:
return fmt.Sprintf("%v:%.20q...", i.Type, val)
default:
return fmt.Sprintf("%v:[%s]", i.Type, val)
}
return fmt.Sprintf("%v:[%s]", i.Type, val)
}
type ItemType int

View file

@ -47,3 +47,217 @@ func TestItemValTyped(t *testing.T) {
source = []byte("xtrue")
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, "xtrue")
}
func TestItemBoolMethods(t *testing.T) {
c := qt.New(t)
source := []byte(" shortcode ")
tests := []struct {
name string
item Item
source []byte
want bool
call func(Item, []byte) bool
}{
{
name: "IsText true",
item: Item{Type: tText},
call: func(i Item, _ []byte) bool { return i.IsText() },
want: true,
},
{
name: "IsIndentation false",
item: Item{Type: tText},
call: func(i Item, _ []byte) bool { return i.IsIndentation() },
want: false,
},
{
name: "IsShortcodeName",
item: Item{Type: tScName},
call: func(i Item, _ []byte) bool { return i.IsShortcodeName() },
want: true,
},
{
name: "IsNonWhitespace true",
item: Item{
Type: tText,
low: 2,
high: 11,
},
source: source,
call: func(i Item, src []byte) bool { return i.IsNonWhitespace(src) },
want: true,
},
{
name: "IsShortcodeParam false",
item: Item{Type: tScParamVal},
call: func(i Item, _ []byte) bool { return i.IsShortcodeParam() },
want: false,
},
{
name: "IsInlineShortcodeName",
item: Item{Type: tScNameInline},
call: func(i Item, _ []byte) bool { return i.IsInlineShortcodeName() },
want: true,
},
{
name: "IsLeftShortcodeDelim tLeftDelimScWithMarkup",
item: Item{Type: tLeftDelimScWithMarkup},
call: func(i Item, _ []byte) bool { return i.IsLeftShortcodeDelim() },
want: true,
},
{
name: "IsLeftShortcodeDelim tLeftDelimScNoMarkup",
item: Item{Type: tLeftDelimScNoMarkup},
call: func(i Item, _ []byte) bool { return i.IsLeftShortcodeDelim() },
want: true,
},
{
name: "IsRightShortcodeDelim tRightDelimScWithMarkup",
item: Item{Type: tRightDelimScWithMarkup},
call: func(i Item, _ []byte) bool { return i.IsRightShortcodeDelim() },
want: true,
},
{
name: "IsRightShortcodeDelim tRightDelimScNoMarkup",
item: Item{Type: tRightDelimScNoMarkup},
call: func(i Item, _ []byte) bool { return i.IsRightShortcodeDelim() },
want: true,
},
{
name: "IsShortcodeClose",
item: Item{Type: tScClose},
call: func(i Item, _ []byte) bool { return i.IsShortcodeClose() },
want: true,
},
{
name: "IsShortcodeParamVal",
item: Item{Type: tScParamVal},
call: func(i Item, _ []byte) bool { return i.IsShortcodeParamVal() },
want: true,
},
{
name: "IsShortcodeMarkupDelimiter tLeftDelimScWithMarkup",
item: Item{Type: tLeftDelimScWithMarkup},
call: func(i Item, _ []byte) bool { return i.IsShortcodeMarkupDelimiter() },
want: true,
},
{
name: "IsShortcodeMarkupDelimiter tRightDelimScWithMarkup",
item: Item{Type: tRightDelimScWithMarkup},
call: func(i Item, _ []byte) bool { return i.IsShortcodeMarkupDelimiter() },
want: true,
},
{
name: "IsFrontMatter TypeFrontMatterYAML",
item: Item{Type: TypeFrontMatterYAML},
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
want: true,
},
{
name: "IsFrontMatter TypeFrontMatterTOML",
item: Item{Type: TypeFrontMatterTOML},
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
want: true,
},
{
name: "IsFrontMatter TypeFrontMatterJSON",
item: Item{Type: TypeFrontMatterJSON},
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
want: true,
},
{
name: "IsFrontMatter TypeFrontMatterORG",
item: Item{Type: TypeFrontMatterORG},
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
want: true,
},
{
name: "IsDone tError",
item: Item{Type: tError},
call: func(i Item, _ []byte) bool { return i.IsDone() },
want: true,
},
{
name: "IsDone tEOF",
item: Item{Type: tEOF},
call: func(i Item, _ []byte) bool { return i.IsDone() },
want: true,
},
{
name: "IsEOF",
item: Item{Type: tEOF},
call: func(i Item, _ []byte) bool { return i.IsEOF() },
want: true,
},
{
name: "IsError",
item: Item{Type: tError},
call: func(i Item, _ []byte) bool { return i.IsError() },
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.call(tt.item, tt.source)
c.Assert(got, qt.Equals, tt.want)
})
}
}
func TestItem_ToString(t *testing.T) {
c := qt.New(t)
source := []byte("src")
long := make([]byte, 100)
for i := range long {
long[i] = byte(i)
}
tests := []struct {
name string
item Item
source []byte
want string
call func(Item, []byte) string
}{
{
name: "EOF",
item: Item{Type: tEOF},
call: func(i Item, _ []byte) string { return i.ToString(source) },
want: "EOF",
},
{
name: "Error",
item: Item{Type: tError},
call: func(i Item, _ []byte) string { return i.ToString(source) },
want: "",
},
{
name: "Indentation",
item: Item{Type: tIndentation},
call: func(i Item, _ []byte) string { return i.ToString(source) },
want: "tIndentation:[]",
},
{
name: "Long",
item: Item{Type: tKeywordMarker + 1, low: 0, high: 100},
call: func(i Item, _ []byte) string { return i.ToString(long) },
want: "<" + string(long) + ">",
},
{
name: "Empty",
item: Item{Type: tKeywordMarker + 1},
call: func(i Item, _ []byte) string { return i.ToString([]byte("")) },
want: "<>",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.call(tt.item, tt.source)
c.Assert(got, qt.Equals, tt.want)
})
}
}

View file

@ -16,6 +16,7 @@ package page
import (
"fmt"
"path/filepath"
"slices"
"strings"
"github.com/gohugoio/hugo/common/loggers"
@ -24,7 +25,6 @@ import (
"github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/resources/kinds"
"github.com/mitchellh/mapstructure"
"slices"
)
// A PageMatcher can be used to match a Page with Glob patterns.
@ -105,9 +105,9 @@ func CheckCascadePattern(logger loggers.Logger, m PageMatcher) {
}
}
func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]], error) {
buildConfig := func(in any) (*maps.Ordered[PageMatcher, maps.Params], any, error) {
cascade := maps.NewOrdered[PageMatcher, maps.Params]()
func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]], error) {
buildConfig := func(in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], any, error) {
cascade := maps.NewOrdered[PageMatcher, PageMatcherParamsConfig]()
if in == nil {
return cascade, []map[string]any{}, nil
}
@ -120,7 +120,11 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
for _, m := range ms {
m = maps.CleanConfigStringMap(m)
c, err := mapToPageMatcherParamsConfig(m)
var (
c PageMatcherParamsConfig
err error
)
c, err = mapToPageMatcherParamsConfig(m)
if err != nil {
return nil, nil, err
}
@ -139,24 +143,29 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
if found {
// Merge
for k, v := range cfg.Params {
if _, found := c[k]; !found {
c[k] = v
if _, found := c.Params[k]; !found {
c.Params[k] = v
}
}
for k, v := range cfg.Fields {
if _, found := c.Fields[k]; !found {
c.Fields[k] = v
}
}
} else {
cascade.Set(m, cfg.Params)
cascade.Set(m, cfg)
}
}
return cascade, cfgs, nil
}
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]](in, buildConfig)
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]](in, buildConfig)
}
// DecodeCascade decodes in which could be either a map or a slice of maps.
func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, maps.Params], error) {
conf, err := DecodeCascadeConfig(logger, in)
func DecodeCascade(logger loggers.Logger, handleLegacyFormat bool, in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], error) {
conf, err := DecodeCascadeConfig(logger, handleLegacyFormat, in)
if err != nil {
return nil, err
}
@ -165,36 +174,30 @@ func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, ma
func mapToPageMatcherParamsConfig(m map[string]any) (PageMatcherParamsConfig, error) {
var pcfg PageMatcherParamsConfig
if pcfg.Fields == nil {
pcfg.Fields = make(maps.Params)
}
for k, v := range m {
switch strings.ToLower(k) {
case "params":
// We simplified the structure of the cascade config in Hugo 0.111.0.
// There is a small chance that someone has used the old structure with the params keyword,
// those values will now be moved to the top level.
// This should be very unlikely as it would lead to constructs like .Params.params.foo,
// and most people see params as an Hugo internal keyword.
params := maps.ToStringMap(v)
if pcfg.Params == nil {
pcfg.Params = params
} else {
for k, v := range params {
if _, found := pcfg.Params[k]; !found {
pcfg.Params[k] = v
}
}
}
case "_target", "target":
var target PageMatcher
if err := decodePageMatcher(v, &target); err != nil {
return pcfg, err
}
pcfg.Target = target
default:
// Legacy config.
case "params":
if pcfg.Params == nil {
pcfg.Params = make(maps.Params)
}
pcfg.Params[k] = v
params := maps.ToStringMap(v)
for k, v := range params {
if _, found := pcfg.Params[k]; !found {
pcfg.Params[k] = v
}
}
default:
pcfg.Fields[k] = v
}
}
return pcfg, pcfg.init()
@ -223,10 +226,14 @@ func decodePageMatcher(m any, v *PageMatcher) error {
type PageMatcherParamsConfig struct {
// Apply Params to all Pages matching Target.
Params maps.Params
// Fields holds all fields but Params.
Fields maps.Params
// Target is the PageMatcher that this config applies to.
Target PageMatcher
}
func (p *PageMatcherParamsConfig) init() error {
maps.PrepareParams(p.Params)
maps.PrepareParams(p.Fields)
return nil
}

View file

@ -88,19 +88,18 @@ func TestPageMatcher(t *testing.T) {
c.Assert(err, qt.IsNil)
return v
}
// Legacy.
c.Assert(fn(map[string]any{"_target": map[string]any{"kind": "page"}, "foo": "bar"}), qt.DeepEquals, PageMatcherParamsConfig{
Params: maps.Params{
Fields: maps.Params{
"foo": "bar",
},
Target: PageMatcher{Path: "", Kind: "page", Lang: "", Environment: ""},
})
// Current format.
c.Assert(fn(map[string]any{"target": map[string]any{"kind": "page"}, "params": map[string]any{"foo": "bar"}}), qt.DeepEquals, PageMatcherParamsConfig{
Params: maps.Params{
"foo": "bar",
},
Fields: maps.Params{},
Target: PageMatcher{Path: "", Kind: "page", Lang: "", Environment: ""},
})
})
@ -129,21 +128,22 @@ func TestDecodeCascadeConfig(t *testing.T) {
},
}
got, err := DecodeCascadeConfig(loggers.NewDefault(), in)
got, err := DecodeCascadeConfig(loggers.NewDefault(), true, in)
c.Assert(err, qt.IsNil)
c.Assert(got, qt.IsNotNil)
c.Assert(got.Config.Keys(), qt.DeepEquals, []PageMatcher{{Kind: "page", Environment: "production"}, {Kind: "page"}})
c.Assert(got.Config.Values(), qt.DeepEquals, []maps.Params{{"a": string("av")}, {"b": string("bv")}})
c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{
{
Params: maps.Params{"a": string("av")},
Fields: maps.Params{},
Target: PageMatcher{Kind: "page", Environment: "production"},
},
{Params: maps.Params{"b": string("bv")}, Target: PageMatcher{Kind: "page"}},
{Params: maps.Params{"b": string("bv")}, Fields: maps.Params{}, Target: PageMatcher{Kind: "page"}},
})
got, err = DecodeCascadeConfig(loggers.NewDefault(), nil)
got, err = DecodeCascadeConfig(loggers.NewDefault(), true, nil)
c.Assert(err, qt.IsNil)
c.Assert(got, qt.IsNotNil)
}

View file

@ -29,9 +29,11 @@ import (
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/markup"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/resources/kinds"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
"github.com/mitchellh/mapstructure"
"github.com/gohugoio/hugo/helpers"
@ -74,35 +76,43 @@ func (d Dates) IsAllDatesZero() bool {
return d.Date.IsZero() && d.Lastmod.IsZero() && d.PublishDate.IsZero() && d.ExpiryDate.IsZero()
}
// Page config that needs to be set early. These cannot be modified by cascade.
type PageConfigEarly struct {
Kind string // The kind of page, e.g. "page", "section", "home" etc. This is usually derived from the content path.
Path string // The canonical path to the page, e.g. /sect/mypage. Note: Leading slash, no trailing slash, no extensions or language identifiers.
Lang string // The language code for this page. This is usually derived from the module mount or filename.
Cascade []map[string]any
// Content holds the content for this page.
Content Source
}
// PageConfig configures a Page, typically from front matter.
// Note that all the top level fields are reserved Hugo keywords.
// Any custom configuration needs to be set in the Params map.
type PageConfig struct {
Dates Dates `json:"-"` // Dates holds the four core dates for this page.
DatesStrings
Title string // The title of the page.
LinkTitle string // The link title of the page.
Type string // The content type of the page.
Layout string // The layout to use for to render this page.
Weight int // The weight of the page, used in sorting if set to a non-zero value.
Kind string // The kind of page, e.g. "page", "section", "home" etc. This is usually derived from the content path.
Path string // The canonical path to the page, e.g. /sect/mypage. Note: Leading slash, no trailing slash, no extensions or language identifiers.
Lang string // The language code for this page. This is usually derived from the module mount or filename.
URL string // The URL to the rendered page, e.g. /sect/mypage.html.
Slug string // The slug for this page.
Description string // The description for this page.
Summary string // The summary for this page.
Draft bool // Whether or not the content is a draft.
Headless bool `json:"-"` // Whether or not the page should be rendered.
IsCJKLanguage bool // Whether or not the content is in a CJK language.
TranslationKey string // The translation key for this page.
Keywords []string // The keywords for this page.
Aliases []string // The aliases for this page.
Outputs []string // The output formats to render this page in. If not set, the site's configured output formats for this page kind will be used.
PageConfigEarly `mapstructure:",squash"`
Title string // The title of the page.
LinkTitle string // The link title of the page.
Type string // The content type of the page.
Layout string // The layout to use for to render this page.
Weight int // The weight of the page, used in sorting if set to a non-zero value.
URL string // The URL to the rendered page, e.g. /sect/mypage.html.
Slug string // The slug for this page.
Description string // The description for this page.
Summary string // The summary for this page.
Draft bool // Whether or not the content is a draft.
Headless bool `json:"-"` // Whether or not the page should be rendered.
IsCJKLanguage bool // Whether or not the content is in a CJK language.
TranslationKey string // The translation key for this page.
Keywords []string // The keywords for this page.
Aliases []string // The aliases for this page.
Outputs []string // The output formats to render this page in. If not set, the site's configured output formats for this page kind will be used.
FrontMatterOnlyValues `mapstructure:"-" json:"-"`
Cascade []map[string]any
Sitemap config.SitemapConfig
Build BuildConfig
Menus any // Can be a string, []string or map[string]any.
@ -110,13 +120,29 @@ type PageConfig struct {
// User defined params.
Params maps.Params
// Content holds the content for this page.
Content Source
// The raw data from the content adapter.
// TODO(bep) clean up the ContentAdapterData vs Params.
ContentAdapterData map[string]any `mapstructure:"-" json:"-"`
// Compiled values.
CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"`
ContentMediaType media.Type `mapstructure:"-" json:"-"`
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
CascadeCompiled *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] `mapstructure:"-" json:"-"`
ContentMediaType media.Type `mapstructure:"-" json:"-"`
ConfiguredOutputFormats output.Formats `mapstructure:"-" json:"-"`
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
}
func ClonePageConfigForRebuild(p *PageConfig, params map[string]any) *PageConfig {
pp := &PageConfig{
PageConfigEarly: p.PageConfigEarly,
IsFromContentAdapter: p.IsFromContentAdapter,
}
if pp.IsFromContentAdapter {
pp.ContentAdapterData = params
} else {
pp.Params = params
}
return pp
}
var DefaultPageConfig = PageConfig{
@ -149,8 +175,7 @@ func (p *PageConfig) Validate(pagesFromData bool) error {
return nil
}
// Compile sets up the page configuration after all fields have been set.
func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, logger loggers.Logger, mediaTypes media.Types) error {
func (p *PageConfig) CompileForPagesFromDataPre(basePath string, logger loggers.Logger, mediaTypes media.Types) error {
// In content adapters, we always get relative paths.
if basePath != "" {
p.Path = path.Join(basePath, p.Path)
@ -158,12 +183,32 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo
if p.Params == nil {
p.Params = make(maps.Params)
} else if pagesFromData {
p.Params = maps.PrepareParamsClone(p.Params)
} else {
maps.PrepareParams(p.Params)
p.Params = maps.PrepareParamsClone(p.Params)
}
if p.Kind == "" {
p.Kind = kinds.KindPage
}
if p.Cascade != nil {
cascade, err := page.DecodeCascade(logger, false, p.Cascade)
if err != nil {
return fmt.Errorf("failed to decode cascade: %w", err)
}
p.CascadeCompiled = cascade
}
// Note that NormalizePathStringBasic will make sure that we don't preserve the unnormalized path.
// We do that when we create pages from the file system; mostly for backward compatibility,
// but also because people tend to use use the filename to name their resources (with spaces and all),
// and this isn't relevant when creating resources from an API where it's easy to add textual meta data.
p.Path = paths.NormalizePathStringBasic(p.Path)
return p.compilePrePost("", mediaTypes)
}
func (p *PageConfig) compilePrePost(ext string, mediaTypes media.Types) error {
if p.Content.Markup == "" && p.Content.MediaType == "" {
if ext == "" {
ext = "md"
@ -194,25 +239,37 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo
if p.Content.Markup == "" {
p.Content.Markup = p.ContentMediaType.SubType
}
return nil
}
if pagesFromData {
if p.Kind == "" {
p.Kind = kinds.KindPage
// Compile sets up the page configuration after all fields have been set.
func (p *PageConfig) Compile(ext string, logger loggers.Logger, outputFormats output.Formats, mediaTypes media.Types) error {
if p.IsFromContentAdapter {
if err := mapstructure.WeakDecode(p.ContentAdapterData, p); err != nil {
err = fmt.Errorf("failed to decode page map: %w", err)
return err
}
// Note that NormalizePathStringBasic will make sure that we don't preserve the unnormalized path.
// We do that when we create pages from the file system; mostly for backward compatibility,
// but also because people tend to use use the filename to name their resources (with spaces and all),
// and this isn't relevant when creating resources from an API where it's easy to add textual meta data.
p.Path = paths.NormalizePathStringBasic(p.Path)
// Not needed anymore.
p.ContentAdapterData = nil
}
if p.Cascade != nil {
cascade, err := page.DecodeCascade(logger, p.Cascade)
if p.Params == nil {
p.Params = make(maps.Params)
} else {
maps.PrepareParams(p.Params)
}
if err := p.compilePrePost(ext, mediaTypes); err != nil {
return err
}
if len(p.Outputs) > 0 {
outFormats, err := outputFormats.GetByNames(p.Outputs...)
if err != nil {
return fmt.Errorf("failed to decode cascade: %w", err)
return fmt.Errorf("failed to resolve output formats %v: %w", p.Outputs, err)
} else {
p.ConfiguredOutputFormats = outFormats
}
p.CascadeCompiled = cascade
}
return nil

View file

@ -22,6 +22,7 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/resources/page/pagemeta"
@ -175,7 +176,7 @@ func TestContentMediaTypeFromMarkup(t *testing.T) {
} {
var pc pagemeta.PageConfig
pc.Content.Markup = test.in
c.Assert(pc.Compile("", true, "", logger, media.DefaultTypes), qt.IsNil)
c.Assert(pc.Compile("", logger, output.DefaultFormats, media.DefaultTypes), qt.IsNil)
c.Assert(pc.ContentMediaType.Type, qt.Equals, test.expected)
}
}

View file

@ -31,7 +31,7 @@ func TestDecodeBuildConfig(t *testing.T) {
c := qt.New(t)
configTempl := `
[_build]
[build]
render = %s
list = %s
publishResources = true`
@ -82,7 +82,7 @@ publishResources = true`
} {
cfg, err := config.FromConfigString(fmt.Sprintf(configTempl, test.args...), "toml")
c.Assert(err, qt.IsNil)
bcfg, err := DecodeBuildConfig(cfg.Get("_build"))
bcfg, err := DecodeBuildConfig(cfg.Get("build"))
c.Assert(err, qt.IsNil)
eq := qt.CmpEquals(hqt.DeepAllowUnexported(BuildConfig{}))

View file

@ -311,7 +311,7 @@ func (l PermalinkExpander) pageToPermalinkSections(p Page, _ string) (string, er
// pageToPermalinkContentBaseName returns the URL-safe form of the content base name.
func (l PermalinkExpander) pageToPermalinkContentBaseName(p Page, _ string) (string, error) {
return l.urlize(p.PathInfo().BaseNameNoIdentifier()), nil
return l.urlize(p.PathInfo().Unnormalized().BaseNameNoIdentifier()), nil
}
// pageToPermalinkSlugOrContentBaseName returns the URL-safe form of the slug, content base name.

View file

@ -14,6 +14,7 @@
package page_test
import (
"strings"
"testing"
"github.com/bep/logg"
@ -343,3 +344,29 @@ slug: "c2slug"
b.AssertFileContent("public/myc/c1/index.html", "C1|/myc/c1/|term|")
b.AssertFileContent("public/myc/c2slug/index.html", "C2|/myc/c2slug/|term|")
}
func TestIssue13755(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
disablePathToLower = false
[permalinks.page]
s1 = "/:contentbasename"
-- content/s1/aBc.md --
---
title: aBc
---
-- layouts/all.html --
{{ .Title }}
`
b := hugolib.Test(t, files)
b.AssertFileExists("public/abc/index.html", true)
files = strings.ReplaceAll(files, "disablePathToLower = false", "disablePathToLower = true")
b = hugolib.Test(t, files)
b.AssertFileExists("public/aBc/index.html", true)
}

View file

@ -24,6 +24,7 @@ import (
"sync/atomic"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/lazy"
"github.com/gohugoio/hugo/resources/internal"
"github.com/gohugoio/hugo/common/hashing"
@ -54,6 +55,7 @@ var (
_ identity.DependencyManagerProvider = (*genericResource)(nil)
_ identity.Identity = (*genericResource)(nil)
_ fileInfo = (*genericResource)(nil)
_ isPublishedProvider = (*genericResource)(nil)
)
type ResourceSourceDescriptor struct {
@ -242,6 +244,7 @@ type baseResourceInternal interface {
fileInfo
mediaTypeAssigner
targetPather
isPublishedProvider
ReadSeekCloser() (hugio.ReadSeekCloser, error)
@ -355,7 +358,7 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo {
// genericResource represents a generic linkable resource.
type genericResource struct {
publishInit *sync.Once
publishInit *lazy.OnceMore
key string
keyInit *sync.Once
@ -536,6 +539,10 @@ func (l *genericResource) Publish() error {
return err
}
func (l *genericResource) isPublished() bool {
return l.publishInit.Done()
}
func (l *genericResource) RelPermalink() string {
return l.spec.PathSpec.GetBasePath(false) + paths.PathEscape(l.paths.TargetLink())
}
@ -629,7 +636,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour
}
func (l genericResource) clone() *genericResource {
l.publishInit = &sync.Once{}
l.publishInit = &lazy.OnceMore{}
l.keyInit = &sync.Once{}
return &l
}
@ -643,6 +650,10 @@ type targetPather interface {
TargetPath() string
}
type isPublishedProvider interface {
isPublished() bool
}
type resourceHash struct {
value uint64
size int64
@ -702,6 +713,11 @@ func InternalResourceSourcePathBestEffort(r resource.Resource) string {
return InternalResourceTargetPath(r)
}
// isPublished returns true if the resource is published.
func IsPublished(r resource.Resource) bool {
return r.(isPublishedProvider).isPublished()
}
type targetPathProvider interface {
// targetPath is the relative path to this resource.
// In most cases this will be the same as the RelPermalink(),

View file

@ -81,11 +81,6 @@ type ResourceWithoutMeta interface {
ResourceDataProvider
}
type ResourceWrapper interface {
UnwrappedResource() Resource
WrapResource(Resource) ResourceWrapper
}
type ResourceTypeProvider interface {
// ResourceType is the resource type. For most file types, this is the main
// part of the MIME type, e.g. "image", "application", "text" etc.

View file

@ -20,6 +20,7 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/lazy"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/resources/internal"
"github.com/gohugoio/hugo/resources/jsconfig"
@ -189,7 +190,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro
gr := &genericResource{
Staler: &AtomicStaler{},
h: &resourceHash{},
publishInit: &sync.Once{},
publishInit: &lazy.OnceMore{},
keyInit: &sync.Once{},
includeHashInKey: isImage,
paths: rp,

View file

@ -69,7 +69,6 @@ func (c *PostCSSClient) Process(res resources.ResourceTransformer, options map[s
}
type InlineImports struct {
// Service `mapstructure:",squash"`
// Enable inlining of @import statements.
// Does so recursively, but currently once only per file;
// that is, it's not possible to import the same file in
@ -78,6 +77,11 @@ type InlineImports struct {
// so you can have @import anywhere in the file.
InlineImports bool
// See issue https://github.com/gohugoio/hugo/issues/13719
// Disable inlining of @import statements
// This is currenty only used for css.TailwindCSS.
DisableInlineImports bool
// When InlineImports is enabled, we fail the build if an import cannot be resolved.
// You can enable this to allow the build to continue and leave the import statement in place.
// Note that the inline importer does not process url location or imports with media queries,

View file

@ -129,9 +129,11 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
t.rs.Assets.Fs, t.rs.Logger, ctx.DependencyManager,
)
src, err = imp.resolve()
if err != nil {
return err
if !options.InlineImports.DisableInlineImports {
src, err = imp.resolve()
if err != nil {
return err
}
}
go func() {
@ -146,7 +148,11 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
Cause: err,
}
}
return imp.toFileError(errBuf.String())
s := errBuf.String()
if options.InlineImports.DisableInlineImports && strings.Contains(s, "Can't resolve") {
s += "You may want to set the 'disableInlineImports' option to false to inline imports, see https://gohugo.io/functions/css/tailwindcss/#disableinlineimports"
}
return imp.toFileError(s)
}
return nil

View file

@ -17,6 +17,7 @@ import (
"testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib"
)
@ -70,3 +71,66 @@ CSS: {{ $css.Content | safeCSS }}|
b.AssertFileContent("public/index.html", "/*! tailwindcss v4.")
}
func TestTailwindCSSNoInlineImportsIssue13719(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
theme = 'my-theme'
[[module.mounts]]
source = 'assets'
target = 'assets'
[[module.mounts]]
source = 'other'
target = 'assets/css'
-- assets/css/main.css --
@import "tailwindcss";
@import "colors/red.css";
@import "colors/blue.css";
@import "colors/purple.css";
-- assets/css/colors/red.css --
@import "green.css";
.red {color: red;}
-- assets/css/colors/green.css --
.green {color: green;}
-- themes/my-theme/assets/css/colors/blue.css --
.blue {color: blue;}
-- other/colors/purple.css --
.purple {color: purple;}
-- layouts/home.html --
{{ with (templates.Defer (dict "key" "global")) }}
{{ with resources.Get "css/main.css" }}
{{ $opts := dict "disableInlineImports" true }}
{{ with . | css.TailwindCSS $opts }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{ end }}
{{ end }}
{{ end }}
-- package.json --
{
"devDependencies": {
"@tailwindcss/cli": "^4.1.7",
"tailwindcss": "^4.1.7"
}
}
`
b, err := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
NeedsOsFS: true,
NeedsNpmInstall: true,
LogLevel: logg.LevelInfo,
}).BuildE()
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, "Can't resolve 'colors/red.css'")
b.Assert(err.Error(), qt.Contains, "You may want to set the 'disableInlineImports' option to false")
}

View file

@ -61,6 +61,7 @@ var (
_ identity.DependencyManagerProvider = (*resourceAdapter)(nil)
_ identity.IdentityGroupProvider = (*resourceAdapter)(nil)
_ resource.NameNormalizedProvider = (*resourceAdapter)(nil)
_ isPublishedProvider = (*resourceAdapter)(nil)
)
// These are transformations that need special support in Hugo that may not
@ -325,6 +326,11 @@ func (r *resourceAdapter) Publish() error {
return r.target.Publish()
}
func (r *resourceAdapter) isPublished() bool {
r.init(false, false)
return r.target.isPublished()
}
func (r *resourceAdapter) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
r.init(false, false)
return r.target.ReadSeekCloser()

View file

@ -1,7 +1,7 @@
# Test the new command.
hugo new site -h
stdout 'Create a new site in the provided directory'
stdout 'Create a new site at the specified path.'
hugo new site my-yaml-site --format yml
checkfile my-yaml-site/hugo.yml
hugo new site mysite -f
@ -19,7 +19,7 @@ exists themes
! exists resources
hugo new theme -h
stdout 'Create a new theme \(skeleton\) called \[name\] in ./themes'
stdout 'Create a new theme with the specified name in the ./themes directory.'
hugo new theme mytheme --format yml
stdout 'Creating new theme'
! exists resources

View file

@ -15,6 +15,7 @@ package template
import (
"fmt"
"iter"
"github.com/gohugoio/hugo/common/types"
template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
@ -38,6 +39,19 @@ func (t *Template) Prepare() (*template.Template, error) {
return t.text, nil
}
func (t *Template) All() iter.Seq[*Template] {
return func(yield func(t *Template) bool) {
ns := t.nameSpace
ns.mu.Lock()
defer ns.mu.Unlock()
for _, v := range ns.set {
if !yield(v) {
return
}
}
}
}
// See https://github.com/golang/go/issues/5884
func StripTags(html string) string {
return stripTags(html)

View file

@ -17,6 +17,7 @@ import (
"context"
"fmt"
"io"
"iter"
"reflect"
"github.com/gohugoio/hugo/common/herrors"
@ -433,3 +434,18 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
func isTrue(val reflect.Value) (truth, ok bool) {
return hreflect.IsTruthfulValue(val), true
}
func (t *Template) All() iter.Seq[*Template] {
return func(yield func(t *Template) bool) {
if t.common == nil {
return
}
t.muTmpl.RLock()
defer t.muTmpl.RUnlock()
for _, v := range t.tmpl {
if !yield(v) {
return
}
}
}
}

View file

@ -533,7 +533,7 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode,
t.rangeDepth--
}
switch next.Type() {
case nodeEnd: //done
case nodeEnd: // done
case nodeElse:
// Special case for "else if" and "else with".
// If the "else" is followed immediately by an "if" or "with",

View file

@ -115,6 +115,13 @@ func init() {
},
)
ns.AddMethodMapping(ctx.MaxInt64,
nil,
[][2]string{
{"{{ math.MaxInt64 }}", "9223372036854775807"},
},
)
ns.AddMethodMapping(ctx.Min,
nil,
[][2]string{

View file

@ -147,6 +147,11 @@ func (ns *Namespace) Max(inputs ...any) (maximum float64, err error) {
return ns.applyOpToScalarsOrSlices("Max", math.Max, inputs...)
}
// MaxInt64 returns the maximum value for a signed 64-bit integer.
func (ns *Namespace) MaxInt64() int64 {
return math.MaxInt64
}
// Min returns the smaller of all numbers in inputs. Any slices in inputs are flattened.
func (ns *Namespace) Min(inputs ...any) (minimum float64, err error) {
return ns.applyOpToScalarsOrSlices("Min", math.Min, inputs...)

View file

@ -258,7 +258,7 @@ func TestMod(t *testing.T) {
{int32(3), int32(2), int64(1)},
{int64(3), int64(2), int64(1)},
{"3", "2", int64(1)},
{"3.1", "2", false},
{"3.1", "2", int64(1)},
{"aaa", "0", false},
{"3", "aaa", false},
} {
@ -304,7 +304,7 @@ func TestModBool(t *testing.T) {
{int64(3), int64(2), false},
{"3", "3", true},
{"3", "2", false},
{"3.1", "2", nil},
{"3.1", "2", false},
{"aaa", "0", nil},
{"3", "aaa", nil},
} {
@ -879,3 +879,14 @@ func TestToRadians(t *testing.T) {
c.Assert(result, qt.Equals, test.expect)
}
}
func TestMaxInt64(t *testing.T) {
t.Parallel()
ns := New(nil)
var want int64 = 9223372036854775807
got := ns.MaxInt64()
if want != got {
t.Errorf("want %d, got %d", want, got)
}
}

View file

@ -133,7 +133,7 @@ func (ns *Namespace) lookup(name string) (*tplimpl.TemplInfo, error) {
if strings.HasPrefix(name, "partials/") {
// This is most likely not what the user intended.
// This worked before Hugo 0.146.0.
ns.deps.Log.Warnidf(constants.WarnPartialSuperfluousPrefix, "Partial name %q starting with 'partials/' (as in {{ partial \"%s\"}}) is most likely not what you want. Before 0.146.0 we did a double lookup in this situation.", name, name)
ns.deps.Log.Warnidf(constants.WarnPartialSuperfluousPrefix, "Doubtful use of partial function in {{ partial \"%s\"}}), this is most likely not what you want. Consider removing superfluous prefix \"partials/\" from template name given as first function argument.", name)
}
v := ns.deps.TemplateStore.LookupPartial(name)
if v == nil {

View file

@ -299,3 +299,19 @@ P2.
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "P1: P1.\nP2: foo bar")
}
func TestTemplateExistsCaseIssue13684(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
-- layouts/home.html --
P1: {{ templates.Exists "_partials/MyPartial.html" }}|P1: {{ templates.Exists "_partials/mypartial.html" }}|
-- layouts/_partials/MyPartial.html --
MyPartial.
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "P1: true|P1: true|")
}

View file

@ -2,7 +2,7 @@
{{- $pc := site.Config.Privacy.Twitter -}}
{{- if not $pc.Disable -}}
{{- if $pc.Simple -}}
{{- template "_internal/shortcodes/twitter_simple.html" . -}}
{{- template "_shortcodes/twitter_simple.html" . -}}
{{- else -}}
{{- $id := or (.Get "id") "" -}}
{{- $user := or (.Get "user") "" -}}
@ -20,7 +20,7 @@
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
{{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
{{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
{{- else with .Value -}}
{{- (. | transform.Unmarshal).html | safeHTML -}}
{{- else -}}

View file

@ -16,7 +16,7 @@
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
{{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
{{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
{{- else with .Value -}}
{{- if not site.Config.Services.Twitter.DisableInlineCSS }}
{{- template "__h_simple_twitter_css" (dict "ctx" $.ctx) }}

View file

@ -18,7 +18,7 @@ title, then loading.
{{- $pc := site.Config.Privacy.Vimeo }}
{{- if not $pc.Disable }}
{{- if $pc.Simple }}
{{- template "_internal/shortcodes/vimeo_simple.html" . }}
{{- template "_shortcodes/vimeo_simple.html" . }}
{{- else }}
{{- $dnt := cond $pc.EnableDNT 1 0 }}

View file

@ -25,7 +25,7 @@
{{- $request := printf "https://vimeo.com/api/oembed.json?%s" $query -}}
{{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
{{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
{{- else with .Value -}}
{{- with . | transform.Unmarshal -}}
{{- $class := printf "%s %s" "s_video_simple" "__h_video" -}}
@ -37,7 +37,7 @@
{{- $thumbnail := .thumbnail_url -}}
{{- $original := $thumbnail | replaceRE "(_.*\\.)" "." -}}
<div class="{{ $class }}">
<a href="{{ .provider_url }}{{ .video_id }}" rel="noopener" target="_blank">
<a href="{{ .provider_url }}{{ .video_id | string }}" rel="noopener" target="_blank">
<img src="{{ $thumbnail }}" srcset="{{ $thumbnail }} 1x, {{ $original }} 2x" alt="{{ .title }}">
<div class="play">
{{ template "__h_simple_icon_play" $.ctx.Page }}

View file

@ -1,7 +1,7 @@
{{- $pc := site.Config.Privacy.X -}}
{{- if not $pc.Disable -}}
{{- if $pc.Simple -}}
{{- template "_internal/shortcodes/x_simple.html" . -}}
{{- template "_shortcodes/x_simple.html" . -}}
{{- else -}}
{{- $id := or (.Get "id") "" -}}
{{- $user := or (.Get "user") "" -}}
@ -19,7 +19,7 @@
{{- $request := printf "https://publish.x.com/oembed?%s" $query -}}
{{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
{{- warnidf "shortcode-x-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
{{- else with .Value -}}
{{- (. | transform.Unmarshal).html | safeHTML -}}
{{- else -}}

View file

@ -15,7 +15,7 @@
{{- $request := printf "https://publish.x.com/oembed?%s" $query -}}
{{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
{{- warnidf "shortcode-x-simple-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
{{- else with .Value -}}
{{- if not site.Config.Services.X.DisableInlineCSS }}
{{- template "__h_simple_x_css" (dict "ctx" $.ctx) }}

View file

@ -17,6 +17,7 @@ import (
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting/hqt"
"github.com/gohugoio/hugo/hugolib"
)
@ -495,7 +496,7 @@ Content: {{ .Content }}
// Simple mode
files = strings.ReplaceAll(files, "privacy.vimeo.simple = false", "privacy.vimeo.simple = true")
b = hugolib.Test(t, files)
b.AssertFileContent("public/p1/index.html", "c5bf16d87e2a370b")
b.AssertFileContent("public/p1/index.html", "04d861fc957ee638")
// Simple mode with non-existent id
files = strings.ReplaceAll(files, "{{< vimeo 55073825 >}}", "{{< vimeo __id_does_not_exist__ >}}")
@ -696,3 +697,100 @@ title: p2
b.AssertFileContent("public/p1/index.html", "78eb19b5c6f3768f")
b.AssertFileContent("public/p2/index.html", "a6db910a9cf54bc1")
}
func TestShortcodePlainTextVsHTMLTemplateIssue13698(t *testing.T) {
t.Parallel()
filesTemplate := `
-- hugo.toml --
markup.goldmark.renderer.unsafe = true
-- layouts/all.html --
Content: {{ .Content }}|
-- layouts/_shortcodes/mymarkdown.md --
<div>Foo bar</div>
-- content/p1.md --
---
title: p1
---
## A shortcode
SHORTCODE
`
files := strings.ReplaceAll(filesTemplate, "SHORTCODE", "{{% mymarkdown %}}")
b := hugolib.Test(t, files)
b.AssertFileContent("public/p1/index.html", "<div>Foo bar</div>")
files = strings.ReplaceAll(filesTemplate, "SHORTCODE", "{{< mymarkdown >}}")
var err error
b, err = hugolib.TestE(t, files)
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, `no compatible template found for shortcode "mymarkdown" in [/_shortcodes/mymarkdown.md]; note that to use plain text template shortcodes in HTML you need to use the shortcode {{% delimiter`)
}
func TestShortcodeOnlyLanguageInBaseIssue13699And13740(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = 'https://example.org/'
disableLanguages = ['de']
[languages]
[languages.en]
weight = 1
[languages.de]
weight = 2
-- layouts/_shortcodes/de.html --
de.html
-- layouts/all.html --
{{ .Content }}
-- content/_index.md --
---
title: home
---
{{< de >}}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "de.html")
}
func TestShortcodeLanguage13767(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
defaultContentLanguage = 'pl'
defaultContentLanguageInSubdir = true
[languages.pl]
weight = 1
[languages.en]
weight = 2
-- content/_index.md --
---
title: dom
---
{{< myshortcode >}}
-- content/_index.en.md --
---
title: home
---
{{< myshortcode >}}
-- layouts/_shortcodes/myshortcode.html --
myshortcode.html
-- layouts/_shortcodes/myshortcode.en.html --
myshortcode.en.html
-- layouts/all.html --
{{ .Content }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/pl/index.html", "myshortcode.html")
b.AssertFileContent("public/en/index.html", "myshortcode.en.html")
}

View file

@ -37,6 +37,7 @@ type TemplateDescriptor struct {
// Misc.
LayoutFromUserMustMatch bool // If set, we only look for the exact layout.
IsPlainText bool // Whether this is a plain text template.
AlwaysAllowPlainText bool // Whether to e.g. allow plain text templates to be rendered in HTML.
}
func (d *TemplateDescriptor) normalizeFromFile() {
@ -64,7 +65,7 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
return weightNoMatch
}
w := this.doCompare(category, isEmbedded, s.opts.DefaultContentLanguage, other)
w := this.doCompare(category, s.opts.DefaultContentLanguage, other)
if w.w1 <= 0 {
if category == CategoryMarkup && (this.Variant1 == other.Variant1) && (this.Variant2 == other.Variant2 || this.Variant2 != "" && other.Variant2 == "") {
@ -74,7 +75,12 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
}
w.w1 = 1
return w
}
if category == CategoryShortcode {
if (this.IsPlainText == other.IsPlainText || !other.IsPlainText) || this.AlwaysAllowPlainText {
w.w1 = 1
}
}
}
@ -82,13 +88,16 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
}
//lint:ignore ST1006 this vs other makes it easier to reason about.
func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, defaultContentLanguage string, other TemplateDescriptor) weight {
func (this TemplateDescriptor) doCompare(category Category, defaultContentLanguage string, other TemplateDescriptor) weight {
w := weightNoMatch
// HTML in plain text is OK, but not the other way around.
if other.IsPlainText && !this.IsPlainText {
return w
if !this.AlwaysAllowPlainText {
// HTML in plain text is OK, but not the other way around.
if other.IsPlainText && !this.IsPlainText {
return w
}
}
if other.Kind != "" && other.Kind != this.Kind {
return w
}

View file

@ -2,6 +2,7 @@ package tplimpl
import (
"io"
"iter"
"regexp"
"strconv"
"strings"
@ -44,16 +45,15 @@ var embeddedTemplatesAliases = map[string][]string{
"_shortcodes/twitter.html": {"_shortcodes/tweet.html"},
}
func (s *TemplateStore) parseTemplate(ti *TemplInfo) error {
err := s.tns.doParseTemplate(ti)
func (s *TemplateStore) parseTemplate(ti *TemplInfo, replace bool) error {
err := s.tns.doParseTemplate(ti, replace)
if err != nil {
return s.addFileContext(ti, "parse of template failed", err)
}
return err
}
func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
func (t *templateNamespace) doParseTemplate(ti *TemplInfo, replace bool) error {
if !ti.noBaseOf || ti.category == CategoryBaseof {
// Delay parsing until we have the base template.
return nil
@ -68,7 +68,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
if ti.D.IsPlainText {
prototype := t.parseText
if prototype.Lookup(name) != nil {
if !replace && prototype.Lookup(name) != nil {
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
}
templ, err = prototype.New(name).Parse(ti.content)
@ -77,7 +77,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
}
} else {
prototype := t.parseHTML
if prototype.Lookup(name) != nil {
if !replace && prototype.Lookup(name) != nil {
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
}
templ, err = prototype.New(name).Parse(ti.content)
@ -181,19 +181,24 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
return nil
}
func (t *templateNamespace) templatesIn(in tpl.Template) []tpl.Template {
var templs []tpl.Template
if textt, ok := in.(*texttemplate.Template); ok {
for _, t := range textt.Templates() {
templs = append(templs, t)
func (t *templateNamespace) templatesIn(in tpl.Template) iter.Seq[tpl.Template] {
return func(yield func(t tpl.Template) bool) {
switch in := in.(type) {
case *htmltemplate.Template:
for t := range in.All() {
if !yield(t) {
return
}
}
case *texttemplate.Template:
for t := range in.All() {
if !yield(t) {
return
}
}
}
}
if htmlt, ok := in.(*htmltemplate.Template); ok {
for _, t := range htmlt.Templates() {
templs = append(templs, t)
}
}
return templs
}
/*
@ -337,8 +342,6 @@ func (t *templateNamespace) createPrototypes(init bool) error {
t.prototypeHTML = htmltemplate.Must(t.parseHTML.Clone())
t.prototypeText = texttemplate.Must(t.parseText.Clone())
}
// t.execHTML = htmltemplate.Must(t.parseHTML.Clone())
// t.execText = texttemplate.Must(t.parseText.Clone())
return nil
}
@ -350,3 +353,14 @@ func newTemplateNamespace(funcs map[string]any) *templateNamespace {
standaloneText: texttemplate.New("").Funcs(funcs),
}
}
func isText(t tpl.Template) bool {
switch t.(type) {
case *texttemplate.Template:
return true
case *htmltemplate.Template:
return false
default:
panic("unknown template type")
}
}

View file

@ -19,6 +19,7 @@ import (
"bytes"
"context"
"embed"
"errors"
"fmt"
"io"
"io/fs"
@ -113,17 +114,18 @@ func NewStore(opts StoreOptions, siteOpts SiteOptions) (*TemplateStore, error) {
panic("HTML output format not found")
}
s := &TemplateStore{
opts: opts,
siteOpts: siteOpts,
optsOrig: opts,
siteOptsOrig: siteOpts,
htmlFormat: html,
storeSite: configureSiteStorage(siteOpts, opts.Watching),
treeMain: doctree.NewSimpleTree[map[nodeKey]*TemplInfo](),
treeShortcodes: doctree.NewSimpleTree[map[string]map[TemplateDescriptor]*TemplInfo](),
templatesByPath: maps.NewCache[string, *TemplInfo](),
shortcodesByName: maps.NewCache[string, *TemplInfo](),
cacheLookupPartials: maps.NewCache[string, *TemplInfo](),
opts: opts,
siteOpts: siteOpts,
optsOrig: opts,
siteOptsOrig: siteOpts,
htmlFormat: html,
storeSite: configureSiteStorage(siteOpts, opts.Watching),
treeMain: doctree.NewSimpleTree[map[nodeKey]*TemplInfo](),
treeShortcodes: doctree.NewSimpleTree[map[string]map[TemplateDescriptor]*TemplInfo](),
templatesByPath: maps.NewCache[string, *TemplInfo](),
shortcodesByName: maps.NewCache[string, *TemplInfo](),
cacheLookupPartials: maps.NewCache[string, *TemplInfo](),
templatesSnapshotSet: maps.NewCache[*parse.Tree, struct{}](),
// Note that the funcs passed below is just for name validation.
tns: newTemplateNamespace(siteOpts.TemplateFuncs),
@ -142,10 +144,10 @@ func NewStore(opts StoreOptions, siteOpts SiteOptions) (*TemplateStore, error) {
if err := s.insertEmbedded(); err != nil {
return nil, err
}
if err := s.parseTemplates(); err != nil {
if err := s.parseTemplates(false); err != nil {
return nil, err
}
if err := s.extractInlinePartials(); err != nil {
if err := s.extractInlinePartials(false); err != nil {
return nil, err
}
if err := s.transformTemplates(); err != nil {
@ -323,6 +325,9 @@ func (ti *TemplInfo) findBestMatchBaseof(s *TemplateStore, d1 TemplateDescriptor
}
ti.baseVariants.WalkPath(k1, func(k2 string, v map[TemplateDescriptor]*TemplWithBaseApplied) (bool, error) {
if !s.inPath(k1, k2) {
return false, nil
}
slashCountK2 := strings.Count(k2, "/")
distance := slashCountK1 - slashCountK2
@ -420,10 +425,11 @@ type TemplateStore struct {
siteOpts SiteOptions
htmlFormat output.Format
treeMain *doctree.SimpleTree[map[nodeKey]*TemplInfo]
treeShortcodes *doctree.SimpleTree[map[string]map[TemplateDescriptor]*TemplInfo]
templatesByPath *maps.Cache[string, *TemplInfo]
shortcodesByName *maps.Cache[string, *TemplInfo]
treeMain *doctree.SimpleTree[map[nodeKey]*TemplInfo]
treeShortcodes *doctree.SimpleTree[map[string]map[TemplateDescriptor]*TemplInfo]
templatesByPath *maps.Cache[string, *TemplInfo]
shortcodesByName *maps.Cache[string, *TemplInfo]
templatesSnapshotSet *maps.Cache[*parse.Tree, struct{}]
dh descriptorHandler
@ -605,7 +611,7 @@ func (s *TemplateStore) LookupShortcodeByName(name string) *TemplInfo {
return ti
}
func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
func (s *TemplateStore) LookupShortcode(q TemplateQuery) (*TemplInfo, error) {
q.init()
k1 := s.key(q.Path)
@ -615,6 +621,9 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
defer s.putBest(best)
s.treeShortcodes.WalkPath(k1, func(k2 string, m map[string]map[TemplateDescriptor]*TemplInfo) (bool, error) {
if !s.inPath(k1, k2) {
return false, nil
}
slashCountK2 := strings.Count(k2, "/")
distance := slashCountK1 - slashCountK2
@ -624,13 +633,15 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
}
for k, vv := range v {
best.candidates = append(best.candidates, vv)
if !q.Consider(vv) {
continue
}
weight := s.dh.compareDescriptors(q.Category, vv.subCategory == SubCategoryEmbedded, q.Desc, k)
weight.distance = distance
if best.isBetter(weight, vv) {
isBetter := best.isBetter(weight, vv)
if isBetter {
best.updateValues(weight, k2, k, vv)
}
}
@ -638,8 +649,21 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
return false, nil
})
// Any match will do.
return best.templ
if best.w.w1 <= 0 {
var err error
if s := best.candidatesAsStringSlice(); s != nil {
msg := fmt.Sprintf("no compatible template found for shortcode %q in %s", q.Name, s)
if !q.Desc.IsPlainText {
msg += "; note that to use plain text template shortcodes in HTML you need to use the shortcode {{% delimiter"
}
err = errors.New(msg)
} else {
err = fmt.Errorf("no template found for shortcode %q", q.Name)
}
return nil, err
}
return best.templ, nil
}
// PrintDebug is for testing/debugging only.
@ -687,12 +711,16 @@ func (s *TemplateStore) RefreshFiles(include func(fi hugofs.FileMetaInfo) bool)
if err := s.insertTemplates(include, true); err != nil {
return err
}
if err := s.parseTemplates(); err != nil {
if err := s.createTemplatesSnapshot(); err != nil {
return err
}
if err := s.extractInlinePartials(); err != nil {
if err := s.parseTemplates(true); err != nil {
return err
}
if err := s.extractInlinePartials(true); err != nil {
return err
}
if err := s.transformTemplates(); err != nil {
return err
}
@ -706,6 +734,7 @@ func (s *TemplateStore) RefreshFiles(include func(fi hugofs.FileMetaInfo) bool)
}
func (s *TemplateStore) HasTemplate(templatePath string) bool {
templatePath = strings.ToLower(templatePath)
templatePath = paths.AddLeadingSlash(templatePath)
return s.templatesByPath.Contains(templatePath)
}
@ -780,8 +809,18 @@ func (s *TemplateStore) findBestMatchGet(key string, category Category, consider
}
}
func (s *TemplateStore) inPath(k1, k2 string) bool {
if k1 != k2 && !strings.HasPrefix(k1, k2+"/") {
return false
}
return true
}
func (s *TemplateStore) findBestMatchWalkPath(q TemplateQuery, k1 string, slashCountK1 int, best *bestMatch) {
s.treeMain.WalkPath(k1, func(k2 string, v map[nodeKey]*TemplInfo) (bool, error) {
if !s.inPath(k1, k2) {
return false, nil
}
slashCountK2 := strings.Count(k2, "/")
distance := slashCountK1 - slashCountK2
@ -907,61 +946,79 @@ func (s *TemplateStore) extractIdentifiers(line string) []string {
return identifiers
}
func (s *TemplateStore) extractInlinePartials() error {
func (s *TemplateStore) extractInlinePartials(rebuild bool) error {
isPartialName := func(s string) bool {
return strings.HasPrefix(s, "partials/") || strings.HasPrefix(s, "_partials/")
}
p := s.tns
// We may find both inline and external partials in the current template namespaces,
// so only add the ones we have not seen before.
addIfNotSeen := func(isText bool, templs ...tpl.Template) error {
for _, templ := range templs {
if templ.Name() == "" || !isPartialName(templ.Name()) {
continue
}
name := templ.Name()
if !paths.HasExt(name) {
// Assume HTML. This in line with how the lookup works.
name = name + s.htmlFormat.MediaType.FirstSuffix.FullSuffix
}
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
ti, err := s.insertTemplate(pi, nil, false, s.treeMain)
if err != nil {
return err
}
if ti != nil {
ti.Template = templ
ti.noBaseOf = true
ti.subCategory = SubCategoryInline
ti.D.IsPlainText = isText
}
for templ := range s.allRawTemplates() {
if templ.Name() == "" || !isPartialName(templ.Name()) {
continue
}
return nil
}
addIfNotSeen(false, p.templatesIn(p.parseHTML)...)
addIfNotSeen(true, p.templatesIn(p.parseText)...)
for _, t := range p.baseofHtmlClones {
if err := addIfNotSeen(false, p.templatesIn(t)...); err != nil {
if rebuild && s.templatesSnapshotSet.Contains(getParseTree(templ)) {
// This partial was not created during this build.
continue
}
name := templ.Name()
if !paths.HasExt(name) {
// Assume HTML. This in line with how the lookup works.
name = name + s.htmlFormat.MediaType.FirstSuffix.FullSuffix
}
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
ti, err := s.insertTemplate(pi, nil, SubCategoryInline, false, s.treeMain)
if err != nil {
return err
}
}
for _, t := range p.baseofTextClones {
if err := addIfNotSeen(true, p.templatesIn(t)...); err != nil {
return err
if ti != nil {
ti.Template = templ
ti.noBaseOf = true
ti.subCategory = SubCategoryInline
ti.D.IsPlainText = isText(templ)
}
}
return nil
}
func (s *TemplateStore) allRawTemplates() iter.Seq[tpl.Template] {
p := s.tns
return func(yield func(tpl.Template) bool) {
for t := range p.templatesIn(p.parseHTML) {
if !yield(t) {
return
}
}
for t := range p.templatesIn(p.parseText) {
if !yield(t) {
return
}
}
for _, tt := range p.baseofHtmlClones {
for t := range p.templatesIn(tt) {
if !yield(t) {
return
}
}
}
for _, tt := range p.baseofTextClones {
for t := range p.templatesIn(tt) {
if !yield(t) {
return
}
}
}
}
}
func (s *TemplateStore) insertEmbedded() error {
return fs.WalkDir(embeddedTemplatesFs, ".", func(path string, d fs.DirEntry, err error) error {
return fs.WalkDir(embeddedTemplatesFs, ".", func(tpath string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
@ -969,7 +1026,7 @@ func (s *TemplateStore) insertEmbedded() error {
return nil
}
templb, err := embeddedTemplatesFs.ReadFile(path)
templb, err := embeddedTemplatesFs.ReadFile(tpath)
if err != nil {
return err
}
@ -977,7 +1034,7 @@ func (s *TemplateStore) insertEmbedded() error {
// Get the newlines on Windows in line with how we had it back when we used Go Generate
// to write the templates to Go files.
templ := string(bytes.ReplaceAll(templb, []byte("\r\n"), []byte("\n")))
name := strings.TrimPrefix(filepath.ToSlash(path), "embedded/templates/")
name := strings.TrimPrefix(filepath.ToSlash(tpath), "embedded/templates/")
insertOne := func(name, content string) error {
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
@ -991,7 +1048,7 @@ func (s *TemplateStore) insertEmbedded() error {
return err
}
} else {
ti, err = s.insertTemplate(pi, nil, false, s.treeMain)
ti, err = s.insertTemplate(pi, nil, SubCategoryEmbedded, false, s.treeMain)
if err != nil {
return err
}
@ -1007,6 +1064,19 @@ func (s *TemplateStore) insertEmbedded() error {
return nil
}
// Copy the embedded HTML table render hook to each output format.
// See https://github.com/gohugoio/hugo/issues/13351.
if name == path.Join(containerMarkup, "render-table.html") {
for _, of := range s.opts.OutputFormats {
path := paths.TrimExt(name) + "." + of.Name + of.MediaType.FirstSuffix.FullSuffix
if err := insertOne(path, templ); err != nil {
return err
}
}
return nil
}
if err := insertOne(name, templ); err != nil {
return err
}
@ -1072,7 +1142,7 @@ func (s *TemplateStore) insertShortcode(pi *paths.Path, fi hugofs.FileMetaInfo,
return ti, nil
}
func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, replace bool, tree doctree.Tree[map[nodeKey]*TemplInfo]) (*TemplInfo, error) {
func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, subCategory SubCategory, replace bool, tree doctree.Tree[map[nodeKey]*TemplInfo]) (*TemplInfo, error) {
key, _, category, d, err := s.toKeyCategoryAndDescriptor(pi)
// See #13577. Warn for now.
if err != nil {
@ -1086,7 +1156,7 @@ func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, r
return nil, nil
}
return s.insertTemplate2(pi, fi, key, category, d, replace, false, tree)
return s.insertTemplate2(pi, fi, key, category, subCategory, d, replace, false, tree)
}
func (s *TemplateStore) insertTemplate2(
@ -1094,6 +1164,7 @@ func (s *TemplateStore) insertTemplate2(
fi hugofs.FileMetaInfo,
key string,
category Category,
subCategory SubCategory,
d TemplateDescriptor,
replace, isLegacyMapped bool,
tree doctree.Tree[map[nodeKey]*TemplInfo],
@ -1116,12 +1187,26 @@ func (s *TemplateStore) insertTemplate2(
tree.Insert(key, m)
}
if !replace {
if v, found := m[nk]; found {
if len(pi.Identifiers()) >= len(v.PathInfo.Identifiers()) {
// e.g. /pages/home.foo.html and /pages/home.html where foo may be a valid language name in another site.
return nil, nil
}
nkExisting, existingFound := m[nk]
if !replace && existingFound && fi != nil && nkExisting.Fi != nil {
// See issue #13715.
// We do the merge on the file system level, but from Hugo v0.146.0 we have a situation where
// the project may well have a different layouts layout compared to the theme(s) it uses.
// We could possibly have fixed that on a lower (file system) level, but since this is just
// a temporary situation (until all projects are updated),
// do a replace here if the file comes from higher up in the module chain.
replace = fi.Meta().ModuleOrdinal < nkExisting.Fi.Meta().ModuleOrdinal
}
if !replace && existingFound {
// Always replace inline partials to allow for reloading.
replace = subCategory == SubCategoryInline && nkExisting.subCategory == SubCategoryInline
}
if !replace && existingFound {
if len(pi.Identifiers()) >= len(nkExisting.PathInfo.Identifiers()) {
// e.g. /pages/home.foo.html and /pages/home.html where foo may be a valid language name in another site.
return nil, nil
}
}
@ -1148,7 +1233,7 @@ func (s *TemplateStore) insertTemplate2(
return ti, nil
}
func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) bool, replace bool) error {
func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) bool, partialRebuild bool) error {
if include == nil {
include = func(fi hugofs.FileMetaInfo) bool {
return true
@ -1330,7 +1415,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
}
if replace && pi.NameNoIdentifier() == baseNameBaseof {
if partialRebuild && pi.NameNoIdentifier() == baseNameBaseof {
// A baseof file has changed.
resetBaseVariants = true
}
@ -1338,12 +1423,12 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
var ti *TemplInfo
var err error
if pi.Type() == paths.TypeShortcode {
ti, err = s.insertShortcode(pi, fi, replace, s.treeShortcodes)
ti, err = s.insertShortcode(pi, fi, partialRebuild, s.treeShortcodes)
if err != nil || ti == nil {
return err
}
} else {
ti, err = s.insertTemplate(pi, fi, replace, s.treeMain)
ti, err = s.insertTemplate(pi, fi, SubCategoryMain, partialRebuild, s.treeMain)
if err != nil || ti == nil {
return err
}
@ -1377,7 +1462,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
desc.IsPlainText = outputFormat.IsPlainText
desc.MediaType = mediaType.Type
ti, err := s.insertTemplate2(pi, fi, targetPath, category, desc, true, true, s.treeMain)
ti, err := s.insertTemplate2(pi, fi, targetPath, category, SubCategoryMain, desc, true, true, s.treeMain)
if err != nil {
return err
}
@ -1388,6 +1473,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
if err := s.tns.readTemplateInto(ti); err != nil {
return err
}
}
if resetBaseVariants {
@ -1414,7 +1500,15 @@ func (s *TemplateStore) key(dir string) string {
return paths.TrimTrailing(dir)
}
func (s *TemplateStore) parseTemplates() error {
func (s *TemplateStore) createTemplatesSnapshot() error {
s.templatesSnapshotSet.Reset()
for t := range s.allRawTemplates() {
s.templatesSnapshotSet.Set(getParseTree(t), struct{}{})
}
return nil
}
func (s *TemplateStore) parseTemplates(replace bool) error {
if err := func() error {
// Read and parse all templates.
for _, v := range s.treeMain.All() {
@ -1422,7 +1516,7 @@ func (s *TemplateStore) parseTemplates() error {
if vv.state == processingStateTransformed {
continue
}
if err := s.parseTemplate(vv); err != nil {
if err := s.parseTemplate(vv, replace); err != nil {
return err
}
}
@ -1442,7 +1536,7 @@ func (s *TemplateStore) parseTemplates() error {
// The regular expression used to detect if a template needs a base template has some
// rare false positives. Assume we don't need one.
vv.noBaseOf = true
if err := s.parseTemplate(vv); err != nil {
if err := s.parseTemplate(vv, replace); err != nil {
return err
}
continue
@ -1471,7 +1565,7 @@ func (s *TemplateStore) parseTemplates() error {
if vvv.state == processingStateTransformed {
continue
}
if err := s.parseTemplate(vvv); err != nil {
if err := s.parseTemplate(vvv, replace); err != nil {
return err
}
}
@ -1791,10 +1885,11 @@ type TextTemplatHandler interface {
}
type bestMatch struct {
templ *TemplInfo
desc TemplateDescriptor
w weight
key string
templ *TemplInfo
desc TemplateDescriptor
w weight
key string
candidates []*TemplInfo
// settings.
defaultOutputformat string
@ -1805,6 +1900,18 @@ func (best *bestMatch) reset() {
best.w = weight{}
best.desc = TemplateDescriptor{}
best.key = ""
best.candidates = nil
}
func (best *bestMatch) candidatesAsStringSlice() []string {
if len(best.candidates) == 0 {
return nil
}
candidates := make([]string, len(best.candidates))
for i, v := range best.candidates {
candidates[i] = v.PathInfo.Path()
}
return candidates
}
func (best *bestMatch) isBetter(w weight, ti *TemplInfo) bool {
@ -1814,7 +1921,6 @@ func (best *bestMatch) isBetter(w weight, ti *TemplInfo) bool {
}
if w.w1 <= 0 {
if best.w.w1 <= 0 {
return ti.PathInfo.Path() < best.templ.PathInfo.Path()
}

View file

@ -920,6 +920,26 @@ func TestPartialHTML(t *testing.T) {
b.AssertFileContent("public/index.html", "<link rel=\"stylesheet\" href=\"/css/style.css\">")
}
func TestPartialPlainTextInHTML(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
-- layouts/all.html --
<html>
<head>
{{ partial "mypartial.txt" . }}
</head>
</html>
-- layouts/partials/mypartial.txt --
My <div>partial</div>.
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "My &lt;div&gt;partial&lt;/div&gt;.")
}
// Issue #13593.
func TestGoatAndNoGoat(t *testing.T) {
t.Parallel()
@ -1103,6 +1123,35 @@ All.
b.AssertLogContains("unrecognized render hook")
}
func TestLayoutNotFound(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
-- layouts/single.html --
Single.
`
b := hugolib.Test(t, files, hugolib.TestOptWarn())
b.AssertLogContains("WARN found no layout file for \"html\" for kind \"home\"")
}
func TestLayoutOverrideThemeWhenThemeOnOldFormatIssue13715(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
theme = "mytheme"
-- layouts/list.html --
layouts/list.html
-- themes/mytheme/layouts/_default/list.html --
mytheme/layouts/_default/list.html
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "layouts/list.html")
}
func BenchmarkExecuteWithContext(b *testing.B) {
files := `
-- hugo.toml --
@ -1197,8 +1246,8 @@ s2.
Category: tplimpl.CategoryShortcode,
Desc: desc,
}
v := store.LookupShortcode(q)
if v == nil {
v, err := store.LookupShortcode(q)
if v == nil || err != nil {
b.Fatal("not found")
}
}
@ -1396,3 +1445,106 @@ layouts/taxononmy.html.html
b.AssertFileExists("public/p1/index.html", false)
}
}
func TestTemplateLoopBlogVsBlogrollIssue13672(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
-- layouts/blog/_shortcodes/myshortcode.html --
layouts/blog/_shortcodes/myshortcode.html
-- layouts/blog/baseof.html --
blog/baseof.html {{ block "main" . }}{{ end }}
-- layouts/blog/all.html --
{{ define "main" }}blog/all.html|{{ .Content}}{{ end }}
-- layouts/blogroll/_shortcodes/myshortcode.html --
layouts/blogroll/myshortcode.html
-- layouts/blogroll/baseof.html --
{{ block "main" . }}blogroll/baseof.html{{ end }}
-- layouts/blogroll/all.html --
{{ define "main" }}blogroll/all.html|{{ .Content}}{{ end }}
-- content/blog/p1.md --
---
title: p1
---
{{< myshortcode >}}
-- content/blogroll/p1.md --
---
title: p1
---
{{< myshortcode >}}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/blog/p1/index.html", "blog/baseof.html blog/all.html|layouts/blog/_shortcodes/myshortcode.html")
b.AssertFileContent("public/blogroll/p1/index.html", "blogroll/all.html|layouts/blogroll/myshortcode.html")
}
// See issue #13668.
func TestPartialPlainTextVsHTML(t *testing.T) {
t.Parallel()
/*
Note that in the below, there's no output format named txt,
so the isPlainText is fetched from the only output format with that extension.
*/
files := `
-- hugo.toml --
-- layouts/_partials/myhtml.html --
<div>myhtml</div>
-- layouts/_partials/mytext.txt --
<div>mytext</div>
-- layouts/all.html --
myhtml: {{ partial "myhtml.html" . }}
mytext: {{ partial "mytext.txt" . }}
mytexts|safeHTML: {{ partial "mytext.txt" . | safeHTML }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html",
"myhtml: <div>myhtml</div>",
"mytext: &lt;div&gt;mytext&lt;/div&gt;",
"mytexts|safeHTML: <div>mytext</div>",
)
}
func TestIssue13351(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
[outputs]
home = ['html','json']
[outputFormats.html]
weight = 1
[outputFormats.json]
weight = 2
-- content/_index.md --
---
title: home
---
a|b
:--|:--
1|2
-- layouts/index.html --
{{ .Content }}
-- layouts/index.json --
{{ .Content }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", "<table>")
b.AssertFileContent("public/index.json", "<table>")
f := strings.ReplaceAll(files, "weight = 1", "weight = 0")
b = hugolib.Test(t, f)
b.AssertFileContent("public/index.html", "<table>")
b.AssertFileContent("public/index.json", "<table>")
f = strings.ReplaceAll(files, "weight = 1", "")
b = hugolib.Test(t, f)
b.AssertFileContent("public/index.html", "<table>")
b.AssertFileContent("public/index.json", "<table>")
}

View file

@ -17,8 +17,10 @@ package transform
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"html"
"html/template"
"io"
@ -234,6 +236,7 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
MinRuleThickness: 0.04,
ErrorColor: "#cc0000",
ThrowOnError: true,
Strict: "error",
},
}
@ -243,8 +246,23 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
}
}
switch katexInput.Options.Strict {
case "error", "ignore", "warn":
// Valid strict mode, continue
default:
return "", fmt.Errorf("invalid strict mode; expected one of error, ignore, or warn; received %s", katexInput.Options.Strict)
}
type fileCacheEntry struct {
Version string `json:"version"`
Output string `json:"output"`
Warnings []string `json:"warnings,omitempty"`
}
const fileCacheEntryVersion = "v1" // Increment on incompatible changes.
s := hashing.HashString(args...)
key := "tomath/" + s[:2] + "/" + s[2:]
key := "tomath/" + fileCacheEntryVersion + "/" + s[:2] + "/" + s[2:]
fileCache := ns.deps.ResourceSpec.FileCaches.MiscCache()
v, err := ns.cacheMath.GetOrCreate(key, func(string) (template.HTML, error) {
@ -265,15 +283,35 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
if err != nil {
return nil, err
}
return hugio.NewReadSeekerNoOpCloserFromString(result.Data.Output), nil
e := fileCacheEntry{
Version: fileCacheEntryVersion,
Output: result.Data.Output,
Warnings: result.Header.Warnings,
}
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(e); err != nil {
return nil, fmt.Errorf("failed to encode file cache entry: %w", err)
}
return hugio.NewReadSeekerNoOpCloserFromBytes(buf.Bytes()), nil
})
if err != nil {
return "", err
}
s, err := hugio.ReadString(r)
var e fileCacheEntry
if err := json.NewDecoder(r).Decode(&e); err != nil {
return "", fmt.Errorf("failed to decode file cache entry: %w", err)
}
return template.HTML(s), err
for _, warning := range e.Warnings {
ns.deps.Log.Warnf("transform.ToMath: %s", warning)
}
return template.HTML(e.Output), err
})
if err != nil {
return "", err

View file

@ -495,3 +495,43 @@ DATA
}
}
}
// Issue 13729
func TestToMathStrictMode(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
-- layouts/all.html --
{{ transform.ToMath "a %" dict }}
-- foo --
`
// strict mode: default
f := strings.ReplaceAll(files, "dict", "")
b, err := hugolib.TestE(t, f)
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
// strict mode: error
f = strings.ReplaceAll(files, "dict", `(dict "strict" "error")`)
b, err = hugolib.TestE(t, f)
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
// strict mode: ignore
f = strings.ReplaceAll(files, "dict", `(dict "strict" "ignore")`)
b = hugolib.Test(t, f, hugolib.TestOptWarn())
b.AssertLogMatches("")
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
// strict: warn
f = strings.ReplaceAll(files, "dict", `(dict "strict" "warn")`)
b = hugolib.Test(t, f, hugolib.TestOptWarn())
b.AssertLogMatches("[commentAtEnd]")
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
// strict mode: invalid value
f = strings.ReplaceAll(files, "dict", `(dict "strict" "foo")`)
b, err = hugolib.TestE(t, f)
b.Assert(err.Error(), qt.Contains, "invalid strict mode")
}