Compare commits

...

121 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
hugoreleaser
7d0039b86d releaser: Bump versions for release of 0.147.0
[ci skip]
2025-04-25 15:26:28 +00:00
Bjørn Erik Pedersen
07983e04e2 tpl: Fix it so we always prefer internal codeblock rendering over render-codeblock-foo.html and similar
Fixes #13651
2025-04-25 10:51:33 +02:00
Joe Mooring
5c491409d3
tpl/tplimpl: Fix allowFullScreen option in Vimeo and YouTube shortcodes
Closes #13650
2025-04-24 14:14:46 -07:00
Joe Mooring
75b219db89 create/skeletons: Adjust template names in theme skeleton 2025-04-24 19:02:11 +02:00
Bjørn Erik Pedersen
ad4f63c92f tpl: Remove some unreached code branches 2025-04-24 14:27:59 +02:00
Bjørn Erik Pedersen
53202314ab images: Add some test cases for aligny on images.Text
See #13414
2025-04-24 14:09:13 +02:00
Pranshu Gaba
2fce0bac03 images: Add option for vertical alignment to images.Text
Add option ``aligny`` to specify the vertical alignment of the text
with respect to the ``y`` offset from the top of the image. Possible
values of ``aligny`` are ``top`` (default), ``center``, and ``bottom``.

The height of the block of text is measured from the top of the first
line to the baseline of the last line.

- ``top``: (Current behaviour) The top of the first line of the block of
  text is at an offset of ``y`` from the top of the image.

- ``center``: The vertical center of the block of text is at an offset of
  ``y`` from the top of the image.

- ``bottom``: The baseline of the last line of the text is at an offset
  of ``y`` from the top of the image.

Resolves #13414
2025-04-24 14:09:13 +02:00
Bjørn Erik Pedersen
179aea11ac config: Fix _merge issue when key doesn't exist on the left side
Fixes #13643
Fixes #13646
2025-04-24 13:56:27 +02:00
Bjørn Erik Pedersen
61a286595e
Merge commit 'b3d87dd0fd' 2025-04-24 10:23:16 +02:00
Bjørn Erik Pedersen
b3d87dd0fd Squashed 'docs/' changes from dc7a9ae12..b654fcba0
b654fcba0 content: Fix links
d44357418 content: Update GitLab Pages workflow example
33968c7e2 content: Update Netlify configuration file
a6d0c8c50 content: Update GitHub Pages workflow example
d1aabfa36 content: Fix broken link
7b50139a6 content: Miscellaneous edits
a30e2c189 Menus: add forgotten link target
5c2aa88b4 content: Updates for v0.146.7
114413c18 netlify: Hugo 0.146.7
67e9261b1 netlify: Hugo 0.146.6
efa040229 content: Update templates/embedded.md
b8f888c76 theme: Rename internal templates from partials/ to _partials/
727178cbb content: Fix broken anchor links
0f12708f1 Fix typo
380b1c163 Update Current.md
8b500f3e5 netlify: Hugo 0.146.5
e3d6b6fad netlify: Hugo 0.146.4
ac1b92713 content: Fix text formatting in templates/partial.md
719329530 content: Clarify usage of template function
a95eca524 theme: Misc adjustments for the themes site
8e6c26067 Add package.hugo.json
9691007fb netlify: Hugo 0.146.3
ec08acc59 netlify: Hugo 0.146.2
8f320a0b6 netlify: Hugo 0.146.1
d5e6cb618 content: Remove expired new-in badges
b5779d7fc content: Update templates.Current function
5df1229d5 theme: Move templates to new structure
a7a6a614d theme: Remove accidentally added template
195b368e8 content: Miscellaneous updates related to v0.146.0
0a906ad49 netlify: Hugo 0.146.0

git-subtree-dir: docs
git-subtree-split: b654fcba0d4385da1e47179ef3763f277f044242
2025-04-24 10:23:16 +02:00
Christian Oliff
6a0e04241a
all: Fix typos 2025-04-24 10:19:17 +02:00
dependabot[bot]
1bd7ac7ed9 build(deps): bump github.com/evanw/esbuild from 0.25.2 to 0.25.3
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.25.2 to 0.25.3.
- [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.2...v0.25.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-24 10:15:48 +02:00
dependabot[bot]
41cb880f9a build(deps): bump github.com/alecthomas/chroma/v2 from 2.16.0 to 2.17.0
Bumps [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) from 2.16.0 to 2.17.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.16.0...v2.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-24 10:11:50 +02:00
hugoreleaser
df44ee1353 releaser: Prepare repository for 0.147.0-DEV
[ci skip]
2025-04-22 17:42:19 +00:00
hugoreleaser
1ad3d39dc4 releaser: Bump versions for release of 0.146.7
[ci skip]
2025-04-22 17:26:42 +00:00
Bjørn Erik Pedersen
496730840e Revert the breaking change from 0.146.0 with dots in content filenames
Closes #13632
2025-04-22 19:23:19 +02:00
Bjørn Erik Pedersen
6d69dc88a4 tpl: Fix indeterminate template lookup with templates with and without lang
Close #13636
2025-04-22 17:29:39 +02:00
Joe Mooring
db72a1f075
parser/metadecoders: Add CSV targetType (map or slice) option to transform.Unmarshal
Closes #8859
2025-04-21 19:33:20 +02:00
dependabot[bot]
ad787614e8 build(deps): bump github.com/yuin/goldmark-emoji from 1.0.5 to 1.0.6
Bumps [github.com/yuin/goldmark-emoji](https://github.com/yuin/goldmark-emoji) from 1.0.5 to 1.0.6.
- [Release notes](https://github.com/yuin/goldmark-emoji/releases)
- [Commits](https://github.com/yuin/goldmark-emoji/compare/v1.0.5...v1.0.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 15:18:02 +02:00
dependabot[bot]
9c65b9e88d build(deps): bump github.com/bep/imagemeta from 0.11.0 to 0.12.0
Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/bep/imagemeta/releases)
- [Commits](https://github.com/bep/imagemeta/compare/v0.11.0...v0.12.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 15:17:49 +02:00
Bjørn Erik Pedersen
4eb0e4286a tpl/collections: Fix where ... not in with empty slice
Fixes #13621
2025-04-21 15:17:20 +02:00
Bjørn Erik Pedersen
5e62cc6fce tpl: Fix layout fall back logic when layout is set in front matter but not found
Fixes #13630
2025-04-21 15:17:20 +02:00
Bjørn Erik Pedersen
1408c156d8 tpl: Detect and fail on infinite template recursion
Fixes #13627
2025-04-21 15:17:20 +02:00
dependabot[bot]
be3b147860 build(deps): bump github.com/yuin/goldmark from 1.7.9 to 1.7.10
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.9 to 1.7.10.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.9...v1.7.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 11:56:44 +02:00
hugoreleaser
99fff2997d releaser: Prepare repository for 0.147.0-DEV
[ci skip]
2025-04-20 11:13:56 +00:00
hugoreleaser
1e0b058efe releaser: Bump versions for release of 0.146.6
[ci skip]
2025-04-20 10:58:40 +00:00
Bjørn Erik Pedersen
088cd2f996 tpl: Fix when layout specified in front matter and no match is found
Fixes #13628
2025-04-20 12:55:18 +02:00
broughtupsy
a88b488181 Update watchtestscripts.sh 2025-04-16 08:28:19 +02:00
hugoreleaser
d5a8c330cb releaser: Prepare repository for 0.147.0-DEV
[ci skip]
2025-04-15 18:09:42 +00:00
hugoreleaser
61328976e1 releaser: Bump versions for release of 0.146.5
[ci skip]
2025-04-15 17:54:38 +00:00
dependabot[bot]
64cf008880 build(deps): bump github.com/yuin/goldmark from 1.7.8 to 1.7.9
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.8 to 1.7.9.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.8...v1.7.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-15 14:33:56 +02:00
Bjørn Erik Pedersen
d61b9fc605 tpl: Fix language handling in partials
We now use the same code path for all templates re this.

Fixes #13612
2025-04-15 11:25:54 +02:00
hugoreleaser
01667275d4 releaser: Prepare repository for 0.147.0-DEV
[ci skip]
2025-04-14 13:25:05 +00:00
hugoreleaser
985af1c097 releaser: Bump versions for release of 0.146.4
[ci skip]
2025-04-14 13:10:30 +00:00
Bjørn Erik Pedersen
65c94c7b23 tpl: Fix issue with partials without suffix
Fixes #13601
2025-04-14 14:38:22 +02:00
Bjørn Erik Pedersen
e8e8ce10d2 tpl: Avoid panic on nonsensical return construct
Fixes #13600
2025-04-14 14:38:22 +02:00
Bjørn Erik Pedersen
cf9e6904cc tpl: Fix the case for a shortcode in a nested folder only
Fixes #13605
2025-04-14 14:38:22 +02:00
Bjørn Erik Pedersen
8a2830f2dc tpl: Add proper file context to template parse errors
Fixes #13604
2025-04-14 14:38:22 +02:00
Bjørn Erik Pedersen
1e0287f472 tpl: Make {{ template "partials/foo.html" . }} work in older setups
Fixes #13599
2025-04-14 14:38:22 +02:00
hugoreleaser
915ba3f7f0 releaser: Prepare repository for 0.147.0-DEV
[ci skip]
2025-04-12 17:36:11 +00:00
225 changed files with 3979 additions and 1213 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: 146,
PatchLevel: 3,
Suffix: "",
Minor: 148,
PatchLevel: 0,
Suffix: "-DEV",
}

View file

@ -120,18 +120,24 @@ 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 := 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 := mayHaveOutputFormat
mayHaveKind := p.posIdentifierKind == -1 && mayHaveOutputFormat
var mayHaveLayout bool
if p.pathType == TypeShortcode {
mayHaveLayout = !isLast && component == files.ComponentFolderLayouts
} else {
mayHaveLayout = component == files.ComponentFolderLayouts
}
var found bool
var high int
if len(p.identifiers) > 0 {
if len(p.identifiersKnown) > 0 {
high = lastDot
} else {
high = len(p.s)
@ -139,9 +145,9 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
id := types.LowHigh[string]{Low: i + 1, High: high}
sid := p.s[id.Low:id.High]
if len(p.identifiers) == 0 {
if len(p.identifiersKnown) == 0 {
// The first is always the extension.
p.identifiers = append(p.identifiers, id)
p.identifiersKnown = append(p.identifiersKnown, id)
found = true
// May also be the output format.
@ -164,9 +170,8 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
}
found = langFound
if langFound {
p.identifiers = append(p.identifiers, id)
p.posIdentifierLanguage = len(p.identifiers) - 1
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierLanguage = len(p.identifiersKnown) - 1
}
}
@ -177,28 +182,33 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
// false positives on the form css.html.
if pp.IsOutputFormat(sid, p.Ext()) {
found = true
p.identifiers = append(p.identifiers, id)
p.posIdentifierOutputFormat = len(p.identifiers) - 1
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierOutputFormat = len(p.identifiersKnown) - 1
}
}
if !found && mayHaveKind {
if kinds.GetKindMain(sid) != "" {
found = true
p.identifiers = append(p.identifiers, id)
p.posIdentifierKind = len(p.identifiers) - 1
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierKind = len(p.identifiersKnown) - 1
}
}
if !found && sid == identifierBaseof {
found = true
p.identifiers = append(p.identifiers, id)
p.posIdentifierBaseof = len(p.identifiers) - 1
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierBaseof = len(p.identifiersKnown) - 1
}
if !found && mayHaveLayout {
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierLayout = len(p.identifiersKnown) - 1
found = true
}
if !found {
p.identifiers = append(p.identifiers, id)
p.identifiersUnknown = append(p.identifiersUnknown, len(p.identifiers)-1)
p.identifiersUnknown = append(p.identifiersUnknown, id)
}
}
@ -228,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 {
@ -252,13 +267,13 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
}
}
if len(p.identifiers) > 0 {
if len(p.identifiersKnown) > 0 {
isContentComponent := p.component == files.ComponentFolderContent || p.component == files.ComponentFolderArchetypes
isContent := isContentComponent && pp.IsContentExt(p.Ext())
id := p.identifiers[len(p.identifiers)-1]
id := p.identifiersKnown[len(p.identifiersKnown)-1]
if id.High > p.posContainerHigh {
b := p.s[p.posContainerHigh:id.High]
if id.Low > p.posContainerHigh {
b := p.s[p.posContainerHigh : id.Low-1]
if isContent {
switch b {
case "index":
@ -276,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 {
@ -294,6 +308,14 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
}
}
if p.pathType == TypeShortcode && p.posIdentifierLayout != -1 {
id := p.identifiersKnown[p.posIdentifierLayout]
if id.Low == p.posContainerHigh {
// First identifier is shortcode name.
p.posIdentifierLayout = -1
}
}
return p, nil
}
@ -350,13 +372,14 @@ type Path struct {
component string
pathType Type
identifiers []types.LowHigh[string]
identifiersKnown []types.LowHigh[string]
identifiersUnknown []types.LowHigh[string]
posIdentifierLanguage int
posIdentifierOutputFormat int
posIdentifierKind int
posIdentifierLayout int
posIdentifierBaseof int
identifiersUnknown []int
disabled bool
trimLeadingSlash bool
@ -388,10 +411,11 @@ func (p *Path) reset() {
p.posSectionHigh = -1
p.component = ""
p.pathType = 0
p.identifiers = p.identifiers[:0]
p.identifiersKnown = p.identifiersKnown[:0]
p.posIdentifierLanguage = -1
p.posIdentifierOutputFormat = -1
p.posIdentifierKind = -1
p.posIdentifierLayout = -1
p.posIdentifierBaseof = -1
p.disabled = false
p.trimLeadingSlash = false
@ -479,7 +503,7 @@ func (p *Path) Name() string {
// Name returns the last element of path without any extension.
func (p *Path) NameNoExt() string {
if i := p.identifierIndex(0); i != -1 {
return p.s[p.posContainerHigh : p.identifiers[i].Low-1]
return p.s[p.posContainerHigh : p.identifiersKnown[i].Low-1]
}
return p.s[p.posContainerHigh:]
}
@ -491,7 +515,7 @@ func (p *Path) NameNoLang() string {
return p.Name()
}
return p.s[p.posContainerHigh:p.identifiers[i].Low-1] + p.s[p.identifiers[i].High:]
return p.s[p.posContainerHigh:p.identifiersKnown[i].Low-1] + p.s[p.identifiersKnown[i].High:]
}
// BaseNameNoIdentifier returns the logical base name for a resource without any identifier (e.g. no extension).
@ -510,15 +534,15 @@ func (p *Path) NameNoIdentifier() string {
}
func (p *Path) nameLowHigh() types.LowHigh[string] {
if len(p.identifiers) > 0 {
lastID := p.identifiers[len(p.identifiers)-1]
if len(p.identifiersKnown) > 0 {
lastID := p.identifiersKnown[len(p.identifiersKnown)-1]
if p.posContainerHigh == lastID.Low {
// The last identifier is the name.
return lastID
}
return types.LowHigh[string]{
Low: p.posContainerHigh,
High: p.identifiers[len(p.identifiers)-1].Low - 1,
High: p.identifiersKnown[len(p.identifiersKnown)-1].Low - 1,
}
}
return types.LowHigh[string]{
@ -566,7 +590,7 @@ func (p *Path) PathNoIdentifier() string {
// PathBeforeLangAndOutputFormatAndExt returns the path up to the first identifier that is not a language or output format.
func (p *Path) PathBeforeLangAndOutputFormatAndExt() string {
if len(p.identifiers) == 0 {
if len(p.identifiersKnown) == 0 {
return p.norm(p.s)
}
i := p.identifierIndex(0)
@ -582,7 +606,7 @@ func (p *Path) PathBeforeLangAndOutputFormatAndExt() string {
return p.norm(p.s)
}
id := p.identifiers[i]
id := p.identifiersKnown[i]
return p.norm(p.s[:id.Low-1])
}
@ -633,11 +657,11 @@ func (p *Path) BaseNoLeadingSlash() string {
}
func (p *Path) base(preserveExt, isBundle bool) string {
if len(p.identifiers) == 0 {
if len(p.identifiersKnown) == 0 {
return p.norm(p.s)
}
if preserveExt && len(p.identifiers) == 1 {
if preserveExt && len(p.identifiersKnown) == 1 {
// Preserve extension.
return p.norm(p.s)
}
@ -659,7 +683,7 @@ func (p *Path) base(preserveExt, isBundle bool) string {
}
// For txt files etc. we want to preserve the extension.
id := p.identifiers[0]
id := p.identifiersKnown[0]
return p.norm(p.s[:high] + p.s[id.Low-1:id.High])
}
@ -676,6 +700,10 @@ func (p *Path) Kind() string {
return p.identifierAsString(p.posIdentifierKind)
}
func (p *Path) Layout() string {
return p.identifierAsString(p.posIdentifierLayout)
}
func (p *Path) Lang() string {
return p.identifierAsString(p.posIdentifierLanguage)
}
@ -689,8 +717,8 @@ func (p *Path) Disabled() bool {
}
func (p *Path) Identifiers() []string {
ids := make([]string, len(p.identifiers))
for i, id := range p.identifiers {
ids := make([]string, len(p.identifiersKnown))
for i, id := range p.identifiersKnown {
ids[i] = p.s[id.Low:id.High]
}
return ids
@ -699,7 +727,7 @@ func (p *Path) Identifiers() []string {
func (p *Path) IdentifiersUnknown() []string {
ids := make([]string, len(p.identifiersUnknown))
for i, id := range p.identifiersUnknown {
ids[i] = p.s[p.identifiers[id].Low:p.identifiers[id].High]
ids[i] = p.s[id.Low:id.High]
}
return ids
}
@ -724,7 +752,7 @@ func (p *Path) IsContentData() bool {
return p.pathType == TypeContentData
}
func (p Path) ForBundleType(t Type) *Path {
func (p Path) ForType(t Type) *Path {
p.pathType = t
return &p
}
@ -735,12 +763,12 @@ func (p *Path) identifierAsString(i int) string {
return ""
}
id := p.identifiers[i]
id := p.identifiersKnown[i]
return p.s[id.Low:id.High]
}
func (p *Path) identifierIndex(i int) int {
if i < 0 || i >= len(p.identifiers) {
if i < 0 || i >= len(p.identifiersKnown) {
return -1
}
return i

View file

@ -171,22 +171,25 @@ func TestParse(t *testing.T) {
"/a/b.a.b.no.txt",
func(c *qt.C, p *Path) {
c.Assert(p.Name(), qt.Equals, "b.a.b.no.txt")
c.Assert(p.NameNoIdentifier(), qt.Equals, "b")
c.Assert(p.NameNoIdentifier(), qt.Equals, "b.a.b")
c.Assert(p.NameNoLang(), qt.Equals, "b.a.b.txt")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no", "b", "a", "b"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{"b", "a", "b"})
c.Assert(p.Base(), qt.Equals, "/a/b.txt")
c.Assert(p.BaseNoLeadingSlash(), qt.Equals, "a/b.txt")
c.Assert(p.Base(), qt.Equals, "/a/b.a.b.txt")
c.Assert(p.BaseNoLeadingSlash(), qt.Equals, "a/b.a.b.txt")
c.Assert(p.Path(), qt.Equals, "/a/b.a.b.no.txt")
c.Assert(p.PathNoLang(), qt.Equals, "/a/b.txt")
c.Assert(p.PathNoLang(), qt.Equals, "/a/b.a.b.txt")
c.Assert(p.Ext(), qt.Equals, "txt")
c.Assert(p.PathNoIdentifier(), qt.Equals, "/a/b")
c.Assert(p.PathNoIdentifier(), qt.Equals, "/a/b.a.b")
},
},
{
"Home branch cundle",
"/_index.md",
func(c *qt.C, p *Path) {
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md"})
c.Assert(p.IsBranchBundle(), qt.IsTrue)
c.Assert(p.IsBundle(), qt.IsTrue)
c.Assert(p.Base(), qt.Equals, "/")
c.Assert(p.BaseReTyped("foo"), qt.Equals, "/foo")
c.Assert(p.Path(), qt.Equals, "/_index.md")
@ -206,7 +209,8 @@ func TestParse(t *testing.T) {
c.Assert(p.ContainerDir(), qt.Equals, "")
c.Assert(p.Dir(), qt.Equals, "/a")
c.Assert(p.Ext(), qt.Equals, "md")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md", "index"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{"index"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md"})
c.Assert(p.IsBranchBundle(), qt.IsFalse)
c.Assert(p.IsBundle(), qt.IsTrue)
c.Assert(p.IsLeafBundle(), qt.IsTrue)
@ -228,7 +232,7 @@ func TestParse(t *testing.T) {
c.Assert(p.ContainerDir(), qt.Equals, "/a")
c.Assert(p.Dir(), qt.Equals, "/a/b")
c.Assert(p.Ext(), qt.Equals, "md")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md", "no", "index"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md", "no"})
c.Assert(p.IsBranchBundle(), qt.IsFalse)
c.Assert(p.IsBundle(), qt.IsTrue)
c.Assert(p.IsLeafBundle(), qt.IsTrue)
@ -250,7 +254,7 @@ func TestParse(t *testing.T) {
c.Assert(p.Container(), qt.Equals, "b")
c.Assert(p.ContainerDir(), qt.Equals, "/a")
c.Assert(p.Ext(), qt.Equals, "md")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md", "no", "_index"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md", "no"})
c.Assert(p.IsBranchBundle(), qt.IsTrue)
c.Assert(p.IsBundle(), qt.IsTrue)
c.Assert(p.IsLeafBundle(), qt.IsFalse)
@ -289,7 +293,7 @@ func TestParse(t *testing.T) {
func(c *qt.C, p *Path) {
c.Assert(p.Base(), qt.Equals, "/a/b/index.txt")
c.Assert(p.Ext(), qt.Equals, "txt")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no", "index"})
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no"})
c.Assert(p.IsLeafBundle(), qt.IsFalse)
c.Assert(p.PathNoIdentifier(), qt.Equals, "/a/b/index")
},
@ -372,7 +376,7 @@ func TestParse(t *testing.T) {
}
for _, test := range tests {
c.Run(test.name, func(c *qt.C) {
if test.name != "Basic Markdown file" {
if test.name != "Home branch cundle" {
// return
}
test.assert(c, testParser.Parse(files.ComponentFolderContent, test.path))
@ -401,10 +405,58 @@ func TestParseLayouts(t *testing.T) {
"/list.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "list"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
c.Assert(p.Base(), qt.Equals, "/list.html")
c.Assert(p.Lang(), qt.Equals, "no")
},
},
{
"Kind",
"/section.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Kind(), qt.Equals, kinds.KindSection)
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
c.Assert(p.Base(), qt.Equals, "/section.html")
c.Assert(p.Lang(), qt.Equals, "no")
},
},
{
"Layout",
"/list.section.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Layout(), qt.Equals, "list")
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
c.Assert(p.Base(), qt.Equals, "/list.html")
c.Assert(p.Lang(), qt.Equals, "no")
},
},
{
"Layout multiple",
"/mylayout.list.section.no.html",
func(c *qt.C, p *Path) {
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, "/mylayout.html")
c.Assert(p.Lang(), qt.Equals, "no")
},
},
{
"Layout shortcode",
"/_shortcodes/myshort.list.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Layout(), qt.Equals, "list")
},
},
{
"Layout baseof",
"/baseof.list.no.html",
func(c *qt.C, p *Path) {
c.Assert(p.Layout(), qt.Equals, "list")
},
},
{
"Lang and output format",
"/list.no.amp.not.html",
@ -429,6 +481,21 @@ func TestParseLayouts(t *testing.T) {
c.Assert(p.OutputFormat(), qt.Equals, "html")
},
},
{
"Shortcode with layout",
"/_shortcodes/myshortcode.list.html",
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"})
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, "")
c.Assert(p.Kind(), qt.Equals, "")
c.Assert(p.OutputFormat(), qt.Equals, "html")
},
},
{
"Sub dir",
"/pages/home.html",
@ -445,7 +512,7 @@ func TestParseLayouts(t *testing.T) {
"/pages/baseof.list.section.fr.amp.html",
func(c *qt.C, p *Path) {
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "amp", "fr", "section", "list", "baseof"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{"list"})
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
c.Assert(p.Kind(), qt.Equals, kinds.KindSection)
c.Assert(p.Lang(), qt.Equals, "fr")
c.Assert(p.OutputFormat(), qt.Equals, "amp")
@ -497,10 +564,32 @@ 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 != "Shortcode lang layout" {
// return
}
test.assert(c, testParser.Parse(files.ComponentFolderLayouts, test.path))
})
}

View file

@ -78,3 +78,26 @@ disablePathToLower = true
b.AssertFileContent("public/en/mysection/mybundle/index.html", "en|Single")
b.AssertFileContent("public/fr/MySection/MyBundle/index.html", "fr|Single")
}
func TestIssue13596(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
-- content/p1/index.md --
---
title: p1
---
-- content/p1/a.1.txt --
-- content/p1/a.2.txt --
-- layouts/all.html --
{{ range .Resources.Match "*" }}{{ .Name }}|{{ end }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/p1/index.html", "a.1.txt|a.2.txt|")
b.AssertFileExists("public/p1/a.1.txt", true)
b.AssertFileExists("public/p1/a.2.txt", true) // fails
}

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
})
@ -800,30 +800,58 @@ func (c *Configs) IsZero() bool {
func (c *Configs) Init() error {
var languages langs.Languages
defaultContentLanguage := c.Base.DefaultContentLanguage
for k, v := range c.LanguageConfigMap {
var langKeys []string
var hasEn bool
const en = "en"
for k := range c.LanguageConfigMap {
langKeys = append(langKeys, k)
if k == en {
hasEn = true
}
}
// Sort the LanguageConfigSlice by language weight (if set) or lang.
sort.Slice(langKeys, func(i, j int) bool {
ki := langKeys[i]
kj := langKeys[j]
lki := c.LanguageConfigMap[ki]
lkj := c.LanguageConfigMap[kj]
li := lki.Languages[ki]
lj := lkj.Languages[kj]
if li.Weight != lj.Weight {
return li.Weight < lj.Weight
}
return ki < kj
})
// See issue #13646.
defaultConfigLanguageFallback := en
if !hasEn {
// Pick the first one.
defaultConfigLanguageFallback = langKeys[0]
}
if c.Base.DefaultContentLanguage == "" {
c.Base.DefaultContentLanguage = defaultConfigLanguageFallback
}
for _, k := range langKeys {
v := c.LanguageConfigMap[k]
if v.DefaultContentLanguage == "" {
v.DefaultContentLanguage = defaultConfigLanguageFallback
}
c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
languageConf := v.Languages[k]
language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf)
if err != nil {
return err
}
languages = append(languages, language)
}
// Sort the sites by language weight (if set) or lang.
sort.Slice(languages, func(i, j int) bool {
li := languages[i]
lj := languages[j]
if li.Weight != lj.Weight {
return li.Weight < lj.Weight
}
return li.Lang < lj.Lang
})
for _, l := range languages {
c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang])
}
// Filter out disabled languages.
var n int
for _, l := range languages {
@ -836,12 +864,12 @@ func (c *Configs) Init() error {
var languagesDefaultFirst langs.Languages
for _, l := range languages {
if l.Lang == defaultContentLanguage {
if l.Lang == c.Base.DefaultContentLanguage {
languagesDefaultFirst = append(languagesDefaultFirst, l)
}
}
for _, l := range languages {
if l.Lang != defaultContentLanguage {
if l.Lang != c.Base.DefaultContentLanguage {
languagesDefaultFirst = append(languagesDefaultFirst, l)
}
}
@ -927,17 +955,48 @@ func (c Configs) GetByLang(lang string) config.AllProvider {
return nil
}
func newDefaultConfig() *Config {
return &Config{
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
RootConfig: RootConfig{
Environment: hugo.EnvironmentProduction,
TitleCaseStyle: "AP",
PluralizeListTitles: true,
CapitalizeListTitles: true,
StaticDir: []string{"static"},
SummaryLength: 70,
Timeout: "60s",
CommonDirs: config.CommonDirs{
ArcheTypeDir: "archetypes",
ContentDir: "content",
ResourceDir: "resources",
PublishDir: "public",
ThemesDir: "themes",
AssetDir: "assets",
LayoutDir: "layouts",
I18nDir: "i18n",
DataDir: "data",
},
},
}
}
// fromLoadConfigResult creates a new Config from res.
func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) {
if !res.Cfg.IsSet("languages") {
// We need at least one
lang := res.Cfg.GetString("defaultContentLanguage")
if lang == "" {
lang = "en"
}
res.Cfg.Set("languages", maps.Params{lang: maps.Params{}})
}
bcfg := res.BaseConfig
cfg := res.Cfg
all := &Config{}
all := newDefaultConfig()
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
if err != nil {
@ -947,6 +1006,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
langConfigMap := make(map[string]*Config)
languagesConfig := cfg.GetStringMap("languages")
var isMultihost bool
if err := all.CompileConfig(logger); err != nil {

View file

@ -5,6 +5,7 @@ import (
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/media"
@ -234,3 +235,147 @@ baseURL = "https://example.com"
b.Assert(c.IsContentFile("foo.md"), qt.Equals, true)
b.Assert(len(s), qt.Equals, 6)
}
func TestMergeDeep(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
theme = ["theme1", "theme2"]
_merge = "deep"
-- themes/theme1/hugo.toml --
[sitemap]
filename = 'mysitemap.xml'
[services]
[services.googleAnalytics]
id = 'foo bar'
[taxonomies]
foo = 'bars'
-- themes/theme2/config/_default/hugo.toml --
[taxonomies]
bar = 'baz'
-- layouts/home.html --
GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
`
b := hugolib.Test(t, files)
conf := b.H.Configs
base := conf.Base
b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction)
b.Assert(base.BaseURL, qt.Equals, "https://example.com")
b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml")
b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"})
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()
files := `
-- hugo.toml --
baseURL = "https://example.com"
[languages]
[languages.nn]
weight = 20
[languages.sv]
weight = 10
[languages.sv.taxonomies]
tag = "taggar"
-- layouts/all.html --
All.
`
b := hugolib.Test(t, files)
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

@ -249,14 +249,18 @@ var allDecoderSetups = map[string]decodeWeight{
key: "sitemap",
decode: func(d decodeWeight, p decodeConfig) error {
var err error
p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key))
if p.p.IsSet(d.key) {
p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key))
}
return err
},
},
"taxonomies": {
key: "taxonomies",
decode: func(d decodeWeight, p decodeConfig) error {
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
if p.p.IsSet(d.key) {
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
}
return nil
},
},
@ -306,15 +310,17 @@ var allDecoderSetups = map[string]decodeWeight{
}
// Validate defaultContentLanguage.
var found bool
for lang := range p.c.Languages {
if lang == p.c.DefaultContentLanguage {
found = true
break
if p.c.DefaultContentLanguage != "" {
var found bool
for lang := range p.c.Languages {
if lang == p.c.DefaultContentLanguage {
found = true
break
}
}
if !found {
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
}
}
if !found {
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
}
return nil
@ -324,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

@ -159,63 +159,9 @@ func (l configLoader) applyConfigAliases() error {
func (l configLoader) applyDefaultConfig() error {
defaultSettings := maps.Params{
"baseURL": "",
"cleanDestinationDir": false,
"watch": false,
"contentDir": "content",
"resourceDir": "resources",
"publishDir": "public",
"publishDirOrig": "public",
"themesDir": "themes",
"assetDir": "assets",
"layoutDir": "layouts",
"i18nDir": "i18n",
"dataDir": "data",
"archetypeDir": "archetypes",
"configDir": "config",
"staticDir": "static",
"buildDrafts": false,
"buildFuture": false,
"buildExpired": false,
"params": maps.Params{},
"environment": hugo.EnvironmentProduction,
"uglyURLs": false,
"verbose": false,
"ignoreCache": false,
"canonifyURLs": false,
"relativeURLs": false,
"removePathAccents": false,
"titleCaseStyle": "AP",
"taxonomies": maps.Params{"tag": "tags", "category": "categories"},
"permalinks": maps.Params{},
"sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"},
"menus": maps.Params{},
"disableLiveReload": false,
"pluralizeListTitles": true,
"capitalizeListTitles": true,
"forceSyncStatic": false,
"footnoteAnchorPrefix": "",
"footnoteReturnLinkContents": "",
"newContentEditor": "",
"paginate": 0, // Moved into the paginator struct in Hugo v0.128.0.
"paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0.
"summaryLength": 70,
"rssLimit": -1,
"sectionPagesMenu": "",
"disablePathToLower": false,
"hasCJKLanguage": false,
"enableEmoji": false,
"defaultContentLanguage": "en",
"defaultContentLanguageInSubdir": false,
"enableMissingTranslationPlaceholders": false,
"enableGitInfo": false,
"ignoreFiles": make([]string, 0),
"disableAliases": false,
"debug": false,
"disableFastRender": false,
"timeout": "30s",
"timeZone": "",
"enableInlineShortcodes": false,
// These dirs are used early/before we build the config struct.
"themesDir": "themes",
"configDir": "config",
}
l.cfg.SetDefaults(defaultSettings)
@ -287,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

@ -17,6 +17,7 @@ import (
"fmt"
"net/http"
"regexp"
"slices"
"sort"
"strings"
@ -28,7 +29,6 @@ import (
"github.com/gohugoio/hugo/common/herrors"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"
"slices"
)
type BaseConfig struct {

View file

@ -76,7 +76,7 @@ type AllProvider interface {
}
// We cannot import the media package as that would create a circular dependency.
// This interface defineds a sub set of what media.ContentTypes provides.
// This interface defines a subset of what media.ContentTypes provides.
type ContentTypesProvider interface {
IsContentSuffix(suffix string) bool
IsContentFile(filename string) bool

View file

@ -101,6 +101,9 @@ func DecodeConfig(cfg config.Provider) (c Config, err error) {
if c.RSS.Limit == 0 {
c.RSS.Limit = cfg.GetInt(rssLimitKey)
if c.RSS.Limit == 0 {
c.RSS.Limit = -1
}
}
return

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},
}
}

View file

@ -249,7 +249,7 @@ HUGO_FILE_LOG_FORMAT
HUGO_MEMORYLIMIT
: {{< new-in 0.123.0 />}}
: (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. Note that The `HUGO_MEMORYLIMIT` is a “best effort” setting. Don't expect Hugo to build a million pages with only 1 GB memory. You can get more information about how this behaves during the build by building with `hugo --logLevel info` and look for the `dynacache` label.
: (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. Note that `HUGO_MEMORYLIMIT` is a "best effort" setting. Don't expect Hugo to build a million pages with only 1 GB of memory. You can get more information about how this behaves during the build by building with `hugo --logLevel info` and look for the `dynacache` label.
HUGO_NUMWORKERMULTIPLIER
: (`int`) The number of workers used in parallel processing. Default is the number of logical CPUs.

View file

@ -11,7 +11,7 @@ aliases: [/extras/menus/]
To create a menu for your site:
1. Define the menu entries
1. [Localize] each entry
1. [Localize](multilingual/#menus) each entry
1. Render the menu with a [template]
Create multiple menus, either flat or nested. For example, create a main menu for the header, and a separate menu for the footer.

View file

@ -112,7 +112,7 @@ Yes → Hugo is fast.
### Function and method descriptions
Start descriptions in the functions and methods sections with "Returns", of for booelan values, "Reports whether".
Start descriptions in the functions and methods sections with "Returns", or for boolean values, "Reports whether".
### File paths and names

View file

@ -87,7 +87,6 @@ Use any of the following logical operators:
: (`bool`) Reports whether the given field value (a slice) contains one or more elements in common with `VALUE`. See&nbsp;[details](/functions/collections/intersect).
`like`
: {{< new-in 0.116.0 />}}
: (`bool`) Reports whether the given field value matches the [regular expression](g) specified in `VALUE`. Use the `like` operator to compare `string` values. The `like` operator returns `false` when comparing other data types to the regular expression.
> [!note]
@ -167,8 +166,6 @@ For example, to return a collection of pages where any of the terms in the "genr
## Regular expression comparison
{{< new-in 0.116.0 />}}
To return a collection of pages where the "author" page parameter begins with either "victor" or "Victor":
```go-html-template

View file

@ -12,13 +12,66 @@ params:
{{< new-in 0.128.0 />}}
```go-html-template
Transpile Sass to CSS using the LibSass transpiler included in Hugo's extended and extended/deploy editions, or [install Dart Sass](#dart-sass) to use the latest features of the Sass language.
Sass has two forms of syntax: [SCSS] and [indented]. Hugo supports both.
[scss]: https://sass-lang.com/documentation/syntax#scss
[indented]: https://sass-lang.com/documentation/syntax#the-indented-syntax
## Options
enableSourceMap
: (`bool`) Whether to generate a source map. Default is `false`.
includePaths
: (`slice`) A slice of paths, relative to the project root, that the transpiler will use when resolving `@use` and `@import` statements.
outputStyle
: (`string`) The output style of the resulting CSS. With LibSass, one of `nested` (default), `expanded`, `compact`, or `compressed`. With Dart Sass, either `expanded` (default) or `compressed`.
precision
: (`int`) The precision of floating point math. Applicable to LibSass. Default is `8`.
silenceDeprecations
: {{< new-in 0.139.0 />}}
: (`slice`) A slice of deprecation IDs to silence. IDs are enclosed in brackets within Dart Sass warning messages (e.g., `import` in `WARN Dart Sass: DEPRECATED [import]`). Applicable to Dart Sass. Default is `false`.
silenceDependencyDeprecations
: {{< new-in 0.146.0 />}}
: (`bool`) Whether to silence deprecation warnings from dependencies, where a dependency is considered any file transitively imported through a load path. This does not apply to `@warn` or `@debug` rules.Default is `false`.
sourceMapIncludeSources
: (`bool`) Whether to embed sources in the generated source map. Applicable to Dart Sass. Default is `false`.
targetPath
: (`string`) The publish path for the transformed resource, relative to the[`publishDir`]. If unset, the target path defaults to the asset's original path with a `.css` extension.
transpiler
: (`string`) The transpiler to use, either `libsass` or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass). Default is `libsass`.
vars
: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/).
```scss
// LibSass
@import "hugo:vars";
// Dart Sass
@use "hugo:vars" as v;
```
## Example
```go-html-template {copy=true}
{{ with resources.Get "sass/main.scss" }}
{{ $opts := dict
"enableSourceMap" (not hugo.IsProduction)
"outputStyle" (cond hugo.IsProduction "compressed" "expanded")
"targetPath" "css/main.css"
"transpiler" "libsass"
"transpiler" "dartsass"
"vars" site.Params.styles
"includePaths" (slice "node_modules/bootstrap/scss")
}}
{{ with . | toCSS $opts }}
{{ if hugo.IsProduction }}
@ -32,63 +85,6 @@ params:
{{ end }}
```
Transpile Sass to CSS using the LibSass transpiler included in Hugo's extended and extended/deploy editions, or [install Dart Sass](#dart-sass) to use the latest features of the Sass language.
Sass has two forms of syntax: [SCSS] and [indented]. Hugo supports both.
[scss]: https://sass-lang.com/documentation/syntax#scss
[indented]: https://sass-lang.com/documentation/syntax#the-indented-syntax
## Options
transpiler
: (`string`) The transpiler to use, either `libsass` (default) or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass) below.
targetPath
: (`string`) If not set, the transformed resource's target path will be the original path of the asset file with its extension replaced by `.css`.
vars
: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/).
```scss
// LibSass
@import "hugo:vars";
// Dart Sass
@use "hugo:vars" as v;
```
outputStyle
: (`string`) Output styles available to LibSass include `nested` (default), `expanded`, `compact`, and `compressed`. Output styles available to Dart Sass include `expanded` (default) and `compressed`.
precision
: (`int`) Precision of floating point math. Not applicable to Dart Sass.
enableSourceMap
: (`bool`) Whether to generate a source map. Default is `false`.
sourceMapIncludeSources
: (`bool`) Whether to embed sources in the generated source map. Not applicable to LibSass. Default is `false`.
includePaths
: (`slice`) A slice of paths, relative to the project root, that the transpiler will use when resolving `@use` and `@import` statements.
```go-html-template
{{ $opts := dict
"transpiler" "dartsass"
"targetPath" "css/style.css"
"vars" site.Params.styles
"enableSourceMap" (not hugo.IsProduction)
"includePaths" (slice "node_modules/bootstrap/scss")
}}
{{ with resources.Get "sass/main.scss" | toCSS $opts | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{ end }}
```
silenceDeprecations
: (`slice`) {{< new-in 0.139.0 />}} A slice of deprecation IDs to silence. The deprecation IDs are printed to in the warning message, e.g "import" in `WARN Dart Sass: DEPRECATED [import] ...`. This is for Dart Sass only.
## Dart Sass
Hugo's extended and extended/deploy editions include [LibSass] to transpile Sass to CSS. In 2020, the Sass team deprecated LibSass in favor of [Dart Sass].
@ -121,6 +117,9 @@ You may also install [prebuilt binaries] for Linux, macOS, and Windows.
Run `hugo env` to list the active transpilers.
> [!note]
> If you build Hugo from source and run `mage test -v`, the test will fail if you install Dart Sass as a Snap package. This is due to the Snap package's strict confinement model.
### Installing in a production environment
For [CI/CD](g) deployments (e.g., GitHub Pages, GitLab Pages, Netlify, etc.) you must edit the workflow to install Dart Sass before Hugo builds the site[^2]. Some providers allow you to use one of the package managers above, or you can download and extract one of the prebuilt binaries.
@ -136,8 +135,6 @@ To install Dart Sass for your builds on GitHub Pages, add this step to the GitHu
run: sudo snap install dart-sass
```
If you are using GitHub Pages for the first time with your repository, GitHub provides a [starter workflow] for Hugo that includes Dart Sass. This is the simplest way to get started.
#### GitLab Pages
To install Dart Sass for your builds on GitLab Pages, the `.gitlab-ci.yml` file should look something like this:
@ -194,34 +191,6 @@ command = """\
"""
```
### Example
To transpile with Dart Sass, set `transpiler` to `dartsass` in the options map passed to `css.Sass`. For example:
```go-html-template
{{ with resources.Get "sass/main.scss" }}
{{ $opts := dict
"enableSourceMap" (not hugo.IsProduction)
"outputStyle" (cond hugo.IsProduction "compressed" "expanded")
"targetPath" "css/main.css"
"transpiler" "dartsass"
}}
{{ with . | toCSS $opts }}
{{ if hugo.IsProduction }}
{{ with . | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{ end }}
{{ else }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{ end }}
{{ end }}
{{ end }}
```
### Miscellaneous
If you build Hugo from source and run `mage test -v`, the test will fail if you install Dart Sass as a Snap package. This is due to the Snap package's strict confinement model.
[brew.sh]: https://brew.sh/
[chocolatey.org]: https://community.chocolatey.org/packages/sass
[dart sass]: https://sass-lang.com/dart-sass
@ -232,3 +201,4 @@ If you build Hugo from source and run `mage test -v`, the test will fail if you
[snap package]: /installation/linux/#snap
[snapcraft.io]: https://snapcraft.io/dart-sass
[starter workflow]: https://github.com/actions/starter-workflows/blob/main/pages/hugo.yml
[`publishDir`]: /configuration/all/#publishdir

View file

@ -10,7 +10,18 @@ params:
signatures: ['template NAME [CONTEXT]']
---
Use the `template` function to execute [embedded templates]. For example:
Use the `template` function to execute any of these [embedded templates](g):
- [`disqus.html`]
- [`google_analytics.html`]
- [`opengraph.html`]
- [`pagination.html`]
- [`schema.html`]
- [`twitter_cards.html`]
For example:
```go-html-template
{{ range (.Paginate .Pages).Pages }}
@ -39,8 +50,21 @@ The example above can be rewritten using an [inline partial] template:
{{ end }}
```
The key distinctions between the preceding two examples are:
1. Inline partials are globally scoped. That means that an inline partial defined in _one_ template may be called from _any_ template.
2. Leveraging the [`partialCached`] function when calling an inline partial allows for performance optimization through result caching.
3. An inline partial can [`return`] a value of any data type instead of rendering a string.
{{% include "/_common/functions/go-template/text-template.md" %}}
[`disqus.html`]: /templates/embedded/#disqus
[`google_analytics.html`]: /templates/embedded/#google-analytics
[`opengraph.html`]: /templates/embedded/#open-graph
[`pagination.html`]: /templates/embedded/#pagination
[`partialCached`]: /functions/partials/includecached/
[`partial`]: /functions/partials/include/
[`return`]: /functions/go-template/return/
[`schema.html`]: /templates/embedded/#schema
[`twitter_cards.html`]: /templates/embedded/#x-twitter-cards
[inline partial]: /templates/partial/#inline-partials
[embedded templates]: /templates/embedded/

View file

@ -18,6 +18,9 @@ alignx
: {{< new-in 0.141.0 />}}
: (`string`) The horizontal alignment of the text relative to the horizontal offset, one of `left`, `center`, or `right`. Default is `left`.
aligny
: (`string`) The vertical alignment of the text relative to the vertical offset, one of `top`, `center`, or `bottom`. Default is `top`.
color
: (`string`) The font color, either a 3-digit or 6-digit hexadecimal color code. Default is `#ffffff` (white).

View file

@ -0,0 +1,155 @@
---
title: templates.Current
description: Returns information about the currently executing template.
categories: []
keywords: []
params:
functions_and_methods:
aliases: []
returnType: tpl.CurrentTemplateInfo
signatures: [templates.Current]
---
> [!note]
> This function is experimental and subject to change.
{{< new-in 0.146.0 />}}
The `templates.Current` function provides introspection capabilities, allowing you to access details about the currently executing templates. This is useful for debugging complex template hierarchies and understanding the flow of execution during rendering.
## Methods
Ancestors
: (`tpl.CurrentTemplateInfos`) Returns a slice containing information about each template in the current execution chain, starting from the parent of the current template and going up towards the initial template called. It excludes any base template applied via `define` and `block`. You can chain the `Reverse` method to this result to get the slice in chronological execution order.
Base
: (`tpl.CurrentTemplateInfoCommonOps`) Returns an object representing the base template that was applied to the current template, if any. This may be `nil`.
Filename
: (`string`) Returns the absolute path of the current template. This will be empty for embedded templates.
Name
: (`string`) Returns the name of the current template. This is usually the path relative to the layouts directory.
Parent
: (`tpl.CurrentTemplateInfo`) Returns an object representing the parent of the current template, if any. This may be `nil`.
## Examples
The examples below help visualize template execution and require a `debug` parameter set to `true` in your site configuration:
{{< code-toggle file=hugo >}}
[params]
debug = true
{{< /code-toggle >}}
### Boundaries
To visually mark where a template begins and ends execution:
```go-html-template {file="layouts/_default/single.html"}
{{ define "main" }}
{{ if site.Params.debug }}
<div class="debug">[entering {{ templates.Current.Filename }}]</div>
{{ end }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ if site.Params.debug }}
<div class="debug">[leaving {{ templates.Current.Filename }}]</div>
{{ end }}
{{ end }}
```
### Call stack
To display the chain of templates that led to the current one, create a partial template that iterates through its ancestors:
```go-html-template {file="layouts/partials/template-call-stack.html" copy=true}
{{ with templates.Current }}
<div class="debug">
{{ range .Ancestors }}
{{ .Filename }}<br>
{{ with .Base }}
{{ .Filename }}<br>
{{ end }}
{{ end }}
</div>
{{ end }}
```
Then call the partial from any template:
```go-html-template {file="layouts/partials/footer/copyright.html" copy=true}
{{ if site.Params.debug }}
{{ partial "template-call-stack.html" . }}
{{ end }}
```
The rendered template stack would look something like this:
```text
/home/user/project/layouts/partials/footer/copyright.html
/home/user/project/themes/foo/layouts/partials/footer.html
/home/user/project/layouts/_default/single.html
/home/user/project/themes/foo/layouts/_default/baseof.html
```
To reverse the order of the entries, chain the `Reverse` method to the `Ancestors` method:
```go-html-template {file="layouts/partials/template-call-stack.html" copy=true}
{{ with templates.Current }}
<div class="debug">
{{ range .Ancestors.Reverse }}
{{ with .Base }}
{{ .Filename }}<br>
{{ end }}
{{ .Filename }}<br>
{{ end }}
</div>
{{ end }}
```
### VS Code
To render links that, when clicked, will open the template in Microsoft Visual Studio Code, create a partial template with anchor elements that use the `vscode` URI scheme:
```go-html-template {file="layouts/partials/template-open-in-vs-code.html" copy=true}
{{ with templates.Current.Parent }}
<div class="debug">
<a href="vscode://file/{{ .Filename }}">{{ .Name }}</a>
{{ with .Base }}
<a href="vscode://file/{{ .Filename }}">{{ .Name }}</a>
{{ end }}
</div>
{{ end }}
```
Then call the partial from any template:
```go-html-template {file="layouts/_default/single.html" copy=true}
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ if site.Params.debug }}
{{ partial "template-open-in-vs-code.html" . }}
{{ end }}
{{ end }}
```
Use the same approach to render the entire call stack as links:
```go-html-template {file="layouts/partials/template-call-stack.html" copy=true}
{{ with templates.Current }}
<div class="debug">
{{ range .Ancestors }}
<a href="vscode://file/{{ .Filename }}">{{ .Filename }}</a><br>
{{ with .Base }}
<a href="vscode://file/{{ .Filename }}">{{ .Filename }}</a><br>
{{ end }}
{{ end }}
</div>
{{ end }}
```

View file

@ -0,0 +1,30 @@
---
title: time.In
description: Returns the given date/time as represented in the specified IANA time zone.
categories: []
keywords: []
params:
functions_and_methods:
aliases: []
returnType: time.Time
signatures: [time.In TIMEZONE INPUT]
---
{{< new-in 0.146.0 />}}
The `time.In` function returns the given date/time as represented in the specified [IANA](g) time zone.
- If the time zone is an empty string or `UTC`, the time is returned in [UTC](g).
- If the time zone is `Local`, the time is returned in the system's local time zone.
- Otherwise, the time zone must be a valid IANA [time zone name].
[time zone name]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
```go-html-template
{{ $layout := "2006-01-02T15:04:05-07:00" }}
{{ $t := time.AsTime "2025-03-31T14:45:00-00:00" }}
{{ $t | time.In "America/Denver" | time.Format $layout }} → 2025-03-31T08:45:00-06:00
{{ $t | time.In "Australia/Adelaide" | time.Format $layout }} → 2025-04-01T01:15:00+10:30
{{ $t | time.In "Europe/Oslo" | time.Format $layout }} → 2025-03-31T16:45:00+02:00
```

View file

@ -114,12 +114,14 @@ A remote resource is a file on a remote server, accessible via HTTP or HTTPS.
>
> `{{ $data = .Content | transform.Unmarshal }}`
## Options
## Working with CSV
### Options
When unmarshaling a CSV file, provide an optional map of options.
delimiter
: (`string`) The delimiter used, default is `,`.
: (`string`) The delimiter used. Default is `,`.
comment
: (`string`) The comment character used in the CSV. If set, lines beginning with the comment character without preceding whitespace are ignored.
@ -128,8 +130,85 @@ lazyQuotes
: {{< new-in 0.122.0 />}}
: (`bool`) Whether to allow a quote in an unquoted field, or to allow a non-doubled quote in a quoted field. Default is `false`.
targetType
: {{< new-in 0.146.7 />}}
: (`string`) The target data type, either `slice` or `map`. Default is `slice`.
### Examples
The examples below use this CSV file:
```csv
"name","type","breed","age"
"Spot","dog","Collie",3
"Rover","dog","Boxer",5
"Felix","cat","Calico",7
```
To render an HTML table from a CSV file:
```go-html-template
{{ $csv := "a;b;c" | transform.Unmarshal (dict "delimiter" ";") }}
{{ $data := slice }}
{{ $file := "pets.csv" }}
{{ with or (.Resources.Get $file) (resources.Get $file) }}
{{ $opts := dict "targetType" "slice" }}
{{ $data = transform.Unmarshal $opts . }}
{{ end }}
{{ with $data }}
<table>
<thead>
<tr>
{{ range index . 0 }}
<th>{{ . }}</th>
{{ end }}
</tr>
</thead>
<tbody>
{{ range . | after 1 }}
<tr>
{{ range . }}
<td>{{ . }}</td>
{{ end }}
</tr>
{{ end }}
</tbody>
</table>
{{ end }}
```
To extract a subset of the data, or to sort the data, unmarshal to a map instead of a slice:
```go-html-template
{{ $data := slice }}
{{ $file := "pets.csv" }}
{{ with or (.Resources.Get $file) (resources.Get $file) }}
{{ $opts := dict "targetType" "map" }}
{{ $data = transform.Unmarshal $opts . }}
{{ end }}
{{ with sort (where $data "type" "dog") "name" "asc" }}
<table>
<thead>
<tr>
<th>name</th>
<th>type</th>
<th>breed</th>
<th>age</th>
</tr>
</thead>
<tbody>
{{ range . }}
<tr>
<td>{{ .name }}</td>
<td>{{ .type }}</td>
<td>{{ .breed }}</td>
<td>{{ .age }}</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}
```
## Working with XML

View file

@ -136,6 +136,8 @@ jobs:
key: hugo-${{ github.run_id }}
restore-keys:
hugo-
- name: Configure Git
run: git config core.quotepath false
- name: Build with Hugo
run: |
hugo \

View file

@ -23,15 +23,15 @@ Define your [CI/CD](g) jobs by creating a `.gitlab-ci.yml` file in the root of y
```yaml {file=".gitlab-ci.yml" copy=true}
variables:
DART_SASS_VERSION: 1.85.0
DART_SASS_VERSION: 1.87.0
GIT_DEPTH: 0
GIT_STRATEGY: clone
GIT_SUBMODULE_STRATEGY: recursive
HUGO_VERSION: 0.144.2
NODE_VERSION: 23.x
HUGO_VERSION: 0.146.7
NODE_VERSION: 22.x
TZ: America/Los_Angeles
image:
name: golang:1.23.4-bookworm
name: golang:1.24.2-bookworm
pages:
script:
@ -53,6 +53,8 @@ pages:
- apt-get install -y nodejs
# Install Node.js dependencies
- "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
# Configure Git
- git config core.quotepath false
# Build
- hugo --gc --minify --baseURL ${CI_PAGES_URL}
# Compress

View file

@ -113,21 +113,23 @@ Create a new file named netlify.toml in the root of your project directory. In i
```toml {file="netlify.toml"}
[build.environment]
HUGO_VERSION = "0.144.2"
GO_VERSION = "1.24"
HUGO_VERSION = "0.146.7"
NODE_VERSION = "22"
TZ = "America/Los_Angeles"
[build]
publish = "public"
command = "hugo --gc --minify"
command = "git config core.quotepath false && hugo --gc --minify"
```
If your site requires Dart Sass to transpile Sass to CSS, the configuration file should look something like this:
```toml {file="netlify.toml"}
[build.environment]
HUGO_VERSION = "0.144.2"
DART_SASS_VERSION = "1.85.0"
DART_SASS_VERSION = "1.87.0"
GO_VERSION = "1.24"
HUGO_VERSION = "0.146.7"
NODE_VERSION = "22"
TZ = "America/Los_Angeles"
@ -138,6 +140,7 @@ command = """\
tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
rm dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
export PATH=/opt/build/repo/dart-sass:$PATH && \
git config core.quotepath false && \
hugo --gc --minify \
"""
```

View file

@ -30,17 +30,33 @@ To install the extended edition of Hugo:
sudo snap install hugo
```
To enable or revoke access to removable media:
To control automatic updates:
```sh
# disable automatic updates
sudo snap refresh --hold hugo
# enable automatic updates
sudo snap refresh --unhold hugo
```
To control access to removable media:
```sh
# allow access
sudo snap connect hugo:removable-media
# revoke access
sudo snap disconnect hugo:removable-media
```
To enable or revoke access to SSH keys:
To control access to SSH keys:
```sh
# allow access
sudo snap connect hugo:ssh-keys
# revoke access
sudo snap disconnect hugo:ssh-keys
```

View file

@ -23,7 +23,8 @@
"dates" $dates
"kind" "page"
"params" $params
"path" .name
"path" (strings.Replace .name "." "-")
"slug" .name
"title" (printf "Release %s" .name)
}}
{{ $.AddPage $page }}

View file

@ -0,0 +1,6 @@
---
title: IANA
reference: https://www.iana.org/about
---
_IANA_ is an abbreviation for the Internet Assigned Numbers Authority, a non-profit organization that manages the allocation of global IP addresses, autonomous system numbers, DNS root zone, media types, and other Internet Protocol-related resources.

View file

@ -0,0 +1,6 @@
---
title: UTC
reference: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
---
_UTC_ is an abbreviation for Coordinated Universal Time, the primary time standard used worldwide to regulate clocks and time. It is the basis for civil time and time zones across the globe.

View file

@ -10,7 +10,7 @@ keywords: []
> To override Hugo's embedded `ref` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory.
> [!note]
> When working with the Markdown [content format], this shortcode has become largely redundant. Its functionality is now primarily handled by [link render hooks], specifically the embedded one provided by Hugo. This hook effectively addresses all the use cases previously covered by this shortcode.
> When working with Markdown, this shortcode is obsolete. Instead, use a [link render hook] that resolves the link destination using the `GetPage` method on the `Page` object. You can either create your own, or simply enable the [embedded link render hook]. The embedded link render hook is automatically enabled for multilingual single-host projects.
## Usage
@ -56,6 +56,7 @@ Rendered:
{{% include "_common/ref-and-relref-error-handling.md" %}}
[content format]: /content-management/formats/
[link render hooks]: /render-hooks/images/#default
[embedded link render hook]: /render-hooks/links/#default
[link render hook]: /render-hooks/links/
[Markdown notation]: /content-management/shortcodes/#notation
[source code]: {{% eturl ref %}}
[source code]: {{% eturl relref %}}

View file

@ -10,7 +10,7 @@ keywords: []
> To override Hugo's embedded `relref` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory.
> [!note]
> When working with the Markdown [content format], this shortcode has become largely redundant. Its functionality is now primarily handled by [link render hooks], specifically the embedded one provided by Hugo. This hook effectively addresses all the use cases previously covered by this shortcode.
> When working with Markdown, this shortcode is obsolete. Instead, use a [link render hook] that resolves the link destination using the `GetPage` method on the `Page` object. You can either create your own, or simply enable the [embedded link render hook]. The embedded link render hook is automatically enabled for multilingual single-host projects.
## Usage
@ -56,6 +56,7 @@ Rendered:
{{% include "_common/ref-and-relref-error-handling.md" %}}
[content format]: /content-management/formats/
[link render hooks]: /render-hooks/links/
[embedded link render hook]: /render-hooks/links/#default
[link render hook]: /render-hooks/links/
[Markdown notation]: /content-management/shortcodes/#notation
[source code]: {{% eturl relref %}}

View file

@ -29,19 +29,27 @@ Hugo renders this to:
## Arguments
id
: (string) The video `id`. Optional if the `id` is provided as a positional argument as shown in the example above.
allowFullScreen
: {{< new-in 0.146.0 />}}
: (`bool`) Whether the `iframe` element can activate full screen mode. Default is `true`.
class
: (`string`) The `class` attribute of the wrapping `div` element. Adding one or more CSS classes disables inline styling.
id
: (`string`) The `id` of the Vimeo video
loading
: {{< new-in 0.146.0 />}}
: (`string`) The loading attribute of the `iframe` element, either `eager` or `lazy`. Default is `eager`.
title
: (`string`) The `title` attribute of the `iframe` element.
If you provide a `class` or `title` you must use a named parameter for the `id`.
Here's an example using some of the available arguments:
```text
{{</* vimeo id=55073825 class="foo bar" title="My Video" */>}}
{{</* vimeo id=55073825 allowFullScreen=false loading=lazy */>}}
```
## Privacy

View file

@ -70,7 +70,7 @@ start
title
: (`string`) The `title` attribute of the `iframe` element. Defaults to "YouTube video".
Example using some of the above:
Here's an example using some of the available arguments:
```text
{{</* youtube id=0RKpf3rK57I start=30 end=60 loading=lazy */>}}

View file

@ -1,6 +1,6 @@
---
title: Embedded templates
description: Hugo provides embedded templates for common use cases.
title: Embedded partial templates
description: Hugo provides embedded partial templates for common use cases.
categories: []
keywords: []
weight: 170
@ -145,6 +145,10 @@ Various optional metadata can also be set:
If using YouTube this will produce a og:video tag like `<meta property="og:video" content="url">`. Use the `https://youtu.be/<id>` format with YouTube videos (example: `https://youtu.be/qtIqKaDlqXo`).
## Pagination
See [details](/templates/pagination/).
## Schema
> [!note]

View file

@ -49,7 +49,7 @@ As shown in the above example directory structure, you can nest your directories
### Variable scoping
The second argument in a partial call is the variable being passed down. The above examples are passing the `.`, which tells the template receiving the partial to apply the current [context][context].
The second argument in a partial call is the variable being passed down. The above examples are passing the dot (`.`), which tells the template receiving the partial to apply the current [context][context].
This means the partial will *only* be able to access those variables. The partial is isolated and cannot access the outer scope. From within the partial, `$.Var` is equivalent to `.Var`.

View file

@ -329,7 +329,7 @@ You can use the `HasShortcode` method in your base template to conditionally loa
[`with`]: /functions/go-template/with/
[content management]: /content-management/shortcodes/
[embedded shortcodes]: /shortcodes/
[GitHub]: https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/shortcodes
[GitHub]: https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/_shortcodes
[introduction to templating]: /templates/introduction/
[Markdown notation]: /content-management/shortcodes/#markdown-notation
[named or positional]: /content-management/shortcodes/#arguments

View file

@ -34,6 +34,11 @@ Use the [`printf`] function (render) or [`warnf`] function (log to console) to i
{{ printf "%[1]v (%[1]T)" $value }} → 42 (int)
```
{{< new-in 0.146.0 />}}
Use the [`templates.Current`] function to visually mark template execution boundaries or to display the template call stack.
[`debug.Dump`]: /functions/debug/dump/
[`printf`]: /functions/fmt/printf/
[`warnf`]: /functions/fmt/warnf/
[`templates.Current`]: /functions/templates/current/

View file

@ -4,39 +4,39 @@
# BaseURL
'base_url' = 'https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates'
# Templates
'alias' = 'alias.html'
'disqus' = 'disqus.html'
'google_analytics' = 'google_analytics.html'
'opengraph' = 'opengraph.html'
'pagination' = 'pagination.html'
'robots' = '_default/robots.txt'
'rss' = '_default/rss.xml'
'schema' = 'schema.html'
'sitemap' = '_default/sitemap.xml'
'sitemapindex' = '_default/sitemapindex.xml'
'twitter_cards' = 'twitter_cards.html'
# Partials
'disqus' = '_partials/disqus.html'
'google_analytics' = '_partials/google_analytics.html'
'opengraph' = '_partials/opengraph.html'
'pagination' = '_partials/pagination.html'
'schema' = '_partials/schema.html'
'twitter_cards' = '_partials/twitter_cards.html'
# Render hooks
'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html'
'render-image' = '_default/_markup/render-image.html'
'render-link' = '_default/_markup/render-link.html'
'render-table' = '_default/_markup/render-table.html'
'render-codeblock-goat' = '_markup/render-codeblock-goat.html'
'render-image' = '_markup/render-image.html'
'render-link' = '_markup/render-link.html'
'render-table' = '_markup/render-table.html'
# Shortcodes
'details' = 'shortcodes/details.html'
'figure' = 'shortcodes/figure.html'
'gist' = 'shortcodes/gist.html'
'highlight' = 'shortcodes/highlight.html'
'instagram' = 'shortcodes/instagram.html'
'param' = 'shortcodes/param.html'
'qr' = 'shortcodes/qr.html'
'ref' = 'shortcodes/ref.html'
'relref' = 'shortcodes/relref.html'
'twitter' = 'shortcodes/twitter.html'
'twitter_simple' = 'shortcodes/twitter_simple.html'
'vimeo' = 'shortcodes/vimeo.html'
'vimeo_simple' = 'shortcodes/vimeo_simple.html'
'x' = 'shortcodes/x.html'
'x_simple' = 'shortcodes/x_simple.html'
'youtube' = 'shortcodes/youtube.html'
'details' = '_shortcodes/details.html'
'figure' = '_shortcodes/figure.html'
'gist' = '_shortcodes/gist.html'
'highlight' = '_shortcodes/highlight.html'
'instagram' = '_shortcodes/instagram.html'
'param' = '_shortcodes/param.html'
'qr' = '_shortcodes/qr.html'
'ref' = '_shortcodes/ref.html'
'relref' = '_shortcodes/relref.html'
'vimeo' = '_shortcodes/vimeo.html'
'vimeo_simple' = '_shortcodes/vimeo_simple.html'
'x' = '_shortcodes/x.html'
'x_simple' = '_shortcodes/x_simple.html'
'youtube' = '_shortcodes/youtube.html'
# Other
'alias' = 'alias.html'
'robots' = 'robots.txt'
'rss' = 'rss.xml'
'sitemap' = 'sitemap.xml'
'sitemapindex' = 'sitemapindex.xml'

View file

@ -196,7 +196,7 @@ either of these shortcodes in conjunction with this render hook.
>{{ .Text }}</a
>
{{- define "partials/inline/h-rh-l/validate-fragment.html" }}
{{- define "_partials/inline/h-rh-l/validate-fragment.html" }}
{{- /*
Validates the fragment portion of a link destination.
@ -248,7 +248,7 @@ either of these shortcodes in conjunction with this render hook.
{{- end }}
{{- end }}
{{- define "partials/inline/h-rh-l/get-glossary-link-attributes.html" }}
{{- define "_partials/inline/h-rh-l/get-glossary-link-attributes.html" }}
{{- /*
Returns the anchor element attributes for a link to the given glossary term.

View file

@ -5,6 +5,7 @@
{{ $image1x := $image.Resize (printf "%dx" $width1x) }}
{{ $image1xWebp := $image.Resize (printf "%dx webp" $width1x) }}
{{ $class := .class | default "h-64 tablet:h-96 lg:h-full w-full object-cover lg:absolute" }}
{{ $loading := .loading | default "eager" }}
<picture>
<source
srcset="{{ $imageWebp.RelPermalink }}"
@ -20,6 +21,7 @@
class="{{ $class }}"
src="{{ $image1x.RelPermalink }}"
alt=""
loading="{{ $loading }}"
width="{{ $image1x.Width }}"
height="{{ $image1x.Height }}">
</picture>

View file

@ -10,7 +10,7 @@
{{ partial "layouts/blocks/modal.html" (dict "modal_button" $qr "modal_content" $qrBig "modal_title" (printf "QR code linking to %s" $.Permalink )) }}
</div>
{{ define "partials/_inline/qr" }}
{{ define "_partials/_inline/qr" }}
{{ $img_class := .img_class | default "w-10" }}
{{ with images.QR $.page.Permalink (dict "targetDir" "images/qr") }}

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Before After
Before After

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