Compare commits

...

162 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen 0c8232e3bd github: Add a "free space" step on Ubuntu 2024-04-14 16:14:16 +02:00
Bjørn Erik Pedersen bfc3122f8e helpers: Add BenchmarkTrimShortHTML 2024-04-14 15:54:49 +02:00
Bjørn Erik Pedersen 00ae8e8c72 github: Update actions 2024-04-14 13:40:46 +02:00
Bjørn Erik Pedersen e423e56273 github: Format GitHub actions files 2024-04-14 13:40:46 +02:00
Joe Mooring 09eb822822 hugolib: Display server address after each rebuild
Closes #12359
2024-04-13 22:16:00 +02:00
Joe Mooring a6e8439176 resources/page: Add taxonomies Page method
Closes #12316
2024-04-12 16:26:02 +02:00
Bjørn Erik Pedersen 38f68cd162 commands: Adjust completions 2024-04-11 15:34:26 +02:00
Ville Skyttä a67650b6f7 completion: Improve existing argument completions, add many more
Do not offer filenames to arguments not taking one, complete arguments
of options taking resource kinds, directory names, --logLevel, release
--step, config and new --format.

As an internal refactoring, use higher level functions to set flag
completions.  SetAnnotation works, but is more verbose than
alternatives, and uses bash specific wording.

While at it, move setting completions next to flag definitions
consistently.

Remove superfluous --destination completer setting, which is already
set elsewhere.
2024-04-11 15:34:26 +02:00
Bjørn Erik Pedersen 2a060b37a3 Upgrade to Go 1.22.2
Closes #12351
2024-04-11 15:14:28 +02:00
dependabot[bot] 97df6be59f build(deps): bump golang.org/x/tools from 0.19.0 to 0.20.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.19.0 to 0.20.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.19.0...v0.20.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 14:18:55 +02:00
Bjørn Erik Pedersen 9323376dfa github: Fix CI build 2024-04-11 13:29:19 +02:00
Eitan Adler bf0b140364 all: Fix duplicate words in comments 2024-04-11 09:31:33 +02:00
dependabot[bot] e9b8bec433 build(deps): bump golang.org/x/net from 0.23.0 to 0.24.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.24.0.
- [Commits](https://github.com/golang/net/compare/v0.23.0...v0.24.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 09:29:14 +02:00
dependabot[bot] 888cc1e61e build(deps): bump github.com/getkin/kin-openapi from 0.123.0 to 0.124.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.123.0 to 0.124.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.123.0...v0.124.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 09:28:50 +02:00
Christian Oliff 17765a7451 all: Typo fixes 2024-04-11 09:23:17 +02:00
Bjørn Erik Pedersen 92de8625c7 babel: Run go fmt 2024-04-08 18:28:04 +02:00
guangwu 7907935a42 babel: Close file before removing 2024-04-08 15:23:08 +02:00
dependabot[bot] 02d5ec14f3 bump golang.org/x/mod from 0.16.0 to 0.17.0
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/mod/compare/v0.16.0...v0.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 17:44:24 +02:00
curegit 26640525a3 hugolib: Fix regression for blank summaries
Fix regression in content summarization so that we can use empty
summary by using the manual summary divider. Since v0.123, there
has been the regression that causes Hugo to use automatic summary
generation when the manual summary results in an empty string,
even if there is a `<!--more-->` summary divider.
2024-04-05 17:43:55 +02:00
Bjørn Erik Pedersen 488b21d15b Fix sectionPagesMenu for pages in root level
Fixes #12306
2024-04-05 17:38:08 +02:00
Soren L. Hansen 4500b0e423 resources/page: Escape hash sign in permalinks
When creating a link to a file with a `#` in the filename, the link gets
truncated. This happens because the filename is eventaully passed to
`url.Parse` which (correctly!) interprets the `#` as fragment separator.

This commit escapes the `#` in the filename before creating the link.

Fixes #4926
Fixes #8232
Fixes #12342

Co-authored-by: Joe Mooring <joe.mooring@veriphor.com>
2024-04-05 15:57:02 +02:00
dependabot[bot] 060cce0a91 build(deps): bump github.com/pelletier/go-toml/v2 from 2.1.1 to 2.2.0
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 11:28:58 +02:00
dependabot[bot] 5608ba1f75 build(deps): bump github.com/yuin/goldmark from 1.7.0 to 1.7.1
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.0...v1.7.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 11:27:59 +02:00
Joe Mooring 7bf1abfc55 tpl/strings: Improve type checking 2024-04-04 18:34:55 +02:00
dependabot[bot] 2fedca6c8a build(deps): bump github.com/aws/aws-sdk-go-v2/service/cloudfront
Bumps [github.com/aws/aws-sdk-go-v2/service/cloudfront](https://github.com/aws/aws-sdk-go-v2) from 1.32.6 to 1.35.4.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/fsx/v1.32.6...service/ecs/v1.35.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 11:16:30 +02:00
dependabot[bot] 07873b74bd build(deps): bump golang.org/x/net from 0.22.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 11:15:10 +02:00
Joe Mooring 8a0ea12d8a tpl/tplimpl: Improve youtube shortcode
Changes:

- Add query string params for controls, loop, mute, start, and end
- Add iframe loading attribute
- Obtain default iframe title from YouTube oEmbed API
- Fix autoplay feature
- Improve readability

Closes #3694
Closes #9213
Closes #10520
Closes #10575
Closes #10576

Co-authored-by: sgharms <sgharms@stevengharms.com>
2024-04-04 11:08:30 +02:00
seiya 6f07e5976d errors: Return error from cast.ToStringE() consistently 2024-04-04 11:00:14 +02:00
Joe Mooring 2da4ec5738 tpl/tplimpl: Improve embedded opengraph template
Changes:

- Add tags per documentation
- Prefer site.Title over site.Params.title
- Plainify titles, tags, and descriptions
- Add fallback values for locale
- Fix pages related by series
- Improve readability

Closes #8296
Closes #8698
Closes #8991
Closes #9818
Closes #9866
Closes #10647

Co-authored-by: tomy0000000 <git@tomy.me>
Co-authored-by: sean-au <sean@powerfulwebdesign.com.au>
2024-04-02 18:35:43 +02:00
Joe Mooring 6624979e1b tpl/strings: Create strings.Diff template function
Closes #12330
2024-04-02 18:25:44 +02:00
Bjørn Erik Pedersen 983b8d537c Fix resource bundling for overlapping page.md vs page.txt
Fixes #12320
2024-04-02 14:32:05 +02:00
Joe Mooring 6738a3e79d tpl/tplimpl: Optionally exclude content from sitemap
Define global inclusion/exclusion in site configuration, and override
via front matter. For example, to exclude a page from the sitemap:

    [sitemap]
    disable = true # default is false

Closes #653
Closes #12282

Co-authored-by: kolappannathan <kolappannathan@users.noreply.github.com>
Co-authored-by: felicianotech <FelicianoTech@gmail.com>
2024-04-02 11:21:03 +02:00
Joe Mooring 2f7df4b926 tpl/tplimpl: Remove trailing slash from void elements
Closes #11867
2024-04-01 08:07:02 -07:00
Joe Mooring f0a26cf58e tpl/tplimpl: Update RSS template
- Use publication date for pubdate
- Include version in generator element

Closes #3918
Closes #11692
2024-03-30 18:32:58 +01:00
Joe Mooring 74ce5dc841 tpl/tplimpl: Update schema template
Changes:

- Remove trailing comma from list of keywords.
- Improve keywords precedence:
  1. Use "keywords" term page titles.
  2. Use "keywords" from front matter if "keywords" is not a taxonomy.
  3. Use "tags" term page titles.
  4. Use term page titles from all taxonomies.
- Enable schema for all page kinds, previously limited to kind = page.
- Remove trailing slashes from void elements.
- Improve readability.

Closes #7570

Co-authored by: 0urobor0s <0urobor0s@users.noreply.github.com>
2024-03-28 14:56:02 +01:00
Joe Mooring 54a8f0ce21 resources: Use different cache key when copying resources
Closes #10412
Closes #12310
2024-03-27 09:59:59 +01:00
Bjørn Erik Pedersen 38e05bd3c7 Fix panic with debug.Dump with Page when running the server
This replaces the current implementation with `json.MarshalIndent` which doesn't produce the same output, but at least it doesn't crash.

There's a bug in the upstream `litter` library. This can probably be fixed, but that needs to wait.

I have tested `go-spew` which does not crash, but it is very data racy in this context.

FIxes #12309
2024-03-26 20:41:30 +01:00
Joe Mooring ebfca61ac4 tpl/tplimpl: Update Google Analytics template and config
Google Analytics 4 (GA4) replaced Google Universal Analytics (UA)
effective 1 July 2023.

See https://support.google.com/analytics/answer/11583528.

Changes:

- Update tpl/tplimpl/embedded/templates/google_analytics.html
- Remove tpl/tplimpl/embedded/templates/google_analytics_async.html
- Remove extraneous config settings

Closes #11802
Closes #10093
2024-03-26 15:40:51 +01:00
Joe Mooring e1917740af hugolib: Conditionally suppress .Site.Author deprecation notice
Suppress the .Site.Author deprecation notice unless the Author key
is present and not empty in the site configuration.

Closes #12297
2024-03-26 10:28:03 +01:00
George Ma 27414d43a0 resources/page: Fix GoDoc comment 2024-03-22 08:56:10 +01:00
Joe Mooring c837f36ab4 markup/asciidocext: Add Level to Heading struct
Closes #12291
2024-03-21 19:14:49 +01:00
hugoreleaser a2f67152b3 releaser: Prepare repository for 0.125.0-DEV
[ci skip]
2024-03-20 11:53:43 +00:00
hugoreleaser db083b05f1 releaser: Bump versions for release of 0.124.1
[ci skip]
2024-03-20 11:40:10 +00:00
Bjørn Erik Pedersen 758a876f90 Fix potential deadlock in Translations
Fixes #12129
2024-03-20 12:33:05 +01:00
Bjørn Erik Pedersen 19937a20ad Fix rebuild when changing mixed case named templates
Fixes #12165
2024-03-20 12:33:05 +01:00
Anthony Fok c1ea22a232 testing: Set usesFMA as true for riscv64 too
This fixes TestImageOperationsGolden "values are not deep equal" error on riscv64 with Go 1.22 and above.
2024-03-20 08:59:13 +01:00
Bjørn Erik Pedersen 0750a9ec91 Fix regression for outputs defined in front matter for term pages
Fixes #12275
2024-03-19 15:07:48 +01:00
hugoreleaser 90bc1f802a releaser: Prepare repository for 0.125.0-DEV
[ci skip]
2024-03-16 15:57:20 +00:00
hugoreleaser 629f84e8ed releaser: Bump versions for release of 0.124.0
[ci skip]
2024-03-16 15:44:32 +00:00
Bjørn Erik Pedersen 76ef3f42fa docs: Regen CLI docs 2024-03-16 15:54:33 +01:00
Bjørn Erik Pedersen 0ccb6cdc04 docs: Regen docshelper 2024-03-16 15:54:02 +01:00
Bjørn Erik Pedersen 1f1c62e6c7 Add segments config + --renderSegments flag
Named segments can be defined in `hugo.toml`.

* Eeach segment consists of zero or more `exclude` filters and zero or more `include` filters.
* Eeach filter consists of one or more field Glob matchers.
* Eeach filter in a section (`exclude` or `include`) is ORed together, each matcher in a filter is ANDed together.

The current list of fields that can be filtered are:

* path as defined in https://gohugo.io/methods/page/path/
* kind
* lang
* output (output format, e.g. html).

It is recommended to put coarse grained filters (e.g. for language and output format) in the excludes section, e.g.:

```toml
[segments.segment1]
  [[segments.segment1.excludes]]
    lang = "n*"
  [[segments.segment1.excludes]]
    no     = "en"
    output = "rss"
  [[segments.segment1.includes]]
    term = "{home,term,taxonomy}"
  [[segments.segment1.includes]]
    path = "{/docs,/docs/**}"
```

By default, Hugo will render all segments, but you can enable filters by setting the `renderSegments` option or `--renderSegments` flag, e.g:

```
hugo --renderSegments segment1,segment2
```

For segment `segment1` in the configuration above, this will:

* Skip rendering of all languages matching `n*`, e.g. `no`.
* Skip rendering of the output format `rss` for the `en` language.
* It will render all pages of kind `home`, `term` or `taxonomy`
* It will render the `/docs` section and all pages below.

Fixes #10106
2024-03-16 15:53:26 +01:00
Bjørn Erik Pedersen f1d755965f Fix .Parent when there are overlapping regular pages inbetween
Fixes #12263
2024-03-16 14:48:04 +01:00
Bjørn Erik Pedersen 558f74f009 hugolib: Remove Site.HomeAbsURL
It's not in use and after #12266 it's also not corret to use on its own (use .Site.Home.Permalink).
2024-03-16 12:49:40 +01:00
Bjørn Erik Pedersen ba03114aa9 deps: Upgrade github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0 => v0.2.0 2024-03-16 11:50:11 +01:00
Joe Mooring 3935faa417 hugolib: Fix sitemap index with monolingual site
Fixes #12266
2024-03-16 11:49:00 +01:00
Joe Mooring d4d49e0f0e hugolib: Deprecate site methods Author, Authors, and Social
Closes #12228
2024-03-15 17:26:45 +01:00
Christian Oliff 78178d0c2a all: Typo fixes 2024-03-15 17:25:52 +01:00
Bjørn Erik Pedersen 68d92ef9dd Fix translationKey handling for term pages
Fixes #12261
2024-03-15 16:54:18 +01:00
Bjørn Erik Pedersen b40f3c7df6 Fix intersect and similar for term entry page collections
Fixes #12254
2024-03-15 16:45:53 +01:00
Bjørn Erik Pedersen 57206e7274 Upgrade to Go 1.22.1
Closes #12250
2024-03-15 16:40:36 +01:00
dependabot[bot] b1f8676347 build(deps): bump github.com/evanw/esbuild from 0.20.1 to 0.20.2
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.20.1 to 0.20.2.
- [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.20.1...v0.20.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-15 15:43:18 +01:00
Bjørn Erik Pedersen 07b2e535be Fix server rebuilds when adding sub sections especially on Windows
This commit also optimizes for the case where change events for both file (e.g. `_index.md`) and the container directory comes in the same event batch.

While testing this on Windows 11 (ARM64), I notice that Windows behaves a little oddly when dumping a folder of files into the content tree; it works (at least after this commit), but it seems like the event batching behaves differently compared to other OSes (even older Win versions).

A related tip would be to try starting the server with polling, to see if that improves the situation, e.g.:

```
hugo server --poll 700ms
```

Fixes #12230
2024-03-15 15:42:56 +01:00
Joe Mooring f038a51b3e tpl/tplimpl: Remove deprecated method from sitemapindex.xml 2024-03-14 21:03:19 +01:00
dependabot[bot] b4bff6190c build(deps): bump golang.org/x/tools from 0.18.0 to 0.19.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.18.0 to 0.19.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.18.0...v0.19.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-14 11:16:50 +01:00
dependabot[bot] d2cebee273 build(deps): bump github.com/tdewolff/minify/v2 from 2.20.17 to 2.20.19
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.17 to 2.20.19.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.17...v2.20.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-14 11:16:07 +01:00
Paul Jolly be914ff34d deps: Upgrade github.com/alecthomas/chroma/v2 to v2.13.0
Closes #11862
2024-03-14 11:04:41 +01:00
Joe Mooring 48a0fea87a tpl/tplimpl: Modify figure shortcode to look for page resource
Closes #12244
Closes #12245
2024-03-14 10:21:40 +02:00
Bjørn Erik Pedersen 9ca1de09dd Fix panic when changing archetype files when servere is running
Fixes #12195
2024-03-13 19:35:28 +02:00
dependabot[bot] e62675002e build(deps): bump golang.org/x/mod from 0.15.0 to 0.16.0
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/mod/compare/v0.15.0...v0.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 19:23:42 +02:00
Bjørn Erik Pedersen 9668759ad8 Fix front matter date location when value gets inherited from other dates
Fixes #12236
2024-03-13 19:00:50 +02:00
Bjørn Erik Pedersen 9e9b1f110c Fix Name for nested resourced fetched in resources.ByName and similar
Fixes #12214
2024-03-13 18:59:50 +02:00
Joe Mooring dc6a292133 common/hugo: Rename IsMultiHost and IsMultiLingual
hugo.IsMultiHost => hugo.IsMultihost
hugo.IsMultiLingual => hugo.IsMultilingual

Closes #12232
2024-03-13 17:34:42 +02:00
Joe Mooring 4f92f949ea hugolib: Deprecate .Site.MultiLingual in favor of hugo.IsMultiLingual
Closes #12224
2024-03-10 10:47:51 +02:00
Joe Mooring d24ffdde5b snap: Transition to from core20 to core22
Closes #12219
2024-03-09 08:47:53 -08:00
hugoreleaser ca31b95f30 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-03-07 13:28:40 +00:00
hugoreleaser 5fed9c591b releaser: Bump versions for release of 0.123.8
[ci skip]
2024-03-07 13:14:42 +00:00
Jonas Zeiger 134e7d1d3d markup/goldmark: TOC: render strikethrough, emojis
Configure the TOC (TableOfContents, toc.go) goldmark renderer to always
enable the Strikethrough and Emoji extensions. This allows handling
ast.KindStrikethrough and ast.KindEmoji AST nodes when rendering the TOC.

Fixes #7169
Fixes #11783
Fixes #12022
2024-03-07 14:08:29 +01:00
razonyang 1f48b717c7 Add hugo.IsMultiHost 2024-03-07 14:04:41 +01:00
Andreas Deininger ada3fceea8 docs: Fix hyphens and grammar in synopsis of command 'hugo server' 2024-03-07 14:03:49 +01:00
Joe Mooring 05e23bd55c resources/images: Retain newlines with text overlays
Closes #12206
2024-03-07 09:52:30 +01:00
Bjørn Erik Pedersen 7afac3f1ad Don't auto-create empty sections for nested taxonomies
Fixes #12188
2024-03-07 09:50:50 +01:00
Bjørn Erik Pedersen a4b17470a8 Fix resource name in resources.ByType
Fixes #12190
2024-03-07 09:50:50 +01:00
Bjørn Erik Pedersen 4d5e173cf8 Fix global resource isn't published when using an uncommon code construct
Fixes #12190
2024-03-07 09:50:50 +01:00
Bjørn Erik Pedersen 4271b6be0f Fix section page resource not published if resource filename partially matches content file name
Fixes #12198
2024-03-07 09:50:50 +01:00
Bjørn Erik Pedersen 0567a3e6f1 Fix taxonomy kind template lookup issue
Fixes #12193
2024-03-07 09:50:50 +01:00
Joe Mooring 632ad74fc5 tpl/tplimpl: Honor markdown attributes in embedded image render hook
Fixes #12203
2024-03-07 08:21:58 +01:00
hugoreleaser b1de03f398 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-03-01 16:30:33 +00:00
hugoreleaser 312735366b releaser: Bump versions for release of 0.123.7
[ci skip]
2024-03-01 16:16:06 +00:00
Bjørn Erik Pedersen 2b2f2b75ef hugofs: Fix vertical mount merge issue
Fixes #12175
2024-03-01 17:10:13 +01:00
Bjørn Erik Pedersen 0d6e593ffb Fix and add integration test for the Bootstrap SCSS module for both Dart Sass and Libsass
This fixes the reverse filesystem lookup (absolute filename to path relative to the composite filesystem).

The old logic had some assumptions about the locality of the actual files that didn't work in more complex scenarios.

This commit now also adds the popular Bootstrap SCSS Hugo module to the CI build (both for libsass and dartsass transpiler), so we can hopefully avoid similar future breakage.

Fixes #12178
2024-03-01 14:18:52 +01:00
Bjørn Erik Pedersen 7023cf0f07 Fix resources.GetMatch, resources.Match, and resources.ByType to they don't normalize permalinks
Fixes #12182
2024-03-01 13:34:05 +01:00
Bjørn Erik Pedersen 9dfa9e70e6 Make sure that sitemaps gets generated even if there is a content bundle with the same path
Fixes #12183
2024-03-01 13:34:05 +01:00
Joe Mooring 3f217fd664 resources/page: Make Taxonomy.Get and Taxonomy.Count case-insensitive
Closes #12177
2024-03-01 12:09:00 +01:00
hugoreleaser 92684f9a26 releaser: Bump versions for release of 0.123.6
[ci skip]
2024-02-28 18:29:40 +00:00
Bjørn Erik Pedersen fce8d82b79 Fix panic when cascading headless from site config to section that does not have an _index.md file
Fixes #12172
2024-02-28 19:26:55 +01:00
Bjørn Erik Pedersen 4a502f7eb4 Fix assets vs data issue
And possibly some other related file mount issues.

Fixes #12133
2024-02-28 19:26:55 +01:00
Bjørn Erik Pedersen be1dbba0f7 Fix draft for non-default content when content in default language does not exist
Fixes #12132
2024-02-28 19:26:55 +01:00
hugoreleaser 1007bcdf49 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-02-28 16:01:41 +00:00
hugoreleaser 1904ba40e8 releaser: Bump versions for release of 0.123.5
[ci skip]
2024-02-28 15:47:09 +00:00
Bjørn Erik Pedersen 6a8b25e291 Fix .Page.Pages with similary named sections
Fixes #12169
2024-02-28 16:43:19 +01:00
Bjørn Erik Pedersen a322282e70 Fix single mount rename panic
Fixes #12141
2024-02-28 13:38:12 +01:00
Bjørn Erik Pedersen 6bc0d745a5 Fix multihost processed image not copied to non-default content languages
Fixes #12163
2024-02-27 10:43:23 +01:00
dependabot[bot] 218690328c build(deps): bump github.com/bep/overlayfs from 0.9.1 to 0.9.2
Bumps [github.com/bep/overlayfs](https://github.com/bep/overlayfs) from 0.9.1 to 0.9.2.
- [Release notes](https://github.com/bep/overlayfs/releases)
- [Commits](https://github.com/bep/overlayfs/compare/v0.9.1...v0.9.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 09:59:15 +01:00
hugoreleaser 673c634131 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-02-26 16:46:48 +00:00
hugoreleaser 21a41003c4 releaser: Bump versions for release of 0.123.4
[ci skip]
2024-02-26 16:33:05 +00:00
Bjørn Erik Pedersen 1736ef7459 Fix cascade-pattern-with-extension for cascade in site config
Also clean up the log handling in the integration tester, most notably lost logs during the config loading.

Fixes #12151
2024-02-26 17:29:37 +01:00
dependabot[bot] d4be1643a0 build(deps): bump github.com/spf13/fsync from 0.10.0 to 0.10.1
Bumps [github.com/spf13/fsync](https://github.com/spf13/fsync) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/spf13/fsync/releases)
- [Commits](https://github.com/spf13/fsync/compare/v0.10.0...v0.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 17:08:34 +01:00
Bjørn Erik Pedersen f27e578718 Fix term template lookup when its backed by a content file
Closes #12146
2024-02-25 18:28:16 +01:00
Bjørn Erik Pedersen d310595a2b resource: Revert the normalization of Resource.Name
Which means that .Name now returns the same as it did in 0.122.0.

Closes #12142
2024-02-25 14:57:29 +01:00
Bjørn Erik Pedersen 049dd1d7e0 Fix panic for disableKinds page for content resources
Fixes #12144
2024-02-25 11:38:52 +01:00
Bjørn Erik Pedersen 189b72331e tocss: Fix the import resolving from absolute to relative assets paths
Fixes #12137
2024-02-24 16:41:18 +01:00
Bjørn Erik Pedersen b2b7bfdd3a Maek Resoyrce.Key as (mostly) internal 2024-02-24 15:17:15 +01:00
Joe Mooring 80e69344da config/allconfig: Fix typo
Closes #12134
2024-02-23 12:40:15 -08:00
hugoreleaser 7508a16470 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-02-23 17:22:38 +00:00
hugoreleaser a75a659f6f releaser: Bump versions for release of 0.123.3
[ci skip]
2024-02-23 17:09:20 +00:00
Bjørn Erik Pedersen 03b88c6ddb cache/dynacache: Reset ticker in case one cache eviction takes some time
Updates #12129
2024-02-23 17:56:29 +01:00
Bjørn Erik Pedersen 7ee0f70507 Revert "cache/dynacache: Prevent multiple concurrent resizes"
This reverts commit 564bae06f6.

Thinking about it, the above doesn't make any sense/having any effect.
2024-02-23 17:44:32 +01:00
Bjørn Erik Pedersen 564bae06f6 cache/dynacache: Prevent multiple concurrent resizes
Updates #12129
2024-02-23 17:27:52 +01:00
Bjørn Erik Pedersen bf14d0cb26 Speed up GetPage
```
name                             old time/op    new time/op    delta
GetPage-10                          413ns ± 0%     287ns ± 1%  -30.47%  (p=0.029 n=4+4)
GetPageRegular/From_root-10         757ns ± 1%     706ns ± 1%   -6.75%  (p=0.029 n=4+4)
GetPageRegular/Page_relative-10     838ns ± 1%     786ns ± 1%   -6.16%  (p=0.029 n=4+4)

name                             old alloc/op   new alloc/op   delta
GetPage-10                           312B ± 0%       24B ± 0%  -92.31%  (p=0.029 n=4+4)
GetPageRegular/From_root-10          328B ± 0%      200B ± 0%  -39.02%  (p=0.029 n=4+4)
GetPageRegular/Page_relative-10      360B ± 0%      232B ± 0%  -35.56%  (p=0.029 n=4+4)

name                             old allocs/op  new allocs/op  delta
GetPage-10                           8.00 ± 0%      2.00 ± 0%  -75.00%  (p=0.029 n=4+4)
GetPageRegular/From_root-10          7.00 ± 0%      5.00 ± 0%  -28.57%  (p=0.029 n=4+4)
GetPageRegular/Page_relative-10      9.00 ± 0%      7.00 ± 0%  -22.22%  (p=0.029 n=4+4)
```
2024-02-23 16:29:21 +01:00
Bjørn Erik Pedersen f521336c81 hugolib: Fix a .Page.GetPage from bundle case
Fixes #12120
2024-02-23 14:40:29 +01:00
Bjørn Erik Pedersen c4fe45ff4c resources: Skip the image golden tests when running locally
See #12119
2024-02-23 10:30:11 +01:00
Benjamin Altpeter 554aa58db6 js: Support JSX and JSXImportSourceOptions
Fixes #12118
2024-02-23 10:25:27 +01:00
Joe Mooring 36bf3cb98c hugolib: Add capitalizeListTitles config option
Whether to capitalize automatic page titles, applicable to section,
taxonomy, and term pages. Default is true.

Closes #9793
Closes #12115
2024-02-23 09:32:32 +01:00
hugoreleaser ea0ba9751d releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-02-22 15:41:43 +00:00
hugoreleaser 929b91fe75 releaser: Bump versions for release of 0.123.2
[ci skip]
2024-02-22 15:27:15 +00:00
Bjørn Erik Pedersen db3bb623d4 Fix it so not all bundled resources are removed when one translation is drafted
Fixes #12105
2024-02-22 16:16:44 +01:00
Razon Yang f5fd7ec8cc commands/mod: Ignore invalid module path 2024-02-22 16:00:03 +01:00
Bjørn Erik Pedersen 16406d9d77 Fix regression on handling of overlapping file mounts
But note that the overlay file system is set up horizontally (project -> module1 -> module2), so I would not recommend too complex overlapping mount setups within the same module.

But this worked in v0.122.0, so we should fix it.

Fixes #12103
2024-02-22 15:36:27 +01:00
Bjørn Erik Pedersen e75784930d Sitemap should not use list.xml
Fixes #12101
2024-02-21 20:07:51 +01:00
Bjørn Erik Pedersen b8ab5c9be6 Fix rebuild when adding a bundle with space in name
Fixes #12092
2024-02-21 17:49:15 +01:00
Bjørn Erik Pedersen cf7023cb90 Fix relative import issue in libsass/dart sass
Fixes #12094
2024-02-21 17:27:45 +01:00
Bjørn Erik Pedersen fd0185a84a Fix relref regression with trailing slash
Fixes #12096
2024-02-21 17:27:45 +01:00
hugoreleaser 6da1d8f370 releaser: Prepare repository for 0.124.0-DEV
[ci skip]
2024-02-21 08:30:57 +00:00
hugoreleaser 3f8434a62f releaser: Bump versions for release of 0.123.1
[ci skip]
2024-02-21 08:17:45 +00:00
Joe Mooring c9f7ebf004 tpl/tplimpl: Resolve fragments in link render hook
Fixes #12084
2024-02-21 09:11:34 +01:00
Bjørn Erik Pedersen eceeb19751 Fix rebuilding of pages without default content language
Fixes #12082
2024-02-20 18:42:14 +01:00
Bjørn Erik Pedersen 621194a319 Fix dart sass import regression
Fixes #12072
2024-02-20 17:00:32 +01:00
Bjørn Erik Pedersen 48eec2a4e6 Fall back to original name in Resources.GetMatch/Match
Same as we do in .Get.

Fixes #12076
2024-02-20 16:47:07 +01:00
Bjørn Erik Pedersen a118cb4138 Fix robots.txt using the built-in template regression
Fixes #12071
2024-02-20 11:17:44 +01:00
Bjørn Erik Pedersen e8cc785a58 Merge commit '2658a71e1b6fe24a8b754a62ce0398a09d270d86' 2024-02-19 18:59:28 +01:00
Bjørn Erik Pedersen 2658a71e1b Squashed 'docs/' changes from a9584e3d1..8c46b304a
8c46b304a Correct description of build options > render > link
f57932df1 Fix front matter example
a7e5fea73 Clarify pagination restriction
3a7e665db Fix typos
f60845249 List translation keys reserved by nicksnyder/go-i18n
94f2a3608 Fix typo
2da1198ac Update FNV32a.md: new-in 0.98.0
d9a4c66ae Fix typo data-templates.md
0d3c2e2c3 Update partials.md
d7e9a0878 Update partials.md

git-subtree-dir: docs
git-subtree-split: 8c46b304a0
2024-02-19 18:59:28 +01:00
hugoreleaser 8406a3e9dc releaser: Bump versions for release of 0.123.0
[ci skip]
2024-02-19 17:28:48 +00:00
dependabot[bot] f54ba6f9ef build(deps): bump github.com/tdewolff/minify/v2 from 2.20.16 to 2.20.17
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.16 to 2.20.17.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.16...v2.20.17)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 17:21:17 +01:00
dependabot[bot] 4019b177e6 build(deps): bump github.com/evanw/esbuild from 0.20.0 to 0.20.1
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.20.0 to 0.20.1.
- [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.20.0...v0.20.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 17:12:14 +01:00
Bjørn Erik Pedersen 5dbc29dc6c Handle rebuilds when resources passed to transform.Unmarshal etc. changes
Fixes #12065
2024-02-19 14:50:23 +01:00
Bjørn Erik Pedersen 5ada27bf65 Fix handling of build options for term pages
Fixes #12058
2024-02-18 16:50:35 +01:00
Bjørn Erik Pedersen 60b176cb57 docs: Regen docshelper 2024-02-18 13:20:15 +01:00
Bjørn Erik Pedersen afe5b6d7df Fix sample logic when adding content files in server
The partial rebuilds works by calaulating a baseline from a change set.

For new content, this doesn't work, so to avoid rebuilding everything, we first
try to collect a sample of surrounding identities (e.g. content files in the same section).

This commit fixes a flaw in that logic that in some (many...) cases would return a too small sample set.

Fixes #12054
2024-02-18 12:24:27 +01:00
Christian Oliff 168d375784 all: Fix typos and some URLs 2024-02-18 12:16:30 +01:00
dependabot[bot] 4a53fd56b7 build(deps): bump golang.org/x/tools from 0.17.0 to 0.18.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.17.0 to 0.18.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.17.0...v0.18.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-17 18:27:49 +01:00
Bjørn Erik Pedersen fc6aabe938 Fix handling of draft term pages
By just removing the term page and all of its page entries.

Fixes #12055
2024-02-17 18:12:25 +01:00
Bjørn Erik Pedersen 9cc65757a1 releaser: Fix archive homepage and description
Fixes #12053
2024-02-17 13:32:15 +01:00
Bjørn Erik Pedersen 4835f9e898 commands: Fix --clock with the list command
Fixes #11888
2024-02-17 13:14:15 +01:00
Bjørn Erik Pedersen 5bdda0bdbf Don't use the same value in .Data.Term.Title as in .Title
Fixes #12041
2024-02-17 12:34:50 +01:00
Bjørn Erik Pedersen 9679443c15 Fix server panic on i18n file change
Fixes #12048
2024-02-17 11:46:45 +01:00
dependabot[bot] 2d1681d915 build(deps): bump golang.org/x/net from 0.20.0 to 0.21.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-17 11:16:15 +01:00
Bjørn Erik Pedersen 68f67c9aeb Fix rebuild regression on non-default content language edits
Fixes #12043
2024-02-16 19:05:18 +01:00
Fabian Kammel 43ea2cd660 Let standard library handle charset parameter to MIME types
Fixes #10734
2024-02-16 18:24:25 +01:00
Bjørn Erik Pedersen f1491c900e Fix i18n rebuild regression
Fixes #12039
2024-02-16 16:17:57 +01:00
Bjørn Erik Pedersen 639073e4fe Fix rebuild with resources.Concat
Fixes #12017
2024-02-16 13:17:53 +01:00
Joe Mooring 21d9057dbf Add images.Dither filter
Closes #8598
2024-02-11 22:51:11 +02:00
220 changed files with 5970 additions and 1510 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ parameters:
defaults: &defaults
resource_class: large
docker:
- image: bepsays/ci-hugoreleaser:1.22200.20000
- image: bepsays/ci-hugoreleaser:1.22200.20200
environment: &buildenv
GOMODCACHE: /root/project/gomodcache
version: 2
@@ -60,7 +60,7 @@ jobs:
environment:
<<: [*buildenv]
docker:
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20000
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20200
steps:
- *restore-cache
- &attach-workspace
+69 -56
View File
@@ -1,13 +1,13 @@
on:
push:
branches: [ master ]
branches: [master]
pull_request:
name: TestDartSassV1
env:
GOPROXY: https://proxy.golang.org
GO111MODULE: on
DART_SASS_VERSION: 1.62.1
DART_SASS_SHA_LINUX: 3574da75a7322a539034648b8ff84ff2cca162eb924d72b663d718cd3936f075
GOPROXY: https://proxy.golang.org
GO111MODULE: on
DART_SASS_VERSION: 1.62.1
DART_SASS_SHA_LINUX: 3574da75a7322a539034648b8ff84ff2cca162eb924d72b663d718cd3936f075
permissions:
contents: read
jobs:
@@ -18,56 +18,69 @@ jobs:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Install Go
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
with:
go-version: ${{ matrix.go-version }}
check-latest: true
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Install Ruby
uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b
with:
ruby-version: '2.7'
bundler-cache: true #
- name: Install Python
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
with:
python-version: '3.x'
- name: Install Mage
run: go install github.com/magefile/mage@v1.15.0
- name: Install asciidoctor
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
- name: Install docutils
run: |
pip install docutils
rst2html.py --version
- if: matrix.os == 'ubuntu-latest'
name: Install pandoc on Linux
run: |
- if: matrix.os == 'ubuntu-latest'
name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
go-version: ${{ matrix.go-version }}
check-latest: true
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Install Ruby
uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899
with:
ruby-version: "2.7"
bundler-cache: true #
- name: Install Python
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: "3.x"
- name: Install Mage
run: go install github.com/magefile/mage@v1.15.0
- name: Install asciidoctor
uses: reitzig/actions-asciidoctor@03fcc74cd74880b697950c4930c9ec8a67c69ecc
- name: Install docutils
run: |
pip install docutils
rst2html --version
- if: matrix.os == 'ubuntu-latest'
name: Install pandoc on Linux
run: |
sudo apt-get update -y
sudo apt-get install -y pandoc
- if: matrix.os == 'macos-latest'
run: |
brew install pandoc
- if: matrix.os == 'windows-latest'
run: |
Choco-Install -PackageName pandoc
- run: pandoc -v
- name: Install dart-sass-embedded Linux
run: |
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
- name: Check
run: |
dart-sass-embedded --version
mage -v check;
env:
HUGO_BUILD_TAGS: extended
- if: matrix.os == 'macos-latest'
run: |
brew install pandoc
- if: matrix.os == 'windows-latest'
run: |
Choco-Install -PackageName pandoc
- run: pandoc -v
- name: Install dart-sass-embedded Linux
run: |
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
- name: Check
run: |
dart-sass-embedded --version
mage -v check;
env:
HUGO_BUILD_TAGS: extended
+116 -103
View File
@@ -1,119 +1,132 @@
on:
push:
branches: [ master ]
branches: [master]
pull_request:
name: Test
env:
GOPROXY: https://proxy.golang.org
GO111MODULE: on
SASS_VERSION: 1.63.2
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
GOPROXY: https://proxy.golang.org
GO111MODULE: on
SASS_VERSION: 1.63.2
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
permissions:
contents: read
jobs:
test:
strategy:
matrix:
go-version: [1.21.x,1.22.x]
go-version: [1.21.x, 1.22.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Install Go
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
with:
go-version: ${{ matrix.go-version }}
check-latest: true
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Install Ruby
uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b
with:
ruby-version: '2.7'
bundler-cache: true #
- name: Install Python
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
with:
python-version: '3.x'
- name: Install Mage
run: go install github.com/magefile/mage@v1.15.0
- name: Install asciidoctor
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
- name: Install docutils
run: |
pip install docutils
rst2html.py --version
- if: matrix.os == 'ubuntu-latest'
name: Install pandoc on Linux
run: |
- if: matrix.os == 'ubuntu-latest'
name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
go-version: ${{ matrix.go-version }}
check-latest: true
cache: true
cache-dependency-path: |
**/go.sum
**/go.mod
- name: Install Ruby
uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899
with:
ruby-version: "2.7"
bundler-cache: true #
- name: Install Python
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: "3.x"
- name: Install Mage
run: go install github.com/magefile/mage@v1.15.0
- name: Install asciidoctor
uses: reitzig/actions-asciidoctor@03fcc74cd74880b697950c4930c9ec8a67c69ecc
- name: Install docutils
run: |
pip install docutils
rst2html --version
- if: matrix.os == 'ubuntu-latest'
name: Install pandoc on Linux
run: |
sudo apt-get update -y
sudo apt-get install -y pandoc
- if: matrix.os == 'macos-latest'
run: |
brew install pandoc
- if: matrix.os == 'windows-latest'
run: |
choco install pandoc
- run: pandoc -v
- if: matrix.os == 'windows-latest'
run: |
choco install mingw
- if: matrix.os == 'ubuntu-latest'
name: Install dart-sass Linux
run: |
echo "Install Dart Sass version ${SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
echo "$GOBIN"
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
- if: matrix.os == 'macos-latest'
name: Install dart-sass MacOS
run: |
echo "Install Dart Sass version ${SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
- if: matrix.os == 'windows-latest'
name: Install dart-sass Windows
run: |
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .;
echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
- if: matrix.os == 'ubuntu-latest'
name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
- if: matrix.os == 'ubuntu-latest'
name: Run staticcheck
run: staticcheck ./...
- if: matrix.os != 'windows-latest'
name: Check
run: |
sass --version;
mage -v check;
env:
HUGO_BUILD_TAGS: extended
- if: matrix.os == 'windows-latest'
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
name: Test
run: |
mage -v test;
env:
HUGO_BUILD_TAGS: extended
- name: Build tags
run: |
go install -tags extended,nodeploy
- if: matrix.os == 'ubuntu-latest'
name: Build for dragonfly
run: |
go install
env:
GOARCH: amd64
GOOS: dragonfly
- if: matrix.os == 'macos-latest'
run: |
brew install pandoc
- if: matrix.os == 'windows-latest'
run: |
choco install pandoc
- run: pandoc -v
- if: matrix.os == 'windows-latest'
run: |
choco install mingw
- if: matrix.os == 'ubuntu-latest'
name: Install dart-sass Linux
run: |
echo "Install Dart Sass version ${SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
echo "$GOBIN"
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
- if: matrix.os == 'macos-latest'
name: Install dart-sass MacOS
run: |
echo "Install Dart Sass version ${SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
- if: matrix.os == 'windows-latest'
name: Install dart-sass Windows
run: |
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .;
echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
- if: matrix.os == 'ubuntu-latest'
name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
- if: matrix.os == 'ubuntu-latest'
name: Run staticcheck
run: staticcheck ./...
- if: matrix.os != 'windows-latest'
name: Check
run: |
sass --version;
mage -v check;
env:
HUGO_BUILD_TAGS: extended
- if: matrix.os == 'windows-latest'
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
name: Test
run: |
mage -v test;
env:
HUGO_BUILD_TAGS: extended
- name: Build tags
run: |
go install -tags extended,nodeploy
- if: matrix.os == 'ubuntu-latest'
name: Build for dragonfly
run: |
go install
env:
GOARCH: amd64
GOOS: dragonfly
+3 -1
View File
@@ -295,6 +295,8 @@ func (c *Cache) start() func() {
select {
case <-ticker.C:
c.adjustCurrentMaxSize()
// Reset the ticker to avoid drift.
ticker.Reset(c.opts.CheckInterval)
case <-quit:
ticker.Stop()
return
@@ -338,7 +340,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
return p.(*Partition[K, V])
}
// At this point, we don't know the the number of partitions or their configuration, but
// At this point, we don't know the number of partitions or their configuration, but
// this will be re-adjusted later.
const numberOfPartitionsEstimate = 10
maxSize := opts.CalculateMaxSize(c.opts.MaxSize / numberOfPartitionsEstimate)
+16 -12
View File
@@ -48,6 +48,7 @@ import (
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/resources/kinds"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)
@@ -482,46 +483,47 @@ Complete documentation is available at https://gohugo.io/.`
// Configure persistent flags
cmd.PersistentFlags().StringVarP(&r.source, "source", "s", "", "filesystem path to read files relative from")
cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagDirname("source")
cmd.PersistentFlags().StringP("destination", "d", "", "filesystem path to write files to")
cmd.PersistentFlags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagDirname("destination")
cmd.PersistentFlags().StringVarP(&r.environment, "environment", "e", "", "build environment")
_ = cmd.RegisterFlagCompletionFunc("environment", cobra.NoFileCompletions)
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
_ = cmd.MarkFlagDirname("themesDir")
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
_ = cmd.RegisterFlagCompletionFunc("ignoreVendorPaths", cobra.NoFileCompletions)
cmd.PersistentFlags().String("clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
_ = cmd.RegisterFlagCompletionFunc("clock", cobra.NoFileCompletions)
cmd.PersistentFlags().StringVar(&r.cfgFile, "config", "", "config file (default is hugo.yaml|json|toml)")
_ = cmd.MarkFlagFilename("config", config.ValidConfigFileExtensions...)
cmd.PersistentFlags().StringVar(&r.cfgDir, "configDir", "config", "config dir")
_ = cmd.MarkFlagDirname("configDir")
cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode")
cmd.PersistentFlags().BoolVar(&r.renderToMemory, "renderToMemory", false, "render to memory (mostly useful when running the server)")
// Set bash-completion
_ = cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, config.ValidConfigFileExtensions)
cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output")
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
_ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp))
cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
// Configure local flags
applyLocalFlagsBuild(cmd, r)
// Set bash-completion.
// Each flag must first be defined before using the SetAnnotation() call.
_ = cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
return nil
}
// A sub set of the complete build flags. These flags are used by new and mod.
func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
_ = cmd.MarkFlagDirname("theme")
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory")
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagDirname("cacheDir")
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
cmd.Flags().StringSliceP("renderSegments", "", []string{}, "named segments to render (configured in the segments config)")
}
// Flags needed to do a build (used by hugo and hugo server commands)
@@ -534,8 +536,10 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages")
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
_ = cmd.MarkFlagDirname("layoutDir")
cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
_ = cmd.RegisterFlagCompletionFunc("poll", cobra.NoFileCompletions)
cmd.Flags().Bool("panicOnWarning", false, "panic on first WARNING log")
cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions")
cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics")
@@ -558,8 +562,8 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().MarkHidden("profile-mutex")
cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)")
_ = cmd.RegisterFlagCompletionFunc("disableKinds", cobra.FixedCompletions(kinds.AllKinds, cobra.ShellCompDirectiveNoFileComp))
cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)")
_ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
}
func (r *rootCommand) timeTrack(start time.Time, name string) {
+4
View File
@@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/modules"
"github.com/gohugoio/hugo/parser"
"github.com/gohugoio/hugo/parser/metadecoders"
"github.com/spf13/cobra"
)
// newConfigCommand creates a new config command and its subcommands.
@@ -112,7 +113,9 @@ func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
cmd.Short = "Print the site configuration"
cmd.Long = `Print the site configuration, both default and custom settings.`
cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)")
_ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp))
cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.")
_ = cmd.RegisterFlagCompletionFunc("lang", cobra.NoFileCompletions)
applyLocalFlagsBuildConfig(cmd, c.r)
return nil
@@ -223,6 +226,7 @@ func (c *configMountsCommand) Init(cd *simplecobra.Commandeer) error {
c.r = cd.Root.Command.(*rootCommand)
cmd := cd.CobraCommand
cmd.Short = "Print the configured file mounts"
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, c.r)
return nil
}
+4
View File
@@ -46,6 +46,7 @@ to use JSON for the front matter.`,
return c.convertContents(metadecoders.JSON)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
&simpleCommand{
@@ -57,6 +58,7 @@ to use TOML for the front matter.`,
return c.convertContents(metadecoders.TOML)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
&simpleCommand{
@@ -68,6 +70,7 @@ to use YAML for the front matter.`,
return c.convertContents(metadecoders.YAML)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
},
@@ -108,6 +111,7 @@ func (c *convertCommand) Init(cd *simplecobra.Commandeer) error {
See convert's subcommands toJSON, toTOML and toYAML for more information.`
cmd.PersistentFlags().StringVarP(&c.outputDir, "output", "o", "", "filesystem path to write files to")
_ = cmd.MarkFlagDirname("output")
cmd.PersistentFlags().BoolVar(&c.unsafe, "unsafe", false, "enable less safe operations, please backup first")
cmd.RunE = nil
+4
View File
@@ -60,13 +60,17 @@ documentation.
return deployer.Deploy(ctx)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
cmd.Flags().Bool("dryRun", false, "dry run")
cmd.Flags().Bool("force", false, "force upload of all files")
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
},
}
}
+7
View File
@@ -19,6 +19,7 @@ import (
"github.com/bep/simplecobra"
"github.com/gohugoio/hugo/common/hugo"
"github.com/spf13/cobra"
)
func newEnvCommand() simplecobra.Commander {
@@ -47,6 +48,9 @@ func newEnvCommand() simplecobra.Commander {
}
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
}
}
@@ -59,5 +63,8 @@ func newVersionCmd() simplecobra.Commander {
},
short: "Print Hugo version and environment info",
long: "Print Hugo version and environment info. This is useful in Hugo bug reports.",
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
}
}
+9 -4
View File
@@ -75,9 +75,13 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style (see https://xyproto.github.io/splash/docs/)")
_ = cmd.RegisterFlagCompletionFunc("style", cobra.NoFileCompletions)
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
_ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions)
cmd.PersistentFlags().StringVar(&linesStyle, "linesStyle", "", "style used for line numbers (see https://github.com/alecthomas/chroma)")
_ = cmd.RegisterFlagCompletionFunc("linesStyle", cobra.NoFileCompletions)
},
}
}
@@ -115,9 +119,9 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.PersistentFlags().StringVar(&genmandir, "dir", "man/", "the directory to write the man pages.")
// For bash-completion
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagDirname("dir")
},
}
}
@@ -172,9 +176,9 @@ url: %s
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.PersistentFlags().StringVar(&gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
// For bash-completion
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
_ = cmd.MarkFlagDirname("dir")
},
}
}
@@ -227,6 +231,7 @@ url: %s
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.Hidden = true
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.PersistentFlags().StringVarP(&docsHelperTarget, "dir", "", "docs/data", "data dir")
},
}
+1
View File
@@ -58,6 +58,7 @@ Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root
return c.importFromJekyll(args)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.Flags().BoolVar(&c.force, "force", false, "allow import into non-empty target directory")
},
},
+14 -2
View File
@@ -23,10 +23,10 @@ import (
"time"
"github.com/bep/simplecobra"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
"github.com/spf13/cobra"
)
// newListCommand creates a new list command and its subcommands.
@@ -46,7 +46,7 @@ func newListCommand() *listCommand {
list := func(cd *simplecobra.Commandeer, r *rootCommand, shouldInclude func(page.Page) bool, opts ...any) error {
bcfg := hugolib.BuildCfg{SkipRender: true}
cfg := config.New()
cfg := flagsToCfg(cd, nil)
for i := 0; i < len(opts); i += 2 {
cfg.Set(opts[i].(string), opts[i+1])
}
@@ -103,6 +103,9 @@ func newListCommand() *listCommand {
"buildExpired", true,
)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
&simpleCommand{
name: "future",
@@ -120,6 +123,9 @@ func newListCommand() *listCommand {
"buildDrafts", true,
)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
&simpleCommand{
name: "expired",
@@ -137,6 +143,9 @@ func newListCommand() *listCommand {
"buildDrafts", true,
)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
&simpleCommand{
name: "all",
@@ -148,6 +157,9 @@ func newListCommand() *listCommand {
}
return list(cd, r, shouldInclude, "buildDrafts", true, "buildFuture", true, "buildExpired", true)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
},
}
+9
View File
@@ -62,6 +62,7 @@ removed from Hugo, but we need to test this out in "real life" to get a feel of
so this may/will change in future versions of Hugo.
`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
@@ -89,6 +90,7 @@ so this may/will change in future versions of Hugo.
inside a subfolder on GitHub, as one example.
`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
@@ -108,6 +110,7 @@ so this may/will change in future versions of Hugo.
short: "Verify dependencies.",
long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
},
@@ -127,6 +130,7 @@ so this may/will change in future versions of Hugo.
Note that for vendored modules, that is the version listed and not the one from go.mod.
`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
},
@@ -144,8 +148,10 @@ Note that for vendored modules, that is the version listed and not the one from
short: "Delete the Hugo Module cache for the current project.",
long: `Delete the Hugo Module cache for the current project.`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`)
_ = cmd.RegisterFlagCompletionFunc("pattern", cobra.NoFileCompletions)
cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache")
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
@@ -167,6 +173,7 @@ Note that for vendored modules, that is the version listed and not the one from
name: "tidy",
short: "Remove unused entries in go.mod and go.sum.",
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
@@ -184,6 +191,7 @@ Note that for vendored modules, that is the version listed and not the one from
If a module is vendored, that is where Hugo will look for it's dependencies.
`,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
applyLocalFlagsBuildConfig(cmd, r)
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
@@ -225,6 +233,7 @@ Run "go help get" for more information. All flags available for "go get" is also
` + commonUsageMod,
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.DisableFlagParsing = true
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
// We currently just pass on the flags we get to Go and
+17
View File
@@ -60,8 +60,15 @@ Ensure you run this within the root directory of your site.`,
return create.NewContent(h, contentType, args[0], force)
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return []string{}, cobra.ShellCompDirectiveNoFileComp
}
return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs
}
cmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
cmd.Flags().String("editor", "", "edit new content with this editor, if provided")
_ = cmd.RegisterFlagCompletionFunc("editor", cobra.NoFileCompletions)
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists")
applyLocalFlagsBuildConfig(cmd, r)
},
@@ -103,8 +110,15 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return []string{}, cobra.ShellCompDirectiveNoFileComp
}
return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs
}
cmd.Flags().BoolVarP(&force, "force", "f", false, "init inside non-empty directory")
cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)")
_ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp))
},
},
&simpleCommand{
@@ -137,6 +151,9 @@ according to your needs.`,
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
},
},
},
}
+2
View File
@@ -43,9 +43,11 @@ func newReleaseCommand() simplecobra.Commander {
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.Hidden = true
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.PersistentFlags().BoolVarP(&skipPush, "skip-push", "", false, "skip pushing to remote")
cmd.PersistentFlags().BoolVarP(&try, "try", "", false, "no changes")
cmd.PersistentFlags().IntVarP(&step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
_ = cmd.RegisterFlagCompletionFunc("step", cobra.FixedCompletions([]string{"1", "2"}, cobra.ShellCompDirectiveNoFileComp))
},
}
}
+22 -20
View File
@@ -123,6 +123,7 @@ func newServerCommand() *serverCommand {
return mclib.RunMain()
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.ValidArgsFunction = cobra.NoFileCompletions
cmd.Flags().BoolVar(&uninstall, "uninstall", false, "Uninstall the local CA (but do not delete it).")
},
},
@@ -236,9 +237,8 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
listener := f.c.serverPorts[i].ln
logger := f.c.r.logger
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
if i == 0 {
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
mainTarget := "disk"
if f.c.r.renderToMemory {
mainTarget = "memory"
@@ -307,7 +307,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
if redirect := serverConfig.MatchRedirect(requestURI); !redirect.IsZero() {
// fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
doRedirect := true
// This matches Netlify's behaviour and is needed for SPA behaviour.
// This matches Netlify's behavior and is needed for SPA behavior.
// See https://docs.netlify.com/routing/redirects/rewrites-proxies/
if !redirect.Force {
path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path()))
@@ -512,9 +512,9 @@ func (c *serverCommand) Init(cd *simplecobra.Commandeer) error {
cmd.Long = `Hugo provides its own webserver which builds and serves the site.
While hugo server is high performance, it is a webserver with limited options.
'hugo server' will by default write and server files from disk, but you can
render to memory by using the '--renderToMemory' flag. This can be faster
in some cases, but it will consume more memory.
The ` + "`" + `hugo server` + "`" + ` command will by default write and serve files from disk, but
you can render to memory by using the ` + "`" + `--renderToMemory` + "`" + ` flag. This can be
faster in some cases, but it will consume more memory.
By default hugo will also watch your files for any changes you make and
automatically rebuild the site. It will then live reload any open browser pages
@@ -523,10 +523,15 @@ of a second, you will be able to save and see your changes nearly instantly.`
cmd.Aliases = []string{"serve"}
cmd.Flags().IntVarP(&c.serverPort, "port", "p", 1313, "port on which the server will listen")
_ = cmd.RegisterFlagCompletionFunc("port", cobra.NoFileCompletions)
cmd.Flags().IntVar(&c.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
_ = cmd.RegisterFlagCompletionFunc("liveReloadPort", cobra.NoFileCompletions)
cmd.Flags().StringVarP(&c.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
_ = cmd.RegisterFlagCompletionFunc("bind", cobra.NoFileCompletions)
cmd.Flags().StringVarP(&c.tlsCertFile, "tlsCertFile", "", "", "path to TLS certificate file")
_ = cmd.MarkFlagFilename("tlsCertFile", "pem")
cmd.Flags().StringVarP(&c.tlsKeyFile, "tlsKeyFile", "", "", "path to TLS key file")
_ = cmd.MarkFlagFilename("tlsKeyFile", "pem")
cmd.Flags().BoolVar(&c.tlsAuto, "tlsAuto", false, "generate and use locally-trusted certificates.")
cmd.Flags().BoolVar(&c.pprof, "pprof", false, "enable the pprof server (port 8080)")
cmd.Flags().BoolVarP(&c.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
@@ -538,9 +543,6 @@ of a second, you will be able to save and see your changes nearly instantly.`
cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
cmd.Flags().SetAnnotation("tlsCertFile", cobra.BashCompSubdirsInDir, []string{})
cmd.Flags().SetAnnotation("tlsKeyFile", cobra.BashCompSubdirsInDir, []string{})
r := cd.Root.Command.(*rootCommand)
applyLocalFlagsBuild(cmd, r)
@@ -566,7 +568,7 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
}
}
if err := c.setBaseURLsInConfig(); err != nil {
if err := c.setServerInfoInConfig(); err != nil {
return err
}
@@ -611,15 +613,15 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
return nil
}
func (c *serverCommand) setBaseURLsInConfig() error {
func (c *serverCommand) setServerInfoInConfig() error {
if len(c.serverPorts) == 0 {
panic("no server ports set")
}
return c.withConfE(func(conf *commonConfig) error {
for i, language := range conf.configs.Languages {
isMultiHost := conf.configs.IsMultihost
isMultihost := conf.configs.IsMultihost
var serverPort int
if isMultiHost {
if isMultihost {
serverPort = c.serverPorts[i].p
} else {
serverPort = c.serverPorts[0].p
@@ -638,7 +640,8 @@ func (c *serverCommand) setBaseURLsInConfig() error {
if c.liveReloadPort != -1 {
baseURLLiveReload, _ = baseURLLiveReload.WithPort(c.liveReloadPort)
}
langConfig.C.SetBaseURL(baseURL, baseURLLiveReload)
langConfig.C.SetServerInfo(baseURL, baseURLLiveReload, c.serverInterface)
}
return nil
})
@@ -737,9 +740,9 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
flags := cd.CobraCommand.Flags()
var cerr error
c.withConf(func(conf *commonConfig) {
isMultiHost := conf.configs.IsMultihost
isMultihost := conf.configs.IsMultihost
c.serverPorts = make([]serverPortListener, 1)
if isMultiHost {
if isMultihost {
if !c.serverAppend {
cerr = errors.New("--appendPort=false not supported when in multihost mode")
return
@@ -852,7 +855,7 @@ func (c *serverCommand) serve() error {
h *hugolib.HugoSites
)
err := c.withConfE(func(conf *commonConfig) error {
isMultiHost := conf.configs.IsMultihost
isMultihost := conf.configs.IsMultihost
var err error
h, err = c.r.HugFromConfig(conf)
if err != nil {
@@ -862,7 +865,7 @@ func (c *serverCommand) serve() error {
// We need the server to share the same logger as the Hugo build (for error counts etc.)
c.r.logger = h.Log
if isMultiHost {
if isMultihost {
for _, l := range conf.configs.ConfigLangs() {
baseURLs = append(baseURLs, l.BaseURL())
roots = append(roots, l.Language().Lang)
@@ -1005,7 +1008,6 @@ func (c *serverCommand) serve() error {
}
}
}()
if err != nil {
c.r.Println("Error:", err)
}
@@ -1087,7 +1089,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
fromPath := ev.Name
relPath, found := sourceFs.MakePathRelative(fromPath)
relPath, found := sourceFs.MakePathRelative(fromPath, true)
if !found {
// Not member of this virtual host.
+18
View File
@@ -111,11 +111,29 @@ func (i HugoInfo) Deps() []*Dependency {
return i.deps
}
// Deprecated: Use hugo.IsMultihost instead.
func (i HugoInfo) IsMultiHost() bool {
Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
return i.conf.IsMultihost()
}
// IsMultihost reports whether each configured language has a unique baseURL.
func (i HugoInfo) IsMultihost() bool {
return i.conf.IsMultihost()
}
// IsMultilingual reports whether there are two or more configured languages.
func (i HugoInfo) IsMultilingual() bool {
return i.conf.IsMultilingual()
}
// ConfigProvider represents the config options that are relevant for HugoInfo.
type ConfigProvider interface {
Environment() string
Running() bool
WorkingDir() string
IsMultihost() bool
IsMultilingual() bool
}
// NewInfo creates a new Hugo Info object.
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugo_test
import (
"strings"
"testing"
"github.com/gohugoio/hugo/hugolib"
)
func TestIsMultilingualAndIsMultihost(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
defaultContentLanguageInSubdir = true
[languages.de]
baseURL = 'https://de.example.org/'
[languages.en]
baseURL = 'https://en.example.org/'
-- content/_index.md --
---
title: home
---
-- layouts/index.html --
multilingual={{ hugo.IsMultilingual }}
multihost={{ hugo.IsMultihost }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/de/index.html",
"multilingual=true",
"multihost=true",
)
b.AssertFileContent("public/en/index.html",
"multilingual=true",
"multihost=true",
)
files = strings.ReplaceAll(files, "baseURL = 'https://de.example.org/'", "")
files = strings.ReplaceAll(files, "baseURL = 'https://en.example.org/'", "")
b = hugolib.Test(t, files)
b.AssertFileContent("public/de/index.html",
"multilingual=true",
"multihost=false",
)
b.AssertFileContent("public/en/index.html",
"multilingual=true",
"multihost=false",
)
files = strings.ReplaceAll(files, "[languages.de]", "")
files = strings.ReplaceAll(files, "[languages.en]", "")
b = hugolib.Test(t, files)
b.AssertFileContent("public/en/index.html",
"multilingual=false",
"multihost=false",
)
}
+13 -3
View File
@@ -65,9 +65,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
}
type testConfig struct {
environment string
running bool
workingDir string
environment string
running bool
workingDir string
multihost bool
multilingual bool
}
func (c testConfig) Environment() string {
@@ -81,3 +83,11 @@ func (c testConfig) Running() bool {
func (c testConfig) WorkingDir() string {
return c.workingDir
}
func (c testConfig) IsMultihost() bool {
return c.multihost
}
func (c testConfig) IsMultilingual() bool {
return c.multilingual
}
+1 -1
View File
@@ -17,7 +17,7 @@ package hugo
// This should be the only one.
var CurrentVersion = Version{
Major: 0,
Minor: 123,
Minor: 125,
PatchLevel: 0,
Suffix: "-DEV",
}
+83 -22
View File
@@ -18,9 +18,11 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/identity"
)
var defaultPathParser PathParser
@@ -50,19 +52,54 @@ func NormalizePathStringBasic(s string) string {
return s
}
// Parse parses component c with path s into Path using Hugo's content path rules.
func (parser PathParser) Parse(c, s string) *Path {
p, err := parser.parse(c, s)
// ParseIdentity parses component c with path s into a StringIdentity.
func (pp *PathParser) ParseIdentity(c, s string) identity.StringIdentity {
p := pp.parsePooled(c, s)
defer putPath(p)
return identity.StringIdentity(p.IdentifierBase())
}
// ParseBaseAndBaseNameNoIdentifier parses component c with path s into a base and a base name without any identifier.
func (pp *PathParser) ParseBaseAndBaseNameNoIdentifier(c, s string) (string, string) {
p := pp.parsePooled(c, s)
defer putPath(p)
return p.Base(), p.BaseNameNoIdentifier()
}
func (pp *PathParser) parsePooled(c, s string) *Path {
s = NormalizePathStringBasic(s)
p := getPath()
p.component = c
p, err := pp.doParse(c, s, p)
if err != nil {
panic(err)
}
return p
}
// Parse parses component c with path s into Path using Hugo's content path rules.
func (pp *PathParser) Parse(c, s string) *Path {
p, err := pp.parse(c, s)
if err != nil {
panic(err)
}
return p
}
func (pp *PathParser) newPath(component string) *Path {
return &Path{
component: component,
posContainerLow: -1,
posContainerHigh: -1,
posSectionHigh: -1,
posIdentifierLanguage: -1,
}
}
func (pp *PathParser) parse(component, s string) (*Path, error) {
ss := NormalizePathStringBasic(s)
p, err := pp.doParse(component, ss)
p, err := pp.doParse(component, ss, pp.newPath(component))
if err != nil {
return nil, err
}
@@ -70,7 +107,7 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
if s != ss {
var err error
// Preserve the original case for titles etc.
p.unnormalized, err = pp.doParse(component, s)
p.unnormalized, err = pp.doParse(component, s, pp.newPath(component))
if err != nil {
return nil, err
@@ -82,15 +119,7 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
return p, nil
}
func (pp *PathParser) doParse(component, s string) (*Path, error) {
p := &Path{
component: component,
posContainerLow: -1,
posContainerHigh: -1,
posSectionHigh: -1,
posIdentifierLanguage: -1,
}
func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
hasLang := pp.LanguageIndex != nil
hasLang = hasLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
@@ -210,7 +239,7 @@ const (
// E.g. /blog/my-post.md
PathTypeContentSingle
// All bewlow are bundled content files.
// All below are bundled content files.
// Leaf bundles, e.g. /blog/my-post/index.md
PathTypeLeaf
@@ -220,6 +249,7 @@ const (
)
type Path struct {
// Note: Any additions to this struct should also be added to the pathPool.
s string
posContainerLow int
@@ -239,6 +269,37 @@ type Path struct {
unnormalized *Path
}
var pathPool = &sync.Pool{
New: func() any {
p := &Path{}
p.reset()
return p
},
}
func getPath() *Path {
return pathPool.Get().(*Path)
}
func putPath(p *Path) {
p.reset()
pathPool.Put(p)
}
func (p *Path) reset() {
p.s = ""
p.posContainerLow = -1
p.posContainerHigh = -1
p.posSectionHigh = -1
p.component = ""
p.bundleType = 0
p.identifiers = p.identifiers[:0]
p.posIdentifierLanguage = -1
p.disabled = false
p.trimLeadingSlash = false
p.unnormalized = nil
}
// TrimLeadingSlash returns a copy of the Path with the leading slash removed.
func (p Path) TrimLeadingSlash() *Path {
p.trimLeadingSlash = true
@@ -252,9 +313,9 @@ func (p *Path) norm(s string) string {
return s
}
// IdentifierBase satifies identity.Identity.
// IdentifierBase satisfies identity.Identity.
func (p *Path) IdentifierBase() string {
return p.Base()[1:]
return p.Base()
}
// Component returns the component for this path (e.g. "content").
@@ -307,7 +368,7 @@ func (p *Path) Name() string {
return p.s
}
// Name returns the last element of path withhout any extension.
// 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]
@@ -315,7 +376,7 @@ func (p *Path) NameNoExt() string {
return p.s[p.posContainerHigh:]
}
// Name returns the last element of path withhout any language identifier.
// Name returns the last element of path without any language identifier.
func (p *Path) NameNoLang() string {
i := p.identifierIndex(p.posIdentifierLanguage)
if i == -1 {
@@ -325,7 +386,7 @@ func (p *Path) NameNoLang() string {
return p.s[p.posContainerHigh:p.identifiers[i].Low-1] + p.s[p.identifiers[i].High:]
}
// BaseNameNoIdentifier returns the logcical base name for a resource without any idenifier (e.g. no extension).
// BaseNameNoIdentifier returns the logical base name for a resource without any identifier (e.g. no extension).
// For bundles this will be the containing directory's name, e.g. "blog".
func (p *Path) BaseNameNoIdentifier() string {
if p.IsBundle() {
@@ -334,7 +395,7 @@ func (p *Path) BaseNameNoIdentifier() string {
return p.NameNoIdentifier()
}
// NameNoIdentifier returns the last element of path withhout any identifier (e.g. no extension).
// NameNoIdentifier returns the last element of path without any identifier (e.g. no extension).
func (p *Path) NameNoIdentifier() string {
if len(p.identifiers) > 0 {
return p.s[p.posContainerHigh : p.identifiers[len(p.identifiers)-1].Low-1]
@@ -374,7 +435,7 @@ func (p *Path) PathNoIdentifier() string {
return p.base(false, false)
}
// PathRel returns the path relativeto the given owner.
// PathRel returns the path relative to the given owner.
func (p *Path) PathRel(owner *Path) string {
ob := owner.Base()
if !strings.HasSuffix(ob, "/") {
+6
View File
@@ -349,3 +349,9 @@ func TestHasExt(t *testing.T) {
c.Assert(HasExt("/a/b/c"), qt.IsFalse)
c.Assert(HasExt("/a/b.c/d"), qt.IsFalse)
}
func BenchmarkParseIdentity(b *testing.B) {
for i := 0; i < b.N; i++ {
testParser.ParseIdentity(files.ComponentFolderAssets, "/a/b.css")
}
}
+8
View File
@@ -99,6 +99,14 @@ type Unwrapper interface {
Unwrapv() any
}
// Unwrap returns the underlying value of v if it implements Unwrapper, otherwise v is returned.
func Unwrapv(v any) any {
if u, ok := v.(Unwrapper); ok {
return u.Unwrapv()
}
return v
}
// LowHigh is typically used to represent a slice boundary.
type LowHigh struct {
Low int
+32 -8
View File
@@ -39,6 +39,7 @@ import (
"github.com/gohugoio/hugo/config/services"
"github.com/gohugoio/hugo/deploy/deployconfig"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugolib/segments"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/markup/markup_config"
"github.com/gohugoio/hugo/media"
@@ -103,9 +104,11 @@ type Config struct {
RootConfig
// Author information.
// Deprecated: Use taxonomies instead.
Author map[string]any
// Social links.
// Deprecated: Use .Site.Params instead.
Social map[string]string
// The build configuration section contains build-related configuration options.
@@ -137,6 +140,9 @@ type Config struct {
// a slice of page matcher and params to apply to those pages.
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"`
// The segments defines segments for the site. Used for partial/segmented builds.
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
// Menu configuration.
// <docsmeta>{"refs": ["config:languages:menus"] }</docsmeta>
Menus *config.ConfigNamespace[map[string]navigation.MenuConfig, navigation.Menus] `mapstructure:"-"`
@@ -364,6 +370,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle),
IsUglyURLSection: isUglyURL,
IgnoreFile: ignoreFile,
SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...),
MainSections: c.MainSections,
Clock: clock,
transientErr: transientErr,
@@ -393,6 +400,7 @@ type ConfigCompiled struct {
Timeout time.Duration
BaseURL urls.BaseURL
BaseURLLiveReload urls.BaseURL
ServerInterface string
KindOutputFormats map[string]output.Formats
DisabledKinds map[string]bool
DisabledLanguages map[string]bool
@@ -400,6 +408,7 @@ type ConfigCompiled struct {
CreateTitle func(s string) string
IsUglyURLSection func(section string) bool
IgnoreFile func(filename string) bool
SegmentFilter segments.SegmentFilter
MainSections []string
Clock time.Time
@@ -426,9 +435,10 @@ func (c *ConfigCompiled) IsMainSectionsSet() bool {
}
// This is set after the config is compiled by the server command.
func (c *ConfigCompiled) SetBaseURL(baseURL, baseURLLiveReload urls.BaseURL) {
func (c *ConfigCompiled) SetServerInfo(baseURL, baseURLLiveReload urls.BaseURL, serverInterface string) {
c.BaseURL = baseURL
c.BaseURLLiveReload = baseURLLiveReload
c.ServerInterface = serverInterface
}
// RootConfig holds all the top-level configuration options in Hugo
@@ -472,6 +482,10 @@ type RootConfig struct {
// A list of languages to disable.
DisableLanguages []string
// The named segments to render.
// This needs to match the name of the segment in the segments configuration.
RenderSegments []string
// Disable the injection of the Hugo generator tag on the home page.
DisableHugoGeneratorInject bool
@@ -538,6 +552,9 @@ type RootConfig struct {
// Note that this currently only works for English, but you can provide your own title in the content file's front matter.
PluralizeListTitles bool
// Whether to capitalize automatic page titles, applicable to section, taxonomy, and term pages.
CapitalizeListTitles bool
// Make all relative URLs absolute using the baseURL.
// <docsmeta>{"identifiers": ["baseURL"] }</docsmeta>
CanonifyURLs bool
@@ -663,11 +680,18 @@ type Configs struct {
// All below is set in Init.
Languages langs.Languages
LanguagesDefaultFirst langs.Languages
ContentPathParser paths.PathParser
ContentPathParser *paths.PathParser
configLangs []config.AllProvider
}
func (c *Configs) Validate(logger loggers.Logger) error {
for p := range c.Base.Cascade.Config {
page.CheckCascadePattern(logger, p)
}
return nil
}
// transientErr returns the last transient error found during config compilation.
func (c *Configs) transientErr() error {
for _, l := range c.LanguageConfigSlice {
@@ -735,7 +759,7 @@ func (c *Configs) Init() error {
c.Languages = languages
c.LanguagesDefaultFirst = languagesDefaultFirst
c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
c.configLangs = make([]config.AllProvider, len(c.Languages))
for i, l := range c.LanguagesDefaultFirst {
@@ -816,7 +840,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
langConfigMap := make(map[string]*Config)
languagesConfig := cfg.GetStringMap("languages")
var isMultiHost bool
var isMultihost bool
if err := all.CompileConfig(logger); err != nil {
return nil, err
@@ -853,7 +877,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
}
if kk == "baseurl" {
// baseURL configure don the language level is a multihost setup.
isMultiHost = true
isMultihost = true
}
mergedConfig.Set(kk, vv)
rootv := cfg.Get(kk)
@@ -903,7 +927,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
}
// Adjust Goldmark config defaults for multilingual, single-host sites.
if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
if len(languagesConfig) > 1 && !isMultihost && !clone.Markup.Goldmark.DuplicateResourceFiles {
if !clone.Markup.Goldmark.DuplicateResourceFiles {
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
@@ -933,7 +957,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
Base: all,
LanguageConfigMap: langConfigMap,
LoadingInfo: res,
IsMultihost: isMultiHost,
IsMultihost: isMultihost,
}
return cm, nil
@@ -966,7 +990,7 @@ func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.Base
})
for _, v := range decoderSetups {
p := decodeConfig{p: p, c: target, fs: fs, logger: logger, bcfg: bcfg}
p := decodeConfig{p: p, c: target, fs: fs, bcfg: bcfg}
if err := v.decode(v, p); err != nil {
return fmt.Errorf("failed to decode %q: %w", v.key, err)
}
+15 -7
View File
@@ -18,7 +18,6 @@ import (
"strings"
"github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/config"
@@ -26,11 +25,13 @@ import (
"github.com/gohugoio/hugo/config/security"
"github.com/gohugoio/hugo/config/services"
"github.com/gohugoio/hugo/deploy/deployconfig"
"github.com/gohugoio/hugo/hugolib/segments"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/markup/markup_config"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/minifiers"
"github.com/gohugoio/hugo/modules"
"github.com/gohugoio/hugo/navigation"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/related"
@@ -43,11 +44,10 @@ import (
)
type decodeConfig struct {
p config.Provider
c *Config
fs afero.Fs
logger loggers.Logger
bcfg config.BaseConfig
p config.Provider
c *Config
fs afero.Fs
bcfg config.BaseConfig
}
type decodeWeight struct {
@@ -122,6 +122,14 @@ var allDecoderSetups = map[string]decodeWeight{
return err
},
},
"segments": {
key: "segments",
decode: func(d decodeWeight, p decodeConfig) error {
var err error
p.c.Segments, err = segments.DecodeSegments(p.p.GetStringMap(d.key))
return err
},
},
"server": {
key: "server",
decode: func(d decodeWeight, p decodeConfig) error {
@@ -293,7 +301,7 @@ var allDecoderSetups = map[string]decodeWeight{
key: "cascade",
decode: func(d decodeWeight, p decodeConfig) error {
var err error
p.c.Cascade, err = page.DecodeCascadeConfig(p.logger, p.p.Get(d.key))
p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key))
return err
},
},
+11 -3
View File
@@ -19,6 +19,7 @@ import (
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/langs"
)
@@ -42,7 +43,7 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages {
return c.m.LanguagesDefaultFirst
}
func (c ConfigLanguage) PathParser() paths.PathParser {
func (c ConfigLanguage) PathParser() *paths.PathParser {
return c.m.ContentPathParser
}
@@ -51,7 +52,7 @@ func (c ConfigLanguage) LanguagePrefix() string {
return c.Language().Lang
}
if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang {
if !c.IsMultilingual() || c.DefaultContentLanguage() == c.Language().Lang {
return ""
}
return c.Language().Lang
@@ -77,7 +78,7 @@ func (c ConfigLanguage) FastRenderMode() bool {
return c.config.Internal.FastRenderMode
}
func (c ConfigLanguage) IsMultiLingual() bool {
func (c ConfigLanguage) IsMultilingual() bool {
return len(c.m.Languages) > 1
}
@@ -133,6 +134,13 @@ func (c ConfigLanguage) Watching() bool {
return c.m.Base.Internal.Watch
}
func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager {
if !c.Watching() {
return identity.NopManager
}
return identity.NewManager(name)
}
// GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use.
func (c ConfigLanguage) GetConfigSection(s string) any {
switch s {
+1
View File
@@ -189,6 +189,7 @@ func (l configLoader) applyDefaultConfig() error {
"menus": maps.Params{},
"disableLiveReload": false,
"pluralizeListTitles": true,
"capitalizeListTitles": true,
"forceSyncStatic": false,
"footnoteAnchorPrefix": "",
"footnoteReturnLinkContents": "",
+2
View File
@@ -215,6 +215,8 @@ type SitemapConfig struct {
Priority float64
// The sitemap filename.
Filename string
// Whether to disable page inclusion.
Disable bool
}
func DecodeSitemap(prototype SitemapConfig, input map[string]any) (SitemapConfig, error) {
+4 -2
View File
@@ -20,6 +20,7 @@ import (
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/langs"
)
@@ -31,10 +32,10 @@ type AllProvider interface {
LanguagePrefix() string
BaseURL() urls.BaseURL
BaseURLLiveReload() urls.BaseURL
PathParser() paths.PathParser
PathParser() *paths.PathParser
Environment() string
IsMultihost() bool
IsMultiLingual() bool
IsMultilingual() bool
NoBuildLock() bool
BaseConfig() BaseConfig
Dirs() CommonDirs
@@ -57,6 +58,7 @@ type AllProvider interface {
BuildDrafts() bool
Running() bool
Watching() bool
NewIdentityManager(name string) identity.Manager
FastRenderMode() bool
PrintUnusedTemplates() bool
EnableMissingTranslationPlaceholders() bool
-6
View File
@@ -44,15 +44,9 @@ type Disqus struct {
type GoogleAnalytics struct {
Service `mapstructure:",squash"`
// Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
UseSessionStorage bool
// Enabling this will make the GA templates respect the
// "Do Not Track" HTTP header. See https://www.paulfurley.com/google-analytics-dnt/.
RespectDoNotTrack bool
// Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
AnonymizeIP bool
}
// Instagram holds the privacy configuration settings related to the Instagram shortcode.
+1 -4
View File
@@ -33,8 +33,6 @@ disable = true
[privacy.googleAnalytics]
disable = true
respectDoNotTrack = true
anonymizeIP = true
useSessionStorage = true
[privacy.instagram]
disable = true
simple = true
@@ -60,8 +58,7 @@ simple = true
got := []bool{
pc.Disqus.Disable, pc.GoogleAnalytics.Disable,
pc.GoogleAnalytics.RespectDoNotTrack, pc.GoogleAnalytics.AnonymizeIP,
pc.GoogleAnalytics.UseSessionStorage, pc.Instagram.Disable,
pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable,
pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT,
pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple,
pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable,
@@ -2,7 +2,7 @@
For a given taxonomy, renders a list of terms assigned to the page.
@context {page} page The current page.
@context {string} taxonomy The taxonony.
@context {string} taxonomy The taxonomy.
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
*/}}
+1 -1
View File
@@ -361,7 +361,7 @@ type BuildState struct {
mu sync.Mutex // protects state below.
// A set of ilenames in /public that
// A set of filenames in /public that
// contains a post-processing prefix.
filenamesWithPostPrefix map[string]bool
}
+1
View File
@@ -56,6 +56,7 @@ hugo [flags]
--printPathWarnings print warnings on duplicate target paths etc.
--printUnusedTemplates print warnings on unused templates.
--quiet build in quiet mode
--renderSegments strings named segments to render (configured in the segments config)
--renderToMemory render to memory (mostly useful when running the server)
-s, --source string filesystem path to read files relative from
--templateMetrics display metrics about template executions
+8 -7
View File
@@ -18,13 +18,14 @@ hugo config [command] [flags]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
--format string preferred file format (toml, yaml or json) (default "toml")
-h, --help help for config
--lang string the language to display config for. Defaults to the first language defined.
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
--format string preferred file format (toml, yaml or json) (default "toml")
-h, --help help for config
--lang string the language to display config for. Defaults to the first language defined.
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
@@ -14,11 +14,12 @@ hugo config mounts [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for mounts
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for mounts
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+8 -7
View File
@@ -18,13 +18,14 @@ hugo mod clean [flags] [args]
### Options
```
--all clean entire module cache
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for clean
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--all clean entire module cache
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for clean
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+7 -6
View File
@@ -20,12 +20,13 @@ hugo mod graph [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
--clean delete module cache for dependencies that fail verification
-c, --contentDir string filesystem path to content directory
-h, --help help for graph
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
--clean delete module cache for dependencies that fail verification
-c, --contentDir string filesystem path to content directory
-h, --help help for graph
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+6 -5
View File
@@ -25,11 +25,12 @@ hugo mod init [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for init
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for init
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
@@ -28,11 +28,12 @@ hugo mod npm pack [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for pack
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for pack
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+6 -5
View File
@@ -14,11 +14,12 @@ hugo mod tidy [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for tidy
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for tidy
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+6 -5
View File
@@ -20,11 +20,12 @@ hugo mod vendor [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for vendor
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
-h, --help help for vendor
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+7 -6
View File
@@ -18,12 +18,13 @@ hugo mod verify [flags] [args]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
--clean delete module cache for dependencies that fail verification
-c, --contentDir string filesystem path to content directory
-h, --help help for verify
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
--clean delete module cache for dependencies that fail verification
-c, --contentDir string filesystem path to content directory
-h, --help help for verify
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+9 -8
View File
@@ -25,14 +25,15 @@ hugo new content [path] [flags]
### Options
```
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
--editor string edit new content with this editor, if provided
-f, --force overwrite file if it already exists
-h, --help help for content
-k, --kind string content type to create
-t, --theme strings themes to use (located in /themes/THEMENAME/)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--cacheDir string filesystem path to cache directory
-c, --contentDir string filesystem path to content directory
--editor string edit new content with this editor, if provided
-f, --force overwrite file if it already exists
-h, --help help for content
-k, --kind string content type to create
--renderSegments strings named segments to render (configured in the segments config)
-t, --theme strings themes to use (located in /themes/THEMENAME/)
```
### Options inherited from parent commands
+47 -46
View File
@@ -12,9 +12,9 @@ A high performance webserver
Hugo provides its own webserver which builds and serves the site.
While hugo server is high performance, it is a webserver with limited options.
'hugo server' will by default write and server files from disk, but you can
render to memory by using the '--renderToMemory' flag. This can be faster
in some cases, but it will consume more memory.
The `hugo server` command will by default write and serve files from disk, but
you can render to memory by using the `--renderToMemory` flag. This can be
faster in some cases, but it will consume more memory.
By default hugo will also watch your files for any changes you make and
automatically rebuild the site. It will then live reload any open browser pages
@@ -28,49 +28,50 @@ hugo server [command] [flags]
### Options
```
--appendPort append port to baseURL (default true)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--bind string interface to which the server will bind (default "127.0.0.1")
-D, --buildDrafts include content marked as draft
-E, --buildExpired include expired content
-F, --buildFuture include content with publishdate in the future
--cacheDir string filesystem path to cache directory
--cleanDestinationDir remove files from destination not found in static directories
-c, --contentDir string filesystem path to content directory
--disableBrowserError do not show build errors in the browser
--disableFastRender enables full re-renders on changes
--disableKinds strings disable different kind of pages (home, RSS etc.)
--disableLiveReload watch without enabling live browser reload on rebuild
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
--forceSyncStatic copy all files when static is changed.
--gc enable to run some cleanup tasks (remove unused cache files) after the build
-h, --help help for server
--ignoreCache ignores the cache directory
-l, --layoutDir string filesystem path to layout directory
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
--minify minify any supported output format (HTML, XML etc.)
--navigateToChanged navigate to changed content file on live browser reload
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noHTTPCache prevent HTTP caching
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
-p, --port int port on which the server will listen (default 1313)
--pprof enable the pprof server (port 8080)
--printI18nWarnings print missing translations
--printMemoryUsage print memory usage to screen at intervals
--printPathWarnings print warnings on duplicate target paths etc.
--printUnusedTemplates print warnings on unused templates.
--renderStaticToDisk serve static files from disk and dynamic files from memory
--templateMetrics display metrics about template executions
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--tlsAuto generate and use locally-trusted certificates.
--tlsCertFile string path to TLS certificate file
--tlsKeyFile string path to TLS key file
--trace file write trace to file (not useful in general)
-w, --watch watch filesystem for changes and recreate as needed (default true)
--appendPort append port to baseURL (default true)
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
--bind string interface to which the server will bind (default "127.0.0.1")
-D, --buildDrafts include content marked as draft
-E, --buildExpired include expired content
-F, --buildFuture include content with publishdate in the future
--cacheDir string filesystem path to cache directory
--cleanDestinationDir remove files from destination not found in static directories
-c, --contentDir string filesystem path to content directory
--disableBrowserError do not show build errors in the browser
--disableFastRender enables full re-renders on changes
--disableKinds strings disable different kind of pages (home, RSS etc.)
--disableLiveReload watch without enabling live browser reload on rebuild
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
--forceSyncStatic copy all files when static is changed.
--gc enable to run some cleanup tasks (remove unused cache files) after the build
-h, --help help for server
--ignoreCache ignores the cache directory
-l, --layoutDir string filesystem path to layout directory
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
--minify minify any supported output format (HTML, XML etc.)
--navigateToChanged navigate to changed content file on live browser reload
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noHTTPCache prevent HTTP caching
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
-p, --port int port on which the server will listen (default 1313)
--pprof enable the pprof server (port 8080)
--printI18nWarnings print missing translations
--printMemoryUsage print memory usage to screen at intervals
--printPathWarnings print warnings on duplicate target paths etc.
--printUnusedTemplates print warnings on unused templates.
--renderSegments strings named segments to render (configured in the segments config)
--renderStaticToDisk serve static files from disk and dynamic files from memory
--templateMetrics display metrics about template executions
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--tlsAuto generate and use locally-trusted certificates.
--tlsCertFile string path to TLS certificate file
--tlsKeyFile string path to TLS key file
--trace file write trace to file (not useful in general)
-w, --watch watch filesystem for changes and recreate as needed (default true)
```
### Options inherited from parent commands
@@ -50,7 +50,7 @@ render
: Always render the page to disk. This is the default value.
- `link`
: Do not render the page to disk, but include it in all page collections.
: Do not render the page to disk, but assign `Permalink` and `RelPermalink` values.
- `never`
: Never render the page to disk, and exclude it from all page collections.
@@ -245,7 +245,7 @@ content/
Set the build options in front matter:
{{< code-toggle file=content/books/_index.md >}}
{{< code-toggle file=content/books/_index.md fm=true >}}
title = 'Books'
[_build]
render = 'never'
+2 -2
View File
@@ -8,7 +8,7 @@ action:
- functions/cast/ToFloat
- functions/cast/ToString
returnType: int
signatures: [cast/ToInt INPUT]
signatures: [cast.ToInt INPUT]
aliases: [/functions/int]
---
@@ -49,5 +49,5 @@ With a hexadecimal (base 16) input:
{{% note %}}
Values with a leading zero are octal (base 8). When casting a string representation of a decimal (base 10) number, remove leading zeros:
`{{ strings/TrimLeft "0" "0011" | int }} → 11`
`{{ strings.TrimLeft "0" "0011" | int }} → 11`
{{% /note %}}
@@ -15,6 +15,8 @@ action:
aliases: [/functions/crypto.fnv32a]
---
{{< new-in 0.98.0 >}}
This function calculates the 32-bit [FNV1a hash](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) of a given string according to the [specification](https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-12):
```go-html-template
+160
View File
@@ -0,0 +1,160 @@
---
title: images.Dither
description: Returns an image filter that dithers an image.
categories: []
keywords: []
action:
aliases: []
related:
- functions/images/Filter
- functions/images/Process
- methods/resource/Colors
- methods/resource/Filter
returnType: images.filter
signatures: ['images.Dither [OPTIONS]']
toc: true
---
## Options
colors
: (`string array`) A slice of two or more colors that make up the dithering palette, each expressed as an RGB or RGBA [hexadecimal] value, with or without a leading hash mark. The default values are opaque black (`000000ff`) and opaque white (`ffffffff`).
[hexadecimal]: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
method
: (`string`) The dithering method. See the [dithering methods](#dithering-methods) section below for a list of the available methods. Default is `FloydSteinberg`.
serpentine
: (`bool`) Applicable to error diffusion dithering methods, serpentine controls whether the error diffusion matrix is applied in a serpentine manner, meaning that it goes right-to-left every other line. This greatly reduces line-type artifacts. Default is `true`.
strength
: (`float`) The strength at which to apply the dithering matrix, typically a value in the range [0, 1]. A value of `1.0` applies the dithering matrix at 100% strength (no modifification of the dither matrix). The `strength` is inversely proportional to contrast; reducing the strength increases the contrast. Setting `strength` to a value such as `0.8` can be useful to reduce noise in the dithered image. Default is `1.0`.
## Usage
Create the options map:
```go-html-template
{{ $opts := dict
"colors" (slice "222222" "808080" "dddddd")
"method" "ClusteredDot4x4"
"strength" 0.85
}}
```
Create the filter:
```go-html-template
{{ $filter := images.Dither $opts }}
```
Or create the filter using the default settings:
```go-html-template
{{ $filter := images.Dither }}
```
{{% include "functions/images/_common/apply-image-filter.md" %}}
## Dithering methods
See the [Go documentation] for descriptions of each of the dithering methods below.
[Go documentation]: https://pkg.go.dev/github.com/makeworld-the-better-one/dither/v2#pkg-variables
Error diffusion dithering methods:
- Atkinson
- Burkes
- FalseFloydSteinberg
- FloydSteinberg
- JarvisJudiceNinke
- Sierra
- Sierra2
- Sierra2_4A
- Sierra3
- SierraLite
- Simple2D
- StevenPigeon
- Stucki
- TwoRowSierra
Ordered dithering methods:
- ClusteredDot4x4
- ClusteredDot6x6
- ClusteredDot6x6_2
- ClusteredDot6x6_3
- ClusteredDot8x8
- ClusteredDotDiagonal16x16
- ClusteredDotDiagonal6x6
- ClusteredDotDiagonal8x8
- ClusteredDotDiagonal8x8_2
- ClusteredDotDiagonal8x8_3
- ClusteredDotHorizontalLine
- ClusteredDotSpiral5x5
- ClusteredDotVerticalLine
- Horizontal3x5
- Vertical5x3
## Example
This example uses the default dithering options.
{{< img
src="images/examples/zion-national-park.jpg"
alt="Zion National Park"
filter="Dither"
filterArgs=""
example=true
>}}
## Recommendations
Regardless of dithering method, do both of the following to obtain the best results:
1. Scale the image _before_ dithering
2. Output the image to a lossless format such as GIF or PNG
The example below does both of these, and it sets the dithering palette to the three most dominant colors in the image.
```go-html-template
{{ with resources.Get "original.jpg" }}
{{ $opts := dict
"method" "ClusteredDotSpiral5x5"
"colors" (first 3 .Colors)
}}
{{ $filters := slice
(images.Process "resize 800x")
(images.Dither $opts)
(images.Process "png")
}}
{{ with . | images.Filter $filters }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
{{ end }}
```
For best results, if the dithering palette is grayscale, convert the image to grayscale before dithering.
```go-html-template
{{ $opts := dict "colors" (slice "222" "808080" "ddd") }}
{{ $filters := slice
(images.Process "resize 800x")
(images.Grayscale)
(images.Dither $opts)
(images.Process "png")
}}
{{ with images.Filter $filters . }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
```
The example above:
1. Resizes the image to be 800 px wide
2. Converts the image to grayscale
3. Dithers the image using the default (`FloydSteinberg`) dithering method with a grayscale palette
4. Converts the image to the PNG format
+23
View File
@@ -111,6 +111,29 @@ format
sourceMap
: (`string`) Whether to generate `inline` or `external` source maps from esbuild. External source maps will be written to the target with the output file name + ".map". Input source maps can be read from js.Build and node modules and combined into the output source maps. By default, source maps are not created.
JSX {{< new-in 0.124.0 >}}
: (`string`) How to handle/transform JSX syntax. One of: `transform`, `preserve`, `automatic`. Default is `transform`. Notably, the `automatic` transform was introduced in React 17+ and will cause the necessary JSX helper functions to be imported automatically. See https://esbuild.github.io/api/#jsx
JSXImportSource {{< new-in 0.124.0 >}}
: (`string`) Which library to use to automatically import its JSX helper functions from. This only works if `JSX` is set to `automatic`. The specified library needs to be installed through NPM and expose certain exports. See https://esbuild.github.io/api/#jsx-import-source
The combination of `JSX` and `JSXImportSource` is helpful if you want to use a non-React JSX library like Preact, e.g.:
```go-html-template
{{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }}
```
With the above, you can use Preact components and JSX without having to manually import `h` and `Fragment` every time:
```jsx
import { render } from 'preact';
const App = () => <>Hello world!</>;
const container = document.getElementById('app');
if (container) render(<App />, container);
```
### Import JS code from /assets
`js.Build` has full support for the virtual union file system in [Hugo Modules](/hugo-modules/). You can see some simple examples in this [test project](https://github.com/gohugoio/hugoTestProjectJSModImports), but in short this means that you can do this:
+139 -19
View File
@@ -8,17 +8,18 @@ action:
related: []
returnType: string
signatures: ['lang.Translate KEY [CONTEXT]']
toc: true
aliases: [/functions/i18n]
---
The `lang.Translate` function returns the value associated with given key as defined in the translation table for the current language.
The `lang.Translate` function returns the value associated with given key as defined in the translation table for the current language.
If the key is not found in the translation table for the current language, the `lang.Translate` function falls back to the translation table for the [`defaultContentLanguage`].
If the key is not found in the translation table for the `defaultContentLanguage`, the `lang.Translate` function returns an empty string.
If the key is not found in the translation table for the current language, the `lang.Translate` function falls back to the translation table for the [`defaultContentLanguage`].
[`defaultContentLanguage`]: /getting-started/configuration/#defaultcontentlanguage
If the key is not found in the translation table for the `defaultContentLanguage`, the `lang.Translate` function returns an empty string.
{{% note %}}
To list missing and fallback translations, use the `--printI18nWarnings` flag when building your site.
@@ -28,6 +29,31 @@ To render placeholders for missing and fallback translations, set
[`enableMissingTranslationPlaceholders`]: /getting-started/configuration/#enablemissingtranslationplaceholders
{{% /note %}}
## Translation tables
Create translation tables in the i18n directory, naming each file according to [RFC 5646]. Translation tables may be JSON, TOML, or YAML. For example:
```text
i18n/en.toml
i18n/en-US.toml
```
The base name must match the language key as defined in your site configuration.
Artificial languages with private use subtags as defined in [RFC 5646 § 2.2.7] are also supported. You may omit the `art-x-` prefix for brevity. For example:
```text
i18n/art-x-hugolang.toml
i18n/hugolang.toml
```
Private use subtags must not exceed 8 alphanumeric characters.
[RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646
[RFC 5646 § 2.2.7]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.7
## Simple translations
Let's say your multilingual site supports two languages, English and Polish. Create a translation table for each language in the `i18n` directory.
```text
@@ -36,10 +62,47 @@ i18n/
└── pl.toml
```
The translation tables can contain both:
The English translation table:
- Simple translations
- Translations with pluralization
{{< code-toggle file=i18n/en >}}
privacy = 'privacy'
security = 'security'
{{< /code-toggle >}}
The Polish translation table:
{{< code-toggle file=i18n/pl >}}
privacy = 'prywatność'
security = 'bezpieczeństwo'
{{< /code-toggle >}}
{{% note %}}
The examples below use the `T` alias for brevity.
{{% /note %}}
When viewing the English language site:
```go-html-template
{{ T "privacy" }} → privacy
{{ T "security" }} → security
````
When viewing the Polish language site:
```go-html-template
{{ T "privacy" }} → prywatność
{{ T "security" }} → bezpieczeństwo
```
## Translations with pluralization
Let's say your multilingual site supports two languages, English and Polish. Create a translation table for each language in the `i18n` directory.
```text
i18n/
├── en.toml
└── pl.toml
```
The Unicode [CLDR Plural Rules chart] describes the pluralization categories for each language.
@@ -48,9 +111,6 @@ The Unicode [CLDR Plural Rules chart] describes the pluralization categories for
The English translation table:
{{< code-toggle file=i18n/en >}}
privacy = 'privacy'
security = 'security'
[day]
one = 'day'
other = 'days'
@@ -63,9 +123,6 @@ other = '{{ . }} days'
The Polish translation table:
{{< code-toggle file=i18n/pl >}}
privacy = 'prywatność'
security = 'bezpieczeństwo'
[day]
one = 'miesiąc'
few = 'miesiące'
@@ -86,9 +143,6 @@ The examples below use the `T` alias for brevity.
When viewing the English language site:
```go-html-template
{{ T "privacy" }} → privacy
{{ T "security" }} → security
{{ T "day" 0 }} → days
{{ T "day" 1 }} → day
{{ T "day" 2 }} → days
@@ -103,9 +157,6 @@ When viewing the English language site:
When viewing the Polish language site:
```go-html-template
{{ T "privacy" }} → prywatność
{{ T "security" }} → bezpieczeństwo
{{ T "day" 0 }} → miesięcy
{{ T "day" 1 }} → miesiąc
{{ T "day" 2 }} → miesiące
@@ -133,3 +184,72 @@ Template code:
{{ T "age" (dict "name" "Will" "count" 1) }} → Will is 1 year old.
{{ T "age" (dict "name" "John" "count" 3) }} → John is 3 years old.
```
{{% note %}}
Translation tables may contain both simple translations and translations with pluralization.
{{% /note %}}
## Reserved keys
Hugo uses the [go-i18n] package to look up values in translation tables. This package reserves the following keys for internal use:
[go-i18n]: https://github.com/nicksnyder/go-i18n
id
: (`string`) Uniquely identifies the message.
description
: (`string`) Describes the message to give additional context to translators that may be relevant for translation.
hash
: (`string`) Uniquely identifies the content of the message that this message was translated from.
leftdelim
: (`string`) The left Go template delimiter.
rightdelim
: (`string`) The right Go template delimiter.
zero
: (`string`) The content of the message for the [CLDR] plural form "zero".
one
: (`string`) The content of the message for the [CLDR] plural form "one".
two
: (`string`) The content of the message for the [CLDR] plural form "two".
few
: (`string`) The content of the message for the [CLDR] plural form "few".
many
: (`string`) The content of the message for the [CLDR] plural form "many".
other
: (`string`) The content of the message for the [CLDR] plural form "other".
[CLDR]: https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html
If you need to provide a translation for one of the reserved keys, you can prepend the word with an underscore. For example:
{{< code-toggle file=i18n/es >}}
_description = 'descripción'
_few = 'pocos'
_many = 'muchos'
_one = 'uno'
_other = 'otro'
_two = 'dos'
_zero = 'cero'
{{< /code-toggle >}}
Then in your templates:
```go-html-template
{{ T "_description" }} → descripción
{{ T "_few" }} → pocos
{{ T "_many" }} → muchos
{{ T "_one" }} → uno
{{ T "_two" }} → dos
{{ T "_zero" }} → cero
{{ T "_other" }} → otro
```
@@ -171,7 +171,7 @@ Override the cache key by setting a `key` in the options map. Use this approach
```go-html-template
{{ $url := "https://example.org/images/a.jpg" }}
{{ $cacheKey := print $url (now.Format "2006-01-02") }}
{{ $resource := resource.GetRemote $url (dict "key" $cacheKey) }}
{{ $resource := resources.GetRemote $url (dict "key" $cacheKey) }}
```
[configure file caches]: /getting-started/configuration/#configure-file-caches
+23
View File
@@ -95,6 +95,29 @@ format
sourceMap
: (`string`) Whether to generate `inline` or `external` source maps from esbuild. External source maps will be written to the target with the output file name + ".map". Input source maps can be read from js.Build and node modules and combined into the output source maps. By default, source maps are not created.
JSX {{< new-in 0.124.0 >}}
: (`string`) How to handle/transform JSX syntax. One of: `transform`, `preserve`, `automatic`. Default is `transform`. Notably, the `automatic` transform was introduced in React 17+ and will cause the necessary JSX helper functions to be imported automatically. See https://esbuild.github.io/api/#jsx
JSXImportSource {{< new-in 0.124.0 >}}
: (`string`) Which library to use to automatically import its JSX helper functions from. This only works if `JSX` is set to `automatic`. The specified library needs to be installed through NPM and expose certain exports. See https://esbuild.github.io/api/#jsx-import-source
The combination of `JSX` and `JSXImportSource` is helpful if you want to use a non-React JSX library like Preact, e.g.:
```go-html-template
{{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }}
```
With the above, you can use Preact components and JSX without having to manually import `h` and `Fragment` every time:
```jsx
import { render } from 'preact';
const App = () => <>Hello world!</>;
const container = document.getElementById('app');
if (container) render(<App />, container);
```
### Import JS code from /assets
`js.Build` has full support for the virtual union file system in [Hugo Modules](/hugo-modules/). You can see some simple examples in this [test project](https://github.com/gohugoio/hugoTestProjectJSModImports), but in short this means that you can do this:
+1 -1
View File
@@ -127,7 +127,7 @@ Achievements:
You can use the following code to render the `Short Description` in your layout:
```go-html-template
<div>Short Description of {{ .Site.Data.User0123.Name }}: <p>{{ index .Site.Data.User0123 "Short Description" | markdownify }}</p></div>
<div>Short Description of {{ .Site.Data.user0123.Name }}: <p>{{ index .Site.Data.user0123 "Short Description" | markdownify }}</p></div>
```
Note the use of the [`markdownify`] function. This will send the description through the Markdown rendering engine.
+1 -1
View File
@@ -31,7 +31,7 @@ Setting `paginate` to a positive value will split the list pages for the homepag
## List paginator pages
{{% note %}}
`.Paginator` is provided to help you build a pager menu. This feature is currently only supported on homepage and list pages (i.e., taxonomies and section lists).
Paginate a page collection in list templates for these page kinds: `home`, `section`, `taxonomy`, or `term`. You cannot paginate a page collection in a template for the `page` page kind.
{{% /note %}}
There are two ways to configure and use a `.Paginator`:
+2 -2
View File
@@ -18,8 +18,8 @@ aliases: [/templates/partial/,/layout/chrome/,/extras/analytics/]
Partial templates---like [single page templates][singletemps] and [list page templates][listtemps]---have a specific [lookup order]. However, partials are simpler in that Hugo will only check in two places:
1. `layouts/partials/*<PARTIALNAME>.html`
2. `themes/<THEME>/layouts/partials/*<PARTIALNAME>.html`
1. `layouts/partials/<PARTIALNAME>.html`
2. `themes/<THEME>/layouts/partials/<PARTIALNAME>.html`
This allows a theme's end user to copy a partial's contents into a file of the same name for [further customization][customize].
+51 -7
View File
@@ -207,6 +207,10 @@ chroma:
- Aliases:
- dax
Name: Dax
- Aliases:
- desktop
- desktop_entry
Name: Desktop file
- Aliases:
- diff
- udiff
@@ -443,6 +447,10 @@ chroma:
- Aliases:
- mason
Name: Mason
- Aliases:
- materialize
- mzsql
Name: Materialize SQL dialect
- Aliases:
- mathematica
- mma
@@ -493,6 +501,9 @@ chroma:
- Aliases:
- natural
Name: Natural
- Aliases:
- ndisasm
Name: NDISASM
- Aliases:
- newspeak
Name: Newspeak
@@ -607,6 +618,9 @@ chroma:
- Aliases:
- prolog
Name: Prolog
- Aliases:
- promela
Name: Promela
- Aliases:
- promql
Name: PromQL
@@ -673,6 +687,9 @@ chroma:
- Aliases:
- registry
Name: reg
- Aliases:
- rego
Name: Rego
- Aliases:
- rst
- rest
@@ -682,6 +699,9 @@ chroma:
- rexx
- arexx
Name: Rexx
- Aliases:
- spec
Name: RPMSpec
- Aliases:
- rb
- ruby
@@ -946,6 +966,7 @@ config:
dir: :cacheDir/modules
maxAge: -1
canonifyURLs: false
capitalizeListTitles: true
cascade: []
cleanDestinationDir: false
contentDir: content
@@ -1536,10 +1557,8 @@ config:
disqus:
disable: false
googleAnalytics:
anonymizeIP: false
disable: false
respectDoNotTrack: false
useSessionStorage: false
instagram:
disable: false
simple: false
@@ -1585,6 +1604,7 @@ config:
toLower: false
relativeURLs: false
removePathAccents: false
renderSegments: null
resourceDir: resources
sectionPagesMenu: ""
security:
@@ -1607,6 +1627,7 @@ config:
- (?i)GET|POST
urls:
- .*
segments: {}
server:
headers: null
redirects:
@@ -1628,6 +1649,7 @@ config:
disableInlineCSS: false
sitemap:
changeFreq: ""
disable: false
filename: sitemap.xml
priority: -1
social: null
@@ -1704,6 +1726,8 @@ config_helpers:
_merge: none
security:
_merge: none
segments:
_merge: none
server:
_merge: none
services:
@@ -2506,7 +2530,7 @@ tpl:
- shuffle
Args:
- l
Description: Shuffle returns list l in a randomised order.
Description: Shuffle returns list l in a randomized order.
Examples: []
Slice:
Aliases:
@@ -2774,8 +2798,8 @@ tpl:
{{ $m.Set "Hugo" "Rocks!" }}
{{ $m.Values | debug.Dump | safeHTML }}
- |-
map[string]interface {}{
"Hugo": "Rocks!",
{
"Hugo": "Rocks!"
}
TestDeprecationErr:
Aliases: null
@@ -2898,7 +2922,7 @@ tpl:
- format
- args
Description: Printf returns string representation of args formatted with the
layouut in format.
layout in format.
Examples:
- - '{{ printf "%s!" "works" }}'
- works!
@@ -2965,6 +2989,21 @@ tpl:
Args: null
Description: ""
Examples: null
IsMultiHost:
Aliases: null
Args: null
Description: ""
Examples: null
IsMultihost:
Aliases: null
Args: null
Description: ""
Examples: null
IsMultilingual:
Aliases: null
Args: null
Description: ""
Examples: null
IsProduction:
Aliases: null
Args: null
@@ -3020,6 +3059,11 @@ tpl:
Args: null
Description: ""
Examples: null
Dither:
Aliases: null
Args: null
Description: ""
Examples: null
Filter:
Aliases: null
Args: null
@@ -4537,5 +4581,5 @@ tpl:
- urlize
Args:
- s
Description: URLize returns the the strings s formatted as an URL.
Description: URLize returns the strings s formatted as an URL.
Examples: []
+381
View File
@@ -0,0 +1,381 @@
{{- /*
Renders the given image using the given filter, if any.
@param {string} src The path to the image which must be a remote, page, or global resource.
@param {string} [filter] The filter to apply to the image (case-insensitive).
@param {string} [filterArgs] A comma-delimited list of arguments to pass to the filter.
@param {bool} [example=false] If true, renders a before/after example.
@param {int} [exampleWidth=384] Image width, in pixels, when rendering a before/after example.
@returns {template.HTML}
@examples
{{< img src="zion-national-park.jpg" >}}
{{< img src="zion-national-park.jpg" alt="Zion National Park" >}}
{{< img
src="zion-national-park.jpg"
alt="Zion National Park"
filter="grayscale"
>}}
{{< img
src="zion-national-park.jpg"
alt="Zion National Park"
filter="process"
filterArgs="resize 400x webp"
>}}
{{< img
src="zion-national-park.jpg"
alt="Zion National Park"
filter="colorize"
filterArgs="180,50,20"
>}}
{{< img
src="zion-national-park.jpg"
alt="Zion National Park"
filter="grayscale"
example=true
>}}
{{< img
src="zion-national-park.jpg"
alt="Zion National Park"
filter="grayscale"
example=true
exampleWidth=400
>}}
When using the text filter, provide the arguments in this order:
0. The text
1. The horizontal offset, in pixels, relative to the left of the image (default 20)
2. The vertical offset, in pixels, relative to the top of the image (default 20)
3. The font size in pixels (default 64)
4. The line height (default 1.2)
5. The font color (default #ffffff)
{{< img
src="images/examples/zion-national-park.jpg"
alt="Zion National Park"
filter="Text"
filterArgs="Zion National Park,25,250,56"
example=true
>}}
When using the padding filter, provide all arguments in this order:
0. Padding top
1. Padding right
2. Padding bottom
3. Padding right
4. Canvas color
{{< img
src="images/examples/zion-national-park.jpg"
alt="Zion National Park"
filter="Padding"
filterArgs="20,50,20,50,#0705"
example=true
>}}
*/}}
{{- /* Initialize. */}}
{{- $alt := "" }}
{{- $src := "" }}
{{- $filter := "" }}
{{- $filterArgs := slice }}
{{- $example := false }}
{{- $exampleWidth := 384 }}
{{- /* Default values to use with the text filter. */}}
{{ $textFilterOpts := dict
"xOffset" 20
"yOffset" 20
"fontSize" 64
"lineHeight" 1.2
"fontColor" "#ffffff"
"fontPath" "https://github.com/google/fonts/raw/main/ofl/lato/Lato-Regular.ttf"
}}
{{- /* Get and validate parameters. */}}
{{- with .Get "alt" }}
{{- $alt = .}}
{{- end }}
{{- with .Get "src" }}
{{- $src = . }}
{{- else }}
{{- errorf "The %q shortcode requires a file parameter. See %s" .Name .Position }}
{{- end }}
{{- with .Get "filter" }}
{{- $filter = . | lower }}
{{- end }}
{{- $validFilters := slice
"autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither"
"gamma" "gaussianblur" "grayscale" "hue" "invert" "none" "opacity" "overlay"
"padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text"
"unsharpmask"
}}
{{- with $filter }}
{{- if not (in $validFilters .) }}
{{- errorf "The filter passed to the %q shortcode is invalid. The filter must be one of %s. See %s" $.Name (delimit $validFilters ", " ", or ") $.Position }}
{{- end }}
{{- end }}
{{- with .Get "filterArgs" }}
{{- $filterArgs = split . "," }}
{{- $filterArgs = apply $filterArgs "trim" "." " " }}
{{- end }}
{{- if in (slice "false" false 0) (.Get "example") }}
{{- $example = false }}
{{- else if in (slice "true" true 1) (.Get "example")}}
{{- $example = true }}
{{- end }}
{{- with .Get "exampleWidth" }}
{{- $exampleWidth = . | int }}
{{- end }}
{{- /* Get image. */}}
{{- $ctx := dict "page" .Page "src" $src "name" .Name "position" .Position }}
{{- $i := partial "inline/get-resource.html" $ctx }}
{{- /* Resize if rendering before/after examples. */}}
{{- if $example }}
{{- $i = $i.Resize (printf "%dx" $exampleWidth) }}
{{- end }}
{{- /* Create filter. */}}
{{- $f := "" }}
{{- $ctx := dict "filter" $filter "args" $filterArgs "name" .Name "position" .Position }}
{{- if eq $filter "autoorient" }}
{{- $ctx = merge $ctx (dict "argsRequired" 0) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.AutoOrient }}
{{- else if eq $filter "brightness" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Brightness (index $filterArgs 0) }}
{{- else if eq $filter "colorbalance" }}
{{- $ctx = merge $ctx (dict "argsRequired" 3) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "percentage red" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "percentage green" "argValue" (index $filterArgs 1) "min" -100 "max" 500) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "percentage blue" "argValue" (index $filterArgs 2) "min" -100 "max" 500) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.ColorBalance (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }}
{{- else if eq $filter "colorize" }}
{{- $ctx = merge $ctx (dict "argsRequired" 3) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "hue" "argValue" (index $filterArgs 0) "min" 0 "max" 360) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "saturation" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 2) "min" 0 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Colorize (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }}
{{- else if eq $filter "contrast" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Contrast (index $filterArgs 0) }}
{{- else if eq $filter "dither" }}
{{- $f = images.Dither }}
{{- else if eq $filter "gamma" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "gamma" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Gamma (index $filterArgs 0) }}
{{- else if eq $filter "gaussianblur" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.GaussianBlur (index $filterArgs 0) }}
{{- else if eq $filter "grayscale" }}
{{- $ctx = merge $ctx (dict "argsRequired" 0) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.Grayscale }}
{{- else if eq $filter "hue" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "shift" "argValue" (index $filterArgs 0) "min" -180 "max" 180) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Hue (index $filterArgs 0) }}
{{- else if eq $filter "invert" }}
{{- $ctx = merge $ctx (dict "argsRequired" 0) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.Invert }}
{{- else if eq $filter "opacity" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "opacity" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Opacity (index $filterArgs 0) }}
{{- else if eq $filter "overlay" }}
{{- $ctx = merge $ctx (dict "argsRequired" 3) }}
{{- template "validate-arg-count" $ctx }}
{{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }}
{{- $overlayImg := partial "inline/get-resource.html" $ctx }}
{{- $f = images.Overlay $overlayImg (index $filterArgs 1 | float ) (index $filterArgs 2 | float) }}
{{- else if eq $filter "padding" }}
{{- $ctx = merge $ctx (dict "argsRequired" 5) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.Padding
(index $filterArgs 0 | int)
(index $filterArgs 1 | int)
(index $filterArgs 2 | int)
(index $filterArgs 3 | int)
(index $filterArgs 4)
}}
{{- else if eq $filter "pixelate" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "size" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Pixelate (index $filterArgs 0) }}
{{- else if eq $filter "process" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $f = images.Process (index $filterArgs 0) }}
{{- else if eq $filter "saturation" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Saturation (index $filterArgs 0) }}
{{- else if eq $filter "sepia" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Sepia (index $filterArgs 0) }}
{{- else if eq $filter "sigmoid" }}
{{- $ctx = merge $ctx (dict "argsRequired" 2) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "midpoint" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "factor" "argValue" (index $filterArgs 1) "min" -10 "max" 10) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.Sigmoid (index $filterArgs 0) (index $filterArgs 1) }}
{{- else if eq $filter "text" }}
{{- $ctx = merge $ctx (dict "argsRequired" 1) }}
{{- template "validate-arg-count" $ctx }}
{{- $ctx := dict "src" $textFilterOpts.fontPath "name" .Name "position" .Position }}
{{- $font := or (partial "inline/get-resource.html" $ctx) }}
{{- $fontSize := or (index $filterArgs 3 | int) $textFilterOpts.fontSize }}
{{- $lineHeight := math.Max (or (index $filterArgs 4 | float) $textFilterOpts.lineHeight) 1 }}
{{- $opts := dict
"x" (or (index $filterArgs 1 | int) $textFilterOpts.xOffset)
"y" (or (index $filterArgs 2 | int) $textFilterOpts.yOffset)
"size" $fontSize
"linespacing" (mul (sub $lineHeight 1) $fontSize)
"color" (or (index $filterArgs 5) $textFilterOpts.fontColor)
"font" $font
}}
{{- $f = images.Text (index $filterArgs 0) $opts }}
{{- else if eq $filter "unsharpmask" }}
{{- $ctx = merge $ctx (dict "argsRequired" 3) }}
{{- template "validate-arg-count" $ctx }}
{{- $filterArgs = apply $filterArgs "float" "." }}
{{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 500) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "amount" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }}
{{- template "validate-arg-value" $ctx }}
{{- $ctx = merge $ctx (dict "argName" "threshold" "argValue" (index $filterArgs 2) "min" 0 "max" 1) }}
{{- template "validate-arg-value" $ctx }}
{{- $f = images.UnsharpMask (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }}
{{- end }}
{{- /* Apply filter. */}}
{{- $fi := $i }}
{{- with $f }}
{{- $fi = $i.Filter . }}
{{- end }}
{{- /* Render. */}}
{{- if $example }}
<p>Original</p>
<img class='di ba b--black-20' style="width: initial;" src="{{ $i.RelPermalink }}" alt="{{ $alt }}">
<p>Processed</p>
<img class='di ba b--black-20' style="width: initial;" src="{{ $fi.RelPermalink }}" alt="{{ $alt }}">
{{- else -}}
<img class='di' style="width: initial;" src="{{ $fi.RelPermalink }}" alt="{{ $alt }}">
{{- end }}
{{- define "validate-arg-count" }}
{{- $msg := "When using the %q filter, the %q shortcode requires an args parameter with %d %s. See %s" }}
{{- if lt (len .args) .argsRequired }}
{{- $text := "values" }}
{{- if eq 1 .argsRequired }}
{{- $text = "value" }}
{{- end }}
{{- errorf $msg .filter .name .argsRequired $text .position }}
{{- end }}
{{- end }}
{{- define "validate-arg-value" }}
{{- $msg := "The %q argument passed to the %q shortcode is invalid. Expected a value in the range [%v,%v], but received %v. See %s" }}
{{- if or (lt .argValue .min) (gt .argValue .max) }}
{{- errorf $msg .argName .name .min .max .argValue .position }}
{{- end }}
{{- end }}
{{- define "partials/inline/get-resource.html" }}
{{- $r := "" }}
{{- $u := urls.Parse .src }}
{{- $msg := "The %q shortcode was unable to resolve %s. See %s" }}
{{- if $u.IsAbs }}
{{- with resources.GetRemote $u.String }}
{{- with .Err }}
{{- errorf "%s" }}
{{- else }}
{{- /* This is a remote resource. */}}
{{- $r = . }}
{{- end }}
{{- else }}
{{- errorf $msg $.name $u.String $.position }}
{{- end }}
{{- else }}
{{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }}
{{- /* This is a page resource. */}}
{{- $r = . }}
{{- else }}
{{- with resources.Get $u.Path }}
{{- /* This is a global resource. */}}
{{- $r = . }}
{{- else }}
{{- errorf $msg $.name $u.Path $.position }}
{{- end }}
{{- end }}
{{- end }}
{{- return $r}}
{{- end -}}
+23 -22
View File
@@ -2,10 +2,10 @@ module github.com/gohugoio/hugo
require (
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
github.com/alecthomas/chroma/v2 v2.12.0
github.com/alecthomas/chroma/v2 v2.13.0
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
github.com/aws/aws-sdk-go-v2 v1.24.1
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6
github.com/aws/aws-sdk-go-v2 v1.26.1
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4
github.com/bep/clocks v0.5.0
github.com/bep/debounce v1.2.0
github.com/bep/gitmap v1.1.2
@@ -18,24 +18,24 @@ require (
github.com/bep/lazycache v0.4.0
github.com/bep/logg v0.4.0
github.com/bep/mclib v1.20400.20402
github.com/bep/overlayfs v0.9.1
github.com/bep/overlayfs v0.9.2
github.com/bep/simplecobra v0.4.0
github.com/bep/tmc v0.5.1
github.com/clbanning/mxj/v2 v2.7.0
github.com/cli/safeexec v1.0.1
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.1
github.com/evanw/esbuild v0.20.0
github.com/evanw/esbuild v0.20.2
github.com/fatih/color v1.16.0
github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.14.6
github.com/fsnotify/fsnotify v1.7.0
github.com/getkin/kin-openapi v0.123.0
github.com/getkin/kin-openapi v0.124.0
github.com/ghodss/yaml v1.0.0
github.com/gobuffalo/flect v1.0.2
github.com/gobwas/glob v0.2.3
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0
github.com/gohugoio/locales v0.14.0
github.com/gohugoio/localescompressed v1.0.1
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95
@@ -46,6 +46,7 @@ require (
github.com/kylelemons/godebug v1.1.0
github.com/kyokomi/emoji/v2 v2.2.12
github.com/magefile/mage v1.15.0
github.com/makeworld-the-better-one/dither/v2 v2.4.0
github.com/marekm4/color-extractor v1.2.1
github.com/mattn/go-isatty v0.0.20
github.com/mitchellh/hashstructure v1.1.0
@@ -54,28 +55,28 @@ require (
github.com/niklasfasching/go-org v1.7.0
github.com/olekukonko/tablewriter v0.0.5
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/pelletier/go-toml/v2 v2.1.1
github.com/pelletier/go-toml/v2 v2.2.0
github.com/rogpeppe/go-internal v1.12.0
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/sanity-io/litter v1.5.5
github.com/spf13/afero v1.11.0
github.com/spf13/cast v1.6.0
github.com/spf13/cobra v1.8.0
github.com/spf13/fsync v0.10.0
github.com/spf13/fsync v0.10.1
github.com/spf13/pflag v1.0.5
github.com/tdewolff/minify/v2 v2.20.16
github.com/tdewolff/parse/v2 v2.7.11
github.com/yuin/goldmark v1.7.0
github.com/tdewolff/minify/v2 v2.20.19
github.com/tdewolff/parse/v2 v2.7.12
github.com/yuin/goldmark v1.7.1
github.com/yuin/goldmark-emoji v1.0.2
go.uber.org/automaxprocs v1.5.3
gocloud.dev v0.36.0
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
golang.org/x/image v0.15.0
golang.org/x/mod v0.15.0
golang.org/x/net v0.20.0
golang.org/x/sync v0.6.0
golang.org/x/mod v0.17.0
golang.org/x/net v0.24.0
golang.org/x/sync v0.7.0
golang.org/x/text v0.14.0
golang.org/x/tools v0.17.0
golang.org/x/tools v0.20.0
google.golang.org/api v0.152.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -99,8 +100,8 @@ require (
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
@@ -111,9 +112,9 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.8 // indirect
github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
@@ -141,9 +142,9 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
+51 -47
View File
@@ -66,16 +66,16 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
@@ -86,16 +86,16 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6Jk
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6 h1:xKbFXea2CIF/Wskauz1TMr//wZ6FyzEafMdSBIQqn80=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6/go.mod h1:iB6PQSb3ULRrrlEiuFfVE318JiBOdk4k46BbuzrrgXc=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4 h1:a4gfRHHCzvV0jEjOUdZOK0oJ4H21x5WT+E4ucWk4jeM=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4/go.mod h1:Pphkts8iBnexoEpcMti5fUvN3/yoGRLtl2heOeppF70=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
@@ -112,8 +112,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsY
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
@@ -138,8 +138,8 @@ github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ=
github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0=
github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA=
github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY=
github.com/bep/overlayfs v0.9.1 h1:SL54SV8A3zRkmQ+83Jj4TLE88jadHd5d1L4NpfmqJJs=
github.com/bep/overlayfs v0.9.1/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40=
github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs=
github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40=
github.com/bep/simplecobra v0.4.0 h1:ufX/6WcOtEVJdCd7hsztTWURlZkOaWYOD+zCqrM8qUE=
github.com/bep/simplecobra v0.4.0/go.mod h1:evSM6iQqRwqpV7W4H4DlYFfe9mZ0x6Hj5GEOnIV7dI4=
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
@@ -168,8 +168,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
@@ -179,8 +179,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanw/esbuild v0.20.0 h1:pcW+/LCNc99Pgfs0kUnvjRCba8Lr9tDMSVg89t1ZLW4=
github.com/evanw/esbuild v0.20.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/evanw/esbuild v0.20.2 h1:E4Y0iJsothpUCq7y0D+ERfqpJmPWrZpNybJA3x3I4p8=
github.com/evanw/esbuild v0.20.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@@ -193,8 +193,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -211,8 +211,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0 h1:oFQ3f1M3Ook6amHmbqVu/uBRrQ6yjMDFkIv4HQr0f1Y=
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0/go.mod h1:g9CCh+Ci2IMbPUrVJuXbBTrA+rIIx5+hDQ4EXYaQDoM=
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0 h1:PCtO5l++psZf48yen2LxQ3JiOXxaRC6v0594NeHvGZg=
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0/go.mod h1:g9CCh+Ci2IMbPUrVJuXbBTrA+rIIx5+hDQ4EXYaQDoM=
github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
@@ -347,6 +347,8 @@ github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE=
github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0=
github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -374,8 +376,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
@@ -408,13 +410,14 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/fsync v0.10.0 h1:j+zUMN41zWj3sEqueD4mAsPDQwyOvMeJCcrawdmbqXk=
github.com/spf13/fsync v0.10.0/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ=
github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE=
github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -423,12 +426,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tdewolff/minify/v2 v2.20.16 h1:/C8dtRkxLTIyUlKlBz46gDiktCrE8a6+c1gTrnPFz+U=
github.com/tdewolff/minify/v2 v2.20.16/go.mod h1:/FvxV9KaTrFu35J9I2FhRvWSBxcHj8sDSdwBFh5voxM=
github.com/tdewolff/parse/v2 v2.7.11 h1:v+W45LnzmjndVlfqPCT5gGjAAZKd1GJGOPJveTIkBY8=
github.com/tdewolff/parse/v2 v2.7.11/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
@@ -439,8 +443,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -464,8 +468,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -506,8 +510,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -543,8 +547,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -568,8 +572,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -613,8 +617,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -683,8 +687,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+8
View File
@@ -50,6 +50,14 @@ func TestTrimShortHTML(t *testing.T) {
}
}
func BenchmarkTrimShortHTML(b *testing.B) {
c := newTestContentSpec(nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.TrimShortHTML([]byte("<p>Simple paragraph</p>"))
}
}
func TestBytesToHTML(t *testing.T) {
c := qt.New(t)
c.Assert(helpers.BytesToHTML([]byte("dobedobedo")), qt.Equals, template.HTML("dobedobedo"))
+9 -2
View File
@@ -188,7 +188,7 @@ func ReaderContains(r io.Reader, subslice []byte) bool {
//
// - "Go" (strings.Title)
// - "AP" (see https://www.apstylebook.com/)
// - "Chicago" (see http://www.chicagomanualofstyle.org/home.html)
// - "Chicago" (see https://www.chicagomanualofstyle.org/home.html)
// - "FirstUpper" (only the first character is upper case)
// - "None" (no transformation)
//
@@ -328,7 +328,14 @@ func PrintFs(fs afero.Fs, path string, w io.Writer) {
}
afero.Walk(fs, path, func(path string, info os.FileInfo, err error) error {
fmt.Fprintln(w, filepath.ToSlash(path))
if err != nil {
panic(fmt.Sprintf("error: path %q: %s", path, err))
}
path = filepath.ToSlash(path)
if path == "" {
path = "."
}
fmt.Fprintln(w, path, info.IsDir())
return nil
})
}
+1 -1
View File
@@ -374,7 +374,7 @@ func cacheDirDefault(cacheDir string) string {
// Turns out that Cloudflare also sets NETLIFY=true in its build environment,
// but all of these 3 should not give any false positives.
if os.Getenv("NETLIFY") == "true" && os.Getenv("PULL_REQUEST") != "" && os.Getenv("DEPLOY_PRIME_URL") != "" {
// Netlify's cache behaviour is not documented, the currently best example
// Netlify's cache behavior is not documented, the currently best example
// is this project:
// https://github.com/philhawksworth/content-shards/blob/master/gulpfile.js
return "/opt/build/cache/hugo_cache/"
+2 -2
View File
@@ -59,7 +59,7 @@ type componentFsDir struct {
fs *componentFs
}
// ReadDir reads count entries from this virtual directorie and
// ReadDir reads count entries from this virtual directory and
// sorts the entries according to the component filesystem rules.
func (f *componentFsDir) ReadDir(count int) ([]iofs.DirEntry, error) {
fis, err := f.DirOnlyOps.(iofs.ReadDirFile).ReadDir(-1)
@@ -241,7 +241,7 @@ type ComponentFsOptions struct {
DefaultContentLanguage string
// The parser used to parse paths provided by this filesystem.
PathParser paths.PathParser
PathParser *paths.PathParser
}
func (fs *componentFs) Open(name string) (afero.File, error) {
+1 -1
View File
@@ -42,7 +42,7 @@ var LanguageDirsMerger overlayfs.DirsMerger = func(lofi, bofi []fs.DirEntry) []f
// AppendDirsMerger merges two directories keeping all regular files
// with the first slice as the base.
// Duplicate directories in the secnond slice will be ignored.
// Duplicate directories in the second slice will be ignored.
// This strategy is used for the i18n and data fs where we need all entries.
var AppendDirsMerger overlayfs.DirsMerger = func(lofi, bofi []fs.DirEntry) []fs.DirEntry {
for _, fi1 := range bofi {
+3 -1
View File
@@ -73,7 +73,9 @@ type FileMeta struct {
InclusionFilter *glob.FilenameFilter
// Rename the name part of the file (not the directory).
Rename func(name string, toFrom bool) string
// Returns the new name and a boolean indicating if the file
// should be included.
Rename func(name string, toFrom bool) (string, bool)
}
func (m *FileMeta) Copy() *FileMeta {
+140 -119
View File
@@ -1,4 +1,4 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import (
"path"
"path/filepath"
"strings"
"sync/atomic"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/paths"
@@ -43,7 +44,7 @@ var _ ReverseLookupProvder = (*RootMappingFs)(nil)
func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {
rootMapToReal := radix.New()
realMapToRoot := radix.New()
var virtualRoots []RootMapping
id := fmt.Sprintf("rfs-%d", rootMappingFsCounter.Add(1))
addMapping := func(key string, rm RootMapping, to *radix.Tree) {
var mappings []RootMapping
@@ -77,45 +78,49 @@ func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {
rm.Meta = NewFileMeta()
}
if rm.FromBase == "" {
panic(" rm.FromBase is empty")
}
rm.Meta.Component = rm.FromBase
rm.Meta.Module = rm.Module
rm.Meta.ModuleOrdinal = rm.ModuleOrdinal
rm.Meta.IsProject = rm.IsProject
rm.Meta.BaseDir = rm.ToBase
if !fi.IsDir() {
// We do allow single file mounts.
// However, the file system logic will be much simpler with just directories.
// So, convert this mount into a directory mount with a nameTo filter and renamer.
// So, convert this mount into a directory mount with a renamer,
// which will tell the caller if name should be included.
dirFrom, nameFrom := filepath.Split(rm.From)
dirTo, nameTo := filepath.Split(rm.To)
dirFrom, dirTo = strings.TrimSuffix(dirFrom, filepathSeparator), strings.TrimSuffix(dirTo, filepathSeparator)
rm.From = dirFrom
fi, err = fs.Stat(rm.To)
if err != nil {
if herrors.IsNotExist(err) {
continue
}
return nil, err
}
rm.fiSingleFile = NewFileMetaInfo(fi, rm.Meta.Copy())
singleFileMeta := rm.Meta.Copy()
singleFileMeta.Name = nameFrom
rm.fiSingleFile = NewFileMetaInfo(fi, singleFileMeta)
rm.To = dirTo
rm.Meta.Rename = func(name string, toFrom bool) string {
rm.Meta.Rename = func(name string, toFrom bool) (string, bool) {
if toFrom {
if name == nameTo {
return nameFrom
return nameFrom, true
}
return name
return "", false
}
if name == nameFrom {
return nameTo
return nameTo, true
}
return name
return "", false
}
nameToFilename := filepathSeparator + nameTo
rm.Meta.InclusionFilter = rm.Meta.InclusionFilter.Append(glob.NewFilenameFilterForInclusionFunc(
func(filename string) bool {
return strings.HasPrefix(nameToFilename, filename)
return nameToFilename == filename
},
))
@@ -129,19 +134,9 @@ func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {
}
}
if rm.FromBase == "" {
panic(" rm.FromBase is empty")
}
// Extract "blog" from "content/blog"
rm.path = strings.TrimPrefix(strings.TrimPrefix(rm.From, rm.FromBase), filepathSeparator)
rm.Meta.SourceRoot = fi.(MetaProvider).Meta().Filename
rm.Meta.BaseDir = rm.ToBase
rm.Meta.Module = rm.Module
rm.Meta.ModuleOrdinal = rm.ModuleOrdinal
rm.Meta.Component = rm.FromBase
rm.Meta.IsProject = rm.IsProject
meta := rm.Meta.Copy()
@@ -160,12 +155,10 @@ func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {
addMapping(rev, rm, realMapToRoot)
virtualRoots = append(virtualRoots, rm)
}
rootMapToReal.Insert(filepathSeparator, virtualRoots)
rfs := &RootMappingFs{
id: id,
Fs: fs,
rootMapToReal: rootMapToReal,
realMapToRoot: realMapToRoot,
@@ -237,11 +230,14 @@ var _ FilesystemUnwrapper = (*RootMappingFs)(nil)
// is directories only, and they will be returned in Readdir and Readdirnames
// in the order given.
type RootMappingFs struct {
id string
afero.Fs
rootMapToReal *radix.Tree
realMapToRoot *radix.Tree
}
var rootMappingFsCounter atomic.Int32
func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) {
base = filepathSeparator + fs.cleanName(base)
roots := fs.getRootsWithPrefix(base)
@@ -252,13 +248,11 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) {
fss := make([]FileMetaInfo, len(roots))
for i, r := range roots {
if r.fiSingleFile != nil {
// A single file mount.
fss[i] = r.fiSingleFile
continue
}
bfs := NewBasePathFs(fs.Fs, r.To)
fs := bfs
if r.Meta.InclusionFilter != nil {
@@ -269,17 +263,16 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) {
if err != nil {
return nil, fmt.Errorf("RootMappingFs.Dirs: %w", err)
}
if !fi.IsDir() {
fi.(FileMetaInfo).Meta().Merge(r.Meta)
}
fss[i] = fi.(FileMetaInfo)
}
return fss, nil
}
func (fs *RootMappingFs) Key() string {
return fs.id
}
func (fs *RootMappingFs) UnwrapFilesystem() afero.Fs {
return fs.Fs
}
@@ -323,7 +316,6 @@ func (fs *RootMappingFs) Stat(name string) (os.FileInfo, error) {
if err != nil {
return nil, err
}
return fis[0], nil
}
@@ -338,13 +330,18 @@ func (c ComponentPath) ComponentPathJoined() string {
}
type ReverseLookupProvder interface {
ReverseLookup(filename string, checkExists bool) ([]ComponentPath, error)
ReverseLookup(filename string) ([]ComponentPath, error)
ReverseLookupComponent(component, filename string) ([]ComponentPath, error)
}
// func (fs *RootMappingFs) ReverseStat(filename string) ([]FileMetaInfo, error)
func (fs *RootMappingFs) ReverseLookup(in string, checkExists bool) ([]ComponentPath, error) {
in = fs.cleanName(in)
key := filepathSeparator + in
func (fs *RootMappingFs) ReverseLookup(filename string) ([]ComponentPath, error) {
return fs.ReverseLookupComponent("", filename)
}
func (fs *RootMappingFs) ReverseLookupComponent(component, filename string) ([]ComponentPath, error) {
filename = fs.cleanName(filename)
key := filepathSeparator + filename
s, roots := fs.getRootsReverse(key)
@@ -358,19 +355,21 @@ func (fs *RootMappingFs) ReverseLookup(in string, checkExists bool) ([]Component
dir, name := filepath.Split(base)
for _, first := range roots {
if first.Meta.Rename != nil {
name = first.Meta.Rename(name, true)
if component != "" && first.FromBase != component {
continue
}
// Now we know that this file _could_ be in this fs.
filename := filepathSeparator + filepath.Join(first.path, dir, name)
if checkExists {
// Confirm that it exists.
_, err := fs.Stat(first.FromBase + filename)
if err != nil {
var filename string
if first.Meta.Rename != nil {
// Single file mount.
if newname, ok := first.Meta.Rename(name, true); ok {
filename = filepathSeparator + filepath.Join(first.path, dir, newname)
} else {
continue
}
} else {
// Now we know that this file _could_ be in this fs.
filename = filepathSeparator + filepath.Join(first.path, dir, name)
}
cps = append(cps, ComponentPath{
@@ -403,16 +402,43 @@ func (fs *RootMappingFs) getRoot(key string) []RootMapping {
}
func (fs *RootMappingFs) getRoots(key string) (string, []RootMapping) {
return fs.getRootsIn(key, fs.rootMapToReal)
tree := fs.rootMapToReal
levels := strings.Count(key, filepathSeparator)
seen := make(map[RootMapping]bool)
var roots []RootMapping
var s string
for {
var found bool
ss, vv, found := tree.LongestPrefix(key)
if !found || (levels < 2 && ss == key) {
break
}
for _, rm := range vv.([]RootMapping) {
if !seen[rm] {
seen[rm] = true
roots = append(roots, rm)
}
}
s = ss
// We may have more than one root for this key, so walk up.
oldKey := key
key = filepath.Dir(key)
if key == oldKey {
break
}
}
return s, roots
}
func (fs *RootMappingFs) getRootsReverse(key string) (string, []RootMapping) {
return fs.getRootsIn(key, fs.realMapToRoot)
}
func (fs *RootMappingFs) getRootsIn(key string, tree *radix.Tree) (string, []RootMapping) {
tree := fs.realMapToRoot
s, v, found := tree.LongestPrefix(key)
if !found {
return "", nil
}
@@ -449,6 +475,11 @@ func (fs *RootMappingFs) newUnionFile(fis ...FileMetaInfo) (afero.File, error) {
return fis[0].Meta().Open()
}
if !fis[0].IsDir() {
// Pick the last file mount.
return fis[len(fis)-1].Meta().Open()
}
openers := make([]func() (afero.File, error), len(fis))
for i := len(fis) - 1; i >= 0; i-- {
fi := fis[i]
@@ -537,9 +568,11 @@ func (rfs *RootMappingFs) collectDirEntries(prefix string) ([]iofs.DirEntry, err
}
fi = newDirNameOnlyFileInfo(name, meta, opener)
} else if rm.Meta.Rename != nil {
if n := rm.Meta.Rename(fi.Name(), true); n != fi.Name() {
fi.(MetaProvider).Meta().Name = n
n, ok := rm.Meta.Rename(fi.Name(), true)
if !ok {
continue
}
fi.(MetaProvider).Meta().Name = n
}
fis = append(fis, fi)
}
@@ -584,25 +617,16 @@ func (rfs *RootMappingFs) collectDirEntries(prefix string) ([]iofs.DirEntry, err
rms := v.([]RootMapping)
for _, rm := range rms {
if !rm.fi.IsDir() {
// A single file mount
fis = append(fis, rm.fi)
continue
}
name := filepath.Base(rm.From)
if seen[name] {
continue
}
seen[name] = true
opener := func() (afero.File, error) {
return rfs.Open(rm.From)
}
fi := newDirNameOnlyFileInfo(name, rm.Meta, opener)
fis = append(fis, fi)
}
return false
@@ -628,6 +652,28 @@ func (rfs *RootMappingFs) collectDirEntries(prefix string) ([]iofs.DirEntry, err
}
func (fs *RootMappingFs) doStat(name string) ([]FileMetaInfo, error) {
fis, err := fs.doDoStat(name)
if err != nil {
return nil, err
}
// Sanity check. Check that all is either file or directories.
var isDir, isFile bool
for _, fi := range fis {
if fi.IsDir() {
isDir = true
} else {
isFile = true
}
}
if isDir && isFile {
// For now.
return nil, os.ErrNotExist
}
return fis, nil
}
func (fs *RootMappingFs) doDoStat(name string) ([]FileMetaInfo, error) {
name = fs.cleanName(name)
key := filepathSeparator + name
@@ -668,48 +714,20 @@ func (fs *RootMappingFs) doStat(name string) ([]FileMetaInfo, error) {
return nil, err
}
fileCount := 0
var wasFiltered bool
for _, root := range roots {
meta := root.fi.Meta()
if !meta.InclusionFilter.Match(strings.TrimPrefix(meta.Filename, meta.SourceRoot), root.fi.IsDir()) {
wasFiltered = true
continue
}
if !root.fi.IsDir() {
fileCount++
}
if fileCount > 1 {
break
}
}
if fileCount == 0 {
if wasFiltered {
return nil, os.ErrNotExist
}
// Dir only.
return []FileMetaInfo{newDirNameOnlyFileInfo(name, roots[0].Meta, fs.virtualDirOpener(name))}, nil
}
if fileCount > 1 {
// Not supported by this filesystem.
return nil, fmt.Errorf("found multiple files with name %q, use .Readdir or the source filesystem directly", name)
}
return []FileMetaInfo{roots[0].fi}, nil
return []FileMetaInfo{newDirNameOnlyFileInfo(name, roots[0].Meta, fs.virtualDirOpener(name))}, nil
}
func (fs *RootMappingFs) statRoot(root RootMapping, filename string) (FileMetaInfo, error) {
dir, name := filepath.Split(filename)
if root.Meta.Rename != nil {
if n := root.Meta.Rename(name, false); n != name {
filename = filepath.Join(dir, n)
n, ok := root.Meta.Rename(name, false)
if !ok {
return nil, os.ErrNotExist
}
filename = filepath.Join(dir, n)
}
if !root.Meta.InclusionFilter.Match(root.trimFrom(filename), root.fi.IsDir()) {
if !root.Meta.InclusionFilter.Match(root.trimFrom(filename), true) {
return nil, os.ErrNotExist
}
@@ -720,24 +738,27 @@ func (fs *RootMappingFs) statRoot(root RootMapping, filename string) (FileMetaIn
}
var opener func() (afero.File, error)
if fi.IsDir() {
// Make sure metadata gets applied in ReadDir.
opener = fs.realDirOpener(filename, root.Meta)
} else {
if root.Meta.Rename != nil {
if n := root.Meta.Rename(fi.Name(), true); n != fi.Name() {
meta := fi.(MetaProvider).Meta()
meta.Name = n
}
}
if !fi.IsDir() {
// Open the file directly.
// Opens the real file directly.
opener = func() (afero.File, error) {
return fs.Fs.Open(filename)
}
} else if root.Meta.Rename != nil {
// A single file mount where we have mounted the containing directory.
n, ok := root.Meta.Rename(fi.Name(), true)
if !ok {
return nil, os.ErrNotExist
}
meta := fi.(MetaProvider).Meta()
meta.Name = n
// Opens the real file directly.
opener = func() (afero.File, error) {
return fs.Fs.Open(filename)
}
} else {
// Make sure metadata gets applied in ReadDir.
opener = fs.realDirOpener(filename, root.Meta)
}
fim := decorateFileInfo(fi, opener, "", root.Meta)
@@ -801,7 +822,7 @@ func (f *rootMappingDir) ReadDir(count int) ([]iofs.DirEntry, error) {
return f.fs.collectDirEntries(f.name)
}
// Sentinal error to signal that a file is a directory.
// Sentinel error to signal that a file is a directory.
var errIsDir = errors.New("isDir")
func (f *rootMappingDir) Stat() (iofs.FileInfo, error) {
+7 -2
View File
@@ -276,15 +276,20 @@ func TestRootMappingFsMount(t *testing.T) {
// Test ReverseLookup.
// Single file mounts.
cps, err := rfs.ReverseLookup(filepath.FromSlash("singlefiles/no.txt"), true)
cps, err := rfs.ReverseLookup(filepath.FromSlash("singlefiles/no.txt"))
c.Assert(err, qt.IsNil)
c.Assert(cps, qt.DeepEquals, []ComponentPath{
{Component: "content", Path: "singles/p1.md", Lang: "no"},
})
cps, err = rfs.ReverseLookup(filepath.FromSlash("singlefiles/sv.txt"))
c.Assert(err, qt.IsNil)
c.Assert(cps, qt.DeepEquals, []ComponentPath{
{Component: "content", Path: "singles/p1.md", Lang: "sv"},
})
// File inside directory mount.
cps, err = rfs.ReverseLookup(filepath.FromSlash("mynoblogcontent/test.txt"), true)
cps, err = rfs.ReverseLookup(filepath.FromSlash("mynoblogcontent/test.txt"))
c.Assert(err, qt.IsNil)
c.Assert(cps, qt.DeepEquals, []ComponentPath{
{Component: "content", Path: "blog/test.txt", Lang: "no"},
+92
View File
@@ -674,6 +674,8 @@ S1|p1:|p2:p2|
// Issue 11977.
func TestCascadeExtensionInPath(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org"
@@ -700,3 +702,93 @@ title: "Post 1"
b.Assert(err, qt.IsNotNil)
b.AssertLogContains(`cascade target path "/posts/post-1.de.md" looks like a path with an extension; since Hugo v0.123.0 this will not match anything, see https://gohugo.io/methods/page/path/`)
}
func TestCascadeExtensionInPathIgnore(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org"
ignoreLogs = ['cascade-pattern-with-extension']
[languages]
[languages.en]
weight = 1
[languages.de]
-- content/_index.de.md --
+++
[[cascade]]
[cascade.params]
foo = 'bar'
[cascade._target]
path = '/posts/post-1.de.md'
+++
-- content/posts/post-1.de.md --
---
title: "Post 1"
---
-- layouts/_default/single.html --
{{ .Title }}|{{ .Params.foo }}$
`
b := Test(t, files)
b.AssertLogNotContains(`looks like a path with an extension`)
}
func TestCascadConfigExtensionInPath(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org"
[[cascade]]
[cascade.params]
foo = 'bar'
[cascade._target]
path = '/p1.md'
`
b, err := TestE(t, files)
b.Assert(err, qt.IsNotNil)
b.AssertLogContains(`looks like a path with an extension`)
}
func TestCascadConfigExtensionInPathIgnore(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.org"
ignoreLogs = ['cascade-pattern-with-extension']
[[cascade]]
[cascade.params]
foo = 'bar'
[cascade._target]
path = '/p1.md'
`
b := Test(t, files)
b.AssertLogNotContains(`looks like a path with an extension`)
}
func TestCascadeIssue12172(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['rss','sitemap','taxonomy','term']
[[cascade]]
headless = true
[cascade._target]
path = '/s1**'
-- content/s1/p1.md --
---
title: p1
---
-- layouts/_default/single.html --
{{ .Title }}|
-- layouts/_default/list.html --
{{ .Title }}|
`
b := Test(t, files)
b.AssertFileExists("public/index.html", true)
b.AssertFileExists("public/s1/index.html", false)
b.AssertFileExists("public/s1/p1/index.html", false)
}
+43 -24
View File
@@ -490,11 +490,10 @@ name = "menu-theme"
got := b.Configs.Base
if mergeStrategy == "none" {
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Priority: -1, Filename: "sitemap.xml"})
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
} else {
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Priority: -1, Filename: "sitemap.xml"})
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"})
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
}
})
@@ -508,12 +507,12 @@ func TestLoadConfigFromThemeDir(t *testing.T) {
theme = "test-theme"
[params]
m1 = "mv1"
m1 = "mv1"
`
themeConfig := `
[params]
t1 = "tv1"
t1 = "tv1"
t2 = "tv2"
`
@@ -854,6 +853,7 @@ Home.
}
func TestConfigParamSetOnLanguageLevel(t *testing.T) {
t.Skip("this has correctly started to fail now.")
t.Parallel()
files := `
@@ -884,9 +884,9 @@ ThisIsAParam: {{ site.Params.thisIsAParam }}
).BuildE()
b.Assert(err, qt.IsNil)
b.AssertFileContent("public/index.html", `
b.AssertFileContent("public/index.html", `
MyParam: enParamValue
ThisIsAParam: thisIsAParamValue
ThisIsAParam: thisIsAParamValue
`)
}
@@ -918,7 +918,7 @@ title: "My Swedish Section"
-- layouts/index.html --
LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Language.LanguageCode }}|
{{ range $i, $e := (slice site .Site) }}
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultiLingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultilingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
{{ end }}
@@ -938,9 +938,9 @@ LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Lang
b.AssertFileContent("public/index.html", `
AllPages: 4|
Sections: true|
Param: enParamValue
Param: enParamValue
IsMultiLingual: true
Param: enParamValue
Param: enParamValue
IsMultilingual: true
LanguageCode: true|en-US|
`)
@@ -1061,7 +1061,7 @@ Home
).BuildE()
b.Assert(err, qt.IsNil)
b.AssertFileContent("public/index.html", `
b.AssertFileContent("public/index.html", `
Home
`)
@@ -1094,7 +1094,7 @@ HTML.
HTACCESS.
`
b := Test(t, files)
@@ -1110,7 +1110,7 @@ languageCode = "en-US"
-- layouts/index.html --
LanguageCode: {{ .Site.LanguageCode }}|{{ site.Language.LanguageCode }}|
`
b := Test(t, files)
@@ -1136,7 +1136,7 @@ suffixes = ["bar"]
-- layouts/index.html --
Home.
`
b := Test(t, files)
@@ -1163,8 +1163,8 @@ func TestConfigMiscPanics(t *testing.T) {
params:
-- layouts/index.html --
Foo: {{ site.Params.foo }}|
`
b := Test(t, files)
@@ -1187,8 +1187,8 @@ defaultContentLanguage = "en"
weight = 1
-- layouts/index.html --
Foo: {{ site.Params.foo }}|
`
b, err := NewIntegrationTestBuilder(
IntegrationTestConfig{
@@ -1214,8 +1214,8 @@ languageCode = "en"
languageName = "English"
weight = 1
`
b, err := NewIntegrationTestBuilder(
IntegrationTestConfig{
@@ -1240,7 +1240,7 @@ contentDir = "mycontent"
-- layouts/index.html --
Home.
`
b := Test(t, files)
@@ -1342,7 +1342,7 @@ disabled = true
-- layouts/index.html --
Home.
`
b := Test(t, files)
@@ -1437,7 +1437,7 @@ home = ["html"]
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "section"]
`
runVariant(t, files, nil)
@@ -1538,3 +1538,22 @@ List.
b.AssertLogContains("WARN DEPRECATED: Kind \"taxonomyterm\" used in disableKinds is deprecated, use \"taxonomy\" instead.\n")
b.AssertLogContains("WARN DEPRECATED: Kind \"taxonomyterm\" used in outputs configuration is deprecated, use \"taxonomy\" instead.\n")
}
func TestDisableKindsIssue12144(t *testing.T) {
files := `
-- hugo.toml --
disableKinds = ["page"]
defaultContentLanguage = "pt-br"
-- layouts/index.html --
Home.
-- content/custom/index.pt-br.md --
---
title: "P1 pt"
---
-- content/custom/index.en-us.md --
---
title: "P1 us"
---
`
Test(t, files)
}
+10 -5
View File
@@ -93,8 +93,8 @@ func (r *resourceSource) GetIdentity() identity.Identity {
return r.path
}
func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) {
f(r.GetIdentity())
func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) bool {
return f(r.GetIdentity())
}
func (r *resourceSource) Path() string {
@@ -142,14 +142,15 @@ func (n resourceSources) GetIdentity() identity.Identity {
return nil
}
func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) {
func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) bool {
for _, r := range n {
if r != nil {
if f(r.GetIdentity()) {
return
return true
}
}
}
return false
}
func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
@@ -180,7 +181,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
var rs *resourceSource
if pi.IsContent() {
// Create the page now as we need it at assemembly time.
// Create the page now as we need it at assembly time.
// The other resources are created if needed.
pageResource, pi, err := m.s.h.newPage(
&pageMeta{
@@ -192,6 +193,10 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
if err != nil {
return err
}
if pageResource == nil {
// Disabled page.
return nil
}
key = pi.Base()
rs = &resourceSource{r: pageResource}
+158 -87
View File
@@ -93,7 +93,8 @@ type pageMap struct {
// Used for simple page lookups by name, e.g. "mypage.md" or "mypage".
pageReverseIndex *contentTreeReverseIndex
cachePages *dynacache.Partition[string, page.Pages]
cachePages1 *dynacache.Partition[string, page.Pages]
cachePages2 *dynacache.Partition[string, page.Pages]
cacheResources *dynacache.Partition[string, resource.Resources]
cacheContentRendered *dynacache.Partition[string, *resources.StaleValue[contentSummary]]
cacheContentPlain *dynacache.Partition[string, *resources.StaleValue[contentPlainPlainWords]]
@@ -103,16 +104,16 @@ type pageMap struct {
}
// pageTrees holds pages and resources in a tree structure for all sites/languages.
// Eeach site gets its own tree set via the Shape method.
// Each site gets its own tree set via the Shape method.
type pageTrees struct {
// This tree contains all Pages.
// This include regular pages, sections, taxonimies and so on.
// This include regular pages, sections, taxonomies and so on.
// Note that all of these trees share the same key structure,
// so you can take a leaf Page key and do a prefix search
// with key + "/" to get all of its resources.
treePages *doctree.NodeShiftTree[contentNodeI]
// This tree contains Resoures bundled in pages.
// This tree contains Resources bundled in pages.
treeResources *doctree.NodeShiftTree[contentNodeI]
// All pages and resources.
@@ -125,39 +126,53 @@ type pageTrees struct {
resourceTrees doctree.MutableTrees
}
// collectIdentities collects all identities from in all trees matching the given key.
// This will at most match in one tree, but may give identies from multiple dimensions (e.g. language).
func (t *pageTrees) collectIdentities(p *paths.Path) []identity.Identity {
ids := t.collectIdentitiesFor(p.Base())
// collectAndMarkStaleIdentities collects all identities from in all trees matching the given key.
// We currently re-read all page/resources for all languages that share the same path,
// so we mark all entries as stale (which will trigger cache invalidation), then
// return the first.
func (t *pageTrees) collectAndMarkStaleIdentities(p *paths.Path) []identity.Identity {
key := p.Base()
var ids []identity.Identity
// We need only one identity sample per dimension.
nCount := 0
cb := func(n contentNodeI) bool {
if n == nil {
return false
}
n.MarkStale()
if nCount > 0 {
return true
}
nCount++
n.ForEeachIdentity(func(id identity.Identity) bool {
ids = append(ids, id)
return false
})
return false
}
tree := t.treePages
nCount = 0
tree.ForEeachInDimension(key, doctree.DimensionLanguage.Index(),
cb,
)
tree = t.treeResources
nCount = 0
tree.ForEeachInDimension(key, doctree.DimensionLanguage.Index(),
cb,
)
if p.Component() == files.ComponentFolderContent {
// It may also be a bundled content resource.
if n := t.treeResources.Get(p.ForBundleType(paths.PathTypeContentResource).Base()); n != nil {
n.ForEeachIdentity(func(id identity.Identity) bool {
ids = append(ids, id)
return false
})
}
}
return ids
}
key := p.ForBundleType(paths.PathTypeContentResource).Base()
tree = t.treeResources
nCount = 0
tree.ForEeachInDimension(key, doctree.DimensionLanguage.Index(),
cb,
)
func (t *pageTrees) collectIdentitiesFor(key string) []identity.Identity {
var ids []identity.Identity
if n := t.treePages.Get(key); n != nil {
n.ForEeachIdentity(func(id identity.Identity) bool {
ids = append(ids, id)
return false
})
}
if n := t.treeResources.Get(key); n != nil {
n.ForEeachIdentity(func(id identity.Identity) bool {
ids = append(ids, id)
return false
})
}
return ids
}
@@ -177,7 +192,7 @@ func (t *pageTrees) collectIdentitiesSurroundingIn(key string, maxSamples int, t
level := strings.Count(prefix, "/")
tree.WalkPrefixRaw(prefix, func(s string, n contentNodeI) bool {
if level != strings.Count(s, "/") {
return true
return false
}
n.ForEeachIdentity(func(id identity.Identity) bool {
ids = append(ids, id)
@@ -207,10 +222,22 @@ func (t pageTrees) Shape(d, v int) *pageTrees {
t.treePages = t.treePages.Shape(d, v)
t.treeResources = t.treeResources.Shape(d, v)
t.treeTaxonomyEntries = t.treeTaxonomyEntries.Shape(d, v)
t.createMutableTrees()
return &t
}
func (t *pageTrees) createMutableTrees() {
t.treePagesResources = doctree.WalkableTrees[contentNodeI]{
t.treePages,
t.treeResources,
}
t.resourceTrees = doctree.MutableTrees{
t.treeResources,
}
}
var (
_ resource.Identifier = pageMapQueryPagesInSection{}
_ resource.Identifier = pageMapQueryPagesBelowPath{}
@@ -298,15 +325,19 @@ func (m *pageMap) forEeachPageIncludingBundledPages(include predicate.P[*pageSta
}
func (m *pageMap) getOrCreatePagesFromCache(
cache *dynacache.Partition[string, page.Pages],
key string, create func(string) (page.Pages, error),
) (page.Pages, error) {
return m.cachePages.GetOrCreate(key, create)
if cache == nil {
cache = m.cachePages1
}
return cache.GetOrCreate(key, create)
}
func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages {
cacheKey := q.Key()
pages, err := m.getOrCreatePagesFromCache(cacheKey, func(string) (page.Pages, error) {
pages, err := m.getOrCreatePagesFromCache(nil, cacheKey, func(string) (page.Pages, error) {
prefix := paths.AddTrailingSlash(q.Path)
var (
@@ -322,31 +353,28 @@ func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages {
w := &doctree.NodeShiftTreeWalker[contentNodeI]{
Tree: m.treePages,
Prefix: prefix,
Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
if q.Recursive {
if p, ok := n.(*pageState); ok && include(p) {
pas = append(pas, p)
}
return false, nil
}
// We store both leafs and branches in the same tree, so for non-recursive walks,
// we need to walk until the end, but can skip
// any not belonging to child branches.
if otherBranch != "" && strings.HasPrefix(key, otherBranch) {
return false, nil
}
}
w.Handle = func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
if q.Recursive {
if p, ok := n.(*pageState); ok && include(p) {
pas = append(pas, p)
}
if n.isContentNodeBranch() {
otherBranch = key + "/"
}
return false, nil
},
}
if p, ok := n.(*pageState); ok && include(p) {
pas = append(pas, p)
}
if n.isContentNodeBranch() {
currentBranch := key + "/"
if otherBranch == "" || otherBranch != currentBranch {
w.SkipPrefix(currentBranch)
}
otherBranch = currentBranch
}
return false, nil
}
err := w.Walk(context.Background())
@@ -374,7 +402,7 @@ func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages {
func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages {
key := q.Key()
v, err := m.cachePages.GetOrCreate(key, func(string) (page.Pages, error) {
v, err := m.cachePages1.GetOrCreate(key, func(string) (page.Pages, error) {
var pas page.Pages
include := q.Include
if include == nil {
@@ -411,7 +439,7 @@ func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages {
func (m *pageMap) getTermsForPageInTaxonomy(path, taxonomy string) page.Pages {
prefix := paths.AddLeadingSlash(taxonomy)
v, err := m.cachePages.GetOrCreate(prefix+path, func(string) (page.Pages, error) {
v, err := m.cachePages1.GetOrCreate(prefix+path, func(string) (page.Pages, error) {
var pas page.Pages
err := m.treeTaxonomyEntries.WalkPrefix(
@@ -461,12 +489,17 @@ func (m *pageMap) forEachResourceInPage(
rw.Handle = func(resourceKey string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
if isBranch {
ownerKey, _ := m.treePages.LongestPrefixAll(resourceKey)
// A resourceKey always represents a filename with extension.
// A page key points to the logical path of a page, which when sourced from the filesystem
// may represent a directory (bundles) or a single content file (e.g. p1.md).
// So, to avoid any overlapping ambiguity, we start looking from the owning directory.
ownerKey, _ := m.treePages.LongestPrefixAll(path.Dir(resourceKey))
if ownerKey != keyPage {
// Stop walking downwards, someone else owns this resource.
rw.SkipPrefix(ownerKey + "/")
return false, nil
}
}
return handle(resourceKey, n, match)
}
@@ -516,7 +549,7 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
for _, r := range res2 {
var found bool
for _, r2 := range res {
if r2.Name() == r.Name() {
if r2.(resource.NameNormalizedProvider).NameNormalized() == r.(resource.NameNormalizedProvider).NameNormalized() {
found = true
break
}
@@ -605,12 +638,15 @@ func (n contentNodeIs) GetIdentity() identity.Identity {
return n[0].GetIdentity()
}
func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) {
func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) bool {
for _, nn := range n {
if nn != nil {
nn.ForEeachIdentity(f)
if nn.ForEeachIdentity(f) {
return true
}
}
}
return false
}
func (n contentNodeIs) resetBuildState() {
@@ -623,9 +659,7 @@ func (n contentNodeIs) resetBuildState() {
func (n contentNodeIs) MarkStale() {
for _, nn := range n {
if nn != nil {
nn.MarkStale()
}
resource.MarkStale(nn)
}
}
@@ -661,9 +695,15 @@ func (s *contentNodeShifter) Delete(n contentNodeI, dimension doctree.Dimension)
}
return wasDeleted, isEmpty
case *resourceSource:
if lidx != v.LangIndex() {
return false, false
}
resource.MarkStale(v)
return true, true
case *pageState:
if lidx != v.s.languagei {
return false, false
}
resource.MarkStale(v)
return true, true
default:
@@ -796,6 +836,7 @@ func (s *contentNodeShifter) Insert(old, new contentNodeI) contentNodeI {
if !ok {
panic(fmt.Sprintf("unknown type %T", new))
}
resource.MarkStale(vv[newp.s.languagei])
vv[newp.s.languagei] = new
return vv
case *resourceSource:
@@ -815,6 +856,7 @@ func (s *contentNodeShifter) Insert(old, new contentNodeI) contentNodeI {
if !ok {
panic(fmt.Sprintf("unknown type %T", new))
}
resource.MarkStale(vv[newp.LangIndex()])
vv[newp.LangIndex()] = newp
return vv
default:
@@ -829,7 +871,8 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) *
m = &pageMap{
pageTrees: pageTrees.Shape(0, i),
cachePages: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pags/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
cachePages1: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag1/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
cachePages2: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag2/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
cacheResources: dynacache.GetOrCreatePartition[string, resource.Resources](mcache, fmt.Sprintf("/ress/%d", i), dynacache.OptionsPartition{Weight: 60, ClearWhen: dynacache.ClearOnRebuild}),
cacheContentRendered: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentSummary]](mcache, fmt.Sprintf("/cont/ren/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
cacheContentPlain: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentPlainPlainWords]](mcache, fmt.Sprintf("/cont/pla/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
@@ -994,7 +1037,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
seen := map[string]bool{
change.IdentifierBase(): true,
}
// Print the top level dependenies.
// Print the top level dependencies.
identity.WalkIdentitiesDeep(change, func(level int, id identity.Identity) bool {
if level > 1 {
return true
@@ -1011,8 +1054,12 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
)
for _, id := range changes {
if staler, ok := id.(resource.Staler); ok {
h.Log.Trace(logg.StringFunc(func() string { return fmt.Sprintf("Marking stale: %s (%T)\n", id, id) }))
if staler, ok := id.(resource.Staler); ok && !staler.IsStale() {
var msgDetail string
if p, ok := id.(*pageState); ok && p.File() != nil {
msgDetail = fmt.Sprintf(" (%s)", p.File().Filename())
}
h.Log.Trace(logg.StringFunc(func() string { return fmt.Sprintf("Marking stale: %s (%T)%s\n", id, id, msgDetail) }))
staler.MarkStale()
}
}
@@ -1045,7 +1092,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
}
}
// Drain the the cache eviction stack.
// Drain the cache eviction stack.
evicted := h.Deps.MemCache.DrainEvictedIdentities()
if len(evicted) < 200 {
changes = append(changes, evicted...)
@@ -1151,7 +1198,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
// First check the top level dependency manager.
for _, id := range changes {
checkedCounter.Add(1)
if r := depsFinder.Contains(id, p.dependencyManager, 100); r > identity.FinderFoundOneOfManyRepetition {
if r := depsFinder.Contains(id, p.dependencyManager, 2); r > identity.FinderFoundOneOfManyRepetition {
for _, po := range p.pageOutputs {
resetPo(po, r)
}
@@ -1167,7 +1214,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
}
for _, id := range changes {
checkedCounter.Add(1)
if r := depsFinder.Contains(id, po.dependencyManagerOutput, 2); r > identity.FinderFoundOneOfManyRepetition {
if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition {
resetPo(po, r)
continue OUTPUTS
}
@@ -1393,8 +1440,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
if err := p.setMetaPost(cascade); err != nil {
return false, err
}
if err := sa.pageMap.treeTaxonomyEntries.WalkPrefix(
if !p.s.shouldBuild(p) {
sa.pageMap.treePages.Delete(s)
sa.pageMap.treeTaxonomyEntries.DeletePrefix(paths.AddTrailingSlash(s))
} else if err := sa.pageMap.treeTaxonomyEntries.WalkPrefix(
doctree.LockTypeRead,
paths.AddTrailingSlash(s),
func(ss string, wn *weightedContentNode) (bool, error) {
@@ -1462,13 +1511,6 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
return false, nil
}
// This is a little out of place, but is conveniently put here.
// Check if translationKey is set by user.
// This is to support the manual way of setting the translationKey in front matter.
if ps.m.pageConfig.TranslationKey != "" {
sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
}
if sa.pageMap.cfg.taxonomyTermDisabled {
return false, nil
}
@@ -1511,8 +1553,10 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
}
pages.InsertIntoValuesDimension(pi.Base(), n)
term = pages.Get(pi.Base())
} else if term.(*pageState).m.term != v {
term.(*pageState).m.term = v
} else {
m := term.(*pageState).m
m.term = v
m.singular = viewName.singular
}
if s == "" {
@@ -1547,6 +1591,13 @@ func (sa *sitePagesAssembler) assembleResources() error {
Handle: func(s string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
ps := n.(*pageState)
// This is a little out of place, but is conveniently put here.
// Check if translationKey is set by user.
// This is to support the manual way of setting the translationKey in front matter.
if ps.m.pageConfig.TranslationKey != "" {
sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
}
// Prepare resources for this page.
ps.shiftToOutputFormat(true, 0)
targetPaths := ps.targetPaths()
@@ -1592,7 +1643,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
TargetBasePaths: targetBasePaths,
BasePathRelPermalink: targetPaths.SubResourceBaseLink,
BasePathTargetPath: baseTarget,
Name: relPath,
NameNormalized: relPath,
NameOriginal: relPathOriginal,
LazyPublish: !ps.m.pageConfig.Build.PublishResources,
}
@@ -1638,6 +1689,11 @@ func (sa *sitePagesAssembler) assemblePagesStep2() error {
if err := sa.applyAggregatesToTaxonomiesAndTerms(); err != nil {
return err
}
return nil
}
func (sa *sitePagesAssembler) assemblePagesStepFinal() error {
if err := sa.assembleResources(); err != nil {
return err
}
@@ -1671,6 +1727,10 @@ func (sa *sitePagesAssembler) removeShouldNotBuild() error {
return err
}
if len(keys) == 0 {
return nil
}
sa.pageMap.DeletePageAndResourcesBelow(keys...)
return nil
@@ -1720,7 +1780,7 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
if s.conf.EnableRobotsTXT {
if m.i == 0 || s.Conf.IsMultihost() {
addStandalone("/robots", kinds.KindRobotsTXT, output.RobotsTxtFormat)
addStandalone("/_robots", kinds.KindRobotsTXT, output.RobotsTxtFormat)
}
}
@@ -1733,11 +1793,11 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
}
if sitemapEnabled {
addStandalone("/sitemap", kinds.KindSitemap, output.SitemapFormat)
skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultiLingual())
addStandalone("/_sitemap", kinds.KindSitemap, output.SitemapFormat)
skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultilingual())
if !skipSitemapIndex {
addStandalone("/sitemapindex", kinds.KindSitemapIndex, output.SitemapIndexFormat)
addStandalone("/_sitemapindex", kinds.KindSitemapIndex, output.SitemapIndexFormat)
}
}
@@ -1770,6 +1830,14 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
return false, nil
}
switch ps.Kind() {
case kinds.KindPage, kinds.KindSection:
// OK
default:
// Skip taxonomy nodes etc.
return false, nil
}
p := ps.m.pathInfo
section := p.Section()
if section == "" || seen[section] {
@@ -1880,6 +1948,9 @@ func (m *pageMap) CreateSiteTaxonomies(ctx context.Context) error {
switch p.Kind() {
case kinds.KindTerm:
if !p.m.shouldList(true) {
return false, nil
}
taxonomy := m.s.taxonomies[viewName.plural]
if taxonomy == nil {
return true, fmt.Errorf("missing taxonomy: %s", viewName.plural)
+33 -1
View File
@@ -160,7 +160,7 @@ Draft5: {{ if (.Site.GetPage "blog/draftsection/sub/page") }}FOUND{{ end }}|
Sections: /blog/|/docs/|/overlap/|/overlap2/|:END
Categories: /categories/funny/; Funny; 12|/categories/sad/; Sad; 2|:END
Category Terms: taxonomy: /categories/funny/; Funny; 12|/categories/sad/; Sad; 2|:END
Category Funny: term; Funny: /blog/subsection/page4/;|/blog/page3/;|/blog/subsection/;|/blog/page2/;|/blog/page1/;|/blog/subsection/page5/;|/docs/page6/;|/blog/bundle/;|/blog/draftsection/page/;|/blog/draftsection/sub/;|/blog/draftsection/sub/page/;|/overlap2/;|:END
Category Funny: term; funny: /blog/subsection/page4/;|/blog/page3/;|/blog/subsection/;|/blog/page2/;|/blog/page1/;|/blog/subsection/page5/;|/docs/page6/;|/blog/bundle/;|/blog/draftsection/page/;|/blog/draftsection/sub/;|/blog/draftsection/sub/page/;|/overlap2/;|:END
Pag Num Pages: 9
Pag Blog Num Pages: 4
Blog Num RegularPages: 4
@@ -326,3 +326,35 @@ R: {{ with $r }}{{ .Content }}{{ end }}|Len: {{ len $bundle.Resources }}|$
b.AssertFileContent("public/index.html", "R: Data 1.txt|", "Len: 1|")
}
}
func TestBundleResourcesNoPublishedIssue12198(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','sitemap','taxonomy','term']
-- content/s1/p1.md --
---
title: p1
---
-- content/s1/foo.txt --
foo.txt
-- content/s1/p1.txt --
p1.txt
-- content/s1/p1-foo.txt --
p1-foo.txt
-- layouts/_default/list.html --
{{.Title }}|
-- layouts/_default/single.html --
{{.Title }}|
`
b := Test(t, files)
b.Build()
b.AssertFileExists("public/s1/index.html", true)
b.AssertFileExists("public/s1/foo.txt", true)
b.AssertFileExists("public/s1/p1.txt", true) // failing test
b.AssertFileExists("public/s1/p1-foo.txt", true) // failing test
b.AssertFileExists("public/s1/p1/index.html", true)
}
+18
View File
@@ -64,3 +64,21 @@ v1: {{ site.Data.MyFolder.MyData.v1 }}|
b.AssertFileContent("public/index.html", "v1: my_v1|")
}
// Issue #12133
func TestDataNoAssets(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
-- assets/data/foo.toml --
content = "I am assets/data/foo.toml"
-- layouts/index.html --
|{{ site.Data.foo.content }}|
`
b := Test(t, files)
b.AssertFileContent("public/index.html", "||")
}
+1 -1
View File
@@ -169,7 +169,7 @@ ExpiryDate: 2099-07-13 15:28:01 +0000 UTC`
// have date literals.
// YAML
// Note: This is with go-yaml v2, I suspect v3 will fail with the unquouted values.
// Note: This is with go-yaml v2, I suspect v3 will fail with the unquoted values.
b.AssertFileContent("public/en/short-date-yaml-unqouted/index.html", expectShortDateEn)
b.AssertFileContent("public/nn/short-date-yaml-unqouted/index.html", expectShortDateNn)
b.AssertFileContent("public/en/short-date-yaml-qouted/index.html", expectShortDateEn)
+2 -2
View File
@@ -67,7 +67,7 @@ package main
## YouTube
{{< youtube PArFPgHrNZM >}}
{{< youtube 0RKpf3rK57I >}}
## Param
@@ -83,7 +83,7 @@ Content: {{ .Content }}|
https://gist.github.com/spf13/7896402.js
<span style="color:#a6e22e">main</span></span>
https://t.co/X94FmYDEZJ
https://www.youtube.com/embed/PArFPgHrNZM
https://www.youtube.com/embed/0RKpf3rK57I
Foo: bar
+26 -65
View File
@@ -15,8 +15,6 @@ package hugolib
import (
"testing"
qt "github.com/frankban/quicktest"
)
func TestInternalTemplatesImage(t *testing.T) {
@@ -62,78 +60,41 @@ title: My Site
b.Build(BuildCfg{})
b.AssertFileContent("public/mybundle/index.html", `
<meta name="twitter:image" content="https://example.org/mybundle/featured-sunset.jpg" />
<meta name="twitter:title" content="My Bundle"/>
<meta property="og:title" content="My Bundle" />
<meta property="og:url" content="https://example.org/mybundle/" />
<meta property="og:image" content="https://example.org/mybundle/featured-sunset.jpg" />
<meta property="article:published_time" content="2021-02-26T18:02:00-01:00" />
<meta property="article:modified_time" content="2021-05-22T19:25:00-01:00" />
<meta name="twitter:image" content="https://example.org/mybundle/featured-sunset.jpg">
<meta name="twitter:title" content="My Bundle">
<meta property="og:title" content="My Bundle">
<meta property="og:url" content="https://example.org/mybundle/">
<meta property="og:image" content="https://example.org/mybundle/featured-sunset.jpg">
<meta property="article:published_time" content="2021-02-26T18:02:00-01:00">
<meta property="article:modified_time" content="2021-05-22T19:25:00-01:00">
<meta itemprop="name" content="My Bundle">
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg" />
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00" />
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00" />
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg">
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00">
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00">
`)
b.AssertFileContent("public/mypage/index.html", `
<meta name="twitter:image" content="https://example.org/pageimg1.jpg" />
<meta property="og:image" content="https://example.org/pageimg1.jpg" />
<meta property="og:image" content="https://example.org/pageimg2.jpg" />
<meta property="og:image" content="https://example.local/logo.png" />
<meta property="og:image" content="https://example.org/mypage/sample.jpg" />
<meta property="article:published_time" content="2021-02-26T18:02:00+01:00" />
<meta property="article:modified_time" content="2021-05-22T19:25:00+01:00" />
<meta itemprop="image" content="https://example.org/pageimg1.jpg" />
<meta itemprop="image" content="https://example.org/pageimg2.jpg" />
<meta itemprop="image" content="https://example.local/logo.png" />
<meta itemprop="image" content="https://example.org/mypage/sample.jpg" />
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00" />
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00" />
<meta name="twitter:image" content="https://example.org/pageimg1.jpg">
<meta property="og:image" content="https://example.org/pageimg1.jpg">
<meta property="og:image" content="https://example.org/pageimg2.jpg">
<meta property="og:image" content="https://example.local/logo.png">
<meta property="og:image" content="https://example.org/mypage/sample.jpg">
<meta property="article:published_time" content="2021-02-26T18:02:00+01:00">
<meta property="article:modified_time" content="2021-05-22T19:25:00+01:00">
<meta itemprop="image" content="https://example.org/pageimg1.jpg">
<meta itemprop="image" content="https://example.org/pageimg2.jpg">
<meta itemprop="image" content="https://example.local/logo.png">
<meta itemprop="image" content="https://example.org/mypage/sample.jpg">
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00">
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00">
`)
b.AssertFileContent("public/mysite/index.html", `
<meta name="twitter:image" content="https://example.org/siteimg1.jpg" />
<meta property="og:image" content="https://example.org/siteimg1.jpg" />
<meta itemprop="image" content="https://example.org/siteimg1.jpg" />
<meta name="twitter:image" content="https://example.org/siteimg1.jpg">
<meta property="og:image" content="https://example.org/siteimg1.jpg">
<meta itemprop="image" content="https://example.org/siteimg1.jpg">
`)
}
// Just some simple test of the embedded templates to avoid
// https://github.com/gohugoio/hugo/issues/4757 and similar.
func TestEmbeddedTemplates(t *testing.T) {
t.Parallel()
c := qt.New(t)
c.Assert(true, qt.Equals, true)
home := []string{"index.html", `
GA:
{{ template "_internal/google_analytics.html" . }}
GA async:
{{ template "_internal/google_analytics_async.html" . }}
Disqus:
{{ template "_internal/disqus.html" . }}
`}
b := newTestSitesBuilder(t)
b.WithSimpleConfigFile().WithTemplatesAdded(home...)
b.Build(BuildCfg{})
// Gheck GA regular and async
b.AssertFileContent("public/index.html",
"'anonymizeIp', true",
"'script','https://www.google-analytics.com/analytics.js','ga');\n\tga('create', 'UA-ga_id', 'auto')",
"<script async src='https://www.google-analytics.com/analytics.js'>")
// Disqus
b.AssertFileContent("public/index.html", "\"disqus_shortname\" + '.disqus.com/embed.js';")
}
func TestEmbeddedPaginationTemplate(t *testing.T) {
t.Parallel()
+29 -15
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -265,6 +265,9 @@ type SourceFilesystem struct {
// This is a virtual composite filesystem. It expects path relative to a context.
Fs afero.Fs
// The source filesystem (usually the OS filesystem).
SourceFs afero.Fs
// When syncing a source folder to the target (e.g. /public), this may
// be set to publish into a subfolder. This is used for static syncing
// in multihost mode.
@@ -320,10 +323,10 @@ func (s SourceFilesystems) IsContent(filename string) bool {
}
// ResolvePaths resolves the given filename to a list of paths in the filesystems.
func (s *SourceFilesystems) ResolvePaths(filename string, checkExists bool) []hugofs.ComponentPath {
func (s *SourceFilesystems) ResolvePaths(filename string) []hugofs.ComponentPath {
var cpss []hugofs.ComponentPath
for _, rfs := range s.RootFss {
cps, err := rfs.ReverseLookup(filename, checkExists)
cps, err := rfs.ReverseLookup(filename)
if err != nil {
panic(err)
}
@@ -336,7 +339,7 @@ func (s *SourceFilesystems) ResolvePaths(filename string, checkExists bool) []hu
// It will return an empty string if the filename is not a member of a static filesystem.
func (s SourceFilesystems) MakeStaticPathRelative(filename string) string {
for _, staticFs := range s.Static {
rel, _ := staticFs.MakePathRelative(filename)
rel, _ := staticFs.MakePathRelative(filename, true)
if rel != "" {
return rel
}
@@ -345,8 +348,8 @@ func (s SourceFilesystems) MakeStaticPathRelative(filename string) string {
}
// MakePathRelative creates a relative path from the given filename.
func (d *SourceFilesystem) MakePathRelative(filename string) (string, bool) {
cps, err := d.ReverseLookup(filename)
func (d *SourceFilesystem) MakePathRelative(filename string, checkExists bool) (string, bool) {
cps, err := d.ReverseLookup(filename, checkExists)
if err != nil {
panic(err)
}
@@ -358,11 +361,21 @@ func (d *SourceFilesystem) MakePathRelative(filename string) (string, bool) {
}
// ReverseLookup returns the component paths for the given filename.
func (d *SourceFilesystem) ReverseLookup(filename string) ([]hugofs.ComponentPath, error) {
func (d *SourceFilesystem) ReverseLookup(filename string, checkExists bool) ([]hugofs.ComponentPath, error) {
var cps []hugofs.ComponentPath
hugofs.WalkFilesystems(d.Fs, func(fs afero.Fs) bool {
if rfs, ok := fs.(hugofs.ReverseLookupProvder); ok {
if c, err := rfs.ReverseLookup(filename, true); err == nil {
if c, err := rfs.ReverseLookupComponent(d.Name, filename); err == nil {
if checkExists {
n := 0
for _, cp := range c {
if _, err := d.Fs.Stat(filepath.FromSlash(cp.Path)); err == nil {
c[n] = cp
n++
}
}
c = c[:n]
}
cps = append(cps, c...)
}
}
@@ -379,11 +392,12 @@ func (d *SourceFilesystem) mounts() []hugofs.FileMetaInfo {
if err == nil {
m = append(m, mounts...)
}
}
return false
})
// Filter out any mounts not belonging to this filesystem.
// TODO(bep) I think this is superflous.
n := 0
for _, mm := range m {
if mm.Meta().Component == d.Name {
@@ -392,6 +406,7 @@ func (d *SourceFilesystem) mounts() []hugofs.FileMetaInfo {
}
}
m = m[:n]
return m
}
@@ -428,10 +443,8 @@ func (d *SourceFilesystem) RealDirs(from string) []string {
if !m.IsDir() {
continue
}
meta := m.Meta()
_, err := d.Fs.Stat(from)
if err == nil {
dirname := filepath.Join(meta.Filename, from)
dirname := filepath.Join(m.Meta().Filename, from)
if _, err := d.SourceFs.Stat(dirname); err == nil {
dirnames = append(dirnames, dirname)
}
}
@@ -519,8 +532,9 @@ func newSourceFilesystemsBuilder(p *paths.Paths, logger loggers.Logger, b *BaseF
func (b *sourceFilesystemsBuilder) newSourceFilesystem(name string, fs afero.Fs) *SourceFilesystem {
return &SourceFilesystem{
Name: name,
Fs: fs,
Name: name,
Fs: fs,
SourceFs: b.sourceFs,
}
}
+132 -7
View File
@@ -234,6 +234,7 @@ foo
b := hugolib.Test(t, files)
bfs := b.H.BaseFs
watchFilenames := bfs.WatchFilenames()
// []string{"/hugo_stats.json", "/content", "/content2", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"}
b.Assert(watchFilenames, qt.HasLen, 6)
}
@@ -314,7 +315,7 @@ func TestStaticFs(t *testing.T) {
checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
}
func TestStaticFsMultiHost(t *testing.T) {
func TestStaticFsMultihost(t *testing.T) {
c := qt.New(t)
v := config.New()
workDir := "mywork"
@@ -380,11 +381,11 @@ Main.
`
b := hugolib.Test(t, files)
rel, found := b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/themes/t1/src/main.js"))
rel, found := b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/themes/t1/src/main.js"), true)
b.Assert(found, qt.Equals, true)
b.Assert(rel, qt.Equals, filepath.FromSlash("foo/bar/main.js"))
rel, found = b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/bar.txt"))
rel, found = b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/bar.txt"), true)
b.Assert(found, qt.Equals, true)
b.Assert(rel, qt.Equals, filepath.FromSlash("foo/baz.txt"))
}
@@ -460,7 +461,7 @@ Home.
b.AssertFileContent("public/index.html", "Home.")
stat := func(path string) hugofs.FileMetaInfo {
ps, err := b.H.BaseFs.Content.ReverseLookup(filepath.FromSlash(path))
ps, err := b.H.BaseFs.Content.ReverseLookup(filepath.FromSlash(path), true)
b.Assert(err, qt.IsNil)
b.Assert(ps, qt.HasLen, 1)
first := ps[0]
@@ -478,11 +479,135 @@ Home.
_ = stat("blog/b1.md")
}
func TestReverseLookupShouldOnlyConsiderFilesInCurrentComponent(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com/"
[module]
[[module.mounts]]
source = "files/layouts"
target = "layouts"
[[module.mounts]]
source = "files/layouts/assets"
target = "assets"
-- files/layouts/l1.txt --
l1
-- files/layouts/assets/l2.txt --
l2
`
b := hugolib.Test(t, files)
assetsFs := b.H.Assets
for _, checkExists := range []bool{false, true} {
cps, err := assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/assets/l2.txt"), checkExists)
b.Assert(err, qt.IsNil)
b.Assert(cps, qt.HasLen, 1)
cps, err = assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/l2.txt"), checkExists)
b.Assert(err, qt.IsNil)
b.Assert(cps, qt.HasLen, 0)
}
}
func TestAssetsIssue12175(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com/"
[module]
[[module.mounts]]
source = "node_modules/@foo/core/assets"
target = "assets"
[[module.mounts]]
source = "assets"
target = "assets"
-- node_modules/@foo/core/assets/js/app.js --
JS.
-- node_modules/@foo/core/assets/scss/app.scss --
body { color: red; }
-- assets/scss/app.scss --
body { color: blue; }
-- layouts/index.html --
Home.
SCSS: {{ with resources.Get "scss/app.scss" }}{{ .RelPermalink }}|{{ .Content }}{{ end }}|
# Note that the pattern below will match 2 resources, which doesn't make much sense,
# but is how the current (and also < v0.123.0) merge logic works, and for most practical purposes, it doesn't matter.
SCSS Match: {{ with resources.Match "**.scss" }}{{ . | len }}|{{ range .}}{{ .RelPermalink }}|{{ end }}{{ end }}|
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html", `
SCSS: /scss/app.scss|body { color: blue; }|
SCSS Match: 2|
`)
}
func TestStaticComposite(t *testing.T) {
files := `
-- hugo.toml --
disableKinds = ["taxonomy", "term"]
[module]
[[module.mounts]]
source = "myfiles/f1.txt"
target = "static/files/f1.txt"
[[module.mounts]]
source = "f3.txt"
target = "static/f3.txt"
[[module.mounts]]
source = "static"
target = "static"
-- static/files/f2.txt --
f2
-- myfiles/f1.txt --
f1
-- f3.txt --
f3
-- layouts/home.html --
Home.
`
b := hugolib.Test(t, files)
b.AssertFs(b.H.BaseFs.StaticFs(""), `
. true
f3.txt false
files true
files/f1.txt false
files/f2.txt false
`)
}
func TestMountIssue12141(t *testing.T) {
files := `
-- hugo.toml --
disableKinds = ["taxonomy", "term"]
[module]
[[module.mounts]]
source = "myfiles"
target = "static"
[[module.mounts]]
source = "myfiles/f1.txt"
target = "static/f2.txt"
-- myfiles/f1.txt --
f1
`
b := hugolib.Test(t, files)
fs := b.H.BaseFs.StaticFs("")
b.AssertFs(fs, `
. true
f1.txt false
f2.txt false
`)
}
func checkFileCount(fs afero.Fs, dirname string, c *qt.C, expected int) {
c.Helper()
count, _, err := countFilesAndGetFilenames(fs, dirname)
c.Assert(err, qt.IsNil)
c.Assert(count, qt.Equals, expected)
count, names, err := countFilesAndGetFilenames(fs, dirname)
namesComment := qt.Commentf("filenames: %v", names)
c.Assert(err, qt.IsNil, namesComment)
c.Assert(count, qt.Equals, expected, namesComment)
}
func checkFileContent(fs afero.Fs, filename string, c *qt.C, expected ...string) {
+5 -1
View File
@@ -269,7 +269,7 @@ func (h *HugoSites) pickOneAndLogTheRest(errors []error) error {
return errors[i]
}
func (h *HugoSites) isMultiLingual() bool {
func (h *HugoSites) isMultilingual() bool {
return len(h.Sites) > 1
}
@@ -410,6 +410,10 @@ type BuildCfg struct {
// shouldRender returns whether this output format should be rendered or not.
func (cfg *BuildCfg) shouldRender(p *pageState) bool {
if p.skipRender() {
return false
}
if !p.renderOnce {
return true
}
+52 -42
View File
@@ -28,16 +28,16 @@ import (
"github.com/bep/logg"
"github.com/gohugoio/hugo/cache/dynacache"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/hugolib/segments"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/publisher"
"github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/para"
@@ -236,7 +236,7 @@ func (h *HugoSites) process(ctx context.Context, l logg.LevelLogger, config *Bui
return h.processFull(ctx, l, *config)
}
// assemble creates missing sections, applies agregate values (e.g. dates, cascading params),
// assemble creates missing sections, applies aggregate values (e.g. dates, cascading params),
// removes disabled pages etc.
func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *BuildCfg) error {
l = l.WithField("step", "assemble")
@@ -282,11 +282,6 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
return err
}
}
h.renderFormats = output.Formats{}
for _, s := range h.Sites {
s.s.initRenderFormats()
h.renderFormats = append(h.renderFormats, s.renderFormats...)
}
for _, s := range assemblers {
if err := s.assemblePagesStep2(); err != nil {
@@ -296,9 +291,16 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
h.renderFormats = output.Formats{}
for _, s := range h.Sites {
s.s.initRenderFormats()
h.renderFormats = append(h.renderFormats, s.renderFormats...)
}
for _, s := range assemblers {
if err := s.assemblePagesStepFinal(); err != nil {
return err
}
}
return nil
}
@@ -318,9 +320,20 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
i := 0
for _, s := range h.Sites {
segmentFilter := s.conf.C.SegmentFilter
if segmentFilter.ShouldExcludeCoarse(segments.SegmentMatcherFields{Lang: s.language.Lang}) {
l.Logf("skip language %q not matching segments set in --renderSegments", s.language.Lang)
continue
}
siteRenderContext.languageIdx = s.languagei
h.currentSite = s
for siteOutIdx, renderFormat := range s.renderFormats {
if segmentFilter.ShouldExcludeCoarse(segments.SegmentMatcherFields{Output: renderFormat.Name, Lang: s.language.Lang}) {
l.Logf("skip output format %q for language %q not matching segments set in --renderSegments", renderFormat.Name, s.language.Lang)
continue
}
siteRenderContext.outIdx = siteOutIdx
siteRenderContext.sitesOutIdx = i
i++
@@ -595,8 +608,10 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
return sb.String()
}))
// For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
events = h.fileEventsFilter(events)
events = h.fileEventsTranslate(events)
eventInfos := h.fileEventsApplyInfo(events)
logger := h.Log
@@ -631,36 +646,12 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
addedContentPaths []*paths.Path
)
for _, ev := range events {
removed := false
added := false
if ev.Op&fsnotify.Remove == fsnotify.Remove {
removed = true
}
fi, statErr := h.Fs.Source.Stat(ev.Name)
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
// Sometimes a rename operation means that file has been renamed other times it means
// it's been updated.
if ev.Op.Has(fsnotify.Rename) {
// If the file is still on disk, it's only been updated, if it's not, it's been moved
if statErr != nil {
removed = true
}
}
if ev.Op.Has(fsnotify.Create) {
added = true
}
isChangedDir := statErr == nil && fi.IsDir()
cpss := h.BaseFs.ResolvePaths(ev.Name, !removed)
for _, ev := range eventInfos {
cpss := h.BaseFs.ResolvePaths(ev.Name)
pss := make([]*paths.Path, len(cpss))
for i, cps := range cpss {
p := cps.Path
if removed && !paths.HasExt(p) {
if ev.removed && !paths.HasExt(p) {
// Assume this is a renamed/removed directory.
// For deletes, we walk up the tree to find the container (e.g. branch bundle),
// so we will catch this even if it is a file without extension.
@@ -671,7 +662,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
}
pss[i] = h.Configs.ContentPathParser.Parse(cps.Component, p)
if added && !isChangedDir && cps.Component == files.ComponentFolderContent {
if ev.added && !ev.isChangedDir && cps.Component == files.ComponentFolderContent {
addedContentPaths = append(addedContentPaths, pss[i])
}
@@ -683,9 +674,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
}
}
if removed {
if ev.removed {
changedPaths.deleted = append(changedPaths.deleted, pss...)
} else if isChangedDir {
} else if ev.isChangedDir {
changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
} else {
changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)
@@ -702,7 +693,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
switch pathInfo.Component() {
case files.ComponentFolderContent:
logger.Println("Source changed", pathInfo.Path())
if ids := h.pageTrees.collectIdentities(pathInfo); len(ids) > 0 {
if ids := h.pageTrees.collectAndMarkStaleIdentities(pathInfo); len(ids) > 0 {
changes = append(changes, ids...)
}
@@ -739,8 +730,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
case files.ComponentFolderLayouts:
tmplChanged = true
h.init.layouts.Reset()
templatePath := pathInfo.TrimLeadingSlash().PathNoLang()
templatePath := pathInfo.Unnormalized().TrimLeadingSlash().PathNoLang()
if !h.Tmpl().HasTemplate(templatePath) {
tmplAdded = true
}
@@ -790,7 +780,11 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
case files.ComponentFolderI18n:
logger.Println("i18n changed", pathInfo.Path())
i18nChanged = true
changes = append(changes, pathInfo)
// It's hard to determine the exact change set of this,
// so be very coarse grained for now.
changes = append(changes, identity.GenghisKhan)
case files.ComponentFolderArchetypes:
// Ignore for now.
default:
panic(fmt.Sprintf("unknown component: %q", pathInfo.Component()))
}
@@ -897,6 +891,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
}
if tmplChanged || i18nChanged {
// TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048.
h.init.layouts.Reset()
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
// TODO(bep) this could probably be optimized to somehow
// only load the changed templates and its dependencies, but that is non-trivial.
@@ -922,9 +919,22 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
}
}
h.logServerAddresses()
return nil
}
func (h *HugoSites) logServerAddresses() {
if h.hugoInfo.IsMultihost() {
for _, s := range h.Sites {
h.Log.Printf("Web Server is available at %s (bind address %s) %s\n", s.conf.C.BaseURL, s.conf.C.ServerInterface, s.Language().Lang)
}
} else {
s := h.Sites[0]
h.Log.Printf("Web Server is available at %s (bind address %s)\n", s.conf.C.BaseURL, s.conf.C.ServerInterface)
}
}
func (h *HugoSites) processFull(ctx context.Context, l logg.LevelLogger, config BuildCfg) (err error) {
if err = h.processFiles(ctx, l, config); err != nil {
err = fmt.Errorf("readAndProcessContent: %w", err)
+48 -2
View File
@@ -66,12 +66,12 @@ robots|{{ site.Language.Lang }}
404|{{ site.Language.Lang }}
`
b := Test(t, files)
b.Assert(b.H.Conf.IsMultiLingual(), qt.Equals, true)
b.Assert(b.H.Conf.IsMultilingual(), qt.Equals, true)
b.Assert(b.H.Conf.IsMultihost(), qt.Equals, true)
// helpers.PrintFs(b.H.Fs.PublishDir, "", os.Stdout)
@@ -162,6 +162,52 @@ title: "Mybundle fr"
b.AssertFileContent("public/fr/section/mybundle/styles.min.css", ".body{color:french}")
}
func TestResourcePerLanguageIssue12163(t *testing.T) {
files := `
-- hugo.toml --
defaultContentLanguage = 'de'
disableKinds = ['rss','sitemap','taxonomy','term']
[languages.de]
baseURL = 'https://de.example.org/'
contentDir = 'content/de'
weight = 1
[languages.en]
baseURL = 'https://en.example.org/'
contentDir = 'content/en'
weight = 2
-- content/de/mybundle/index.md --
---
title: mybundle-de
---
-- content/de/mybundle/pixel.png --
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
-- content/en/mybundle/index.md --
---
title: mybundle-en
---
-- layouts/_default/single.html --
{{ with .Resources.Get "pixel.png" }}
{{ with .Resize "2x2" }}
{{ .RelPermalink }}|
{{ end }}
{{ end }}
`
b := Test(t, files)
b.AssertFileExists("public/de/mybundle/index.html", true)
b.AssertFileExists("public/en/mybundle/index.html", true)
b.AssertFileExists("public/de/mybundle/pixel.png", true)
b.AssertFileExists("public/en/mybundle/pixel.png", true)
b.AssertFileExists("public/de/mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_2x2_resize_box_3.png", true)
// failing test below
b.AssertFileExists("public/en/mybundle/pixel_hu8aa3346827e49d756ff4e630147c42b5_70_2x2_resize_box_3.png", true)
}
func TestMultihostResourceOneBaseURLWithSuPath(t *testing.T) {
files := `
-- hugo.toml --
+54 -16
View File
@@ -169,10 +169,11 @@ type IntegrationTestBuilder struct {
renamedFiles []string
renamedDirs []string
buildCount int
GCCount int
counters *buildCounters
logBuff lockingBuffer
buildCount int
GCCount int
counters *buildCounters
logBuff lockingBuffer
lastBuildLog string
builderInit sync.Once
}
@@ -182,6 +183,13 @@ type lockingBuffer struct {
bytes.Buffer
}
func (b *lockingBuffer) ReadFrom(r io.Reader) (n int64, err error) {
b.Lock()
n, err = b.Buffer.ReadFrom(r)
b.Unlock()
return
}
func (b *lockingBuffer) Write(p []byte) (n int, err error) {
b.Lock()
n, err = b.Buffer.Write(p)
@@ -192,21 +200,21 @@ func (b *lockingBuffer) Write(p []byte) (n int, err error) {
func (s *IntegrationTestBuilder) AssertLogContains(els ...string) {
s.Helper()
for _, el := range els {
s.Assert(s.logBuff.String(), qt.Contains, el)
s.Assert(s.lastBuildLog, qt.Contains, el)
}
}
func (s *IntegrationTestBuilder) AssertLogNotContains(els ...string) {
s.Helper()
for _, el := range els {
s.Assert(s.logBuff.String(), qt.Not(qt.Contains), el)
s.Assert(s.lastBuildLog, qt.Not(qt.Contains), el)
}
}
func (s *IntegrationTestBuilder) AssertLogMatches(expression string) {
s.Helper()
re := regexp.MustCompile(expression)
s.Assert(re.MatchString(s.logBuff.String()), qt.IsTrue, qt.Commentf(s.logBuff.String()))
s.Assert(re.MatchString(s.lastBuildLog), qt.IsTrue, qt.Commentf(s.lastBuildLog))
}
func (s *IntegrationTestBuilder) AssertBuildCountData(count int) {
@@ -275,9 +283,13 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
}
func (s *IntegrationTestBuilder) AssertPublishDir(matches ...string) {
s.AssertFs(s.fs.PublishDir, matches...)
}
func (s *IntegrationTestBuilder) AssertFs(fs afero.Fs, matches ...string) {
s.Helper()
var buff bytes.Buffer
helpers.PrintFs(s.H.Fs.PublishDir, "", &buff)
s.Assert(s.printAndCheckFs(fs, "", &buff), qt.IsNil)
printFsLines := strings.Split(buff.String(), "\n")
sort.Strings(printFsLines)
content := strings.TrimSpace((strings.Join(printFsLines, "\n")))
@@ -300,6 +312,34 @@ func (s *IntegrationTestBuilder) AssertPublishDir(matches ...string) {
}
}
func (s *IntegrationTestBuilder) printAndCheckFs(fs afero.Fs, path string, w io.Writer) error {
if fs == nil {
return nil
}
return afero.Walk(fs, path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error: path %q: %s", path, err)
}
path = filepath.ToSlash(path)
if path == "" {
path = "."
}
if !info.IsDir() {
f, err := fs.Open(path)
if err != nil {
return fmt.Errorf("error: path %q: %s", path, err)
}
defer f.Close()
// This will panic if the file is a directory.
var buf [1]byte
io.ReadFull(f, buf[:])
}
fmt.Fprintln(w, path, info.IsDir())
return nil
})
}
func (s *IntegrationTestBuilder) AssertFileExists(filename string, b bool) {
checker := qt.IsNil
if !b {
@@ -337,7 +377,7 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
s.Helper()
_, err := s.BuildE()
if s.Cfg.Verbose || err != nil {
fmt.Println(s.logBuff.String())
fmt.Println(s.lastBuildLog)
if s.H != nil && err == nil {
for _, s := range s.H.Sites {
m := s.pageMap
@@ -348,7 +388,7 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
}
}
} else if s.Cfg.LogLevel <= logg.LevelDebug {
fmt.Println(s.logBuff.String())
fmt.Println(s.lastBuildLog)
}
s.Assert(err, qt.IsNil)
if s.Cfg.RunGC {
@@ -360,7 +400,7 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
}
func (s *IntegrationTestBuilder) LogString() string {
return s.logBuff.String()
return s.lastBuildLog
}
func (s *IntegrationTestBuilder) BuildE() (*IntegrationTestBuilder, error) {
@@ -377,6 +417,7 @@ func (s *IntegrationTestBuilder) Init() *IntegrationTestBuilder {
if err := s.initBuilder(); err != nil {
s.Fatalf("Failed to init builder: %s", err)
}
s.lastBuildLog = s.logBuff.String()
return s
}
@@ -622,10 +663,11 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error {
s.Helper()
defer func() {
s.reset()
s.lastBuildLog = s.logBuff.String()
s.logBuff.Reset()
}()
changeEvents := s.changeEvents()
s.logBuff.Reset()
s.counters = &buildCounters{}
cfg.testCounters = s.counters
@@ -639,10 +681,6 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error {
if err != nil {
return err
}
logErrorCount := s.H.NumLogErrors()
if logErrorCount > 0 {
return fmt.Errorf("logged %d error(s): %s", logErrorCount, s.logBuff.String())
}
return nil
}
+40
View File
@@ -636,3 +636,43 @@ Menu Item: {{ $i }}|{{ .URL }}|
Menu Item: 0|/foo/posts|
`)
}
func TestSectionPagesMenuMultilingualWarningIssue12306(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['section','rss','sitemap','taxonomy','term']
defaultContentLanguageInSubdir = true
sectionPagesMenu = "main"
[languages.en]
[languages.fr]
-- layouts/_default/home.html --
{{- range site.Menus.main -}}
<a href="{{ .URL }}">{{ .Name }}</a>
{{- end -}}
-- layouts/_default/single.html --
{{ .Title }}
-- content/p1.en.md --
---
title: p1
menu: main
---
-- content/p1.fr.md --
---
title: p1
menu: main
---
-- content/p2.en.md --
---
title: p2
menu: main
---
`
b := Test(t, files, TestOptWarn())
b.AssertFileContent("public/en/index.html", `<a href="/en/p1/">p1</a><a href="/en/p2/">p2</a>`)
b.AssertFileContent("public/fr/index.html", `<a href="/fr/p1/">p1</a>`)
b.AssertLogNotContains("WARN")
}
+27 -4
View File
@@ -22,6 +22,7 @@ import (
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib/doctree"
"github.com/gohugoio/hugo/hugolib/segments"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/output"
@@ -36,6 +37,7 @@ import (
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/source"
@@ -120,8 +122,8 @@ func (p *pageState) GetIdentity() identity.Identity {
return p
}
func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) {
f(p)
func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) bool {
return f(p)
}
func (p *pageState) GetDependencyManager() identity.Manager {
@@ -151,6 +153,19 @@ func (p *pageState) reusePageOutputContent() bool {
return p.pageOutputTemplateVariationsState.Load() == 1
}
func (p *pageState) skipRender() bool {
b := p.s.conf.C.SegmentFilter.ShouldExcludeFine(
segments.SegmentMatcherFields{
Path: p.Path(),
Kind: p.Kind(),
Lang: p.Lang(),
Output: p.pageOutput.f.Name,
},
)
return b
}
func (po *pageState) isRenderedAny() bool {
for _, o := range po.pageOutputs {
if o.isRendered() {
@@ -365,7 +380,9 @@ func (p *pageState) TranslationKey() string {
// AllTranslations returns all translations, including the current Page.
func (p *pageState) AllTranslations() page.Pages {
key := p.Path() + "/" + "translations-all"
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
// This is called from Translations, so we need to use a different partition, cachePages2,
// to avoid potential deadlocks.
pages, err := p.s.pageMap.getOrCreatePagesFromCache(p.s.pageMap.cachePages2, key, func(string) (page.Pages, error) {
if p.m.pageConfig.TranslationKey != "" {
// translationKey set by user.
pas, _ := p.s.h.translationKeyPages.Get(p.m.pageConfig.TranslationKey)
@@ -398,7 +415,7 @@ func (p *pageState) AllTranslations() page.Pages {
// Translations returns the translations excluding the current Page.
func (p *pageState) Translations() page.Pages {
key := p.Path() + "/" + "translations"
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
pages, err := p.s.pageMap.getOrCreatePagesFromCache(nil, key, func(string) (page.Pages, error) {
var pas page.Pages
for _, pp := range p.AllTranslations() {
if !pp.Eq(p) {
@@ -731,3 +748,9 @@ func (p pageWithWeight0) Weight0() int {
func (p pageWithWeight0) page() page.Page {
return p.pageState
}
var _ types.Unwrapper = (*pageWithWeight0)(nil)
func (p pageWithWeight0) Unwrapv() any {
return p.pageState
}
+1 -1
View File
@@ -770,7 +770,7 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
result.readingTime = (result.wordCount + 212) / 213
}
if rendered.summary != "" {
if c.pi.hasSummaryDivider || rendered.summary != "" {
result.summary = rendered.summary
result.summaryTruncated = rendered.summaryTruncated
} else if cp.po.p.m.pageConfig.Summary != "" {
+1 -1
View File
@@ -44,7 +44,7 @@ func (p *pageData) Data() any {
p.data[name.singular] = term
p.data["Singular"] = name.singular
p.data["Plural"] = name.plural
p.data["Term"] = p.Title()
p.data["Term"] = p.m.term
case kinds.KindTaxonomy:
viewCfg := p.s.pageMap.cfg.getTaxonomyConfig(p.Path())
p.data["Singular"] = viewCfg.singular
+23 -12
View File
@@ -92,7 +92,7 @@ type pageMetaParams struct {
// From page front matter.
type pageMetaFrontMatter struct {
configuredOutputFormats output.Formats // outputs defiend in front matter.
configuredOutputFormats output.Formats // outputs defined in front matter.
}
func (m *pageMetaParams) init(preserveOringal bool) {
@@ -106,9 +106,9 @@ func (p *pageMeta) Aliases() []string {
return p.pageConfig.Aliases
}
// Deprecated: use taxonomies.
// Deprecated: Use taxonomies instead.
func (p *pageMeta) Author() page.Author {
hugo.Deprecate(".Author", "Use taxonomies.", "v0.98.0")
hugo.Deprecate(".Page.Author", "Use taxonomies instead.", "v0.98.0")
authors := p.Authors()
for _, author := range authors {
@@ -117,9 +117,9 @@ func (p *pageMeta) Author() page.Author {
return page.Author{}
}
// Deprecated: use taxonomies.
// Deprecated: Use taxonomies instead.
func (p *pageMeta) Authors() page.AuthorList {
hugo.Deprecate(".Author", "Use taxonomies.", "v0.112.0")
hugo.Deprecate(".Page.Authors", "Use taxonomies instead.", "v0.112.0")
return nil
}
@@ -333,7 +333,7 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
}
// Apply cascades first so they can be overriden later.
// Apply cascades first so they can be overridden later.
if cascade != nil {
if ps.m.pageConfig.Cascade != nil {
for k, v := range cascade {
@@ -446,7 +446,7 @@ title: "My Title"
params:
build: "My Build"
---
´
´
`
}
@@ -523,7 +523,7 @@ params:
// pages.
isHeadless := cast.ToBool(v)
params[loki] = isHeadless
if p.File().TranslationBaseName() == "index" && isHeadless {
if isHeadless {
pm.pageConfig.Build.List = pagemeta.Never
pm.pageConfig.Build.Render = pagemeta.Never
}
@@ -676,7 +676,7 @@ params:
}
// shouldList returns whether this page should be included in the list of pages.
// glogal indicates site.Pages etc.
// global indicates site.Pages etc.
func (p *pageMeta) shouldList(global bool) bool {
if p.isStandalone() {
// Never list 404, sitemap and similar.
@@ -746,15 +746,26 @@ func (p *pageMeta) applyDefaultValues() error {
if p.s.conf.PluralizeListTitles {
sectionName = flect.Pluralize(sectionName)
}
p.pageConfig.Title = p.s.conf.C.CreateTitle(sectionName)
if p.s.conf.CapitalizeListTitles {
sectionName = p.s.conf.C.CreateTitle(sectionName)
}
p.pageConfig.Title = sectionName
case kinds.KindTerm:
if p.term != "" {
p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
if p.s.conf.CapitalizeListTitles {
p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
} else {
p.pageConfig.Title = p.term
}
} else {
panic("term not set")
}
case kinds.KindTaxonomy:
p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
if p.s.conf.CapitalizeListTitles {
p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
} else {
p.pageConfig.Title = strings.Replace(p.pathInfo.Unnormalized().BaseNameNoIdentifier(), "-", " ", -1)
}
case kinds.KindStatus404:
p.pageConfig.Title = "404 Page not found"
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
"strings"
"testing"
)
// Issue 9793
// Issue 12115
func TestListTitles(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','sitemap']
capitalizeListTitles = true
pluralizeListTitles = true
[taxonomies]
tag = 'tags'
-- content/section-1/page-1.md --
---
title: page-1
tags: 'tag-a'
---
-- layouts/_default/list.html --
{{ .Title }}
-- layouts/_default/single.html --
{{ .Title }}
`
b := Test(t, files)
b.AssertFileContent("public/section-1/index.html", "Section-1s")
b.AssertFileContent("public/tags/index.html", "Tags")
b.AssertFileContent("public/tags/tag-a/index.html", "Tag-A")
files = strings.Replace(files, "true", "false", -1)
b = Test(t, files)
b.AssertFileContent("public/section-1/index.html", "section-1")
b.AssertFileContent("public/tags/index.html", "tags")
b.AssertFileContent("public/tags/tag-a/index.html", "tag-a")
}
func TestDraftNonDefaultContentLanguage(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
defaultContentLanguage = "en"
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
-- content/p1.md --
-- content/p2.nn.md --
---
title: "p2"
draft: true
---
-- layouts/_default/single.html --
{{ .Title }}
`
b := Test(t, files)
b.AssertFileExists("public/nn/p2/index.html", false)
}
+3 -8
View File
@@ -20,7 +20,6 @@ import (
"sync/atomic"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/common/maps"
@@ -146,9 +145,11 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
// Either a taxonomy or a term.
if tc.pluralTreeKey == m.Path() {
m.pageConfig.Kind = kinds.KindTaxonomy
m.singular = tc.singular
} else {
m.pageConfig.Kind = kinds.KindTerm
m.term = m.pathInfo.Unnormalized().BaseNameNoIdentifier()
m.singular = tc.singular
}
}
} else if m.f != nil {
@@ -160,12 +161,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
return nil, nil
}
var dependencyManager identity.Manager = identity.NopManager
if m.s.conf.Internal.Watch {
dependencyManager = identity.NewManager(m.Path())
}
// Parse the rest of the page content.
m.content, err = m.newCachedContent(h, pi)
if err != nil {
@@ -178,7 +173,7 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
pageOutputTemplateVariationsState: &atomic.Uint32{},
resourcesPublishInit: &sync.Once{},
Staler: m,
dependencyManager: dependencyManager,
dependencyManager: m.s.Conf.NewIdentityManager(m.Path()),
pageCommon: &pageCommon{
FileProvider: m,
AuthorProvider: m,
+1 -6
View File
@@ -51,11 +51,6 @@ func newPageOutput(
})
}
var dependencyManager identity.Manager = identity.NopManager
if ps.s.conf.Internal.Watch {
dependencyManager = identity.NewManager(ps.Path() + "/" + f.Name)
}
providers := struct {
page.PaginatorProvider
resource.ResourceLinksProvider
@@ -75,7 +70,7 @@ func newPageOutput(
TableOfContentsProvider: page.NopPage,
render: render,
paginator: pag,
dependencyManagerOutput: dependencyManager,
dependencyManagerOutput: ps.s.Conf.NewIdentityManager((ps.Path() + "/" + f.Name)),
}
return po
+1 -1
View File
@@ -150,7 +150,7 @@ func createTargetPathDescriptor(p *pageState) (page.TargetPathDescriptor, error)
opath, _ = url.QueryUnescape(opath)
if strings.HasSuffix(opath, "//") {
// When rewriting the _index of the section the permalink config is applied to,
// we get douple slashes at the end sometimes; clear them up here
// we get double slashes at the end sometimes; clear them up here
opath = strings.TrimSuffix(opath, "/")
}
+9 -4
View File
@@ -124,11 +124,16 @@ func (pt pageTree) Parent() page.Page {
return pt.p.s.home
}
_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, nil)
if n != nil {
return n.(page.Page)
for {
_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, nil)
if n == nil {
return pt.p.s.home
}
if pt.p.m.bundled || n.isContentNodeBranch() {
return n.(page.Page)
}
dir = paths.Dir(dir)
}
return nil
}
func (pt pageTree) Ancestors() page.Pages {
+73 -3
View File
@@ -63,6 +63,15 @@ Summary Next Line
<!--more-->
Some more text
`
simplePageWithBlankSummary = `---
title: SimpleWithBlankSummary
---
<!--more-->
Some text.
`
simplePageWithSummaryParameter = `---
@@ -351,6 +360,9 @@ func normalizeExpected(ext, str string) string {
return expected
case "rst":
if str == "" {
return "<div class=\"document\"></div>"
}
return fmt.Sprintf("<div class=\"document\">\n\n\n%s</div>", str)
}
}
@@ -578,7 +590,7 @@ date: 2012-01-12
b.Assert(s.getPageOldVersion("/with-index-no-date").Date().IsZero(), qt.Equals, true)
checkDate(s.getPageOldVersion("/with-index-date"), 2018)
b.Assert(s.Site().LastChange().Year(), qt.Equals, 2018)
b.Assert(s.Site().Lastmod().Year(), qt.Equals, 2018)
}
func TestCreateNewPage(t *testing.T) {
@@ -630,6 +642,19 @@ func TestPageWithDelimiter(t *testing.T) {
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiter)
}
func TestPageWithBlankSummary(t *testing.T) {
t.Parallel()
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
p := pages[0]
checkPageTitle(t, p, "SimpleWithBlankSummary")
checkPageContent(t, p, normalizeExpected(ext, "<p>Some text.</p>\n"), ext)
checkPageSummary(t, p, normalizeExpected(ext, ""), ext)
checkPageType(t, p, "page")
}
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithBlankSummary)
}
func TestPageWithSummaryParameter(t *testing.T) {
t.Parallel()
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
@@ -709,7 +734,7 @@ func TestPageWithShortCodeInSummary(t *testing.T) {
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
p := pages[0]
checkPageTitle(t, p, "Simple")
checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"/> </figure> . More text here.</p><p>Some more text</p>"))
checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"> </figure> . More text here.</p><p>Some more text</p>"))
checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text")
checkPageType(t, p, "page")
}
@@ -773,7 +798,7 @@ func TestSummaryManualSplit(t *testing.T) {
title: Simple
---
This is **summary**.
<!--more-->
<!--more-->
This is **content**.
-- layouts/_default/single.html --
Summary: {{ .Summary }}|Truncated: {{ .Truncated }}|
@@ -1350,6 +1375,51 @@ AllTranslations: {{ range .AllTranslations }}{{ .Language.Lang }}|{{ end }}|
)
}
func TestTranslationKeyTermPages(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy']
defaultContentLanguage = 'en'
defaultContentLanguageInSubdir = true
[languages.en]
weight = 1
[languages.pt]
weight = 2
[taxonomies]
category = 'categories'
-- layouts/_default/list.html --
{{ .IsTranslated }}|{{ range .Translations }}{{ .RelPermalink }}|{{ end }}
-- layouts/_default/single.html --
{{ .Title }}|
-- content/p1.en.md --
---
title: p1 (en)
categories: [music]
---
-- content/p1.pt.md --
---
title: p1 (pt)
categories: [música]
---
-- content/categories/music/_index.en.md --
---
title: music
translationKey: foo
---
-- content/categories/música/_index.pt.md --
---
title: música
translationKey: foo
---
`
b := Test(t, files)
b.AssertFileContent("public/en/categories/music/index.html", "true|/pt/categories/m%C3%BAsica/|")
b.AssertFileContent("public/pt/categories/música/index.html", "true|/en/categories/music/|")
}
// Issue #11540.
func TestTranslationKeyResourceSharing(t *testing.T) {
files := `
+84 -2
View File
@@ -516,7 +516,7 @@ baseURL = "https://example.org"
disableKinds = ["taxonomy", "term"]
disableLiveReload = true
[outputs]
# This looks odd, but it triggers the behaviour in #5858
# This looks odd, but it triggers the behavior in #5858
# The total output formats list gets sorted, so CSS before HTML.
home = [ "CSS" ]
-- content/mybundle/index.md --
@@ -871,8 +871,90 @@ RegularPages: {{ range .RegularPages }}{{ .RelPermalink }}|File LogicalName: {{
b := Test(t, files)
// Note that the sort order gives us the most specific data file for the en language (the data.en.json).
b.AssertFileContent("public/mysection/mybundle/index.html", `Single:|/mysection/mybundle|File LogicalName: index.md||/mysection/mybundle/|page|Resources: data.json: Data JSON.|foo/p1.html: |p1.html: |p1.md: |data.txt: Data en txt.|$`)
b.AssertFileContent("public/mysection/mybundle/index.html", `Single:|/mysection/mybundle|File LogicalName: index.md||/mysection/mybundle/|page|Resources: data.en.txt: Data en txt.|data.json: Data JSON.|foo/p1.html: |p1.html: |p1.md: |$`)
b.AssertFileContent("public/mysection/index.html",
"List: |/mysection|File LogicalName: _index.md|/mysection/|section|Resources: sectiondata.json: Secion data JSON.|sectiondata.txt: Section data TXT.|$",
"RegularPages: /mysection/foo/p2/|File LogicalName: p2.md|/mysection/mybundle/|File LogicalName: index.md|/mysection/p2/|File LogicalName: p2.md|$")
}
func TestBundleResourcesGetMatchOriginalName(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
-- content/mybundle/index.md --
-- content/mybundle/f1.en.txt --
F1.
-- layouts/_default/single.html --
GetMatch: {{ with .Resources.GetMatch "f1.en.*" }}{{ .Name }}: {{ .Content }}|{{ end }}
Match: {{ range .Resources.Match "f1.En.*" }}{{ .Name }}: {{ .Content }}|{{ end }}
`
b := Test(t, files)
b.AssertFileContent("public/mybundle/index.html", "GetMatch: f1.en.txt: F1.|", "Match: f1.en.txt: F1.|")
}
func TestBundleResourcesWhenLanguageVariantIsDraft(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
defaultContentLanguage = "en"
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
-- content/mybundle/index.en.md --
-- content/mybundle/index.nn.md --
---
draft: true
---
-- content/mybundle/f1.en.txt --
F1.
-- layouts/_default/single.html --
GetMatch: {{ with .Resources.GetMatch "f1.*" }}{{ .Name }}: {{ .Content }}|{{ end }}$
`
b := Test(t, files)
b.AssertFileContent("public/mybundle/index.html", "GetMatch: f1.en.txt: F1.|")
}
func TestBundleBranchIssue12320(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['rss','sitemap','taxonomy','term']
defaultContentLanguage = 'en'
defaultContentLanguageInSubdir = true
[languages.en]
baseURL = "https://en.example.org/"
contentDir = "content/en"
[languages.fr]
baseURL = "https://fr.example.org/"
contentDir = "content/fr"
-- content/en/s1/p1.md --
---
title: p1
---
-- content/en/s1/p1.txt --
---
p1.txt
---
-- layouts/_default/single.html --
{{ .Title }}|
-- layouts/_default/list.html --
{{ .Title }}|
`
b := Test(t, files)
b.AssertFileExists("public/en/s1/index.html", true)
b.AssertFileExists("public/en/s1/p1/index.html", true)
b.AssertFileExists("public/en/s1/p1.txt", true)
b.AssertFileExists("public/fr/s1/index.html", false)
b.AssertFileExists("public/fr/s1/p1/index.html", false)
b.AssertFileExists("public/fr/s1/p1.txt", false) // failing test
}
+20 -27
View File
@@ -56,7 +56,7 @@ func (c *pageFinder) getPageRef(context page.Page, ref string) (page.Page, error
}
func (c *pageFinder) getPage(context page.Page, ref string) (page.Page, error) {
n, err := c.getContentNode(context, false, paths.ToSlashTrimTrailing(ref))
n, err := c.getContentNode(context, false, ref)
if err != nil {
return nil, err
}
@@ -121,6 +121,7 @@ func (c *pageFinder) getPageForRefs(ref ...string) (page.Page, error) {
const defaultContentExt = ".md"
func (c *pageFinder) getContentNode(context page.Page, isReflink bool, ref string) (contentNodeI, error) {
ref = paths.ToSlashTrimTrailing(ref)
inRef := ref
if ref == "" {
ref = "/"
@@ -130,22 +131,21 @@ func (c *pageFinder) getContentNode(context page.Page, isReflink bool, ref strin
return c.getContentNodeForRef(context, isReflink, true, inRef, ref)
}
var refs []string
// We are always looking for a content file and having an extension greatly simplifies the code that follows,
// even in the case where the extension does not match this one.
if ref == "/" {
refs = append(refs, "/_index"+defaultContentExt)
if n, err := c.getContentNodeForRef(context, isReflink, false, inRef, "/_index"+defaultContentExt); n != nil || err != nil {
return n, err
}
} else if strings.HasSuffix(ref, "/index") {
refs = append(refs, ref+"/index"+defaultContentExt)
refs = append(refs, ref+defaultContentExt)
if n, err := c.getContentNodeForRef(context, isReflink, false, inRef, ref+"/index"+defaultContentExt); n != nil || err != nil {
return n, err
}
if n, err := c.getContentNodeForRef(context, isReflink, false, inRef, ref+defaultContentExt); n != nil || err != nil {
return n, err
}
} else {
refs = append(refs, ref+defaultContentExt)
}
for _, ref := range refs {
n, err := c.getContentNodeForRef(context, isReflink, false, inRef, ref)
if n != nil || err != nil {
if n, err := c.getContentNodeForRef(context, isReflink, false, inRef, ref+defaultContentExt); n != nil || err != nil {
return n, err
}
}
@@ -165,21 +165,16 @@ func (c *pageFinder) getContentNodeForRef(context page.Page, isReflink, hadExten
// Given the above, for regular pages we use the containing folder.
var baseDir string
if pi := context.PathInfo(); pi != nil {
if pi.IsBranchBundle() || (hadExtension) {
if pi.IsBranchBundle() || (hadExtension && strings.HasPrefix(ref, "../")) {
baseDir = pi.Dir()
} else {
baseDir = pi.ContainerDir()
}
}
rel := path.Join(baseDir, inRef)
rel := path.Join(baseDir, ref)
if !hadExtension && !paths.HasExt(rel) {
// See comment above.
rel += defaultContentExt
}
relPath := contentPathParser.Parse(files.ComponentFolderContent, rel)
relPath, _ := contentPathParser.ParseBaseAndBaseNameNoIdentifier(files.ComponentFolderContent, rel)
n, err := c.getContentNodeFromPath(relPath, ref)
if n != nil || err != nil {
@@ -199,9 +194,9 @@ func (c *pageFinder) getContentNodeForRef(context page.Page, isReflink, hadExten
return nil, nil
}
refPath := contentPathParser.Parse(files.ComponentFolderContent, ref)
relPath, nameNoIdentifier := contentPathParser.ParseBaseAndBaseNameNoIdentifier(files.ComponentFolderContent, ref)
n, err := c.getContentNodeFromPath(refPath, ref)
n, err := c.getContentNodeFromPath(relPath, ref)
if n != nil || err != nil {
return n, err
@@ -225,7 +220,7 @@ func (c *pageFinder) getContentNodeForRef(context page.Page, isReflink, hadExten
return nil, nil
}
n = c.pageMap.pageReverseIndex.Get(refPath.BaseNameNoIdentifier())
n = c.pageMap.pageReverseIndex.Get(nameNoIdentifier)
if n == ambiguousContentNode {
return nil, fmt.Errorf("page reference %q is ambiguous", inRef)
}
@@ -243,7 +238,7 @@ func (c *pageFinder) getContentNodeFromRefReverseLookup(ref string, fi hugofs.Fi
realFilename := filepath.Join(dir, ref)
pcs, err := s.BaseFs.Content.ReverseLookup(realFilename)
pcs, err := s.BaseFs.Content.ReverseLookup(realFilename, true)
if err != nil {
return nil, err
}
@@ -258,9 +253,7 @@ func (c *pageFinder) getContentNodeFromRefReverseLookup(ref string, fi hugofs.Fi
return nil, nil
}
func (c *pageFinder) getContentNodeFromPath(refPath *paths.Path, ref string) (contentNodeI, error) {
s := refPath.Base()
func (c *pageFinder) getContentNodeFromPath(s string, ref string) (contentNodeI, error) {
n := c.pageMap.treePages.Get(s)
if n != nil {
return n, nil
+57
View File
@@ -386,6 +386,33 @@ Home. {{ with .Page.GetPage "p1.xyz" }}{{ else }}OK 1{{ end }} {{ with .Site.Get
b.AssertFileContent("public/index.html", "Home. OK 1 OK 2")
}
func TestGetPageIssue12120(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
-- content/s1/p1/index.md --
---
title: p1
layout: p1
---
-- content/s1/p2.md --
---
title: p2
layout: p2
---
-- layouts/_default/p1.html --
{{ (.GetPage "p2.md").Title }}|
-- layouts/_default/p2.html --
{{ (.GetPage "p1").Title }}|
`
b := Test(t, files)
b.AssertFileContent("public/s1/p1/index.html", "p2") // failing test
b.AssertFileContent("public/s1/p2/index.html", "p1")
}
func TestGetPageBundleToRegular(t *testing.T) {
files := `
-- hugo.toml --
@@ -636,3 +663,33 @@ RegularPagesRecursive: {{ range .RegularPagesRecursive }}{{ .Kind }}:{{ .RelPerm
b.AssertFileContent("public/index.html", `RegularPagesRecursive: page:/p1/|page:/post/p2/||End.`)
}
// Issue #12169.
func TestPagesSimilarSectionNames(t *testing.T) {
files := `
-- hugo.toml --
-- content/draftsection/_index.md --
---
draft: true
---
-- content/draftsection/sub/_index.md --got
-- content/draftsection/sub/d1.md --
-- content/s1/_index.md --
-- content/s1/p1.md --
-- content/s1-foo/_index.md --
-- content/s1-foo/p2.md --
-- content/s1-foo/s2/_index.md --
-- content/s1-foo/s2/p3.md --
-- content/s1-foo/s2-foo/_index.md --
-- content/s1-foo/s2-foo/p4.md --
-- layouts/_default/list.html --
{{ .RelPermalink }}: Pages: {{ range .Pages }}{{ .RelPermalink }}|{{ end }}$
`
b := Test(t, files)
b.AssertFileContent("public/index.html", "/: Pages: /s1-foo/|/s1/|$")
b.AssertFileContent("public/s1/index.html", "/s1/: Pages: /s1/p1/|$")
b.AssertFileContent("public/s1-foo/index.html", "/s1-foo/: Pages: /s1-foo/p2/|/s1-foo/s2-foo/|/s1-foo/s2/|$")
b.AssertFileContent("public/s1-foo/s2/index.html", "/s1-foo/s2/: Pages: /s1-foo/s2/p3/|$")
}
+3 -3
View File
@@ -161,7 +161,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
// We always start from a directory.
collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
if id.delete || id.isDir {
if id.isDir {
if id.isDir && fim.Meta().PathInfo.IsLeafBundle() {
return strings.HasPrefix(fim.Meta().PathInfo.Path(), paths.AddTrailingSlash(id.p.Path()))
}
@@ -195,9 +195,9 @@ func (c *pagesCollector) collectDir(dirPath *paths.Path, isDir bool, inFilter fu
var dpath string
if dirPath != nil {
if isDir {
dpath = filepath.FromSlash(dirPath.Path())
dpath = filepath.FromSlash(dirPath.Unnormalized().Path())
} else {
dpath = filepath.FromSlash(dirPath.Dir())
dpath = filepath.FromSlash(dirPath.Unnormalized().Dir())
}
}
+257 -4
View File
@@ -77,6 +77,27 @@ func TestRebuildEditTextFileInLeafBundle(t *testing.T) {
b.AssertRenderCountContent(1)
}
func TestRebuiEditUnmarshaledYamlFileInLeafBundle(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404", "rss"]
-- content/mybundle/index.md --
-- content/mybundle/mydata.yml --
foo: bar
-- layouts/_default/single.html --
MyData: {{ .Resources.Get "mydata.yml" | transform.Unmarshal }}|
`
b := TestRunning(t, files)
b.AssertFileContent("public/mybundle/index.html", "MyData: map[foo:bar]")
b.EditFileReplaceAll("content/mybundle/mydata.yml", "bar", "bar edited").Build()
b.AssertFileContent("public/mybundle/index.html", "MyData: map[foo:bar edited]")
}
func TestRebuildEditTextFileInHomeBundle(t *testing.T) {
b := TestRunning(t, rebuildFilesSimple)
b.AssertFileContent("public/index.html", "Home Content.")
@@ -261,6 +282,52 @@ func TestRebuilErrorRecovery(t *testing.T) {
b.EditFileReplaceAll("content/mysection/mysectionbundle/index.md", "{{< foo }}", "{{< foo >}}").Build()
}
func TestRebuildAddPageListPagesInHome(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- content/asection/s1.md --
-- content/p1.md --
---
title: "P1"
weight: 1
---
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .Content }}|
-- layouts/index.html --
Pages: {{ range .RegularPages }}{{ .RelPermalink }}|{{ end }}$
`
b := TestRunning(t, files)
b.AssertFileContent("public/index.html", "Pages: /p1/|$")
b.AddFiles("content/p2.md", ``).Build()
b.AssertFileContent("public/index.html", "Pages: /p1/|/p2/|$")
}
func TestRebuildAddPageWithSpaceListPagesInHome(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- content/asection/s1.md --
-- content/p1.md --
---
title: "P1"
weight: 1
---
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .Content }}|
-- layouts/index.html --
Pages: {{ range .RegularPages }}{{ .RelPermalink }}|{{ end }}$
`
b := TestRunning(t, files)
b.AssertFileContent("public/index.html", "Pages: /p1/|$")
b.AddFiles("content/test test/index.md", ``).Build()
b.AssertFileContent("public/index.html", "Pages: /p1/|/test-test/|$")
}
func TestRebuildScopedToOutputFormat(t *testing.T) {
files := `
-- hugo.toml --
@@ -462,7 +529,7 @@ func TestRebuildVariations(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disdableKinds = ["term", "taxonomy"]
disableKinds = ["term", "taxonomy"]
disableLiveReload = true
defaultContentLanguage = "nn"
paginate = 20
@@ -597,7 +664,7 @@ Codeblock Include: {{ .Title }}|
// - Edit content file direct
// - Edit content file transitive shortcode
// - Edit content file transitive render hook
// - Rename one languge version of a content file
// - Rename one language version of a content file
// - Delete content file, check site.RegularPages and section.RegularPagesRecursive (length)
// - Add content file (see above).
// - Edit shortcode
@@ -1131,7 +1198,7 @@ Single.
Running: true,
NeedsOsFS: true,
NeedsNpmInstall: true,
// LogLevel: logg.LevelTrace,
// LogLevel: logg.LevelDebug,
},
).Build()
@@ -1149,6 +1216,118 @@ Single.
b.AssertFileContent("public/index.html", "Home.", "<style>body {\n\tbackground: blue;\n}</style>")
}
func TestRebuildI18n(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- i18n/en.toml --
hello = "Hello"
-- layouts/index.html --
Hello: {{ i18n "hello" }}
`
b := TestRunning(t, files)
b.AssertFileContent("public/index.html", "Hello: Hello")
b.EditFileReplaceAll("i18n/en.toml", "Hello", "Hugo").Build()
b.AssertFileContent("public/index.html", "Hello: Hugo")
}
func TestRebuildEditContentNonDefaultLanguage(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
[languages]
[languages.en]
weight = 1
[languages.nn]
weight = 2
-- content/p1/index.en.md --
---
title: "P1 en"
---
P1 en.
-- content/p1/b.en.md --
---
title: "B en"
---
B en.
-- content/p1/f1.en.txt --
F1 en
-- content/p1/index.nn.md --
---
title: "P1 nn"
---
P1 nn.
-- content/p1/b.nn.md --
---
title: "B nn"
---
B nn.
-- content/p1/f1.nn.txt --
F1 nn
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .Content }}|Bundled File: {{ with .Resources.GetMatch "f1.*" }}{{ .Content }}{{ end }}|Bundled Page: {{ with .Resources.GetMatch "b.*" }}{{ .Content }}{{ end }}|
`
b := TestRunning(t, files)
b.AssertFileContent("public/nn/p1/index.html", "Single: P1 nn|<p>P1 nn.</p>", "F1 nn|")
b.EditFileReplaceAll("content/p1/index.nn.md", "P1 nn.", "P1 nn edit.").Build()
b.AssertFileContent("public/nn/p1/index.html", "Single: P1 nn|<p>P1 nn edit.</p>\n|")
b.EditFileReplaceAll("content/p1/f1.nn.txt", "F1 nn", "F1 nn edit.").Build()
b.AssertFileContent("public/nn/p1/index.html", "Bundled File: F1 nn edit.")
b.EditFileReplaceAll("content/p1/b.nn.md", "B nn.", "B nn edit.").Build()
b.AssertFileContent("public/nn/p1/index.html", "B nn edit.")
}
func TestRebuildEditContentNonDefaultLanguageDifferentBundles(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
[languages]
[languages.en]
weight = 1
contentDir = "content/en"
[languages.nn]
weight = 2
contentDir = "content/nn"
-- content/en/p1en/index.md --
---
title: "P1 en"
---
-- content/nn/p1nn/index.md --
---
title: "P1 nn"
---
P1 nn.
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .Content }}|
`
b := TestRunning(t, files)
b.AssertFileContent("public/nn/p1nn/index.html", "Single: P1 nn|<p>P1 nn.</p>")
b.EditFileReplaceAll("content/nn/p1nn/index.md", "P1 nn.", "P1 nn edit.").Build()
b.AssertFileContent("public/nn/p1nn/index.html", "Single: P1 nn|<p>P1 nn edit.</p>\n|")
b.AssertFileContent("public/nn/p1nn/index.html", "P1 nn edit.")
}
func TestRebuildVariationsAssetsSassImport(t *testing.T) {
if !htesting.IsCI() {
t.Skip("skip CI only")
@@ -1209,7 +1388,7 @@ func benchmarkFilesEdit(count int) string {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disdableKinds = ["term", "taxonomy"]
disableKinds = ["term", "taxonomy"]
disableLiveReload = true
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .Content }}|
@@ -1261,3 +1440,77 @@ func BenchmarkRebuildContentFileChange(b *testing.B) {
// fmt.Println(bb.LogString())
}
}
func TestRebuildConcat(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404", "rss"]
-- assets/a.css --
a
-- assets/b.css --
b
-- assets/c.css --
c
-- assets/common/c1.css --
c1
-- assets/common/c2.css --
c2
-- layouts/index.html --
{{ $a := resources.Get "a.css" }}
{{ $b := resources.Get "b.css" }}
{{ $common := resources.Match "common/*.css" | resources.Concat "common.css" | minify }}
{{ $ab := slice $a $b $common | resources.Concat "ab.css" }}
all: {{ $ab.RelPermalink }}
`
b := TestRunning(t, files)
b.AssertFileContent("public/ab.css", "abc1c2")
b.EditFileReplaceAll("assets/common/c2.css", "c2", "c2 edited").Build()
b.AssertFileContent("public/ab.css", "abc1c2 edited")
b.AddFiles("assets/common/c3.css", "c3").Build()
b.AssertFileContent("public/ab.css", "abc1c2 editedc3")
}
func TestRebuildEditArchetypeFile(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- archetypes/default.md --
---
title: "Default"
---
`
b := TestRunning(t, files)
// Just make sure that it doesn't panic.
b.EditFileReplaceAll("archetypes/default.md", "Default", "Default Edited").Build()
}
func TestRebuildEditMixedCaseTemplateFileIssue12165(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
-- layouts/partials/MyTemplate.html --
MyTemplate
-- layouts/index.html --
MyTemplate: {{ partial "MyTemplate.html" . }}|
`
b := TestRunning(t, files)
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate")
b.EditFileReplaceAll("layouts/partials/MyTemplate.html", "MyTemplate", "MyTemplate Edited").Build()
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited")
}
+13
View File
@@ -39,3 +39,16 @@ func TestRobotsTXTOutput(t *testing.T) {
b.AssertFileContent("public/robots.txt", "User-agent: Googlebot")
}
func TestRobotsTXTDefaultTemplate(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "http://auth/bub/"
enableRobotsTXT = true
`
b := Test(t, files)
b.AssertFileContent("public/robots.txt", "User-agent: *")
}

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