Compare commits

...

151 Commits

Author SHA1 Message Date
hugoreleaser 94cd8a205c releaser: Prepare repository for 0.161.0-DEV
[ci skip]
2026-04-08 14:23:48 +00:00
hugoreleaser d6bc8165e6 releaser: Bump versions for release of 0.160.1
[ci skip]
2026-04-08 14:02:42 +00:00
Bjørn Erik Pedersen 8b00030b34 Fix panic when passthrough elements are used in headings
Fixes #14677

Co-Authored-By: xingzihai <1315258019@qq.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:03:05 +02:00
Bjørn Erik Pedersen c48551677c Fix panic on edit of legacy mapped template names that's also a valid path in the new setup
This mapping was added in Hugo `v0.146.0`.

Fixes #14740
2026-04-08 13:19:45 +02:00
Bjørn Erik Pedersen 161d0d4757 Fix RenderShortcodes leaking context markers when indented
Strip leading whitespace from Hugo context marker lines before
Goldmark parsing to prevent them from being treated as indented
code blocks.

Fixes #12457

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:05:42 +02:00
Bjørn Erik Pedersen 45e4596630 Strip nested page context markers from standalone RenderShortcodes
Fixes #14732

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:27:40 +02:00
Bjørn Erik Pedersen 58927aa14a Rename deprecated cascade._target to cascade.target in tests 2026-04-07 13:58:06 +02:00
Bjørn Erik Pedersen ce009e3aa9 Fix auto-creation of root sections in multilingual sites
Fixes #14681

Co-authored-by: Joe Mooring <joe@mooring.com>
2026-04-07 13:58:06 +02:00
Christopher Hicks 0755872424 readme: Fix links
* docs: fix broken links in top README.md

* point at the docs site instead of github

* fix typo

Co-authored-by: Joe Mooring <joe@mooring.com>

---------

Co-authored-by: Joe Mooring <joe@mooring.com>
2026-04-06 08:48:45 -07:00
hugoreleaser 6b5554bac9 releaser: Prepare repository for 0.161.0-DEV
[ci skip]
2026-04-04 13:53:16 +00:00
hugoreleaser 652fc5acdd releaser: Bump versions for release of 0.160.0
[ci skip]
2026-04-04 13:32:34 +00:00
dependabot[bot] bf6e35a755 build(deps): bump github.com/magefile/mage from 1.16.1 to 1.17.1
Bumps [github.com/magefile/mage](https://github.com/magefile/mage) from 1.16.1 to 1.17.1.
- [Release notes](https://github.com/magefile/mage/releases)
- [Commits](https://github.com/magefile/mage/compare/v1.16.1...v1.17.1)

---
updated-dependencies:
- dependency-name: github.com/magefile/mage
  dependency-version: 1.17.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-04 14:56:33 +02:00
Bjørn Erik Pedersen 4e91e14cb0 Fix some recently introduced Position issues
Fixes #14710
2026-04-04 14:53:20 +02:00
Bjørn Erik Pedersen dc9b51d2e2 markup/goldmark: Fix double-escaping of ampersands in link URLs
The XSS fix in 479fe6c65 accidentally called util.EscapeHTML twice
on link destinations, causing `&` to render as `&amp;amp;` instead
of `&amp;`.

Fixes #14715

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 14:52:29 +02:00
Bjørn Erik Pedersen 481baa0896 all: Replace NewIntegrationTestBuilder with Test/TestE/TestRunning 2026-04-03 21:17:45 +02:00
Bjørn Erik Pedersen 43aad7118d tpl: Fix stray quotes from partial decorator in script context
Use template.JS for the falsy return value of _PopPartialDecorator
so Go's html/template JS escaper doesn't wrap the empty string in
quotes inside <script> tags.

Fixes #14711

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:30:26 +02:00
Joe Mooring 9f1f1be0be readme: Revise edition descriptions and installation instructions 2026-04-03 11:54:31 +02:00
dependabot[bot] 0eda24e65f build(deps): bump github.com/go-jose/go-jose/v4 from 4.1.3 to 4.1.4
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-03 11:32:24 +02:00
Bjørn Erik Pedersen 5d09b5e32a tpl/css: Support @import "hugo:vars" for CSS custom properties in css.Build
Closes #14699

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:02:13 +02:00
Bjørn Erik Pedersen 303e443ea7 Improve and extend .Position handling in Goldmark render hooks
Goldmark v1.8 reports source positions in render hooks.
Use this to build a source map that translates Goldmark positions back to
the original content source.

Now all render hooks' context object implenent the `BaseContext` interface:

```go
type BaseContext interface {
	Position() Position
	Page() any
	PageInner() any
	Ordinal() int
}
```

Closes #14663

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:30:28 +02:00
dependabot[bot] beb57a6839 build(deps): bump golang.org/x/image from 0.37.0 to 0.38.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/image/compare/v0.37.0...v0.38.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-01 19:16:47 +02:00
Bjørn Erik Pedersen 638262cec3 markup/goldmark: Clean up test 2026-04-01 17:05:08 +02:00
hugoreleaser 397980aab3 releaser: Prepare repository for 0.160.0-DEV
[ci skip]
2026-04-01 13:21:05 +00:00
hugoreleaser 5f4646acaa releaser: Bump versions for release of 0.159.2
[ci skip]
2026-04-01 12:59:20 +00:00
Bjørn Erik Pedersen 479fe6c654 Fix potential content XSS by escaping dangerous URLs in links and images
* This issue in question was fixed upstream in https://github.com/yuin/goldmark/releases/tag/v1.8.2
* We do, however, have a copy of the affected functions, used as fallbacks when no render hook are defined for e.g. links and images, so we need to port these fixes to our copy of the affected functions.
2026-04-01 14:23:24 +02:00
Bjørn Erik Pedersen 81a5cdca07 releaser: Add standard withdeploy release assets
Closes #14687

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:01:53 +02:00
Joe Mooring df520e3150 resources/page: Fix shared reader in Source.ValueAsOpenReadSeekCloser
Closes #14684

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:32:28 +01:00
Bjørn Erik Pedersen b55d452e46 testing: Simplify line ending handling in tests 2026-03-28 19:48:28 +01:00
Joe Mooring ea7eac6558 readme: Update Go version to 1.25.0 2026-03-27 08:43:24 -07:00
hugoreleaser 458ebdd448 releaser: Prepare repository for 0.160.0-DEV
[ci skip]
2026-03-26 10:13:11 +00:00
hugoreleaser 86c7d3afac releaser: Bump versions for release of 0.159.1
[ci skip]
2026-03-26 09:54:15 +00:00
Bjørn Erik Pedersen 42289d76f9 minifiers: Keep x-bind and blank namespace in SVG minification
Update tdewolff/minify to v2.24.11 and add "" and "x-bind" to KeepNamespaces
to prevent Alpine.js directives from being stripped in SVGs.

This is a new option in tdewolff/minify v2.24.11, and it is needed to prevent breakage of Alpine.js, and possilby others, directives in SVGs.

Fixes #14669
2026-03-26 10:49:53 +01:00
Bjørn Erik Pedersen 0c013c2326 Adjust depreceated syntax in tests 2026-03-24 11:07:01 +01:00
hugoreleaser bd071d78a5 releaser: Prepare repository for 0.160.0-DEV
[ci skip]
2026-03-23 18:34:59 +00:00
hugoreleaser 2ed7d193cf releaser: Bump versions for release of 0.159.0
[ci skip]
2026-03-23 18:16:59 +00:00
Bjørn Erik Pedersen d2043cfae5 docs: Update docs.yaml 2026-03-23 18:27:14 +01:00
Bjørn Erik Pedersen 5997b71db6 Merge commit 'e7afabb927a79b179ae57013fd5f49e32829671e' 2026-03-23 18:25:55 +01:00
Bjørn Erik Pedersen e7afabb927 Squashed 'docs/' changes from 80dd7b067..0755fb534
0755fb534 misc: Update docs.yaml
f0c69a38c content: Remove outdated content
43ce895b9 content: Remove outdated new-in badges
c3a382fb7 content: Improve GitLab Pages workflow example
f2a851966 content: Update Vimeo example
044565ab3 content: Fix typo
9f5e8b5f4 content: Update version references
9dc30f225 theme: Address deprecations in v0.158.0
ea0787a6b content: Document css.Build function
f98e4f55c content: Standardize Node.js references
bf683e36f content: Use consistent file system terminology
54ed4c12b content: Document strings.ReplacePairs
779d37978 all: Miscellaneous updates for v0.158.0
be1a20c27 Update HUGO_VERSION to 0.158.0
24cdd7bf7 Fix typos in module configuration documentation
ce37e7b05 content: Address Markdown linting error
592049f02 content: Remove unnecessary workflow customization section
b17b18ce1 content: Update version references in hosting guides
32ec89596 content: Improve urls.PathEscape/PathUnescape examples
9297bb6fa content: Clarify page collection sort order
60192a16f content: Add reference link to glossary term
63d4a992a content: Update documentation contribution guide
946273d67 theme: Adjust feature state notices
d70dfd651 theme: Simplify border classes per Tailwind linter
fc53c7263 theme: Refactor feature status shortcodes
850dc5b2b content: Clean up content for deprecated features
35da1aa3c content: Miscellaneous udpates for v0.157.0
05b69f84e content: More site-to-project changes
291ffc3f6 content: Add blogger2hugo migration tool
53a1bdee3 Update HUGO_VERSION to 0.157.0
808f0431d Merge branch 'tempv0.157.0'
f02222e46 docs: Regen and fix the imaging docshelper output
b0d3364f1 Merge commit '0c2fa2460f485e0eca564dcccf36d34538374922'
d1f37cc6d resources/images: Adjust WebP processing defaults
9a9f02fb8 Add per-request timeout option to `resources.GetRemote`
900c10201 docs: Fix lineNos default value in docs.yaml
0052c975a docs: Regenerate docs.yaml
3e2f98235 Merge commit '8f3c066d23f431fb2c53d97ea489e4c28b42bd82'
dbfd34a1a docs: Update docs.yaml
7e1a08e54 Merge commit '08e1ea5c709d3d49bdc3ce3c21e8fa05a33150d0'
cb6e0a42a tpl: Add missing functions to init files
05544e400 markup/asciidocext: Improve Asciidoctor integration
820b5536a github: Add ai-watchdog workflow and update other workflows' versions
3848aec5f markup/goldmark: Enhance footnote extension with backlinkHTML option
070074dac markup/goldmark: Enhance footnote extension with auto-prefixing option
f92f7ac5c config/security: Add PROGRAMDATA to the osenv allowlist
d1ab84530 minifiers: Update deprecation handling
6c911d1e5 Merge commit 'bfa74537929f409fca841540b971125b7678963a'
61916ab4c resources/page: Add :sectionslug and :sectionslugs permalink tokens
80667667c Add Ancestors (plural) method to GitInfo, rename Ancestor field to Parent
e8fdc1d4d source: Expose Ancestor in GitInfo
079671b41 Merge commit 'bb147f91ee9078e6a55e8c32ab4b2e5dbc5cee45'
9e8a08520 images: Add option for vertical alignment to images.Text
f7c795479 Merge commit 'b3d87dd0fd746f07f9afa6e6a2969aea41da6a38'
283e97783 Merge commit '5be51ac3db225d5df501ed1fa1499c41d97dbf65'
4c6ec5c19 resources/page: Revise the new contentbasename permalinks tokens
fc69d3981 resources/page: Add :contentbasename and :contentbasenameorslug permalink tokens
c5f654716 modules: Add GOAUTH to module config
2a1a832fb js/esbuild: Add drop option
d9fb55352 Merge commit 'a024bc7d76fcc5e49e8210f9b0896db9ef21861a'
84678f5af helpers: Add Chroma styles to docs.yaml
fc8d9e344 Merge commit '346b60358dd8ec2ca228e6635bff9d7914b398b7'
4e4da5e47 Merge commit '81a7b6390036138356773c87a886679c81c524e1'
5df38fde0 docs: Regen CLI docs
3b0f8034b tpl/images: Change signature of images.QR to images.QR TEXT OPTIONS
68187036f images.Text: Add "alignx" option for horizontal alignment
240a85734 docs: Regen CLI docs
21b2917c6 Merge commit 'e9fbadacc3f09191e2e19f112a49777eeb8df06c'
5cc7ad562 tpl/images: Add images.QR function
9b588ee9e Update gocloud and docs for S3-Compatible Endpoints
b24377040 tpl/tplimpl: Update details shortcode
0535f4b22 tpl/tplimpl: Add details shortcode
2dd21cf90 Merge commit 'e477373487abcccdbed95688e37aa74b9b8fc198'
869321ffa dartsass: Add silenceDeprecations option
492981933 Merge commit '838bd312b1a287bb33962ad478dbc54737654f35'
c79b7d840 docs: Regen CLI docs
101410f08 docs: Regenerate CLI docs
700846bc9 Merge commit 'de0df119b504a91c9e1f442b07954f366ffb2932'
1fb46bb7c docs: Regen CLI docs
923fd9044 commands: Add "hugo build" as an alias for "hugo"
244ea3067 Merge commit '39fd3b557014e339bc6c68a7ff34a7734a735ee0'
f2e8913d2 Add support for Obsidian type blockquote alerts
9777cfa83 Merge commit 'dec8cd4ada29218971743333f8ac662a9c06aad8'
b3f6d546c output: Fix docshelper template lookup order for AMP pages
476507712 Add config options page.nextPrevSortOrder/nextPrevInSectionSortOrder
b2507b8ab tpl/transform: Make Plainify and ToMath return template.HTML
154525780 docs: Regen docshelper
9d9a0848c Merge commit 'a6e635ca7d905d9ec3ffd708db2694f680b03aae'
b56a8baa1 math: Add trigonometric functions and some angle helper functions
59f708c0e source: Expose GitInfo Body
7a330c378 Merge commit '8b9803425e63e1b1801f8d5d676e96368d706722'
1ea10926d deploy: Add stripIndexHtml target option
903da161a markup/goldmark: Add the Hugo Goldmark Extras "delete" extension
63cf1ea35 deps: Upgrade github.com/alecthomas/chroma v2.13.0 => v2.14.0
6dcbebe11 config: Remove extraneous BuildConfig setting
1c89b76c2 docs: Regen docshelper
976b30877 markup/goldmark: Support extras extension
0da4eee49 commands: Add gen chromastyles --lineNumbersTableStyle flag
daf545b56 docs: Regen docshelper
a46a8ac97 all: Fix duplicate words in comments
9a3c76650 tpl/tplimpl: Optionally exclude content from sitemap
737b3b210 tpl/tplimpl: Update Google Analytics template and config
9283d3071 docs: Regen CLI docs
fa0437be3 docs: Regen docshelper
c318cd850 docs: Fix hyphens and grammar in synopsis of command 'hugo server'
5896f23a9 js: Support JSX and JSXImportSourceOptions
08b0bca77 Merge commit '2658a71e1b6fe24a8b754a62ce0398a09d270d86'
c4c5be6c2 docs: Regen docshelper
ec07d66fe Add images.Dither filter
3b23f2b89 docs: Regen CLI docs
d1c5d6a5e docs: Regenerate docshelper
6c8c9da77 Merge commit '6efb279bfacbd7304cef994be8181c6f804e7dd4'
5ce83ee91 docs: Make null booleans falsy in the docs helper
1185cfc71 docs: Regen docs helper
aab11aad7 Merge commit '9b0050e9aabe4be65c78ccf292a348f309d50ccd' as 'docs'

git-subtree-dir: docs
git-subtree-split: 0755fb534d8b4cde8a0227698da634e3536283ff
2026-03-23 18:21:25 +01:00
Bjørn Erik Pedersen 4f3c39890e commands: Update docs linke to Node.js docs 2026-03-23 18:13:50 +01:00
eason 807cae1df1 create: Return error instead of panic when page not found
When `hugo new` creates a content file on a case-insensitive filesystem
where a file with the same name but different case already exists (e.g.
Floop.md vs floop.md), the internal page lookup fails because it uses
exact string comparison. This previously caused an unhelpful panic.

Replace the panic with a descriptive error message that tells the user
what went wrong and suggests checking for case-variant filenames.

Fixes #14112

Co-authored-by: easonysliu <easonysliu@tencent.com>
Co-authored-by: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
2026-03-23 17:50:15 +01:00
Alexandre Vaz c4fb61d9df commands: Preserve non-content files in convert output
When running `hugo convert` with `--output`, copy the content tree first so non-content bundle resources are kept in the destination, then overwrite converted content files.

Also avoid recursive self-copy when the output path points inside the content tree by skipping output directories during copy.

Fixes #4621
2026-03-23 17:49:13 +01:00
Bjørn Erik Pedersen d88a29e002 npm: Use workspaces to simplify hugo mod npm pack
Rewrite `hugo mod npm pack` to use npm workspaces. Module deps are now
written to packages/hugoautogen/package.json and the root package.json
gets a "workspaces" reference. A hugo_packagemeta.json sidecar stores a
hash of all input package files so regular commands can warn when npm
deps are out of sync.

Other changes:
- Workspace glob patterns (*, **, {a,b}) are resolved via gobwas/glob.
- Workspaces defined in package.hugo.json are supported.
- package.hugo.json is only recognised at module roots, not in workspaces.
- When package.hugo.json exists, package.json is not mounted or vendored.
- packages/hugoautogen is not mounted or vendored from dependencies.
- Add usePackageJSON import option (auto/always/never) to control whether
  a module's npm deps are included. "auto" checks for Hugo config files
  or package.hugo.json.
- The staleness check is skipped when running `hugo mod npm pack` itself.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 16:00:50 +01:00
dependabot[bot] 3ff9b7f89f build(deps): bump github.com/olekukonko/tablewriter from 1.1.3 to 1.1.4 (#14641)
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/olekukonko/tablewriter/releases)
- [Commits](https://github.com/olekukonko/tablewriter/compare/v1.1.3...v1.1.4)

---
updated-dependencies:
- dependency-name: github.com/olekukonko/tablewriter
  dependency-version: 1.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-21 21:19:50 +01:00
Tay 9dd9c76027 commands: Close cpu profile file when StartCPUProfile fails 2026-03-21 21:17:12 +01:00
Bjørn Erik Pedersen a8fca598e6 Replace deprecated site.Data with hugo.Data in tests
This also fixes a subtle server reload issue when changing files inside /data and using the recently added hugo.Data method.
2026-03-21 19:09:14 +01:00
Bjørn Erik Pedersen 182b104571 Replace deprecated excludeFiles and includeFiles with files in tests 2026-03-21 16:59:13 +01:00
dependabot[bot] be93ccd304 build(deps): bump github.com/yuin/goldmark from 1.7.16 to 1.7.17
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.16 to 1.7.17.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.16...v1.7.17)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 11:37:30 +01:00
dependabot[bot] 2669bca6fd build(deps): bump github.com/magefile/mage from 1.15.0 to 1.16.1
Bumps [github.com/magefile/mage](https://github.com/magefile/mage) from 1.15.0 to 1.16.1.
- [Release notes](https://github.com/magefile/mage/releases)
- [Commits](https://github.com/magefile/mage/compare/v1.15.0...v1.16.1)

---
updated-dependencies:
- dependency-name: github.com/magefile/mage
  dependency-version: 1.16.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 20:39:12 +01:00
dependabot[bot] 753d447f11 build(deps): bump golang.org/x/image from 0.36.0 to 0.37.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/image/compare/v0.36.0...v0.37.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 20:38:51 +01:00
dependabot[bot] 4f39d724fe build(deps): bump google.golang.org/grpc from 1.78.0 to 1.79.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.78.0 to 1.79.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.78.0...v1.79.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 20:21:44 +01:00
Joe Mooring eaf4c7515a tpl/tplimpl: Fix Vimeo shortcode test
Closes #14649
2026-03-19 20:21:21 +01:00
Bjørn Erik Pedersen 3315a86d67 Remove the AI Watchdog workflow for now
It's failing with a 404 that I don't know how to fix (or don't have the time to investigate). It works fine when run locally.
2026-03-19 17:28:20 +01:00
Bjørn Erik Pedersen 38244842e0 Remove 'bep' from PR user logins skip list
To test it and see why it fails.
2026-03-19 17:02:16 +01:00
Bjørn Erik Pedersen 7813c5c89d tpl/tplimpl: Comment out the Vimeo simple shortcode tests
See #14649
2026-03-19 13:52:36 +01:00
Bjørn Erik Pedersen eb11c3d0bc Replace deprecated :filename with :contentbasename in the permalinks test 2026-03-18 08:47:26 +01:00
hugoreleaser fa8658111f releaser: Prepare repository for 0.159.0-DEV
[ci skip]
2026-03-16 17:56:18 +00:00
hugoreleaser f41be7959a releaser: Bump versions for release of 0.158.0
[ci skip]
2026-03-16 17:42:04 +00:00
Bjørn Erik Pedersen 0e46a97e8a deps: Upgrade github.com/evanw/esbuild v0.27.3 => v0.27.4 2026-03-16 18:39:08 +01:00
dependabot[bot] c27d9e8fcf build(deps): bump github.com/getkin/kin-openapi from 0.133.0 to 0.134.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.133.0 to 0.134.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.133.0...v0.134.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 11:59:42 +01:00
dependabot[bot] 098eac59a9 build(deps): bump golang.org/x/tools from 0.42.0 to 0.43.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.42.0 to 0.43.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.42.0...v0.43.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 11:56:42 +01:00
Bjørn Erik Pedersen 3c980c072e resources: Re-publish on transformation cache hit
When a resource transformation result was served from cache (same
options as a previous build), the output file was not re-written to
disk. This caused incorrect output when toggling transformation
options (e.g. minify) back to a previously seen value in server mode.

Fixes #14629
2026-03-15 18:32:41 +01:00
Joe Mooring 404ac00001 create/skeletons: Use css.Build in theme skeleton
Closes #14626
2026-03-15 16:51:24 +01:00
Bjørn Erik Pedersen 06fcb72421 tpl/css: Add a test case for rebuilds on CSS options changes 2026-03-15 11:09:02 +01:00
Joe Mooring 9b5f1d491d hugolib: Allow regular pages to cascade to self
Closes #14627
2026-03-15 10:17:45 +01:00
dependabot[bot] 87f8de8c7a build(deps): bump gocloud.dev from 0.44.0 to 0.45.0
Bumps [gocloud.dev](https://github.com/google/go-cloud) from 0.44.0 to 0.45.0.
- [Release notes](https://github.com/google/go-cloud/releases)
- [Commits](https://github.com/google/go-cloud/compare/v0.44.0...v0.45.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 16:06:09 +01:00
dependabot[bot] 67ef6c68de build(deps): bump golang.org/x/sync from 0.19.0 to 0.20.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/sync/compare/v0.19.0...v0.20.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 16:05:19 +01:00
Bjørn Erik Pedersen 623722bb07 tpl/css: Allow the user to override single loader entries
Fixes #14623

 Co-authored-by: Joe Mooring <joe.mooring@veriphor.com>
2026-03-14 15:17:21 +01:00
Bjørn Erik Pedersen e431f90bc6 tpl/css: Fix external source maps
Fixes #14620
2026-03-14 12:08:18 +01:00
Bjørn Erik Pedersen a7cbcf15f0 tpl/css: Make default loader resolution for CSS @import and url() always behave the same
Before this commit, we did dynamic loader resolution for CSS bundling for resources resolved by Hugo while any fallback to ESBuild would fall back to a (potentially) empty loaders config.

This revises the logic to always use a static list (see below) if `loaders` is not set. This should be easier do document and less confusing for the end user.

````
".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".avif",
".woff", ".woff2", ".ttf", ".eot", ".otf"
````

Fixes #14619
2026-03-13 20:24:32 +01:00
Joe Mooring 59e0446fe5 hugolib: Fix server no watch
Closes #14615

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:28:37 +01:00
Joe Mooring 36cdb2c789 internal/js: Add default mainFields for CSS builds
Closes #14614
2026-03-13 08:33:17 +01:00
Bjørn Erik Pedersen 3e3b849cc7 Add css.Build
Fixes #14609
Fixes #14613
2026-03-12 13:32:40 +01:00
Bjørn Erik Pedersen 1f578f160c Upgrade to to Go 1.26.1 (#14597) (note)
This fixes a security issue: https://www.cve.org/CVERecord?id=CVE-2026-27142

Closes #14595
2026-03-11 12:36:13 +01:00
Bjørn Erik Pedersen 842d8f1052 resources: Fix context canceled on GetRemote with per-request timeout
The per-request timeout context was cancelled via defer in the getRes
closure, before io.ReadAll(res.Body) in the outer scope could read
the response body. Fix this by returning the cancel function from
getRes so each caller manages its own cancel lifecycle.

Fixes #14611

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:45:14 +01:00
Bjørn Erik Pedersen c47ec23342 resources: Use full path for Exif etc. decoding error/warning messages
Fixes #12693
2026-03-08 23:03:57 +01:00
Bjørn Erik Pedersen 4652ae4a44 Move to new locales library and upgrade CLDR from v36.1 to v48.1
* I had all kinds of issues upgrading CLDR on the old library, so I decided to start fresh in github.com/bep/golocales
* It shaves off about 3 MB of the binary, and package init cost is zero compared to the old localescompressed (5000x faster).
* We will probably move to the `text/**` packages at this point, but this will have to do for now.
2026-03-08 21:43:37 +01:00
Bjørn Erik Pedersen 4eafd9eb7b tpl/tplimpl: Prefer early suffixes when media type matches
Fixes #13877
Close #14601

Co-Authored-By: Joe Mooring <joe.mooring@veriphor.com>
2026-03-08 21:42:36 +01:00
Joe Mooring 13a95b9c01 tpl/strings: Add strings.ReplacePairs function
Closes #14594
2026-03-07 20:21:02 +01:00
Bjørn Erik Pedersen e3108225bf all: Run go fix ./... 2026-03-07 18:30:42 +01:00
Bjørn Erik Pedersen c9b88e4d15 internal/warpc: Fix SIGSEGV in Close() when dispatcher fails to start
Only set started=true when Start() succeeds, so Close() doesn't
attempt to close a nil dispatcher.

Fixes #14536
2026-03-07 15:30:07 +01:00
Bjørn Erik Pedersen 54c804876a github: Remove pull_request_template.md
* Main reason is that it slows down my own `gh` workflow (I also frequently accidentally select that template).
* We also don't have a large volume of PRs from new contributors, so the template isn't as necessary.
2026-03-07 12:48:15 +01:00
Bjørn Erik Pedersen 157bfddda5 testing: Make commands tests pass in Go 1.26.1 2026-03-06 12:23:25 +01:00
Joe Mooring d4f2122dea refactor: Deprecate language configuration and template methods
Configuration:

- languageCode      -> locale (either in root or per-language)
- languageName      -> label
- languageDirection -> direction

Methods:

- .Language.LanguageCode      -> .Language.Locale
- .Language.LanguageName      -> .Language.Label
- .Language.LanguageDirection -> .Language.Direction
- .Site.LanguageCode          -> .Site.Language.Locale

Example configuration:

[languages.en]
  direction = 'ltr'
  label     = 'English'
  locale    = 'en-US'
  weight    = 1

Closes #14269

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 11:45:52 +01:00
Bjørn Erik Pedersen 991d2f9aba Replace Exif with Meta in tests 2026-03-04 11:38:28 +01:00
dependabot[bot] b29c2f7ade build(deps): bump golang.org/x/net from 0.50.0 to 0.51.0 (#14569)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.50.0 to 0.51.0.
- [Commits](https://github.com/golang/net/compare/v0.50.0...v0.51.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 13:01:37 +01:00
dependabot[bot] e91d191155 build(deps): bump github.com/tdewolff/minify/v2 from 2.24.9 to 2.24.10 (#14585)
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.24.9 to 2.24.10.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.24.9...v2.24.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 12:57:29 +01:00
dependabot[bot] a8a54bd020 build(deps): bump github.com/bep/imagemeta from 0.15.0 to 0.17.0 (#14584)
Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.15.0 to 0.17.0.
- [Release notes](https://github.com/bep/imagemeta/releases)
- [Commits](https://github.com/bep/imagemeta/compare/v0.15.0...v0.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 12:57:10 +01:00
Bjørn Erik Pedersen f797f84902 Fix index out of range panic in fileEventsContentPaths
The nested loop had dirs as the outer loop and others as the inner loop
with a single counter, causing n to exceed len(others) when multiple
dirs existed. Swap the loop order so each file in others is checked
against all dirs exactly once.

Fixes #14573

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:05:13 +01:00
Joe Mooring e8577771c3 resources: Improve getImageOps error message
Closes #14571
2026-02-26 22:20:08 +01:00
Bjørn Erik Pedersen 44dc3847f4 resources/images: Add IsImageResourceWithMeta etc. tests for bmp and gif
Closes #14568
2026-02-26 16:49:11 +01:00
hugoreleaser 5594b28229 releaser: Prepare repository for 0.158.0-DEV
[ci skip]
2026-02-25 16:57:59 +00:00
hugoreleaser 7747abbb31 releaser: Bump versions for release of 0.157.0
[ci skip]
2026-02-25 16:38:33 +00:00
Bjørn Erik Pedersen 3dff7c8c7a Fix menu pageRef resolution in multidimensional setups
Fixes #14566
2026-02-25 17:35:20 +01:00
Bjørn Erik Pedersen d98cd4aecf Handle GitInfo for modules where Origin is not set when running go list
This is an upstream bug, but this workaround seem to work fine.

Fixes #14564
2026-02-25 14:26:50 +01:00
Joe Mooring 68059972e8 commands: Update link to highlighting style examples
Closes #14556
2026-02-25 12:57:25 +01:00
Bjørn Erik Pedersen 8e28668b09 docs: Regen and fix the imaging docshelper output
Fixes #14562
2026-02-25 10:43:58 +01:00
Bjørn Erik Pedersen a3ea9cd18f Merge commit '0c2fa2460f485e0eca564dcccf36d34538374922' 2026-02-25 10:29:05 +01:00
Bjørn Erik Pedersen 0c2fa2460f Squashed 'docs/' changes from 42914c50e..80dd7b067
80dd7b067 theme: Fix border class in render-codeblock template
0d3fde6c9 content: Fix typo
23a4adb29 content: More site => project changes
9243e9f6b content: Rename site configuration to project configuration (phase 2)
330aa2249 content: Miscellaneous edits
25ce893be content: Clarify sort order with PAGE.Rotate
b398506dc content: Various dimension improvements
c54573d13 content: Use singular form in method section descriptions
d9a95fe54 content: Improve description of PAGE.Rotate
154f30600 content: Fix link
c71bd2776 content: Address Markdown linting error
72c88e68e content: Improve pages related to content dimensions
b85afa645 content: Fix formatting
7aaca8947 content: More site-to-project changes
8e1579030 content: Update quick start guide
f7ba1fde2 content: Make new-in into a note
8b9d51584 theme: Address TailwindCSS warnings
3a4c2a25c theme: Address Hugo v0.156.0 deprecations
4f1a2bd7b content: Improve glossary entries
21bcd23a6 content: Update glossary entry
c72ef1116 content: Update quick start guide
c0737ffab content: Add typography plugin to TailwindCSS example
80bcb868e content: Remove Site.AllPages from examples
2b9ba5831 content: Replace Page.Sites and Site.Sites with hugo.Sites
2cdcc2556 content: Remove outdated badges
5a4beb530 content: Replace Site.Data with hugo.Data
de4651b5d content: Update glossary entry
2302825a7 content: Add hugo.Data and note other deprecations
bac5da4b5 content: Add hugo.Sites and update the other Sites methods
0f7d99153 content: Add Site.IsDefault
f888f33bb content: Update glossary entries
65311c2de content: Include the "build" subcommand where appropriate
4991320d2 content: Standard shell language code in info strings
b16d9f162 theme: Style todo lists
f121450c6 content: Update version references
5a81dd8c2 content: Update CLI documentation
f3ae0ce86 content: Rename site configuration to project configuration (phase 1)
9c8327119 content: Miscellaneous edits
fbff91e22 Update HUGO_VERSION to 0.156.0
2ec625ae8 content: Restore snap installation instructions
fc8c246b8 content: Clarify module initialization

git-subtree-dir: docs
git-subtree-split: 80dd7b067c31c28b13c51f3ac4636890509a4bb7
2026-02-24 21:38:42 +01:00
Bjørn Erik Pedersen 49bfb1070b Add AVIF, HEIF and HEIC partial support (only metadata for now)
* Add AVIF, HEIF and HEIC partial support

* Add them as media types.
* Support reading metadata (Width, Height, Exif, etc.) from these formats.
* Add a new template function IsImageResourceMeta to check if a resource supports image metadata operations, which will return true for AVIF, HEIF and HEIC resources even if they don't support full image operations yet.

Fixes #14549
2026-02-24 19:45:12 +01:00
Joe Mooring b7203bbb3a resources/images: Adjust WebP processing defaults
defaultWebpUseSharpYuv: true => false
defaultWebpMethod: 4 => 2
2026-02-24 10:02:48 +01:00
Bjørn Erik Pedersen dfece5b674 Add Page.GitInfo support for content from Git modules
Use blobless git clone (--filter=blob:none --no-checkout) to fetch
commit history for content from Git modules, then map it using the
existing gitmap library.  Cloned repos are cached in the modulegitinfo file cache.

Closes #14431
Fixes #5533
2026-02-23 17:43:17 +01:00
Paul van Brouwershaven 2d691c7e47 Add per-request timeout option to resources.GetRemote 2026-02-23 16:38:13 +01:00
Bjørn Erik Pedersen b96d58a144 Update AI Watchdog action version in workflow 2026-02-23 15:05:07 +01:00
dependabot[bot] 9869e71aaa build(deps): bump github.com/tdewolff/minify/v2 from 2.24.8 to 2.24.9
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.24.8 to 2.24.9.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.24.8...v2.24.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 12:23:59 +01:00
dependabot[bot] 8f47fe8c87 build(deps): bump github.com/bep/imagemeta from 0.14.0 to 0.15.0
Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.14.0 to 0.15.0.
- [Release notes](https://github.com/bep/imagemeta/releases)
- [Commits](https://github.com/bep/imagemeta/compare/v0.14.0...v0.15.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 12:23:01 +01:00
Bjørn Erik Pedersen 65b4287c68 config: Skip taxonomy entries with empty keys or values
When non-taxonomy keys (e.g. disableKinds = []) are placed after
[taxonomies] in TOML, they become part of the taxonomies table.
An empty-valued entry creates a phantom taxonomy with an empty
pluralTreeKey, causing .Ancestors to loop indefinitely.

Fixes #14550

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:22:39 +01:00
Bjørn Erik Pedersen cc338a9d48 Add guideline for brevity in code and comments
Added a guideline emphasizing brevity in code, comments, and commit messages.
2026-02-21 18:53:41 +01:00
Bjørn Erik Pedersen 3850881f2a modules: Include JSON error info from go mod download in error messages
Fixes #14543
2026-02-21 17:21:08 +01:00
Bjørn Erik Pedersen a18bec1123 hugolib: Fix automatic section pages not replaced by sites.complements
When a home page or section page in language A is backed by a file and
language B has no content file for that section, the automatic page for
language B was not replaced by the complement from language A. This
happened because:

1. The content node shifter returned auto pages (not backed by files) as
   exact matches without trying complement fallback.
2. findContentNodeForSiteVector immediately returned auto pages as exact
   matches without considering file-backed complements.
3. getPagesInSection used Get (no fallback) for IncludeSelf, preventing
   complement resolution for home/section pages.

Fix by preferring file-backed complement pages over auto pages in the
shift and lookup mechanisms, and using fallback for self-inclusion.

Fixes #14540

Co-Authored-By: Joe Mooring <joe.mooring@veriphor.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:23:20 +01:00
hugoreleaser 3c9b41f9ec releaser: Prepare repository for 0.157.0-DEV
[ci skip]
2026-02-18 16:58:30 +00:00
hugoreleaser 9d914726de releaser: Bump versions for release of 0.156.0
[ci skip]
2026-02-18 16:39:55 +00:00
Bjørn Erik Pedersen 86aa62524f hugolib: Move site.Data to hugo.Data, deprecate Site.AllPages/BuildDrafts/Languages
This moves Site.Data to the hugo namespace as hugo.Data, following the
same pattern used for hugo.Sites. Also deprecate Site.AllPages,
Site.BuildDrafts, and Site.Languages.

Closes #14521

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:34:47 +01:00
dependabot[bot] d8ec0eeeaf build(deps): bump google.golang.org/api from 0.255.0 to 0.267.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.255.0 to 0.267.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.255.0...v0.267.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 10:54:40 +01:00
Joe Mooring 4148eded9c hugolib: Add Page.Sites to Site.Sites deprecation notice
Closes #14532
2026-02-17 20:33:18 +01:00
Bjørn Erik Pedersen bba2aed352 hugolib: Simplify sites collection 2026-02-17 19:10:47 +01:00
Bjørn Erik Pedersen 29b8e17d29 hugolib: Adjust hugo.Sites.Default
Fixes #14531
2026-02-17 19:10:47 +01:00
Bjørn Erik Pedersen 3c823408ee Move common/hugo/HugoInfo to resources/page
* This break the circular dependency between hugolib and resources/page, and allow HugoInfo to use the proper Page ans Site interfaces.
* It's a little misplaced, but it makes everything easier to maintain and test, so that's well worth it.
2026-02-17 19:10:47 +01:00
Bjørn Erik Pedersen 3f9d0ad2b6 commands: Fix --panicOnWarning flag having no effect with module version warnings
The --panicOnWarning flag was not wired into the root command's logger,
so warnings emitted during config loading (such as module version
incompatibility warnings) would not trigger a panic.

Bind the panicOnWarning flag to the rootCommand struct and set the
PanicOnWarningHook on the logger created in createLogger.

Fixes #14524

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:34:29 +01:00
Joe Mooring ab62320d6b hugolib: Add hugo.Sites and .Site.IsDefault(), modify .Site.Sites
Changes:

- Add hugo.Sites to return all sites for all dimensions
- Modify .Site.Sites to return all sites for all dimensions
- Deprecate .Site.Sites in favor of hugo.Sites
- Add .Site.IsDefault() to report whether the current site is the
  default site across all dimensions
- Consolidate tests

Closes #14479
Closes #14481

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 18:57:26 +01:00
dependabot[bot] 21be4afd49 build(deps): bump github.com/bep/textandbinarywriter
Bumps [github.com/bep/textandbinarywriter](https://github.com/bep/textandbinarywriter) from 0.0.0-20251212174530-cd9f0732f60f to 0.1.0.
- [Release notes](https://github.com/bep/textandbinarywriter/releases)
- [Commits](https://github.com/bep/textandbinarywriter/commits/v0.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 17:48:37 +01:00
khayyam 32027e50a1 paths: Fix handling of _ as a path name
Fixes #14344
2026-02-16 17:32:45 +01:00
Bjørn Erik Pedersen 8d19f5a78b output: Remove unused method
Closes #14522
2026-02-16 11:00:33 +01:00
dependabot[bot] e549016bc5 build(deps): bump github.com/bep/simplecobra from 0.6.1 to 0.7.0
Bumps [github.com/bep/simplecobra](https://github.com/bep/simplecobra) from 0.6.1 to 0.7.0.
- [Release notes](https://github.com/bep/simplecobra/releases)
- [Commits](https://github.com/bep/simplecobra/compare/v0.6.1...v0.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:56:28 +01:00
dependabot[bot] a392906b55 build(deps): bump github.com/bep/tmc from 0.5.1 to 0.6.0
Bumps [github.com/bep/tmc](https://github.com/bep/tmc) from 0.5.1 to 0.6.0.
- [Release notes](https://github.com/bep/tmc/releases)
- [Commits](https://github.com/bep/tmc/compare/v0.5.1...v0.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 10:55:54 +01:00
Joe Mooring e6f01bb464 snap: Stop building for ppc64el and s390x
Closes #14519
2026-02-14 19:59:26 +01:00
Joe Mooring 498431d641 docs: Fix lineNos default value in docs.yaml 2026-02-14 19:15:02 +01:00
Bjørn Erik Pedersen 773664b812 hugolib: Fix term title when taxonomy name contains spaces
The pluralTreeKey is normalized (spaces become hyphens), but the
unnormalized path preserves spaces. Use the plural name directly
for prefix trimming on the unnormalized path.

Fixes #13422

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 19:14:40 +01:00
Bjørn Erik Pedersen 7729473500 Update AI assistance guidelines in CONTRIBUTING.md
Clarified expectations for AI contributions from non-maintainers.
2026-02-14 18:21:47 +01:00
Bjørn Erik Pedersen 47e39af2cf tpl/collections: Speed up where and sort performance
```bash
goos: darwin
goarch: arm64
pkg: github.com/gohugoio/hugo/tpl/collections
cpu: Apple M1 Pro
                                        │ master.bench │       fix-whereperfalloc.bench       │
                                        │    sec/op    │   sec/op     vs base                 │
WhereOps/eq-10                            139.62µ ± 3%   66.04µ ± 2%  -52.70% (p=0.002 n=6)
WhereOps/ne-10                            139.54µ ± 1%   65.68µ ± 2%  -52.93% (p=0.002 n=6)
WhereOps/like-10                           179.8µ ± 3%   106.7µ ± 1%  -40.66% (p=0.002 n=6)
WhereSliceOfStructPointersWithMethod-10    350.7µ ± 1%   252.2µ ± 2%  -28.09% (p=0.002 n=6)
WhereAndSortPages/Where-10                120.93µ ± 1%   81.69µ ± 1%  -32.45% (p=0.002 n=6)
WhereAndSortPages/Sort-10                  1.608m ± 1%   1.165m ± 1%  -27.52% (p=0.002 n=6)
geomean                                    117.2µ        149.4µ       -40.03%               ¹

                                        │ master.bench  │       fix-whereperfalloc.bench        │
                                        │     B/op      │     B/op      vs base                 │
WhereOps/eq-10                             52.18Ki ± 0%   36.62Ki ± 0%  -29.82% (p=0.002 n=6)
WhereOps/ne-10                             52.18Ki ± 0%   36.62Ki ± 0%  -29.82% (p=0.002 n=6)
WhereOps/like-10                           52.20Ki ± 0%   36.63Ki ± 0%  -29.83% (p=0.002 n=6)
WhereSliceOfStructPointersWithMethod-10    64.52Ki ± 0%   64.59Ki ± 0%   +0.12% (p=0.002 n=6)
WhereAndSortPages/Where-10                 21.05Ki ± 0%   21.11Ki ± 0%   +0.30% (p=0.002 n=6)
WhereAndSortPages/Sort-10                 591.15Ki ± 0%   51.82Ki ± 0%  -91.23% (p=0.002 n=6)
geomean                                    7.690Ki        38.91Ki       -44.13%               ¹

                                        │ master.bench │       fix-whereperfalloc.bench       │
                                        │  allocs/op   │  allocs/op   vs base                 │
WhereOps/eq-10                             2.515k ± 0%   1.517k ± 0%  -39.68% (p=0.002 n=6)
WhereOps/ne-10                             2.515k ± 0%   1.517k ± 0%  -39.68% (p=0.002 n=6)
WhereOps/like-10                           2.515k ± 0%   1.517k ± 0%  -39.68% (p=0.002 n=6)
WhereSliceOfStructPointersWithMethod-10    4.503k ± 0%   4.504k ± 0%   +0.02% (p=0.002 n=6)
WhereAndSortPages/Where-10                 1.029k ± 0%   1.030k ± 0%   +0.10% (p=0.002 n=6)
WhereAndSortPages/Sort-10                 20.736k ± 0%   1.012k ± 0%  -95.12% (p=0.002 n=6)
geomean                                     358.5        1.594k       -53.04%               ¹
```
2026-02-14 16:33:15 +01:00
Bjørn Erik Pedersen 4a641d1594 tpl/internal: Replace deprecated parser.ParseDir and doc.New
Replace parser.ParseDir (deprecated since Go 1.25) with manual
file iteration using parser.ParseFile, and replace doc.New with
doc.NewFromFiles.

Fixes #14513

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 14:49:09 +01:00
Bjørn Erik Pedersen 8b7b6af92c docs: Regenerate docs.yaml
Mostly so I can test #14513
2026-02-14 12:15:58 +01:00
Bjørn Erik Pedersen 30a20122b7 Merge commit '8f3c066d23f431fb2c53d97ea489e4c28b42bd82'
# Conflicts:
#	docs/data/docs.yaml
2026-02-14 12:14:33 +01:00
Bjørn Erik Pedersen 8f3c066d23 Squashed 'docs/' changes from 1ad3c75ad..42914c50e
42914c50e content: Correct module mount example
d7b80be5f content: Add a "vendor" glossary entry
e1bd65866 content: Miscellaneous edits
deb1c4b1c content: Fix formatting
7ff740aae content: Update capitalizeListTitles documentation
a9f40164f content: Remove data namespace from functions
b3386a13a content: Remove expired content
e21f66fa5 content: Clean up deprecated service configurations
39ab42192 content: Change default value of respectDoNotTrack to true
067cbb771 Update partial-decorators.md
b419af329 content: Minor edits to collections functions
b12dd9366 content: Improve documentation for partial decorators
573b8610f content: Improve descriptions and signatures for collections functions
81befbae8 content: Update version references
684dbcedb Update HUGO_VERSION to 0.155.3
198e5f1df misc: Update README.md
4d20bbc04 content: Correct and improve glob patterns documentation
39d369ddc content: Update disablePathToLower documentation
be2da46e9 content: Miscellaneous edits to configuration documentation
19a580828 content: Add Commentix to the list of commercial commenting systems
d350725cd theme: Pin npm dependencies and adjust Netlify build commands
9b33bb5fa content: Define canonical output format and default site
84b72792e content: Document glob pattern matching logic
1839dd114 content: Create glob pattern quick reference
30f8f9ca0 content: Revise multi-dimensional content model description
b7e36e479 content: Fix formatting
b1722ac6b content: Indicate data types returned by the or function
1bbe3a566 content: Fix typo
b7e1ac7bb content: Improve example of _redirects rendering
37c71ef80 content: Improve and/or descriptions
0226c14ed content: Updates for v0.155.0
301fa208b content: Change GitHub Pages workflow to support dot files
5d07a679c Update HUGO_VERSION to 0.155.2
adb8e8b2c theme: Pin turbo
9206cb64b Update netlify.toml
84e712633 content: Refine ToMath admonition and provide example
a099af3cd content: Warn about conditional KaTeX CSS on list pages
c3ca7ac4c misc: Address build warnings
80a9139f4 Update HUGO_VERSION to 0.155.0
b096f6279 content: Restore homebrew on linux instructions
05132aa29 content: Hide snap installation instructions
d0e448a14 content: Refactor pages related to image processing
d141bc04f content: Clarify default site redirect description
1d6a0bed5 content: Adjust paragraph order
fa53534cb content: Clarify usage of quote characters in templates
1d62aa670 content: Miscellaneous edits
579c0678b content: Fix description of disableDefaultLanguageRedirect
cd12583ee Update all.md
37b01c320 Update HUGO_VERSION to 0.154.5
66053b6df content: Update version references
3f6fb8737 content: Refresh deployment dependencies to latest versions
c2915ee98 theme: Add a mobile menu variant
6457c3f59 Remove trailing whitespace from content/en/configuration/all.md
46c4d117a config: Mention disableDefaultDimensionRedirect
338500de0 Regen docs helper
98b9d25f3 Updates for 0.154.4 release
8306956e2 Update HUGO_VERSION to 0.154.4
af3f62992 Update HUGO_VERSION to 0.154.3
d06de65bd content: Fix typo
0d841a59e content: Fix typo
ed6fa87cf content: Fix typo
dcf80dbea content: Describe Markdown attribute access in render hook templates
70d3513f1 content: Update version references
7e581c529 Update netlify.toml
c6a706943 content: Remove expired pages
e64ee7d54 misc: Update workflows
bbec5a198 Update HUGO_VERSION to 0.154.1
e8717cb72 content: Fix formatting of glossary entry
0da233817 Add a link to templates.Inner from the partial decorator glossary entry
d55f7ad5e Update Inner.md
b674dfd48 Some more about templates.Inner
ef354e664 Remove some old new-in markers
7d1e407fd content: Misc updates for v0.154.0
43589a017 Update HUGO_VERSION to 0.154.0
46f832b9b content: Change shortcode usage note to an admonition
71ea41250 Adjust sponstors.toml
a5dc99838 Update netlify.toml
c3edd4d5b Update netlify.toml
c1d306d6b Add Zoomment to comments management options
cb222be99 Update HUGO_VERSION to 0.153.3
92ee87b4f content: Update CI/CD and version references
170bf3693 content: Note disabled extended check in v0.153.2 and later
34b5ef8b3 Update HUGO_VERSION to 0.153.2
c3c42b171 content: Add Dart Sass installation to SourceHut CI workflow
1768cc2f5 content: Fix links
63ba04562 theme: Modernize sans-serif font stack for cross-platform consistency
287da5c1c content: Update edition comparison and guidance
44b5010ca content: Update min version required for quick start
ec4f60cb7 content: Update version references
fafa69e7c Update HUGO_VERSION to 0.153.1
37bedd7d3 content: Fix formatting
ed9056088 content: Miscellaneous edits
fa4d61004 content: Miscellaneous post-release updates for v0.153.0
b99f47449 Misc v0.153.0 related edits
018c3e969 Regen docs.yml
be80ec4c7 content: Document new dimensions
ba397d5aa content: Miscellaneous updates for v0.153.0
59ca8fa3f Update HUGO_VERSION to 0.153.0

git-subtree-dir: docs
git-subtree-split: 42914c50ee338b00d0b1cda2c3af8066cd9ad65c
2026-02-14 12:13:44 +01:00
Joe Mooring 7850e7ceb5 commands: Skip chmod for files without owner-write permission
Closes #14507

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 18:23:56 +01:00
dependabot[bot] 86786c91e5 build(deps): bump github.com/gohugoio/hugo-goldmark-extensions/extras
Bumps [github.com/gohugoio/hugo-goldmark-extensions/extras](https://github.com/gohugoio/hugo-goldmark-extensions) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/gohugoio/hugo-goldmark-extensions/releases)
- [Commits](https://github.com/gohugoio/hugo-goldmark-extensions/compare/extras/v0.5.0...extras/v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/gohugoio/hugo-goldmark-extensions/extras
  dependency-version: 0.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 13:43:28 +01:00
dependabot[bot] 7a7308aec2 build(deps): bump github.com/gohugoio/hugo-goldmark-extensions/passthrough
Bumps [github.com/gohugoio/hugo-goldmark-extensions/passthrough](https://github.com/gohugoio/hugo-goldmark-extensions) from 0.3.1 to 0.4.0.
- [Release notes](https://github.com/gohugoio/hugo-goldmark-extensions/releases)
- [Commits](https://github.com/gohugoio/hugo-goldmark-extensions/compare/passthrough/v0.3.1...extras/v0.4.0)

---
updated-dependencies:
- dependency-name: github.com/gohugoio/hugo-goldmark-extensions/passthrough
  dependency-version: 0.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 12:42:44 +01:00
Bjørn Erik Pedersen 2e5132d8d2 tpl/collections: Add some more benchmarks for where and sort 2026-02-13 10:34:39 +01:00
Joe Mooring 19ab3f581c all: Change site to project where appropriate
Closes #14504
2026-02-12 20:52:56 +01:00
Joe Mooring 7137714827 docker: Add full tar and openssh-client to support GitHub Actions
Closes #14502
2026-02-12 19:35:14 +01:00
Joe Mooring 3158a51283 markup/highlight: Allow lineNos to be true, false, "inline", or "table"
Closes #13481
2026-02-12 19:34:39 +01:00
Bjørn Erik Pedersen af5051e759 Remove items deprecated <= v0.136.0 (note)
Template functions:
- data.GetCSV / getCSV (use resources.GetRemote)
- data.GetJSON / getJSON (use resources.GetRemote)
- crypto.FNV32a (use hash.FNV32a)
- resources.Babel (use js.Babel)
- resources.PostCSS (use css.PostCSS)
- resources.ToCSS (use css.Sass)

Page methods:
- .Page.NextPage (use .Page.Next)
- .Page.PrevPage (use .Page.Prev)

Paginator:
- .Paginator.PageSize (use .Paginator.PagerSize)

Site methods:
- .Site.LastChange (use .Site.Lastmod)
- .Site.Author (use .Site.Params.Author)
- .Site.Authors (use .Site.Params.Authors)
- .Site.Social (use .Site.Params.Social)
- .Site.IsMultiLingual (use hugo.IsMultilingual)
- .Sites.First (use .Sites.Default)

Site config:
- paginate (use pagination.pagerSize)
- paginatePath (use pagination.path)

File caches:
- getjson cache
- getcsv cache
2026-02-12 19:33:58 +01:00
Bjørn Erik Pedersen b358a65c8b Upgrade to Go 1.26 2026-02-12 15:34:33 +01:00
Bjørn Erik Pedersen 8ae5e1a8be tpl: Move from md5 to xxhash for some in memory keys 2026-02-12 15:27:00 +01:00
dependabot[bot] 2d1d7312d0 build(deps): bump golang.org/x/tools from 0.41.0 to 0.42.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.41.0 to 0.42.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.41.0...v0.42.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 14:10:05 +01:00
dependabot[bot] b26fbf80a0 build(deps): bump github.com/bep/helpers from 0.6.0 to 0.7.0
Bumps [github.com/bep/helpers](https://github.com/bep/helpers) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/bep/helpers/releases)
- [Commits](https://github.com/bep/helpers/compare/v0.6.0...v0.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 14:09:09 +01:00
Joe Mooring 828ee720cc resources/page: Deprecate cascade._target in favor of cascade.target
Closes #14341
2026-02-12 13:50:37 +01:00
Joe Mooring 4384f9b8c2 tpl/tplimpl: Throw error when calling gist shortcode
Closes #14491
2026-02-12 13:49:23 +01:00
Joe Mooring 55d136dd66 tpl/tplimpl: Throw error when calling twitter/twitter_simple shortcodes
Closes #14488
2026-02-12 11:59:09 +01:00
Dr. Tobias Quathamer e2776593c0 testscripts/commands: Update 'future' date to far future
Fixes #14486
2026-02-11 13:34:10 +01:00
Joe Mooring de0c9f3eca Reapply "release: Support alpha, beta, and RC releases"
This reverts commit e66a33d369.

Closes #14466
2026-02-10 10:40:29 +01:00
dependabot[bot] 73583091e8 build(deps): bump golang.org/x/image from 0.35.0 to 0.36.0
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.35.0 to 0.36.0.
- [Commits](https://github.com/golang/image/compare/v0.35.0...v0.36.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 10:39:02 +01:00
897 changed files with 13481 additions and 8587 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ parameters:
defaults: &defaults
resource_class: large
docker:
- image: bepsays/ci-hugoreleaser:1.22500.20400
- image: bepsays/ci-hugoreleaser:1.22600.20100
environment: &buildenv
GOMODCACHE: /root/project/gomodcache
version: 2
@@ -58,7 +58,7 @@ jobs:
environment:
<<: [*buildenv]
docker:
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22500.20400
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22600.20100
steps:
- *restore-cache
- &attach-workspace
-10
View File
@@ -1,10 +0,0 @@
🛑 Before submitting this PR
All PRs must be linked to an Issue in this repository, and that Issue must be approved by a project maintainer:
- For a bug, approval means that the "needs triage" label is removed.
- For a proposal, approval means that the "proposal" label is replaced by an "enhancement" label.
PRs without a linked, approved Issue will be closed immediately.
Please review the contribution guide (CONTRIBUTING.md) to understand commit message guidelines and AI assistance disclosures.
-22
View File
@@ -1,22 +0,0 @@
name: AI Detection
on:
# Use pull_request_target instead of pull_request (we need the secrets.OPENAI_API_KEY to be available for forks)
pull_request_target:
types: [opened, ready_for_review]
permissions:
contents: read
pull-requests: write
jobs:
detect-ai:
runs-on: ubuntu-latest
if: ${{ !github.event.pull_request.draft }}
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: AI Watchdog
uses: bep/ai-watchdog@995475dd81767f8035dc2c943e9230918d06caca # temporary fork (probably)
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
pr-label: AI Suspect
fail-when-confident: false
# CSV list.
pr-user-logins-skip: dependabot[bot],bep,jmooring
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.24.x, 1.25.x]
go-version: [1.25.x, 1.26.x]
os: [ubuntu-latest, windows-latest] # macos disabled for now because of disk space issues.
runs-on: ${{ matrix.os }}
steps:
+3
View File
@@ -7,6 +7,9 @@
* Never export symbols that's not needed outside of the package.
* Avoid global state at (almost) all cost.
* This is a project with a long history; assume that a similiar problem has been solved before, look hard for helper functions before creating new ones.
* In tests, use `qt` matchers (e.g. `b.Assert(err, qt.ErrorMatches, ...)`) instead of raw `if`/`t.Fatal` checks.
* In tests, always use the latest Hugo specification, e.g. for layouts, it's `layouts/page.html` and not `layouts/_default/single.html`, `layouts/list.html` and not `layouts/_default/list.html`
* Brevity is good. This applies to code, comments and commit messages. Don't write a novel.
* Use `./check.sh ./somepackage/...` when iterating.
* Use `./check.sh` when you're done.
+1 -1
View File
@@ -88,7 +88,7 @@ An example disclosure:
> This PR was written primarily by Claude Code.
When using AI assistance, we expect contributors to understand the code that is produced and be able to answer critical questions about it.
When using AI assistance, we expect contributors to understand the code that is produced and be able to answer critical questions about it. Also, AI contributions from non-maintainers needs to have a fairly narrow scope (e.g. a bug fix), as we have limited review capacity.
### Git Commit Message Guidelines
+4 -2
View File
@@ -2,7 +2,7 @@
# Twitter: https://twitter.com/gohugoio
# Website: https://gohugo.io/
ARG GO_VERSION="1.25"
ARG GO_VERSION="1.26"
ARG ALPINE_VERSION="3.22"
ARG DART_SASS_VERSION="1.79.3"
@@ -65,7 +65,9 @@ RUN apk add --no-cache \
git \
runuser \
nodejs \
npm
npm \
openssh-client \
tar
RUN mkdir -p /var/hugo/bin /cache && \
addgroup -Sg 1000 hugo && \
+66 -45
View File
@@ -1,14 +1,19 @@
[Clang]: https://clang.llvm.org/
[GCC]: https://gcc.gnu.org/
[Git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
[Go]: https://go.dev/doc/install
[bep]: https://github.com/bep
[bugs]: https://github.com/gohugoio/hugo/issues?q=is%3Aopen+is%3Aissue+label%3ABug
[contributing]: CONTRIBUTING.md
[create a proposal]: https://github.com/gohugoio/hugo/issues/new?labels=Proposal%2C+NeedsTriage&template=feature_request.md
[dart sass]: https://gohugo.io/functions/css/sass/#dart-sass
[details]: https://gohugo.io/host-and-deploy/deploy-with-hugo-deploy/
[documentation repository]: https://github.com/gohugoio/hugoDocs
[documentation]: https://gohugo.io/documentation
[dragonfly bsd, freebsd, netbsd, and openbsd]: https://gohugo.io/installation/bsd
[features]: https://gohugo.io/about/features/
[forum]: https://discourse.gohugo.io
[friends]: https://github.com/gohugoio/hugo/graphs/contributors
[go]: https://go.dev/
[hugo modules]: https://gohugo.io/hugo-modules/
[installation]: https://gohugo.io/installation
[issue queue]: https://github.com/gohugoio/hugo/issues
@@ -20,12 +25,13 @@
[static site generator]: https://en.wikipedia.org/wiki/Static_site_generator
[support]: https://discourse.gohugo.io
[themes]: https://themes.gohugo.io/
[transpile sass to css]: https://gohugo.io/functions/css/sass/
[website]: https://gohugo.io
[windows]: https://gohugo.io/installation/windows
<a href="https://gohugo.io/"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/hugo-logo-wide.svg?sanitize=true" alt="Hugo" width="565"></a>
A fast and flexible static site generator built with love by [bep], [spf13], and [friends] in [Go].
A fast and flexible static site generator built with love by [bep][], [spf13][], and [friends][] in Go.
---
@@ -33,11 +39,11 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
[![Tests on Linux, MacOS and Windows](https://github.com/gohugoio/hugo/workflows/Test/badge.svg)](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/gohugoio/hugo)](https://goreportcard.com/report/github.com/gohugoio/hugo)
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
[Website][] | [Installation][] | [Documentation][] | [Support][] | [Contributing][] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
## Overview
Hugo is a [static site generator] written in [Go], optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
Hugo is a [static site generator][] written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create:
@@ -52,14 +58,15 @@ Use Hugo's embedded web server during development to instantly see changes to co
Hugo's fast asset pipelines include:
- CSS Processing &ndash; Bundle, transform, minify, create source maps, perform SRI hashing, and integrate with PostCSS.
- Image processing &ndash; Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract metadata
- JavaScript bundling &ndash; Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing.
- Sass processing &ndash; Transpile Sass to CSS, bundle, tree shake, minify, create source maps, perform SRI hashing, and integrate with PostCSS
- Tailwind CSS processing &ndash; Compile Tailwind CSS utility classes into standard CSS, bundle, tree shake, optimize, minify, perform SRI hashing, and integrate with PostCSS
And with [Hugo Modules], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
And with [Hugo Modules][], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
See the [features] section of the documentation for a comprehensive summary of Hugo's capabilities.
See the [features][] section of the documentation for a comprehensive summary of Hugo's capabilities.
## Sponsors
@@ -72,49 +79,63 @@ See the [features] section of the documentation for a comprehensive summary of H
## Editions
Hugo offers a standard edition with core features, plus extended and extended/deploy editions with more. Use the standard edition unless you need the features below.
Hugo is available in several editions. Use the standard edition unless you need additional features.
Feature|extended edition|extended/deploy edition
:--|:-:|:-:
[Transpile Sass to CSS] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass] transpiler instead, which is compatible with any edition.|:heavy_check_mark:|:heavy_check_mark:
Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See&nbsp;[details].|:x:|:heavy_check_mark:
Feature|standard|deploy|extended|extended/deploy
:--|:-:|:-:|:-:|:-:
Core features|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:
Direct cloud deployment (1)|:x:|:heavy_check_mark:|:x:|:heavy_check_mark:
LibSass support (2)|:x:|:x:|:heavy_check_mark:|:heavy_check_mark:
[dart sass]: https://gohugo.io/functions/css/sass/#dart-sass
[transpile sass to css]: https://gohugo.io/functions/css/sass/
[details]: https://gohugo.io/hosting-and-deployment/hugo-deploy/
(1) Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See&nbsp;[details][].
(2) [Transpile Sass to CSS][] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass][] transpiler instead, which is compatible with any edition.
## Installation
Install Hugo from a [prebuilt binary], package manager, or package repository. Please see the installation instructions for your operating system:
Install Hugo from a [prebuilt binary][], package manager, or package repository. Please see the installation instructions for your operating system:
- [macOS]
- [Linux]
- [Windows]
- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD]
- [macOS][]
- [Linux][]
- [Windows][]
- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD][]
## Build from source
Prerequisites to build Hugo from source:
To build Hugo from source you must install:
- Standard edition: Go 1.24.0 or later
- Extended edition: Go 1.24.0 or later, and GCC
- Extended/deploy edition: Go 1.24.0 or later, and GCC
1. [Git][]
1. [Go][] version 1.25.0 or later
Build the standard edition:
### Standard edition
```text
go install github.com/gohugoio/hugo@latest
To build and install the standard edition:
```sh
CGO_ENABLED=0 go install github.com/gohugoio/hugo@latest
```
Build the extended edition:
### Deploy edition
```text
To build and install the deploy edition:
```sh
CGO_ENABLED=0 go install -tags withdeploy github.com/gohugoio/hugo@latest
```
### Extended edition
To build and install the extended edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command.
```sh
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
```
Build the extended/deploy edition:
### Extended/deploy edition
```text
To build and install the extended/deploy edition, first install a C compiler such as [GCC][] or [Clang][] and then run the following command.
```sh
CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest
```
@@ -124,35 +145,35 @@ CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@late
## Documentation
Hugo's [documentation] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
Hugo's [documentation][] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
Please submit documentation issues and pull requests to the [documentation repository].
Please submit documentation issues and pull requests to the [documentation repository][].
## Support
Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum].
Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum][].
Hugos [forum] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question.
Hugo's [forum][] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help][] before asking your first question.
## Contributing
You can contribute to the Hugo project by:
- Answering questions on the [forum]
- Improving the [documentation]
- Monitoring the [issue queue]
- Creating or improving [themes]
- Squashing [bugs]
- Answering questions on the [forum][]
- Improving the [documentation][]
- Monitoring the [issue queue][]
- Creating or improving [themes][]
- Squashing [bugs][]
Please submit documentation issues and pull requests to the [documentation repository].
Please submit documentation issues and pull requests to the [documentation repository][].
If you have an idea for an enhancement or new feature, create a new topic on the [forum] in the "Feature" category. This will help you to:
If you have an idea for an enhancement or new feature, create a new topic on the [forum][] in the "Feature" category. This will help you to:
- Determine if the capability already exists
- Measure interest
- Refine the concept
If there is sufficient interest, [create a proposal]. Do not submit a pull request until the project lead accepts the proposal.
If there is sufficient interest, [create a proposal][]. Do not submit a pull request until the project lead accepts the proposal.
For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
@@ -162,9 +183,9 @@ For the Hugo source code, see [LICENSE](/LICENSE).
We also bundle some libraries in binary/WASM form:
* [libwebp](https://github.com/webmproject/libwebp), [BSD-3-Clause license](https://github.com/webmproject/libwebp?tab=BSD-3-Clause-1-ov-file#readme)
* [Katex](https://github.com/KaTeX/KaTeX), [MIT license](https://github.com/KaTeX/KaTeX?tab=MIT-1-ov-file#readme)
* [QuickJS](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme), [License](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme)
- [libwebp](https://github.com/webmproject/libwebp), [BSD-3-Clause license](https://github.com/webmproject/libwebp?tab=BSD-3-Clause-1-ov-file#readme)
- [Katex](https://github.com/KaTeX/KaTeX), [MIT license](https://github.com/KaTeX/KaTeX?tab=MIT-1-ov-file#readme)
- [QuickJS](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme), [License](https://github.com/bellard/quickjs?tab=License-1-ov-file#readme)
## Dependencies
+100 -61
View File
@@ -1,4 +1,4 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
// Copyright 2026 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.
@@ -46,17 +46,9 @@ const (
type Cache struct {
Fs afero.Fs
// Max age for items in this cache. Negative duration means forever,
// 0 is effectively turning this cache off.
maxAge time.Duration
cfg FileCacheConfig
// When set, we just remove this entire root directory on expiration.
pruneAllRootDir string
nlocker *lockTracker
isResourceDir bool
baseDir string
entryLocker *lockTracker
initOnce sync.Once
isInited bool
@@ -93,12 +85,15 @@ type ItemInfo struct {
}
// NewCache creates a new file cache with the given filesystem and max age.
func NewCache(fs afero.Fs, maxAge time.Duration, pruneAllRootDir string) *Cache {
func NewCache(fs afero.Fs, cfg FileCacheConfig) *Cache {
if err := cfg.init(); err != nil {
panic(fmt.Sprintf("invalid cache config: %s", err))
}
return &Cache{
Fs: fs,
nlocker: &lockTracker{Locker: locker.NewLocker(), seen: make(map[string]struct{})},
maxAge: maxAge,
pruneAllRootDir: pruneAllRootDir,
Fs: fs,
entryLocker: &lockTracker{Locker: locker.NewLocker(), seen: make(map[string]struct{})},
cfg: cfg,
}
}
@@ -117,6 +112,7 @@ func (c *Cache) init() error {
if c == nil {
panic("cache is nil")
}
c.initOnce.Do(func() {
c.isInited = true
// Create the base dir if it does not exist.
@@ -136,19 +132,19 @@ func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) {
}
id = cleanID(id)
c.nlocker.Lock(id)
c.entryLocker.Lock(id)
info := ItemInfo{Name: id}
f, err := helpers.OpenFileForWriting(c.Fs, id)
if err != nil {
c.nlocker.Unlock(id)
c.entryLocker.Unlock(id)
return info, nil, err
}
return info, &lockedFile{
File: f,
unlock: func() { c.nlocker.Unlock(id) },
unlock: func() { c.entryLocker.Unlock(id) },
}, nil
}
@@ -166,8 +162,8 @@ func (c *Cache) ReadOrCreate(id string,
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info = ItemInfo{Name: id}
@@ -196,9 +192,9 @@ func (c *Cache) ReadOrCreate(id string,
// NamedLock locks the given id. The lock is released when the returned function is called.
func (c *Cache) NamedLock(id string) func() {
id = cleanID(id)
c.nlocker.Lock(id)
c.entryLocker.Lock(id)
return func() {
c.nlocker.Unlock(id)
c.entryLocker.Unlock(id)
}
}
@@ -211,8 +207,8 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
}
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info := ItemInfo{Name: id}
@@ -230,7 +226,7 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
return info, nil, err
}
if c.maxAge == 0 {
if c.cfg.MaxAge == 0 {
// No caching.
return info, hugio.ToReadCloser(r), nil
}
@@ -241,6 +237,40 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
c.writeReader(id, io.TeeReader(r, &buff))
}
// AbsFilenameFromID returns the filename for the given id in the cache.
// This will be an absolute path.
func (c *Cache) AbsFilenameFromID(id string) string {
return filepath.Join(c.cfg.DirCompiled, cleanID(id))
}
// GetOrCreateInfo tries to get the item info with the given id from cache. If not found or expired, create will
// be invoked with the id. The create function is expected to create the cache item with the given id. The returned ItemInfo will have the id as Name.
// This method is protected by a named lock using the given id as identifier.
func (c *Cache) GetOrCreateInfo(id string, create func(id string) error) (ItemInfo, error) {
if err := c.init(); err != nil {
return ItemInfo{}, err
}
id = cleanID(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info := ItemInfo{Name: id}
if !c.removeIfNeeded(id) {
// The file exists and is not expired, so we consider it a cache hit.
return info, nil
}
if err := create(id); err != nil {
c.remove(id)
return info, err
}
return info, nil
}
func (c *Cache) writeReader(id string, r io.Reader) error {
dir := filepath.Dir(id)
if dir != "" {
@@ -264,8 +294,8 @@ func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (Item
}
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info := ItemInfo{Name: id}
@@ -285,7 +315,7 @@ func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (Item
return info, nil, err
}
if c.maxAge == 0 {
if c.cfg.MaxAge == 0 {
return info, b, nil
}
@@ -303,10 +333,10 @@ func (c *Cache) SetBytes(id string, data []byte) error {
}
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
if c.maxAge == 0 {
if c.cfg.MaxAge == 0 {
// No caching.
return nil
}
@@ -327,8 +357,8 @@ func (c *Cache) GetItemBytes(id string) (ItemInfo, []byte, error) {
}
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info := ItemInfo{Name: id}
@@ -348,8 +378,8 @@ func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) {
}
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
info := ItemInfo{Name: id}
@@ -358,10 +388,23 @@ func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) {
return info, r, nil
}
// removeIfNeeded checks if the file with the given id should be re-created.
func (c *Cache) removeIfNeeded(id string) bool {
if c.cfg.MaxAge == 0 {
// No caching, remove.
c.remove(id)
return true
}
if removed, err := c.removeIfExpired(id); err != nil || removed {
return true
}
return false
}
// getOrRemove gets the file with the given id. If it's expired, it will
// be removed.
func (c *Cache) getOrRemove(id string) hugio.ReadSeekCloser {
if c.maxAge == 0 {
if c.cfg.MaxAge == 0 {
// No caching.
return nil
}
@@ -379,7 +422,7 @@ func (c *Cache) getOrRemove(id string) hugio.ReadSeekCloser {
}
func (c *Cache) getBytesAndRemoveIfExpired(id string) ([]byte, bool) {
if c.maxAge == 0 {
if c.cfg.MaxAge == 0 {
// No caching.
return nil, false
}
@@ -404,7 +447,7 @@ func (c *Cache) getBytesAndRemoveIfExpired(id string) ([]byte, bool) {
}
func (c *Cache) removeIfExpired(id string) (bool, error) {
if c.maxAge <= 0 {
if c.cfg.MaxAge <= 0 {
return false, nil
}
@@ -414,29 +457,37 @@ func (c *Cache) removeIfExpired(id string) (bool, error) {
}
if c.isExpired(fi.ModTime()) {
c.Fs.Remove(id)
c.remove(id)
return true, nil
}
return false, nil
}
func (c *Cache) remove(id string) {
if c.cfg.entryIsDir {
c.Fs.RemoveAll(id)
} else {
c.Fs.Remove(id)
}
}
func (c *Cache) isExpired(modTime time.Time) bool {
if c.maxAge < 0 {
if c.cfg.MaxAge < 0 {
return false
}
// Note the use of time.Since here.
// We cannot use Hugo's global Clock for this.
return c.maxAge == 0 || time.Since(modTime) > c.maxAge
return c.cfg.MaxAge == 0 || time.Since(modTime) > c.cfg.MaxAge
}
// For testing
func (c *Cache) GetString(id string) string {
id = cleanID(id)
c.nlocker.Lock(id)
defer c.nlocker.Unlock(id)
c.entryLocker.Lock(id)
defer c.entryLocker.Unlock(id)
f, err := c.Fs.Open(id)
if err != nil {
@@ -453,11 +504,11 @@ type Caches map[string]*Cache
func (f Caches) SetResourceFs(fs afero.Fs) {
for _, c := range f {
if c.isResourceDir {
if c.cfg.IsResourceDir {
if c.isInited {
panic("cannot set resource fs after init")
}
c.Fs = hugofs.NewBasePathFs(fs, c.baseDir)
c.Fs = hugofs.NewBasePathFs(fs, c.cfg.DirCompiled)
}
}
}
@@ -470,7 +521,6 @@ func (f Caches) Get(name string) *Cache {
// NewCaches creates a new set of file caches from the given
// configuration.
func NewCaches(dcfg Configs, sourceFs afero.Fs) (Caches, error) {
// dcfg := p.Cfg.GetConfigSection("caches").(Configs)
fs := sourceFs
m := make(Caches)
@@ -479,21 +529,10 @@ func NewCaches(dcfg Configs, sourceFs afero.Fs) (Caches, error) {
if v.IsResourceDir {
cfs = nil // Set later. TODO(bep) this needs to be cleanded up.
} else {
cfs = fs
cfs = hugofs.NewBasePathFs(fs, v.DirCompiled)
}
baseDir := v.DirCompiled
bfs := hugofs.NewBasePathFs(cfs, baseDir)
var pruneAllRootDir string
if k == CacheKeyModules {
pruneAllRootDir = "pkg"
}
c := NewCache(bfs, v.MaxAge, pruneAllRootDir)
c.isResourceDir = v.IsResourceDir
c.baseDir = baseDir
c := NewCache(cfs, v)
m[k] = c
}
@@ -524,7 +563,7 @@ func (h *httpCache) Get(id string) (resp []byte, ok bool) {
}
func (h *httpCache) Set(id string, resp []byte) {
if h.c.maxAge == 0 {
if h.c.cfg.MaxAge == 0 {
return
}
+60 -30
View File
@@ -35,18 +35,12 @@ const (
cacheDirProject = ":cacheDir/:project"
)
var defaultCacheConfig = FileCacheConfig{
MaxAge: -1, // Never expire
Dir: cacheDirProject,
}
const (
CacheKeyGetJSON = "getjson"
CacheKeyGetCSV = "getcsv"
CacheKeyImages = "images"
CacheKeyAssets = "assets"
CacheKeyModules = "modules"
CacheKeyModuleQueries = "modulequeries"
CacheKeyModuleGitInfo = "modulegitinfo"
CacheKeyGetResource = "getresource"
CacheKeyMisc = "misc"
)
@@ -69,13 +63,22 @@ var defaultCacheConfigs = Configs{
CacheKeyModules: {
MaxAge: -1,
Dir: ":cacheDir/modules",
fileCacheConfigInternal: fileCacheConfigInternal{
entryIsDir: true,
isReadOnly: true, // we need to make it writable when pruning.
},
},
CacheKeyModuleQueries: {
MaxAge: 24 * time.Hour,
Dir: ":cacheDir/modules",
},
CacheKeyGetJSON: defaultCacheConfig,
CacheKeyGetCSV: defaultCacheConfig,
CacheKeyModuleGitInfo: {
MaxAge: 24 * time.Hour,
Dir: ":cacheDir/modules",
fileCacheConfigInternal: fileCacheConfigInternal{
entryIsDir: true,
},
},
CacheKeyImages: {
MaxAge: -1,
Dir: resourcesGenDir,
@@ -94,6 +97,13 @@ var defaultCacheConfigs = Configs{
},
}
func init() {
for k, v := range defaultCacheConfigs {
v.name = k
defaultCacheConfigs[k] = v
}
}
type FileCacheConfig struct {
// Max age of cache entries in this cache. Any items older than this will
// be removed and not returned from the cache.
@@ -105,12 +115,33 @@ type FileCacheConfig struct {
MaxAge time.Duration
// The directory where files are stored.
Dir string
DirCompiled string `json:"-"`
Dir string
// Will resources/_gen will get its own composite filesystem that
// also checks any theme.
IsResourceDir bool `json:"-"`
fileCacheConfigInternal `json:"-"`
}
func (cfg *FileCacheConfig) init() error {
if cfg.DirCompiled == "" {
// From unit tests. Just check that it does not contain any placeholders.
if strings.Contains(cfg.Dir, ":") {
return fmt.Errorf("cache dir %q contains unresolved placeholders", cfg.Dir)
}
cfg.DirCompiled = cfg.Dir
}
// Sanity check the config.
if len(cfg.DirCompiled) < 5 {
panic(fmt.Sprintf("invalid cache dir: %q", cfg.DirCompiled))
}
return nil
}
type fileCacheConfigInternal struct {
DirCompiled string
name string // The name of this cache, e.g. "images", "modules" etc.
entryIsDir bool // when set, the cache entries represents directories directly below the base dir.
isReadOnly bool // when set, the cache is read only and needs to be pruned differently. This is used for the Go modules cache.
IsResourceDir bool // resources/_gen will get its own composite filesystem that also checks any theme. TODO(bep) unexport this.
}
// MarshalJSON marshals FileCacheConfig to JSON with MaxAge as a human-readable string.
@@ -130,16 +161,6 @@ func (c FileCacheConfig) MarshalJSON() ([]byte, error) {
})
}
// GetJSONCache gets the file cache for getJSON.
func (f Caches) GetJSONCache() *Cache {
return f[CacheKeyGetJSON]
}
// GetCSVCache gets the file cache for getCSV.
func (f Caches) GetCSVCache() *Cache {
return f[CacheKeyGetCSV]
}
// ImageCache gets the file cache for processed images.
func (f Caches) ImageCache() *Cache {
return f[CacheKeyImages]
@@ -160,6 +181,15 @@ func (f Caches) ModuleQueriesCache() *Cache {
return c
}
// ModuleGitInfoCache gets the file cache for Hugo Module git info.
func (f Caches) ModuleGitInfoCache() *Cache {
c, ok := f[CacheKeyModuleGitInfo]
if !ok {
panic("module git info cache not set")
}
return c
}
// AssetsCache gets the file cache for assets (processed resources, SCSS etc.).
func (f Caches) AssetsCache() *Cache {
return f[CacheKeyAssets]
@@ -190,7 +220,11 @@ func DecodeConfig(fs afero.Fs, bcfg config.BaseConfig, m map[string]any) (Config
if _, ok := v.(hmaps.Params); !ok {
continue
}
cc := defaultCacheConfig
var ok bool
cc, ok := c[k]
if !ok {
return nil, fmt.Errorf("%q is not a valid cache name", k)
}
dc := &mapstructure.DecoderConfig{
Result: &cc,
@@ -211,12 +245,8 @@ func DecodeConfig(fs afero.Fs, bcfg config.BaseConfig, m map[string]any) (Config
return c, errors.New("must provide cache Dir")
}
name := strings.ToLower(k)
if !valid[name] {
return nil, fmt.Errorf("%q is not a valid cache name", name)
}
c[k] = cc
c[name] = cc
}
for k, v := range c {
+15 -17
View File
@@ -44,12 +44,9 @@ assetDir = "assets"
archetypeDir = "archetypes"
[caches]
[caches.getJSON]
[caches.misc]
maxAge = "10m"
dir = "/path/to/c1"
[caches.getCSV]
maxAge = "11h"
dir = "/path/to/c2"
[caches.images]
dir = "/path/to/c3"
[caches.getResource]
@@ -60,11 +57,11 @@ dir = "/path/to/c4"
c.Assert(err, qt.IsNil)
fs := afero.NewMemMapFs()
decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches
c.Assert(len(decoded), qt.Equals, 8)
c.Assert(len(decoded), qt.Equals, 7)
c2 := decoded["getcsv"]
c.Assert(c2.MaxAge.String(), qt.Equals, "11h0m0s")
c.Assert(c2.DirCompiled, qt.Equals, filepath.FromSlash("/path/to/c2/filecache/getcsv"))
c2 := decoded["misc"]
c.Assert(c2.MaxAge.String(), qt.Equals, "10m0s")
c.Assert(c2.DirCompiled, qt.Equals, filepath.FromSlash("/path/to/c1/filecache/misc"))
c3 := decoded["images"]
c.Assert(c3.MaxAge, qt.Equals, time.Duration(-1))
@@ -91,12 +88,9 @@ archeTypedir = "archetypes"
ignoreCache = true
[caches]
[caches.getJSON]
[caches.misc]
maxAge = 1234
dir = "/path/to/c1"
[caches.getCSV]
maxAge = 3456
dir = "/path/to/c2"
[caches.images]
dir = "/path/to/c3"
[caches.getResource]
@@ -107,7 +101,7 @@ dir = "/path/to/c4"
c.Assert(err, qt.IsNil)
fs := afero.NewMemMapFs()
decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches
c.Assert(len(decoded), qt.Equals, 8)
c.Assert(len(decoded), qt.Equals, 7)
for _, v := range decoded {
c.Assert(v.MaxAge, qt.Equals, time.Duration(0))
@@ -130,20 +124,20 @@ func TestDecodeConfigDefault(t *testing.T) {
fs := afero.NewMemMapFs()
decoded := testconfig.GetTestConfigs(fs, cfg).Base.Caches
c.Assert(len(decoded), qt.Equals, 8)
c.Assert(len(decoded), qt.Equals, 7)
imgConfig := decoded[filecache.CacheKeyImages]
jsonConfig := decoded[filecache.CacheKeyGetJSON]
miscConfig := decoded[filecache.CacheKeyMisc]
if runtime.GOOS == "windows" {
c.Assert(imgConfig.DirCompiled, qt.Equals, filepath.FromSlash("_gen/images"))
} else {
c.Assert(imgConfig.DirCompiled, qt.Equals, "_gen/images")
c.Assert(jsonConfig.DirCompiled, qt.Equals, "/cache/thecache/hugoproject/filecache/getjson")
c.Assert(miscConfig.DirCompiled, qt.Equals, "/cache/thecache/hugoproject/filecache/misc")
}
c.Assert(imgConfig.IsResourceDir, qt.Equals, true)
c.Assert(jsonConfig.IsResourceDir, qt.Equals, false)
c.Assert(miscConfig.IsResourceDir, qt.Equals, false)
}
func TestFileCacheConfigMarshalJSON(t *testing.T) {
@@ -159,6 +153,10 @@ func TestFileCacheConfigMarshalJSON(t *testing.T) {
moduleQueriesConfig := decoded[filecache.CacheKeyModuleQueries]
c.Assert(moduleQueriesConfig.MaxAge, qt.Equals, 24*time.Hour)
// Also verify the new moduleGitInfo cache.
moduleGitInfoConfig := decoded[filecache.CacheKeyModuleGitInfo]
c.Assert(moduleGitInfoConfig.MaxAge, qt.Equals, 24*time.Hour)
b, err := json.Marshal(moduleQueriesConfig)
c.Assert(err, qt.IsNil)
+6 -7
View File
@@ -18,7 +18,6 @@ import (
"testing"
"time"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib"
@@ -39,9 +38,9 @@ title: "Home"
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{T: t, TxtarString: files, RunGC: true, NeedsOsFS: true},
).Build()
b := hugolib.Test(t, files, hugolib.TestOptOsFs(), hugolib.TestOptWithConfig(func(c *hugolib.IntegrationTestConfig) {
c.RunGC = true
}))
_, err := b.H.BaseFs.ResourcesCache.Stat(filepath.Join("_gen", "images"))
@@ -77,9 +76,9 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{T: t, TxtarString: files, Running: true, RunGC: true, NeedsOsFS: true, LogLevel: logg.LevelInfo},
).Build()
b := hugolib.TestRunning(t, files, hugolib.TestOptOsFs(), hugolib.TestOptInfo(), hugolib.TestOptWithConfig(func(c *hugolib.IntegrationTestConfig) {
c.RunGC = true
}))
b.Assert(b.GCCount, qt.Equals, 0)
b.Assert(b.H, qt.IsNotNil)
+43 -7
View File
@@ -50,8 +50,8 @@ func (c Caches) Prune() (int, error) {
// Prune removes expired and unused items from this cache.
// If force is set, everything will be removed not considering expiry time.
func (c *Cache) Prune(force bool) (int, error) {
if c.pruneAllRootDir != "" {
return c.pruneRootDir(force)
if c.cfg.entryIsDir {
return c.pruneRootDirs(force)
}
if err := c.init(); err != nil {
return 0, err
@@ -93,9 +93,9 @@ func (c *Cache) Prune(force bool) (int, error) {
shouldRemove := force || c.isExpired(info.ModTime())
if !shouldRemove && len(c.nlocker.seen) > 0 {
if !shouldRemove && len(c.entryLocker.seen) > 0 {
// Remove it if it's not been touched/used in the last build.
_, seen := c.nlocker.seen[name]
_, seen := c.entryLocker.seen[name]
shouldRemove = !seen
}
@@ -117,11 +117,43 @@ func (c *Cache) Prune(force bool) (int, error) {
return counter, err
}
func (c *Cache) pruneRootDir(force bool) (int, error) {
func (c *Cache) pruneRootDirs(force bool) (int, error) {
dirs, err := afero.ReadDir(c.Fs, "")
if err != nil {
if herrors.IsNotExist(err) {
return 0, nil
}
return 0, err
}
counter := 0
for _, dir := range dirs {
if !dir.IsDir() {
continue
}
count, err := c.pruneRootDir(dir.Name(), force)
if err != nil {
return counter, err
}
counter += count
}
return counter, nil
}
func (c *Cache) pruneRootDir(dirname string, force bool) (int, error) {
if err := c.init(); err != nil {
return 0, err
}
info, err := c.Fs.Stat(c.pruneAllRootDir)
// Sanity check.
if dirname != "pkg" && len(dirname) < 5 {
panic(fmt.Sprintf("invalid cache dir name: %q", dirname))
}
info, err := c.Fs.Stat(dirname)
if err != nil {
if herrors.IsNotExist(err) {
return 0, nil
@@ -133,5 +165,9 @@ func (c *Cache) pruneRootDir(force bool) (int, error) {
return 0, nil
}
return hugofs.MakeReadableAndRemoveAllModulePkgDir(c.Fs, c.pruneAllRootDir)
if c.cfg.isReadOnly {
return hugofs.MakeReadableAndRemoveAllModulePkgDir(c.Fs, dirname)
}
return 1, c.Fs.RemoveAll(dirname)
}
+2 -5
View File
@@ -39,12 +39,9 @@ assetDir = "assets"
archeTypedir = "archetypes"
[caches]
[caches.getjson]
[caches.misc]
maxAge = "200ms"
dir = "/cache/c"
[caches.getcsv]
maxAge = "200ms"
dir = "/cache/d"
[caches.assets]
maxAge = "200ms"
dir = ":resourceDir/_gen"
@@ -53,7 +50,7 @@ maxAge = "200ms"
dir = ":resourceDir/_gen"
`
for _, name := range []string{filecache.CacheKeyGetCSV, filecache.CacheKeyGetJSON, filecache.CacheKeyAssets, filecache.CacheKeyImages} {
for _, name := range []string{filecache.CacheKeyAssets, filecache.CacheKeyImages} {
msg := qt.Commentf("cache: %s", name)
fs := afero.NewMemMapFs()
p := newPathsSpec(t, fs, configStr)
+13 -10
View File
@@ -18,12 +18,12 @@ import (
"fmt"
"io"
"strings"
"github.com/gohugoio/hugo/htesting"
"sync"
"testing"
"time"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/config"
@@ -66,7 +66,7 @@ assetDir = "assets"
archeTypedir = "archetypes"
[caches]
[caches.getJSON]
[caches.misc]
maxAge = "10h"
dir = ":cacheDir/c"
@@ -86,7 +86,7 @@ dir = ":cacheDir/c"
c.Assert(err, qt.IsNil)
caches.SetResourceFs(p.SourceFs)
cache := caches.Get("GetJSON")
cache := caches.Get("Misc")
c.Assert(cache, qt.Not(qt.IsNil))
cache = caches.Get("Images")
@@ -108,7 +108,7 @@ dir = ":cacheDir/c"
return []byte("bcd"), nil
}
for _, ca := range []*filecache.Cache{caches.ImageCache(), caches.AssetsCache(), caches.GetJSONCache(), caches.GetCSVCache()} {
for _, ca := range []*filecache.Cache{caches.ImageCache(), caches.AssetsCache()} {
for range 2 {
info, r, err := ca.GetOrCreate("a", rf("abc"))
c.Assert(err, qt.IsNil)
@@ -136,8 +136,6 @@ dir = ":cacheDir/c"
}
}
c.Assert(caches.Get("getJSON"), qt.Not(qt.IsNil))
info, w, err := caches.ImageCache().WriteCloser("mykey")
c.Assert(err, qt.IsNil)
c.Assert(info.Name, qt.Equals, "mykey")
@@ -177,7 +175,7 @@ assetDir = "assets"
archeTypedir = "archetypes"
[caches]
[caches.getjson]
[caches.misc]
maxAge = "1s"
dir = "/cache/c"
@@ -189,7 +187,7 @@ dir = "/cache/c"
c.Assert(err, qt.IsNil)
caches.SetResourceFs(p.Fs.Source)
const cacheName = "getjson"
const cacheName = "misc"
filenameData := func(i int) (string, string) {
data := fmt.Sprintf("data: %d", i)
@@ -254,7 +252,12 @@ func TestFileCacheReadOrCreateErrorInRead(t *testing.T) {
}
}
cache := filecache.NewCache(afero.NewMemMapFs(), 100*time.Hour, "")
cfg := filecache.FileCacheConfig{
MaxAge: 100 * time.Hour,
Dir: "cache/c",
}
cache := filecache.NewCache(afero.NewMemMapFs(), cfg)
const id = "a32"
+1 -1
View File
@@ -52,7 +52,7 @@ run_staticcheck() {
run_tests() {
echo "==> Running tests..."
local output
if ! output=$(go test $PACKAGES 2>&1); then
if ! output=$(go test -failfast $PACKAGES 2>&1); then
echo "$output"
exit 1
fi
+1 -1
View File
@@ -73,7 +73,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T
nameAndPackage := func(t reflect.Type) (string, string) {
var name, pkg string
isPointer := t.Kind() == reflect.Ptr
isPointer := t.Kind() == reflect.Pointer
if isPointer {
t = t.Elem()
+3 -3
View File
@@ -26,9 +26,9 @@ import (
func TestMethods(t *testing.T) {
var (
zeroIE = reflect.TypeOf((*IEmbed)(nil)).Elem()
zeroIEOnly = reflect.TypeOf((*IEOnly)(nil)).Elem()
zeroI = reflect.TypeOf((*I)(nil)).Elem()
zeroIE = reflect.TypeFor[IEmbed]()
zeroIEOnly = reflect.TypeFor[IEOnly]()
zeroI = reflect.TypeFor[I]()
)
dir, _ := os.Getwd()
+23 -11
View File
@@ -97,6 +97,7 @@ type commonConfig struct {
type configKey struct {
counter int32
ignoreModulesDoesNotExists bool
skipNpmCheck bool
}
// This is the root command.
@@ -131,6 +132,7 @@ type rootCommand struct {
gc bool
poll string
forceSyncStatic bool
panicOnWarning bool
// Profile flags (for debugging of performance problems)
cpuprofile string
@@ -194,6 +196,7 @@ func (r *rootCommand) ConfigFromConfig(key configKey, oldConf *commonConfig) (*c
Logger: r.logger,
Environment: r.environment,
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
SkipNpmCheck: key.skipNpmCheck,
},
)
if err != nil {
@@ -250,6 +253,7 @@ func (r *rootCommand) ConfigFromProvider(key configKey, cfg config.Provider) (*c
Environment: r.environment,
Logger: r.logger,
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
SkipNpmCheck: key.skipNpmCheck,
},
)
if err != nil {
@@ -488,12 +492,18 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
}
}
var logHookLast func(e *logg.Entry) error
if r.panicOnWarning {
logHookLast = loggers.PanicOnWarningHook
}
optsLogger := loggers.Options{
DistinctLevel: logg.LevelWarn,
Level: level,
StdOut: r.StdOut,
StdErr: r.StdErr,
StoreErrors: running,
HandlerPost: logHookLast,
}
return loggers.New(optsLogger), nil
@@ -520,8 +530,8 @@ func (r *rootCommand) initRootCommand(subCommandName string, cd *simplecobra.Com
commandName = subCommandName
}
cmd.Use = fmt.Sprintf("%s [flags]", commandName)
cmd.Short = "Build your site"
cmd.Long = `COMMAND_NAME is the main command, used to build your Hugo site.
cmd.Short = "Build your project"
cmd.Long = `COMMAND_NAME is the main command, used to build your Hugo project.
Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.
@@ -591,7 +601,7 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
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().BoolVar(&r.panicOnWarning, "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")
cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.")
@@ -622,13 +632,14 @@ func (r *rootCommand) timeTrack(start time.Time, name string) {
}
type simpleCommand struct {
use string
name string
short string
long string
run func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *rootCommand, args []string) error
withc func(cmd *cobra.Command, r *rootCommand)
initc func(cd *simplecobra.Commandeer) error
use string
name string
short string
long string
aliases []string
run func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *rootCommand, args []string) error
withc func(cmd *cobra.Command, r *rootCommand)
initc func(cd *simplecobra.Commandeer) error
commands []simplecobra.Commander
@@ -655,6 +666,7 @@ func (c *simpleCommand) Init(cd *simplecobra.Commandeer) error {
cmd := cd.CobraCommand
cmd.Short = c.short
cmd.Long = c.long
cmd.Aliases = c.aliases
if c.use != "" {
cmd.Use = c.use
}
@@ -672,7 +684,7 @@ func (c *simpleCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
}
func mapLegacyArgs(args []string) []string {
if len(args) > 1 && args[0] == "new" && !hstrings.EqualAny(args[1], "site", "theme", "content") {
if len(args) > 1 && args[0] == "new" && !hstrings.EqualAny(args[1], "project", "site", "theme", "content") {
// Insert "content" as the second argument
args = append(args[:1], append([]string{"content"}, args[1:]...)...)
}
+2 -2
View File
@@ -112,8 +112,8 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
c.r = cd.Root.Command.(*rootCommand)
cmd := cd.CobraCommand
cmd.Short = "Display site configuration"
cmd.Long = `Display site configuration, both default and custom settings.`
cmd.Short = "Display project configuration"
cmd.Long = `Display project 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.")
+58
View File
@@ -18,10 +18,12 @@ import (
"context"
"fmt"
"path/filepath"
"slices"
"strings"
"time"
"github.com/bep/simplecobra"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
@@ -200,6 +202,56 @@ func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, tar
return nil
}
func (c *convertCommand) copyContentDirsForOutput(pagesBackedByFile page.Pages) error {
contentDirs := make(map[string]bool)
for _, p := range pagesBackedByFile {
filename := p.File().Filename()
contentDir := strings.TrimSuffix(filename, p.File().Path())
if contentDir == filename {
continue
}
contentDirs[filepath.Clean(contentDir)] = true
}
var contentDirList []string
for contentDir := range contentDirs {
contentDirList = append(contentDirList, contentDir)
}
slices.Sort(contentDirList)
outputDirAbs, err := filepath.Abs(c.outputDir)
if err != nil {
return fmt.Errorf("failed to resolve output path %q: %w", c.outputDir, err)
}
for _, contentDir := range contentDirList {
outputContentDirAbs := filepath.Join(outputDirAbs, filepath.Base(contentDir))
skipDirs := make(map[string]bool)
relToOutputDir, err := filepath.Rel(contentDir, outputDirAbs)
if err == nil && relToOutputDir != ".." && !strings.HasPrefix(relToOutputDir, ".."+string(filepath.Separator)) {
skipDirs[filepath.Clean(outputDirAbs)] = true
}
relToOutputContentDir, err := filepath.Rel(contentDir, outputContentDirAbs)
if err == nil && relToOutputContentDir != ".." && !strings.HasPrefix(relToOutputContentDir, ".."+string(filepath.Separator)) {
skipDirs[filepath.Clean(outputContentDirAbs)] = true
}
var shouldCopy func(filename string) bool
if len(skipDirs) > 0 {
shouldCopy = func(filename string) bool {
return !skipDirs[filepath.Clean(filename)]
}
}
if err := hugio.CopyDir(hugofs.Os, contentDir, outputContentDirAbs, shouldCopy); err != nil {
return fmt.Errorf("failed to copy %q to %q: %w", contentDir, outputContentDirAbs, err)
}
}
return nil
}
func (c *convertCommand) convertContents(format metadecoders.Format) error {
if c.outputDir == "" && !c.unsafe {
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
@@ -219,6 +271,12 @@ func (c *convertCommand) convertContents(format metadecoders.Format) error {
pagesBackedByFile = append(pagesBackedByFile, p)
}
if c.outputDir != "" {
if err := c.copyContentDirsForOutput(pagesBackedByFile); err != nil {
return err
}
}
site.Log.Println("processing", len(pagesBackedByFile), "content files")
for _, p := range site.AllPages() {
if err := c.convertAndSavePage(p, site, format); err != nil {
+2 -2
View File
@@ -27,8 +27,8 @@ import (
func newDeployCommand() simplecobra.Commander {
return &simpleCommand{
name: "deploy",
short: "Deploy your site to a cloud provider",
long: `Deploy your site to a cloud provider
short: "Deploy your project to a cloud provider",
long: `Deploy your project to a cloud provider
See https://gohugo.io/hosting-and-deployment/hugo-deploy/ for detailed
documentation.
+2 -2
View File
@@ -60,7 +60,7 @@ func newGenCommand() *genCommand {
short: "Generate CSS stylesheet for the Chroma code highlighter",
long: `Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if markup.highlight.noClasses is disabled in config.
See https://xyproto.github.io/splash/docs/all.html for a preview of the available styles`,
See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
style = strings.ToLower(style)
@@ -98,7 +98,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
},
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.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style")
_ = cmd.RegisterFlagCompletionFunc("style", cobra.NoFileCompletions)
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", `foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`)
_ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions)
+1 -1
View File
@@ -81,7 +81,7 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
"editor": "newContentEditor",
}
// Flags that we for some reason don't want to expose in the site config.
// Flags that we for some reason don't want to expose in the project config.
internalKeySet := map[string]bool{
"quiet": true,
"verbose": true,
+2 -1
View File
@@ -179,6 +179,7 @@ func (c *hugoBuilder) initCPUProfile() (func(), error) {
return nil, fmt.Errorf("failed to create CPU profile: %w", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
f.Close()
return nil, fmt.Errorf("failed to start CPU profile: %w", err)
}
return func() {
@@ -1113,7 +1114,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
if len(conf.configs.LoadingInfo.ConfigFiles) == 0 {
//lint:ignore ST1005 end user message.
return errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\nRun `hugo help new` for details.")
return errors.New("Unable to locate config file or config directory. Perhaps you need to create a new project.\nRun `hugo help new` for details.")
}
c.conf = conf
+8 -8
View File
@@ -49,7 +49,7 @@ func newImportCommand() *importCommand {
name: "jekyll",
short: "hugo import from Jekyll",
long: `hugo import from Jekyll.
Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
if len(args) < 2 {
@@ -90,8 +90,8 @@ func (c *importCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
func (c *importCommand) Init(cd *simplecobra.Commandeer) error {
cmd := cd.CobraCommand
cmd.Short = "Import a site from another system"
cmd.Long = `Import a site from another system.
cmd.Short = "Import a project from another system"
cmd.Long = `Import a project from another system.
Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`."
@@ -105,7 +105,7 @@ func (c *importCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
}
func (i *importCommand) createConfigFromJekyll(fs afero.Fs, inpath string, kind metadecoders.Format, jekyllConfig map[string]any) (err error) {
title := "My New Hugo Site"
title := "My New Hugo Project"
baseURL := "http://example.org/"
for key, value := range jekyllConfig {
@@ -127,7 +127,7 @@ func (i *importCommand) createConfigFromJekyll(fs afero.Fs, inpath string, kind
in := map[string]any{
"baseURL": baseURL,
"title": title,
"languageCode": "en-us",
"locale": "en-us",
"disablePathToLower": true,
}
@@ -159,7 +159,7 @@ func (c *importCommand) getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[st
return postDirs, hasAnyPost
}
func (c *importCommand) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool) error {
func (c *importCommand) createProjectFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool) error {
fs := &afero.OsFs{}
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
@@ -419,7 +419,7 @@ func (c *importCommand) importFromJekyll(args []string) error {
return errors.New("abort: jekyll root contains neither posts nor drafts")
}
err = c.createSiteFromJekyll(jekyllRoot, targetDir, jekyllPostDirs)
err = c.createProjectFromJekyll(jekyllRoot, targetDir, jekyllPostDirs)
if err != nil {
return newUserError(err)
}
@@ -463,7 +463,7 @@ func (c *importCommand) importFromJekyll(args []string) error {
}
c.r.Println("Congratulations!", fileCount, "post(s) imported!")
c.r.Println("Now, start Hugo by yourself:\n")
c.r.Println("Now, start Hugo by yourself:")
c.r.Println("cd " + args[1])
c.r.Println("git init")
c.r.Println("git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke")
+23 -13
View File
@@ -21,12 +21,13 @@ import (
"github.com/bep/simplecobra"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/modules/npm"
"github.com/spf13/cobra"
)
const commonUsageMod = `
Note that Hugo will always start out by resolving the components defined in the site
Note that Hugo will always start out by resolving the components defined in the project
configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided),
Go Modules, or a folder inside the themes directory, in that order.
@@ -49,28 +50,34 @@ func newModCommands() *modCommands {
commands: []simplecobra.Commander{
&simpleCommand{
name: "pack",
short: "Experimental: Prepares and writes a composite package.json file for your project",
long: `Prepares and writes a composite package.json file for your project.
short: "Merges module Node.js dependencies into an npm workspace",
long: `Merges Node.js dependencies from all Hugo modules into a "packages/hugoautogen" npm workspace.
On first run it creates a "package.hugo.json" in the project root if not already there. This file will be used as a template file
with the base dependency set.
The merged dependencies are written to packages/hugoautogen/package.json, and the root package.json
is updated with a "workspaces" entry pointing to "packages/hugoautogen".
This set will be merged with all "package.hugo.json" files found in the dependency tree, picking the version closest to the project.
The source entries are read from either package.hugo.json or package.json in the module root, with package.hugo.json taking precedence if both exist.
This command is marked as 'Experimental'. We think it's a great idea, so it's not likely to be
removed from Hugo, but we need to test this out in "real life" to get a feel of it,
so this may/will change in future versions of Hugo.
See [Node.js dependencies](/hugo-modules/nodejs-dependencies/) for more information.
`,
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 {
h, err := r.Hugo(flagsToCfg(cd, nil))
cfg := flagsToCfg(cd, nil)
k := configKey{counter: r.configVersionID.Load(), skipNpmCheck: true}
h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) {
conf, err := r.ConfigFromProvider(key, cfg)
if err != nil {
return nil, err
}
return hugolib.NewHugoSites(r.newDepsConfig(conf))
})
if err != nil {
return err
}
return npm.Pack(h.BaseFs.ProjectSourceFs, h.BaseFs.AssetsWithDuplicatesPreserved.Fs)
return npm.Pack(h.BaseFs.ProjectSourceFs, h.BaseFs.AssetsWithDuplicatesPreserved.Fs, h.Configs.Modules)
},
},
},
@@ -166,7 +173,7 @@ Note that for vendored modules, that is the version listed and not the one from
if all {
modCache := h.ResourceSpec.FileCaches.ModulesCache()
count, err := modCache.Prune(true)
r.Printf("Deleted %d files from module cache.", count)
r.Printf("Deleted %d directories from module cache.", count)
return err
}
@@ -293,7 +300,10 @@ Run "go help get" for more information. All flags available for "go get" is also
return err
}
client := conf.configs.ModulesClient
return client.Get(args...)
if err := client.Get(args...); err != nil {
return err
}
return nil
}
},
},
+13 -12
View File
@@ -46,9 +46,9 @@ It will guess which kind of file to create based on the path provided.
You can also specify the kind with ` + "`-k KIND`" + `.
If archetypes are provided in your theme or site, they will be used.
If archetypes are provided in your theme or project, they will be used.
Ensure you run this within the root directory of your site.`,
Ensure you run this within the root directory of your project.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
if len(args) < 1 {
return newUserError("path needs to be provided")
@@ -76,10 +76,11 @@ Ensure you run this within the root directory of your site.`,
},
},
&simpleCommand{
name: "site",
use: "site [path]",
short: "Create a new site",
long: `Create a new site at the specified path.`,
name: "project",
use: "project [path]",
short: "Create a new project",
long: `Create a new project at the specified path.`,
aliases: []string{"site"},
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
if len(args) < 1 {
return newUserError("path needs to be provided")
@@ -99,13 +100,13 @@ Ensure you run this within the root directory of your site.`,
}
sourceFs := conf.fs.Source
err = skeletons.CreateSite(createpath, sourceFs, force, format)
err = skeletons.CreateProject(createpath, sourceFs, force, format)
if err != nil {
return err
}
r.Printf("Congratulations! Your new Hugo site was created in %s.\n\n", createpath)
r.Println(c.newSiteNextStepsText(createpath, format))
r.Printf("Congratulations! Your new Hugo project was created in %s.\n\n", createpath)
r.Println(c.newProjectNextStepsText(createpath, format))
return nil
},
@@ -192,9 +193,9 @@ It will guess which kind of file to create based on the path provided.
You can also specify the kind with ` + "`-k KIND`" + `.
If archetypes are provided in your theme or site, they will be used.
If archetypes are provided in your theme or project, they will be used.
Ensure you run this within the root directory of your site.`
Ensure you run this within the root directory of your project.`
cmd.RunE = nil
return nil
@@ -205,7 +206,7 @@ func (c *newCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
return nil
}
func (c *newCommand) newSiteNextStepsText(path string, format string) string {
func (c *newCommand) newProjectNextStepsText(path string, format string) string {
format = strings.ToLower(format)
var nextStepsText bytes.Buffer
+3 -1
View File
@@ -28,13 +28,14 @@ func newReleaseCommand() simplecobra.Commander {
step int
skipPush bool
try bool
version string
)
return &simpleCommand{
name: "release",
short: "Release a new version of Hugo",
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
rel, err := releaser.New(skipPush, try, step)
rel, err := releaser.New(skipPush, try, step, version)
if err != nil {
return err
}
@@ -47,6 +48,7 @@ func newReleaseCommand() simplecobra.Commander {
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.PersistentFlags().StringVarP(&version, "version", "", "", "version to release (derived from branch name if not set)")
_ = cmd.RegisterFlagCompletionFunc("step", cobra.FixedCompletions([]string{"1", "2"}, cobra.ShellCompDirectiveNoFileComp))
},
}
+12 -12
View File
@@ -246,7 +246,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
logger := f.c.r.logger
if i == 0 {
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment())
mainTarget := "disk"
if f.c.r.renderToMemory {
mainTarget = "memory"
@@ -523,7 +523,7 @@ func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
func (c *serverCommand) Init(cd *simplecobra.Commandeer) error {
cmd := cd.CobraCommand
cmd.Short = "Start the embedded web server"
cmd.Long = `Hugo provides its own webserver which builds and serves the site.
cmd.Long = `Hugo provides its own webserver which builds and serves the project.
While hugo server is high performance, it is a webserver with limited options.
The ` + "`" + `hugo server` + "`" + ` command will by default write and serve files from disk, but
@@ -531,8 +531,8 @@ you can render to memory by using the ` + "`" + `--renderToMemory` + "`" + ` fla
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
and push the latest content to them. As most Hugo sites are built in a fraction
automatically rebuild the project. It will then live reload any open browser pages
and push the latest content to them. As most Hugo projects are built in a fraction
of a second, you will be able to save and see your changes nearly instantly.`
cmd.Aliases = []string{"serve"}
@@ -553,7 +553,7 @@ of a second, you will be able to save and see your changes nearly instantly.`
cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL")
cmd.Flags().BoolVar(&c.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
cmd.Flags().BoolVarP(&c.navigateToChanged, "navigateToChanged", "N", false, "navigate to changed content file on live browser reload")
cmd.Flags().BoolVarP(&c.openBrowser, "openBrowser", "O", false, "open the site in a browser after server startup")
cmd.Flags().BoolVarP(&c.openBrowser, "openBrowser", "O", false, "open the project in a browser after server startup")
cmd.Flags().BoolVar(&c.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory")
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")
@@ -1160,14 +1160,14 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
return err
}
// chmodFilter is a ChmodFilter for static syncing.
// Returns true to skip syncing permissions for directories and files without
// owner-write permission. The primary use case is files from the module cache (0444).
func chmodFilter(dst, src os.FileInfo) bool {
// Hugo publishes data from multiple sources, potentially
// with overlapping directory structures. We cannot sync permissions
// for directories as that would mean that we might end up with write-protected
// directories inside /public.
// One example of this would be syncing from the Go Module cache,
// which have 0555 directories.
return src.IsDir()
if src.IsDir() {
return true
}
return src.Mode().Perm()&0o200 == 0
}
func cleanErrorLog(content string) string {
+2 -4
View File
@@ -38,9 +38,7 @@ func TestXxHashFromReaderPara(t *testing.T) {
var wg sync.WaitGroup
for i := range 10 {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
for j := range 100 {
s := strings.Repeat("Hello ", i+j+1*42)
r := strings.NewReader(s)
@@ -50,7 +48,7 @@ func TestXxHashFromReaderPara(t *testing.T) {
expect, _ := XXHashFromString(s)
c.Assert(got, qt.Equals, expect)
}
}()
})
}
wg.Wait()
+3 -3
View File
@@ -32,15 +32,15 @@ func TestNewFileError(t *testing.T) {
fe := NewFileErrorFromName(errors.New("bar"), "foo.html")
c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`)
lines := ""
var lines strings.Builder
for i := 1; i <= 100; i++ {
lines += fmt.Sprintf("line %d\n", i)
lines.WriteString(fmt.Sprintf("line %d\n", i))
}
fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2})
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`)
fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212})
fe.UpdateContent(strings.NewReader(lines), nil)
fe.UpdateContent(strings.NewReader(lines.String()), nil)
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`)
errorContext := fe.ErrorContext()
c.Assert(errorContext, qt.IsNotNil)
+1 -1
View File
@@ -96,7 +96,7 @@ func errConvert(v reflect.Value, s string) error {
// See Issue 14079.
func ConvertIfPossible(val reflect.Value, typ reflect.Type) (reflect.Value, bool) {
switch val.Kind() {
case reflect.Ptr, reflect.Interface:
case reflect.Pointer, reflect.Interface:
if val.IsNil() {
// Return typ's zero value.
return reflect.Zero(typ), true
+8 -8
View File
@@ -27,7 +27,7 @@ import (
// IsInterfaceOrPointer returns whether the given kind is an interface or a pointer.
func IsInterfaceOrPointer(kind reflect.Kind) bool {
return kind == reflect.Interface || kind == reflect.Ptr
return kind == reflect.Interface || kind == reflect.Pointer
}
// TODO(bep) replace the private versions in /tpl with these.
@@ -92,7 +92,7 @@ func IsSlice(v any) bool {
return reflect.ValueOf(v).Kind() == reflect.Slice
}
var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem()
var zeroType = reflect.TypeFor[types.Zeroer]()
var isZeroCache sync.Map
@@ -136,7 +136,7 @@ func IsTruthfulValue(val reflect.Value) (truth bool) {
truth = val.Bool()
case reflect.Complex64, reflect.Complex128:
truth = val.Complex() != 0
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
case reflect.Chan, reflect.Func, reflect.Pointer, reflect.UnsafePointer, reflect.Interface:
truth = !val.IsNil()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
truth = val.Int() != 0
@@ -216,8 +216,8 @@ func GetMethodIndexByName(tp reflect.Type, name string) int {
}
var (
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
asTimeProviderType = reflect.TypeOf((*htime.AsTimeProvider)(nil)).Elem()
timeType = reflect.TypeFor[time.Time]()
asTimeProviderType = reflect.TypeFor[htime.AsTimeProvider]()
)
// IsTime returns whether tp is a time.Time type or if it can be converted into one
@@ -240,7 +240,7 @@ func IsValid(v reflect.Value) bool {
}
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return !v.IsNil()
}
@@ -347,13 +347,13 @@ func IsNil(v reflect.Value) bool {
return true
}
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return v.IsNil()
}
return false
}
var contextInterface = reflect.TypeOf((*context.Context)(nil)).Elem()
var contextInterface = reflect.TypeFor[context.Context]()
var isContextCache = hmaps.NewCache[reflect.Type, bool]()
+18 -18
View File
@@ -18,10 +18,10 @@ import (
"strings"
"time"
"github.com/bep/golocales"
"github.com/bep/clocks"
"github.com/spf13/cast"
"github.com/gohugoio/locales"
)
var (
@@ -78,9 +78,9 @@ var (
Clock = clocks.System()
)
func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
func NewTimeFormatter(ltr golocales.Translator) TimeFormatter {
if ltr == nil {
panic("must provide a locales.Translator")
panic("must provide a golocales.Translator")
}
return TimeFormatter{
ltr: ltr,
@@ -89,7 +89,7 @@ func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
// TimeFormatter is locale aware.
type TimeFormatter struct {
ltr locales.Translator
ltr golocales.Translator
}
func (f TimeFormatter) Format(t time.Time, layout string) string {
@@ -101,39 +101,39 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
// It may be one of Hugo's custom layouts.
switch strings.ToLower(layout[1:]) {
case "date_full":
return f.ltr.FmtDateFull(t)
return f.ltr.FormatDateFull(t)
case "date_long":
return f.ltr.FmtDateLong(t)
return f.ltr.FormatDateLong(t)
case "date_medium":
return f.ltr.FmtDateMedium(t)
return f.ltr.FormatDateMedium(t)
case "date_short":
return f.ltr.FmtDateShort(t)
return f.ltr.FormatDateShort(t)
case "time_full":
return f.ltr.FmtTimeFull(t)
return f.ltr.FormatTimeFull(t)
case "time_long":
return f.ltr.FmtTimeLong(t)
return f.ltr.FormatTimeLong(t)
case "time_medium":
return f.ltr.FmtTimeMedium(t)
return f.ltr.FormatTimeMedium(t)
case "time_short":
return f.ltr.FmtTimeShort(t)
return f.ltr.FormatTimeShort(t)
}
}
s := t.Format(layout)
monthIdx := t.Month() - 1 // Month() starts at 1.
monthIdx := t.Month() - 1 // time.Month is 1-based, but our month name slices are 0-based.
dayIdx := t.Weekday()
if strings.Contains(layout, "January") {
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthsWide()[monthIdx])
} else if strings.Contains(layout, "Jan") {
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthsAbbreviated()[monthIdx])
}
if strings.Contains(layout, "Monday") {
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdaysWide()[dayIdx])
} else if strings.Contains(layout, "Mon") {
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdaysAbbreviated()[dayIdx])
}
return s
+19 -18
View File
@@ -17,8 +17,9 @@ import (
"testing"
"time"
"github.com/bep/golocales"
qt "github.com/frankban/quicktest"
translators "github.com/gohugoio/localescompressed"
)
func TestTimeFormatter(t *testing.T) {
@@ -34,7 +35,7 @@ func TestTimeFormatter(t *testing.T) {
mondayNovemberFirst = mondayNovemberFirst.Add(33 * time.Second)
c.Run("Norsk nynorsk", func(c *qt.C) {
f := NewTimeFormatter(translators.GetTranslator("nn"))
f := NewTimeFormatter(golocales.New("nn"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "onsdag juni 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "on. juni 6 2018")
@@ -42,35 +43,35 @@ func TestTimeFormatter(t *testing.T) {
})
c.Run("Custom layouts Norsk nynorsk", func(c *qt.C) {
f := NewTimeFormatter(translators.GetTranslator("nn"))
f := NewTimeFormatter(golocales.New("nn"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "onsdag 6. juni 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "6. juni 2018")
c.Assert(f.Format(june06, ":date_medium"), qt.Equals, "6. juni 2018")
c.Assert(f.Format(june06, ":date_short"), qt.Equals, "06.06.2018")
c.Assert(f.Format(june06, ":date_short"), qt.Equals, "06.06.18")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "kl. 02:09:37 UTC")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "02:09:37 UTC")
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "02:09:37 UTC")
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "02:09:37")
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "02:09")
})
c.Run("Custom layouts English", func(c *qt.C) {
f := NewTimeFormatter(translators.GetTranslator("en"))
f := NewTimeFormatter(golocales.New("en"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "Wednesday, June 6, 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "June 6, 2018")
c.Assert(f.Format(june06, ":date_medium"), qt.Equals, "Jun 6, 2018")
c.Assert(f.Format(june06, ":date_short"), qt.Equals, "6/6/18")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "2:09:37 am UTC")
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "2:09:37 am UTC")
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "2:09:37 am")
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "2:09 am")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "2:09:37\u202fam UTC")
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "2:09:37\u202fam UTC")
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "2:09:37\u202fam")
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "2:09\u202fam")
})
c.Run("English", func(c *qt.C) {
f := NewTimeFormatter(translators.GetTranslator("en"))
f := NewTimeFormatter(golocales.New("en"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "Wednesday Jun 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "Wed June 6 2018")
@@ -78,31 +79,31 @@ func TestTimeFormatter(t *testing.T) {
})
c.Run("Weekdays German", func(c *qt.C) {
tr := translators.GetTranslator("de")
tr := golocales.New("de")
f := NewTimeFormatter(tr)
// Issue #9107
for i, weekDayWideGerman := range []string{"Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"} {
date := mondayNovemberFirst.Add(time.Duration(i*24) * time.Hour)
c.Assert(tr.WeekdayWide(date.Weekday()), qt.Equals, weekDayWideGerman)
c.Assert(tr.WeekdaysWide()[date.Weekday()], qt.Equals, weekDayWideGerman)
c.Assert(f.Format(date, "Monday"), qt.Equals, weekDayWideGerman)
}
for i, weekDayAbbreviatedGerman := range []string{"Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.", "So."} {
date := mondayNovemberFirst.Add(time.Duration(i*24) * time.Hour)
c.Assert(tr.WeekdayAbbreviated(date.Weekday()), qt.Equals, weekDayAbbreviatedGerman)
c.Assert(tr.WeekdaysAbbreviated()[date.Weekday()], qt.Equals, weekDayAbbreviatedGerman)
c.Assert(f.Format(date, "Mon"), qt.Equals, weekDayAbbreviatedGerman)
}
})
c.Run("Months German", func(c *qt.C) {
tr := translators.GetTranslator("de")
tr := golocales.New("de")
f := NewTimeFormatter(tr)
// Issue #9107
for i, monthWideNorway := range []string{"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli"} {
date := jan06.Add(time.Duration(i*24*31) * time.Hour)
c.Assert(tr.MonthWide(date.Month()), qt.Equals, monthWideNorway)
c.Assert(tr.MonthsWide()[date.Month()-1], qt.Equals, monthWideNorway)
c.Assert(f.Format(date, "January"), qt.Equals, monthWideNorway)
}
})
@@ -121,7 +122,7 @@ func BenchmarkTimeFormatter(b *testing.B) {
})
b.Run("Localized", func(b *testing.B) {
f := NewTimeFormatter(translators.GetTranslator("nn"))
f := NewTimeFormatter(golocales.New("nn"))
b.ResetTimer()
for b.Loop() {
got := f.Format(june06, "Monday Jan 2 2006")
@@ -132,7 +133,7 @@ func BenchmarkTimeFormatter(b *testing.B) {
})
b.Run("Localized Custom", func(b *testing.B) {
f := NewTimeFormatter(translators.GetTranslator("nn"))
f := NewTimeFormatter(golocales.New("nn"))
b.ResetTimer()
for b.Loop() {
got := f.Format(june06, ":date_medium")
+19 -9
View File
@@ -19,16 +19,10 @@ import (
"strings"
)
// ReadSeeker wraps io.Reader and io.Seeker.
type ReadSeeker interface {
io.Reader
io.Seeker
}
// ReadSeekCloser is implemented by afero.File. We use this as the common type for
// content in Resource objects, even for strings.
type ReadSeekCloser interface {
ReadSeeker
io.ReadSeeker
io.Closer
}
@@ -70,7 +64,7 @@ type ReadSeekCloserProvider interface {
// readSeekerNopCloser implements ReadSeekCloser by doing nothing in Close.
type readSeekerNopCloser struct {
ReadSeeker
io.ReadSeeker
}
// Close does nothing.
@@ -79,7 +73,7 @@ func (r readSeekerNopCloser) Close() error {
}
// NewReadSeekerNoOpCloser creates a new ReadSeekerNoOpCloser with the given ReadSeeker.
func NewReadSeekerNoOpCloser(r ReadSeeker) ReadSeekCloser {
func NewReadSeekerNoOpCloser(r io.ReadSeeker) ReadSeekCloser {
return readSeekerNopCloser{r}
}
@@ -111,6 +105,22 @@ func NewReadSeekerNoOpCloserFromBytes(content []byte) readSeekerNopCloser {
return readSeekerNopCloser{bytes.NewReader(content)}
}
// NewReadSeekerNoOpCloserFromReader creates a new ReadSeekerNoOpCloser from the given io.Reader.
// If the given io.Reader is not an io.ReadSeeker, the entire content will be read into memory.
func NewReadSeekerNoOpCloserFromReader(r io.Reader) (readSeekerNopCloser, error) {
var rs io.ReadSeeker
if s, ok := r.(io.ReadSeeker); ok {
rs = s
} else {
b, err := io.ReadAll(r)
if err != nil {
return readSeekerNopCloser{rs}, err
}
rs = bytes.NewReader(b)
}
return readSeekerNopCloser{rs}, nil
}
// NewOpenReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker.
// The ReadSeeker will be seeked to the beginning before returned.
func NewOpenReadSeekCloser(r ReadSeekCloser) OpenReadSeekCloser {
+23 -122
View File
@@ -16,7 +16,6 @@ package hugo
import (
"context"
"fmt"
"html/template"
"os"
"path/filepath"
"runtime/debug"
@@ -25,17 +24,16 @@ import (
"sync"
"time"
"github.com/bep/helpers/contexthelpers"
"github.com/bep/logg"
"github.com/bep/godartsass/v2"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/hstore"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/version"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/bep/helpers/contexthelpers"
"github.com/spf13/afero"
iofs "io/fs"
@@ -56,89 +54,24 @@ var (
vendorInfo string
)
var _ hstore.StoreProvider = (*HugoInfo)(nil)
// HugoInfo contains information about the current Hugo environment
type HugoInfo struct {
CommitHash string
BuildDate string
// The build environment.
// Defaults are "production" (hugo) and "development" (hugo server).
// This can also be set by the user.
// It can be any string, but it will be all lower case.
Environment string
// version of go that the Hugo binary was built with
GoVersion string
conf ConfigProvider
deps []*Dependency
store *hstore.Scratch
// Context gives access to some of the context scoped variables.
Context Context
// BuildInfo holds build information extracted from runtime/debug.
type BuildInfo struct {
Revision string
RevisionTime string
GoVersion string
}
// Version returns the current version as a comparable version string.
func (i HugoInfo) Version() version.VersionString {
return CurrentVersion.Version()
}
// Generator a Hugo meta generator HTML tag.
func (i HugoInfo) Generator() template.HTML {
return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s">`, CurrentVersion.String()))
}
// IsDevelopment reports whether the current running environment is "development".
func (i HugoInfo) IsDevelopment() bool {
return i.Environment == EnvironmentDevelopment
}
// IsProduction reports whether the current running environment is "production".
func (i HugoInfo) IsProduction() bool {
return i.Environment == EnvironmentProduction
}
// IsServer reports whether the built-in server is running.
func (i HugoInfo) IsServer() bool {
return i.conf.Running()
}
// IsExtended reports whether the Hugo binary is the extended version.
func (i HugoInfo) IsExtended() bool {
return IsExtended
}
// WorkingDir returns the project working directory.
func (i HugoInfo) WorkingDir() string {
return i.conf.WorkingDir()
}
// Deps gets a list of dependencies for this Hugo build.
func (i HugoInfo) Deps() []*Dependency {
return i.deps
}
func (i HugoInfo) Store() *hstore.Scratch {
return i.store
}
// 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()
// GetBuildInfo returns the build info for the current binary.
func GetBuildInfo() *BuildInfo {
bi := getBuildInfo()
if bi == nil {
return nil
}
return &BuildInfo{
Revision: bi.Revision,
RevisionTime: bi.RevisionTime,
GoVersion: bi.GoVersion,
}
}
type contextKey uint8
@@ -149,6 +82,7 @@ const (
var markupScope = contexthelpers.NewContextDispatcher[string](contextKeyMarkupScope)
// Context gives access to some of the context scoped variables.
type Context struct{}
func (c Context) MarkupScope(ctx context.Context) string {
@@ -165,44 +99,6 @@ func GetMarkupScope(ctx context.Context) string {
return markupScope.Get(ctx)
}
// 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.
func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo {
if conf.Environment() == "" {
panic("environment not set")
}
var (
commitHash string
buildDate string
goVersion string
)
bi := getBuildInfo()
if bi != nil {
commitHash = bi.Revision
buildDate = bi.RevisionTime
goVersion = bi.GoVersion
}
return HugoInfo{
CommitHash: commitHash,
BuildDate: buildDate,
Environment: conf.Environment(),
conf: conf,
deps: deps,
store: hstore.NewScratch(),
GoVersion: goVersion,
}
}
// GetExecEnviron creates and gets the common os/exec environment used in the
// external programs we interact with via os/exec, e.g. postcss.
func GetExecEnviron(workDir string, cfg config.AllProvider, fs afero.Fs) []string {
@@ -438,8 +334,13 @@ func deprecateLevel(item, alternative, version string, level logg.Level) {
// DeprecateLevel informs about a deprecation logging at the given level.
func deprecateLevelWithLogger(item, alternative, version string, level logg.Level, log logg.Logger) {
//if strings.Contains(item, "module.mounts.lang") || strings.Contains(item, "includeFiles") {
// hdebug.Panicf("Deprecated")
//}
var msg string
if level == logg.LevelError {
// Useful to debug deprecation errors that needs to be removedor fixed. Comment out when done debugging.
// hdebug.Panicf("deprecation error: %s was removed in Hugo %s. %s", item, version, alternative)
msg = fmt.Sprintf("%s was deprecated in Hugo %s and subsequently removed. %s", item, version, alternative)
} else {
msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative)
+36
View File
@@ -75,3 +75,39 @@ multihost={{ hugo.IsMultihost }}
"multihost=false",
)
}
func TestHugoSites(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
defaultContentLanguage = 'fr'
defaultContentLanguageInSubdir = true
defaultContentVersionInSubdir = true
defaultContentRoleInSubdir = true
[languages]
[languages.en]
weight = 1
[languages.fr]
weight = 2
[languages.de]
weight = 3
[roles]
[roles.guest]
weight = 1
[roles.member]
weight = 2
[versions]
[versions.'v1.0.0']
weight = 1
[versions.'v2.0.0']
weight = 2
-- layouts/home.html --
{{ range hugo.Sites }}{{ .Language.Name }}-{{ .Role.Name }}-{{ .Version.Name }}|{{ end }}
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/guest/v1.0.0/en/index.html", "en-guest-v1.0.0|en-member-v1.0.0|en-guest-v2.0.0|en-member-v2.0.0|fr-guest-v1.0.0|fr-member-v1.0.0|fr-guest-v2.0.0|fr-member-v2.0.0|de-guest-v1.0.0|de-member-v1.0.0|de-guest-v2.0.0|de-member-v2.0.0|")
}
+12 -63
View File
@@ -15,43 +15,12 @@ package hugo
import (
"context"
"fmt"
"testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/version"
)
func TestHugoInfo(t *testing.T) {
c := qt.New(t)
conf := testConfig{environment: "production", workingDir: "/mywork", running: false}
hugoInfo := NewInfo(conf, nil)
c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
c.Assert(fmt.Sprintf("%T", version.VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
c.Assert(hugoInfo.WorkingDir(), qt.Equals, "/mywork")
bi := getBuildInfo()
if bi != nil {
c.Assert(hugoInfo.CommitHash, qt.Equals, bi.Revision)
c.Assert(hugoInfo.BuildDate, qt.Equals, bi.RevisionTime)
c.Assert(hugoInfo.GoVersion, qt.Equals, bi.GoVersion)
}
c.Assert(hugoInfo.Environment, qt.Equals, "production")
c.Assert(string(hugoInfo.Generator()), qt.Contains, fmt.Sprintf("Hugo %s", hugoInfo.Version()))
c.Assert(hugoInfo.IsDevelopment(), qt.Equals, false)
c.Assert(hugoInfo.IsProduction(), qt.Equals, true)
c.Assert(hugoInfo.IsExtended(), qt.Equals, IsExtended)
c.Assert(hugoInfo.IsServer(), qt.Equals, false)
devHugoInfo := NewInfo(testConfig{environment: "development", running: true}, nil)
c.Assert(devHugoInfo.IsDevelopment(), qt.Equals, true)
c.Assert(devHugoInfo.IsProduction(), qt.Equals, false)
c.Assert(devHugoInfo.IsServer(), qt.Equals, true)
}
func TestDeprecationLogLevelFromVersion(t *testing.T) {
c := qt.New(t)
@@ -66,47 +35,27 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError)
// Added just to find the threshold for where we can remove deprecated items.
// Subtract 5 from the minor version of the first ERRORed version => 0.122.0.
c.Assert(deprecationLogLevelFromVersion("0.127.0"), qt.Equals, logg.LevelError)
// Subtract 5 from the minor version of the first ERRORed version => 0.136.0.
c.Assert(deprecationLogLevelFromVersion("0.141.0"), qt.Equals, logg.LevelError)
}
func TestMarkupScope(t *testing.T) {
c := qt.New(t)
conf := testConfig{environment: "production", workingDir: "/mywork", running: false}
info := NewInfo(conf, nil)
ctx := context.Background()
ctx = SetMarkupScope(ctx, "foo")
c.Assert(info.Context.MarkupScope(ctx), qt.Equals, "foo")
var hugoCtx Context
c.Assert(hugoCtx.MarkupScope(ctx), qt.Equals, "foo")
c.Assert(GetMarkupScope(ctx), qt.Equals, "foo")
}
type testConfig struct {
environment string
running bool
workingDir string
multihost bool
multilingual bool
}
func TestGetBuildInfo(t *testing.T) {
c := qt.New(t)
func (c testConfig) Environment() string {
return c.environment
}
func (c testConfig) Running() bool {
return c.running
}
func (c testConfig) WorkingDir() string {
return c.workingDir
}
func (c testConfig) IsMultihost() bool {
return c.multihost
}
func (c testConfig) IsMultilingual() bool {
return c.multilingual
bi := GetBuildInfo()
// In test mode, build info may or may not be available.
if bi != nil {
c.Assert(bi.GoVersion, qt.Not(qt.Equals), "")
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ import "github.com/gohugoio/hugo/common/version"
// This should be the only one.
var CurrentVersion = version.Version{
Major: 0,
Minor: 156,
Minor: 161,
PatchLevel: 0,
Suffix: "-DEV",
}
+2 -2
View File
@@ -286,7 +286,7 @@ func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
if milli < 500 {
return
}
fmt.Fprintf(l.stdErr, "%s in %v ms", name, milli)
fmt.Fprintf(l.stdErr, "%s in %v ms\n", name, milli)
}
func (l *logAdapter) Printf(format string, v ...any) {
@@ -352,7 +352,7 @@ func (l *logAdapter) Warnidf(id, format string, v ...any) {
}
func (l *logAdapter) idfInfoStatement(what, id, format string) string {
return fmt.Sprintf("\nYou can suppress this %s by adding the following to your site configuration:\nignoreLogs = ['%s']", what, id)
return fmt.Sprintf("\nYou can suppress this %s by adding the following to your project configuration:\nignoreLogs = ['%s']", what, id)
}
func (l *logAdapter) Trace(s logg.StringFunc) {
+12 -4
View File
@@ -30,10 +30,18 @@ import (
)
const (
identifierBaseof = "baseof"
identifierCurstomWrapper = "_"
identifierBaseof = "baseof"
identifierCustomWrapper = "_"
)
// isCustomWrapperIdentifier tells whether a supplied path is of the form _xyz_.
// must have non-empty content between the identifierCustomWrapper's to pass.
func isCustomWrapperIdentifier(s string) bool {
return len(s) > 2*len(identifierCustomWrapper) &&
strings.HasPrefix(s, identifierCustomWrapper) &&
strings.HasSuffix(s, identifierCustomWrapper)
}
// PathParser parses and manages paths.
type PathParser struct {
// Maps the language code to its index in the languages/sites slice.
@@ -188,7 +196,7 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot,
id := types.LowHigh[string]{Low: i + 1, High: high}
sid := p.s[id.Low:id.High]
if strings.HasPrefix(sid, identifierCurstomWrapper) && strings.HasSuffix(sid, identifierCurstomWrapper) {
if isCustomWrapperIdentifier(sid) {
p.identifiersKnown = append(p.identifiersKnown, id)
p.posIdentifierCustom = len(p.identifiersKnown) - 1
found = true
@@ -775,7 +783,7 @@ func (p *Path) Lang() string {
}
func (p *Path) Custom() string {
return strings.TrimSuffix(strings.TrimPrefix(p.identifierAsString(p.posIdentifierCustom), identifierCurstomWrapper), identifierCurstomWrapper)
return strings.TrimSuffix(strings.TrimPrefix(p.identifierAsString(p.posIdentifierCustom), identifierCustomWrapper), identifierCustomWrapper)
}
func (p *Path) Identifier(i int) string {
+2 -4
View File
@@ -56,9 +56,7 @@ func TestEvictingStringQueueConcurrent(t *testing.T) {
queue := NewEvictingQueue[string](3)
for range 100 {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
queue.Add(val)
v := queue.Peek()
if v != val {
@@ -68,7 +66,7 @@ func TestEvictingStringQueueConcurrent(t *testing.T) {
if len(vals) != 1 || vals[0] != val {
t.Error("wrong val")
}
}()
})
}
wg.Wait()
}
+1 -1
View File
@@ -89,7 +89,7 @@ func IsNil(v any) bool {
value := reflect.ValueOf(v)
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return value.IsNil()
}
+7
View File
@@ -47,6 +47,13 @@ var (
_ compare.Comparer = (*VersionString)(nil)
)
// IsAlphaBetaOrRC returns whether this version is an alpha, beta, or release candidate.
func (v Version) IsAlphaBetaOrRC() bool {
s := strings.ToLower(v.Suffix)
// e.g. "alpha.1", "beta.2", "rc.3"
return strings.Contains(s, "alpha.") || strings.Contains(s, "beta.") || strings.Contains(s, "rc.")
}
func (v Version) IsZero() bool {
return v.Major == 0 && v.Minor == 0 && v.PatchLevel == 0 && v.Suffix == ""
}
+70 -26
View File
@@ -399,33 +399,82 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
return err
}
// Legacy paginate values.
if c.Paginate != 0 {
hugo.DeprecateWithLogger("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0", logger.Logger())
c.Pagination.PagerSize = c.Paginate
// Legacy language values.
if c.LanguageCode != "" {
if !c.isLanguageClone {
hugo.DeprecateWithLogger("project config key languageCode", "Use locale instead.", "v0.158.0", logger.Logger())
}
if c.Locale == "" {
c.Locale = c.LanguageCode
}
c.LanguageCode = ""
}
if c.PaginatePath != "" {
hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger())
c.Pagination.Path = c.PaginatePath
for k, v := range c.Languages.Config.LanguageConfigs {
//lint:ignore SA1019 Keep as adapter for now.
if v.LanguageCode != "" {
if !c.isLanguageClone {
hugo.DeprecateWithLogger(fmt.Sprintf("project config key languages.%s.languageCode", k), fmt.Sprintf("Use languages.%s.locale instead.", k), "v0.158.0", logger.Logger())
}
if v.Locale == "" {
//lint:ignore SA1019 Keep as adapter for now.
v.Locale = v.LanguageCode
}
//lint:ignore SA1019 Keep as adapter for now.
v.LanguageCode = ""
}
//lint:ignore SA1019 Keep as adapter for now.
if v.LanguageName != "" {
if !c.isLanguageClone {
hugo.DeprecateWithLogger(fmt.Sprintf("project config key languages.%s.languageName", k), fmt.Sprintf("Use languages.%s.label instead.", k), "v0.158.0", logger.Logger())
}
if v.Label == "" {
//lint:ignore SA1019 Keep as adapter for now.
v.Label = v.LanguageName
}
//lint:ignore SA1019 Keep as adapter for now.
v.LanguageName = ""
}
//lint:ignore SA1019 Keep as adapter for now.
if v.LanguageDirection != "" {
if !c.isLanguageClone {
hugo.DeprecateWithLogger(fmt.Sprintf("project config key languages.%s.languageDirection", k), fmt.Sprintf("Use languages.%s.direction instead.", k), "v0.158.0", logger.Logger())
}
if v.Direction == "" {
//lint:ignore SA1019 Keep as adapter for now.
v.Direction = v.LanguageDirection
}
//lint:ignore SA1019 Keep as adapter for now.
v.LanguageDirection = ""
}
c.Languages.Config.LanguageConfigs[k] = v
// Sorted is a snapshot of LanguageConfigs taken at decode time; keep it
// in sync so Configs.Init, which reads from Sorted, sees the migrated values.
for i, s := range c.Languages.Config.Sorted {
if s.Name == k {
c.Languages.Config.Sorted[i].LanguageConfig = v
break
}
}
}
// Legacy privacy values.
if c.Privacy.Twitter.Disable {
hugo.DeprecateWithLogger("site config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger())
hugo.DeprecateWithLogger("project config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger())
c.Privacy.X.Disable = c.Privacy.Twitter.Disable
}
if c.Privacy.Twitter.EnableDNT {
hugo.DeprecateWithLogger("site config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger())
hugo.DeprecateWithLogger("project config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger())
c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT
}
if c.Privacy.Twitter.Simple {
hugo.DeprecateWithLogger("site config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger())
hugo.DeprecateWithLogger("project config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger())
c.Privacy.X.Simple = c.Privacy.Twitter.Simple
}
// Legacy services values.
if c.Services.Twitter.DisableInlineCSS {
hugo.DeprecateWithLogger("site config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0", logger.Logger())
hugo.DeprecateWithLogger("project config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0", logger.Logger())
c.Services.X.DisableInlineCSS = c.Services.Twitter.DisableInlineCSS
}
@@ -446,7 +495,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
)
if c.Markup.Goldmark.RenderHooks.Image.EnableDefault != nil {
alternative := "Use markup.goldmark.renderHooks.image.useEmbedded instead." + " " + alternativeDetails
hugo.DeprecateWithLogger("site config key markup.goldmark.renderHooks.image.enableDefault", alternative, "0.148.0", logger.Logger())
hugo.DeprecateWithLogger("project config key markup.goldmark.renderHooks.image.enableDefault", alternative, "0.148.0", logger.Logger())
if *c.Markup.Goldmark.RenderHooks.Image.EnableDefault {
c.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
} else {
@@ -455,7 +504,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
}
if c.Markup.Goldmark.RenderHooks.Link.EnableDefault != nil {
alternative := "Use markup.goldmark.renderHooks.link.useEmbedded instead." + " " + alternativeDetails
hugo.DeprecateWithLogger("site config key markup.goldmark.renderHooks.link.enableDefault", alternative, "0.148.0", logger.Logger())
hugo.DeprecateWithLogger("project config key markup.goldmark.renderHooks.link.enableDefault", alternative, "0.148.0", logger.Logger())
if *c.Markup.Goldmark.RenderHooks.Link.EnableDefault {
c.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
} else {
@@ -471,10 +520,10 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
gc.RenderHookUseEmbeddedNever,
}
if !slices.Contains(renderHookUseEmbeddedModes, c.Markup.Goldmark.RenderHooks.Image.UseEmbedded) {
return fmt.Errorf("site config markup.goldmark.renderHooks.image.useEmbedded must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
return fmt.Errorf("project config markup.goldmark.renderHooks.image.useEmbedded must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
}
if !slices.Contains(renderHookUseEmbeddedModes, c.Markup.Goldmark.RenderHooks.Link.UseEmbedded) {
return fmt.Errorf("site config markup.goldmark.renderHooks.link.useEmbedded must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
return fmt.Errorf("project config markup.goldmark.renderHooks.link.useEmbedded must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
}
c.C = &ConfigCompiled{
@@ -681,20 +730,15 @@ type RootConfig struct {
// The configured environment. Default is "development" for server and "production" for build.
Environment string
// The default language code.
LanguageCode string
// Deprecated: Use Locale instead.
LanguageCode string `json:"-"`
// The default locale.
Locale string
// Enable if the site content has CJK language (Chinese, Japanese, or Korean). This affects how Hugo counts words.
HasCJKLanguage bool
// The default number of pages per page when paginating.
// Deprecated: Use the Pagination struct.
Paginate int
// The path to use when creating pagination URLs, e.g. "page" in /page/2/.
// Deprecated: Use the Pagination struct.
PaginatePath string
// Whether to pluralize default list titles.
// Note that this currently only works for English, but you can provide your own title in the content file's front matter.
PluralizeListTitles bool
@@ -861,7 +905,7 @@ func (c *Configs) Init(sourceFs afero.Fs, logger loggers.Logger) error {
if !found {
return fmt.Errorf("invalid language configuration for %q", f.Name)
}
language, err := langs.NewLanguage(f.Name, c.Base.DefaultContentLanguage, v.TimeZone, f.LanguageConfig)
language, err := langs.NewLanguage(f.Name, c.Base.DefaultContentLanguage, v.TimeZone, f.LanguageConfig, logger)
if err != nil {
return err
}
@@ -43,9 +43,7 @@ title: "p1"
Title: {{ .Title }}
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{T: t, TxtarString: files},
).Build()
b := hugolib.Test(t, files)
// b.AssertFileContent("public/p1/index.html", "Title: p1")
@@ -81,9 +79,7 @@ baseURL = "https://example.com"
logI18nWarnings = true
logPathWarnings = true
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{T: t, TxtarString: files},
).Build()
b := hugolib.Test(t, files)
conf := b.H.Configs.Base
+15 -18
View File
@@ -226,27 +226,17 @@ var allDecoderSetups = map[string]decodeWeight{
key: "languages",
decode: func(d decodeWeight, p decodeConfig) error {
m := hmaps.CleanConfigStringMap(p.p.GetStringMap(d.key))
if len(m) == 1 {
// In v0.112.4 we moved this to the language config, but it's very commmon for mono language sites to have this at the top level.
var first hmaps.Params
var ok bool
for _, v := range m {
first, ok = v.(hmaps.Params)
if ok {
break
}
}
if first != nil {
if _, found := first["languagecode"]; !found {
first["languagecode"] = p.p.GetString("languagecode")
}
}
}
// Root-level locale/languageCode is passed to DecodeConfig so it can
// be applied to the default content language inside langs.DecodeConfig.
// They are passed separately so that an explicit per-lang languageCode
// can override a root-level languageCode (but not a root-level locale).
rootLocale := p.p.GetString("locale")
rootLanguageCode := p.p.GetString("languagecode")
var (
err error
defaultContentLanguage string
)
p.c.Languages, defaultContentLanguage, err = langs.DecodeConfig(p.c.RootConfig.DefaultContentLanguage, p.c.RootConfig.DisableLanguages, m)
p.c.Languages, defaultContentLanguage, err = langs.DecodeConfig(p.c.RootConfig.DefaultContentLanguage, rootLocale, rootLanguageCode, p.c.RootConfig.DisableLanguages, m)
if err != nil {
return fmt.Errorf("failed to decode languages config: %w", err)
}
@@ -335,7 +325,14 @@ var allDecoderSetups = map[string]decodeWeight{
key: "taxonomies",
decode: func(d decodeWeight, p decodeConfig) error {
if p.p.IsSet(d.key) {
p.c.Taxonomies = hmaps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
m := hmaps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
// Remove invalid entries (e.g. non-taxonomy keys placed inside [taxonomies] in TOML).
for k, v := range m {
if k == "" || v == "" {
delete(m, k)
}
}
p.c.Taxonomies = m
}
return nil
},
+11 -3
View File
@@ -34,12 +34,13 @@ import (
"github.com/gohugoio/hugo/helpers"
hglob "github.com/gohugoio/hugo/hugofs/hglob"
"github.com/gohugoio/hugo/modules"
"github.com/gohugoio/hugo/modules/npm"
"github.com/gohugoio/hugo/parser/metadecoders"
"github.com/spf13/afero"
)
//lint:ignore ST1005 end user message.
var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new project.\n Run `hugo help new` for details.\n")
func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) {
defer func() {
@@ -95,6 +96,10 @@ func LoadConfig(d ConfigSourceDescriptor) (configs *Configs, err error) {
configs.Modules = moduleConfig.AllModules
configs.ModulesClient = modulesClient
if !d.SkipNpmCheck && npm.NpmPackNeedsUpdate(d.Fs, configs.Modules) {
d.Logger.Warnln(`npm dependencies are out of sync, please run "hugo mod npm pack" (you may also want to run "npm install" after that)`)
}
if err := configs.Init(d.Fs, d.Logger); err != nil {
return nil, fmt.Errorf("failed to init config: %w", err)
}
@@ -127,6 +132,9 @@ type ConfigSourceDescriptor struct {
// If set, this will be used to ignore the module does not exist error.
IgnoreModuleDoesNotExist bool
// If set, skip the npm pack staleness check (used by hugo mod npm pack).
SkipNpmCheck bool
}
func (d ConfigSourceDescriptor) configFilenames() []string {
@@ -179,12 +187,12 @@ func (l configLoader) applyDefaultConfig() error {
func (l configLoader) normalizeCfg(cfg config.Provider) error {
if b, ok := cfg.Get("minifyOutput").(bool); ok {
hugo.Deprecate("site config minifyOutput", "Use minify.minifyOutput instead.", "v0.150.0")
hugo.Deprecate("project config minifyOutput", "Use minify.minifyOutput instead.", "v0.150.0")
if b {
cfg.Set("minify.minifyOutput", true)
}
} else if b, ok := cfg.Get("minify").(bool); ok {
hugo.Deprecate("site config minify", "Use minify.minifyOutput instead.", "v0.150.0")
hugo.Deprecate("project config minify", "Use minify.minifyOutput instead.", "v0.150.0")
if b {
cfg.Set("minify", hmaps.Params{"minifyOutput": true})
}
+1 -1
View File
@@ -202,7 +202,7 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType {
t reflect.Type,
data any,
) (any, error) {
if t != reflect.TypeOf(Whitelist{}) {
if t != reflect.TypeFor[Whitelist]() {
return data, nil
}
+2 -2
View File
@@ -36,7 +36,7 @@ import (
)
const (
// DefaultArchetypeTemplateTemplate is the template used in 'hugo new site'
// DefaultArchetypeTemplateTemplate is the template used in 'hugo new project'
// and the template we use as a fall back.
DefaultArchetypeTemplateTemplate = `---
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
@@ -271,7 +271,7 @@ func (b *contentBuilder) setArcheTypeFilenameToUse(ext string) {
func (b *contentBuilder) applyArcheType(contentFilename string, archetypeFi hugofs.FileMetaInfo) error {
p := b.h.GetContentPage(contentFilename)
if p == nil {
panic(fmt.Sprintf("[BUG] no Page found for %q", contentFilename))
return fmt.Errorf("no page found for %q; if a file with the same name but different case already exists, please use a different filename or remove the existing file", contentFilename)
}
f, err := b.sourceFs.Create(contentFilename)
+17 -17
View File
@@ -27,8 +27,8 @@ import (
"github.com/spf13/afero"
)
//go:embed all:site/*
var siteFs embed.FS
//go:embed all:project/*
var projectFs embed.FS
//go:embed all:theme/*
var themeFs embed.FS
@@ -41,10 +41,10 @@ func CreateTheme(createpath string, sourceFs afero.Fs, format string) error {
format = strings.ToLower(format)
siteConfig := map[string]any{
"baseURL": "https://example.org/",
"languageCode": "en-US",
"title": "My New Hugo Site",
projectConfig := map[string]any{
"baseURL": "https://example.org/",
"locale": "en-US",
"title": "My New Hugo Project",
"menus": map[string]any{
"main": []any{
map[string]any{
@@ -72,7 +72,7 @@ func CreateTheme(createpath string, sourceFs afero.Fs, format string) error {
},
}
err := createSiteConfig(sourceFs, createpath, siteConfig, format)
err := createProjectConfig(sourceFs, createpath, projectConfig, format)
if err != nil {
return err
}
@@ -91,8 +91,8 @@ func CreateTheme(createpath string, sourceFs afero.Fs, format string) error {
return copyFiles(createpath, sourceFs, themeFs)
}
// CreateSite creates a site skeleton.
func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string) error {
// CreateProject creates a project skeleton.
func CreateProject(createpath string, sourceFs afero.Fs, force bool, format string) error {
format = strings.ToLower(format)
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
if isDir, _ := helpers.IsDir(createpath, sourceFs); !isDir {
@@ -106,7 +106,7 @@ func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string)
return errors.New(createpath + " already exists and is not empty. See --force.")
case !isEmpty && force:
var all []string
fs.WalkDir(siteFs, ".", func(path string, d fs.DirEntry, err error) error {
fs.WalkDir(projectFs, ".", func(path string, d fs.DirEntry, err error) error {
if d.IsDir() && path != "." {
all = append(all, path)
}
@@ -121,13 +121,13 @@ func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string)
}
}
siteConfig := map[string]any{
"baseURL": "https://example.org/",
"title": "My New Hugo Site",
"languageCode": "en-us",
projectConfig := map[string]any{
"baseURL": "https://example.org/",
"locale": "en-us",
"title": "My New Hugo Project",
}
err := createSiteConfig(sourceFs, createpath, siteConfig, format)
err := createProjectConfig(sourceFs, createpath, projectConfig, format)
if err != nil {
return err
}
@@ -143,7 +143,7 @@ func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string)
return err
}
return copyFiles(createpath, sourceFs, siteFs)
return copyFiles(createpath, sourceFs, projectFs)
}
func copyFiles(createpath string, sourceFs afero.Fs, skeleton embed.FS) error {
@@ -161,7 +161,7 @@ func copyFiles(createpath string, sourceFs afero.Fs, skeleton embed.FS) error {
})
}
func createSiteConfig(fs afero.Fs, createpath string, in map[string]any, format string) (err error) {
func createProjectConfig(fs afero.Fs, createpath string, in map[string]any, format string) (err error) {
var buf bytes.Buffer
err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(format), &buf)
if err != nil {
@@ -0,0 +1,4 @@
footer {
border-top: 1px solid #222;
margin-top: 1rem;
}
@@ -0,0 +1,4 @@
header {
border-bottom: 1px solid #222;
margin-bottom: 1rem;
}
+3 -10
View File
@@ -1,3 +1,6 @@
@import "components/header.css";
@import "components/footer.css";
body {
color: #222;
font-family: sans-serif;
@@ -6,16 +9,6 @@ body {
max-width: 768px;
}
header {
border-bottom: 1px solid #222;
margin-bottom: 1rem;
}
footer {
border-top: 1px solid #222;
margin-top: 1rem;
}
a {
color: #00e;
text-decoration: none;
@@ -1,9 +1,15 @@
{{- with resources.Get "css/main.css" }}
{{- if hugo.IsDevelopment }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{- else }}
{{- with . | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{- $opts := dict
"minify" (cond hugo.IsDevelopment false true)
"sourceMap" (cond hugo.IsDevelopment "linked" "none")
}}
{{- with . | css.Build $opts }}
{{- if hugo.IsDevelopment }}
<link rel="stylesheet" href="{{ .RelPermalink }}">
{{- else }}
{{- with . | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{- end }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,8 +1,7 @@
{{- with resources.Get "js/main.js" }}
{{- $opts := dict
"minify" (not hugo.IsDevelopment)
"sourceMap" (cond hugo.IsDevelopment "external" "")
"targetPath" "js/main.js"
"minify" (cond hugo.IsDevelopment false true)
"sourceMap" (cond hugo.IsDevelopment "linked" "none")
}}
{{- with . | js.Build $opts }}
{{- if hugo.IsDevelopment }}
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ site.Language.LanguageCode }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
<html lang="{{ site.Language.Locale }}" dir="{{ or site.Language.Direction `ltr` }}">
<head>
{{ partial "head.html" . }}
</head>
+8
View File
@@ -441,6 +441,9 @@ type DepsCfg struct {
type BuildState struct {
counter uint64
// Tracks invocations of the Build method.
BuildCounter atomic.Uint64
mu sync.Mutex // protects state below.
OnSignalRebuild func(ids ...identity.Identity)
@@ -472,6 +475,11 @@ type DeferredExecutions struct {
var _ identity.SignalRebuilder = (*BuildState)(nil)
// IsRebuild reports whether this is a rebuild.
func (b *BuildState) IsRebuild() bool {
return b.BuildCounter.Load() > 0
}
// StartStageRender will be called before a stage is rendered.
func (b *BuildState) StartStageRender(stage tpl.RenderingContext) {
}
+2 -2
View File
@@ -4,10 +4,10 @@
[codespell]
# Comma separated list of dirs to be skipped.
skip = *.ai,chroma.css,chroma_dark.css,.cspell.json
skip = *.ai,chroma.css,chroma_dark.css,.cspell.json,./data/docs.yaml
# Comma separated list of words to be ignored. Words must be lowercased.
ignore-words-list = abl,edn,januar,te,trys,ue,womens
ignore-words-list = abl,edn,ist,januar,te,trys,ue,womens
# Check file names as well.
check-filenames = true
+28 -27
View File
@@ -23,28 +23,27 @@
"**/emojis.md",
"**/commands/*",
"**/showcase/*",
"**/tools/*",
"data/docs.yaml"
"**/tools/*"
],
"ignoreRegExpList": [
"# cspell: ignore fenced code blocks",
// cspell: ignore fenced code blocks
"^(\\s*`{3,}).*[\\s\\S]*?^\\1$",
"# cspell: ignore words joined with dot",
// cspell: ignore words joined with dot
"\\w+\\.\\w+",
"# cspell: ignore strings within backticks",
// cspell: ignore strings within backticks
"`.+`",
"# cspell: ignore strings within double quotes",
// cspell: ignore strings within double quotes
"\".+\"",
"# cspell: ignore strings within brackets",
// cspell: ignore strings within brackets
"\\[.+\\]",
"# cspell: ignore strings within parentheses",
// cspell: ignore strings within parentheses
"\\(.+\\)",
"# cspell: ignore words that begin with a slash",
// cspell: ignore words that begin with a slash
"/\\w+",
"# cspell: ignore everything within action delimiters",
// cspell: ignore everything within action delimiters
"\\{\\{.+\\}\\}",
"# cspell: ignore everything after a right arrow",
"\\s+→\\s+.+"
// cspell: ignore everything after a right arrow
"\\s+→\\s+.+",
],
"language": "en",
"words": [
@@ -77,9 +76,9 @@
"unmarshaled",
"unmarshaling",
"unmarshals",
"# ----------------------------------------------------------------------",
"# cspell: ignore hugo terminology",
"# ----------------------------------------------------------------------",
// ------------------------------------------------------------------------
// cspell: ignore hugo terminology",
// ------------------------------------------------------------------------
"alignx",
"aligny",
"attrlink",
@@ -102,9 +101,9 @@
"unmarshal",
"unpublishdate",
"zgotmplz",
"# ----------------------------------------------------------------------",
"# cspell: ignore foreign language words",
"# ----------------------------------------------------------------------",
// ------------------------------------------------------------------------
// cspell: ignore foreign language words",
// ------------------------------------------------------------------------
"bezpieczeństwo",
"blatt",
"buch",
@@ -130,9 +129,9 @@
"prywatność",
"referenz",
"régime",
"# ----------------------------------------------------------------------",
"# cspell: ignore names",
"# ----------------------------------------------------------------------",
// ------------------------------------------------------------------------
// cspell: ignore names",
// ------------------------------------------------------------------------
"Atishay",
"Cosette",
"Eliott",
@@ -143,14 +142,15 @@
"Ninke",
"Noll",
"Pastorius",
"Pontmercy",
"Samsa",
"Stucki",
"Thénardier",
"Vitter",
"WASI",
"# ----------------------------------------------------------------------",
"# cspell: ignore operating systems and software packages",
"# ----------------------------------------------------------------------",
// ------------------------------------------------------------------------
// cspell: ignore operating systems and software packages",
// ------------------------------------------------------------------------
"asciidoctor",
"brotli",
"cifs",
@@ -172,9 +172,9 @@
"pkgin",
"rclone",
"xubuntu",
"# ----------------------------------------------------------------------",
"# cspell: ignore miscellaneous",
"# ----------------------------------------------------------------------",
// ------------------------------------------------------------------------
// cspell: ignore miscellaneous",
// ------------------------------------------------------------------------
"achristie",
"ccpa",
"cpra",
@@ -182,6 +182,7 @@
"dring",
"fleqn",
"inor",
"iptc",
"jausten",
"jdoe",
"jsmith",
+1
View File
@@ -1,5 +1,6 @@
.DS_Store
.hugo_build.lock
.hvm
/.idea
/.vscode
/dist
+4 -1
View File
@@ -80,7 +80,7 @@ config:
url_inline: false
MD055:
style: consistent
MD056: true
MD056: false # interferes with Markdown attributes
# MD057 deprecated
MD058: true
MD059:
@@ -89,3 +89,6 @@ config:
- here
- link
- more
MD060:
aligned_delimiter: false
style: any
+25
View File
@@ -0,0 +1,25 @@
## Theme
### Role
You are an expert front-end and Tailwind CSS developer. Your primary goal is to build reliable, readable, and scalable user interfaces using the existing project structure and design system.
### Directives
* **ALWAYS** use existing Tailwind utility classes for styling.
* **NEVER** write raw CSS in `.css` files unless absolutely necessary for a non-utility-based global reset.
* **NEVER** use inline styles (e.g., `<div style="...">`).
### Project Knowledge
* **Tech Stack:** Hugo (static site generator), AlpineJS, Tailwind CSS. Icons from https://heroicons.com/.
* **Core Files and Directories:**
- `hugo.toml`: Hugo configuration file.
- `assets/css/styles.css`: Main CSS file with Tailwind directives.
- `assets/js/main.js`: Main JavaScript with AlpineJS configuration.
- `package.json`: Project dependencies and scripts.
- `content/`: Directory for markdown content files.
- `layouts/`: Directory for Hugo templates and partials.
- `layouts/_partials/icons.html`: SVG icon sprites.
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
-2
View File
@@ -23,5 +23,3 @@ Please see the [contributing] section for guidelines, examples, and process.
npm i
hugo server
```
**Note:** We're working on removing the need to run `npm i` for local development. Stay tuned.
+1
View File
@@ -5,3 +5,4 @@
@import "./shortcodes.css";
@import "./tableofcontents.css";
@import "./view-transitions.css";
@import "./todo-lists.css";
+15
View File
@@ -0,0 +1,15 @@
ul.todo {
padding-left: 0;
}
ul.todo li {
display: flex;
gap: 0.8em;
}
ul.todo li input[type="checkbox"] {
width: 1em;
height: 1em;
flex-shrink: 0;
margin-top: 0.3em;
}
+11 -4
View File
@@ -77,8 +77,9 @@
/* Fonts. */
--font-sans:
"Mulish", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
"Mulish", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", "Noto Color Emoji";
}
html {
@@ -104,14 +105,14 @@ body {
padding-right: max(env(safe-area-inset-right), 1rem);
}
@media screen(md) {
@media (min-width: theme('--breakpoint-md')) {
.px-main {
padding-left: max(env(safe-area-inset-left), 2rem);
padding-right: max(env(safe-area-inset-right), 2rem);
}
}
@media screen(lg) {
@media (min-width: theme('--breakpoint-lg')) {
.px-main {
padding-left: max(env(safe-area-inset-left), 3rem);
padding-right: max(env(safe-area-inset-right), 3rem);
@@ -134,3 +135,9 @@ body {
:not(pre) > code {
white-space: nowrap;
}
/* Utility class for tables to prevent word wrapping in the first column. */
.no-wrap-first-col td:first-child,
.no-wrap-first-col th:first-child {
white-space: nowrap;
}
+10
View File
@@ -31,6 +31,16 @@ export const navStore = (Alpine) => ({
atTop: true,
},
mobileMenu: {
open: false,
toggle() {
this.open = !this.open;
},
close() {
this.open = false;
},
},
userSettings: {
// settings gets persisted between page navigations.
settings: Alpine.$persist({
@@ -2,13 +2,13 @@
_comment: Do not remove front matter.
---
Hugo uses Go's [text/template] and [html/template] packages.
Hugo uses Go's [`text/template`][] and [`html/template`][] packages.
The text/template package implements data-driven templates for generating textual output, while the html/template package implements data-driven templates for generating HTML output safe against code injection.
The `text/template` package implements data-driven templates for generating textual output, while the `html/template` package implements data-driven templates for generating HTML output safe against code injection.
By default, Hugo uses the html/template package when rendering HTML files.
By default, Hugo uses the `html/template` package when rendering HTML files.
To generate HTML output that is safe against code injection, the html/template package escapes strings in certain contexts.
To generate HTML output that is safe against code injection, the `html/template` package escapes strings in certain contexts.
[text/template]: https://pkg.go.dev/text/template
[html/template]: https://pkg.go.dev/html/template
[`text/template`]: https://pkg.go.dev/text/template
[`html/template`]: https://pkg.go.dev/html/template
@@ -2,6 +2,6 @@
_comment: Do not remove front matter.
---
See Go's [text/template] documentation for more information.
See Go's [`text/template`][] documentation for more information.
[text/template]: https://pkg.go.dev/text/template
[`text/template`]: https://pkg.go.dev/text/template
@@ -0,0 +1,9 @@
---
_comment: Do not remove front matter.
---
The returned collection follows a hierarchical sort where each subsequent dimension acts as a tie-breaker for the one above it.
1. [Language](g) is sorted by [weight](g) in ascending order, falling back to lexicographical order if weights are tied or undefined.
1. [Version](g) is then sorted by weight in ascending order, with Hugo defaulting to a descending semantic sort for any ties.
1. [Role](g) is finally sorted by weight in ascending order, using lexicographical order as the final fallback.
@@ -18,7 +18,7 @@ params
Note that this is meant for small data sets, e.g., configuration settings. For larger data sets, please put/mount the files into `assets` and import them directly.
minify
: (`bool`) Whether to let `js.Build` handle the minification.
: (`bool`) Whether to minify the generated CSS code. Default is `false`.
loaders
: {{< new-in 0.140.0 />}}
@@ -77,18 +77,16 @@ drop
: See <https://esbuild.github.io/api/#drop>
sourceMap
: (`string`) Whether to generate `inline`, `linked`, or `external` source maps from esbuild. Linked and external source maps will be written to the target with the output file name + ".map". When `linked` a `sourceMappingURL` will also be written to the output file. By default, source maps are not created. Note that the `linked` option was added in Hugo 0.140.0.
: (`string`) The type of source map to generate. One of `external`, `inline`, `linked`, or `none`. Default is `none`. Linked and external source maps will be written to the target with the output file name + ".map". When `linked` a `sourceMappingURL` will also be written to the output file.
sourcesContent
: {{< new-in 0.140.0 />}}
: (`bool`) Whether to include the content of the source files in the source map. By default, this is `true`.
: (`bool`) Whether to include the content of the source files in the source map. Default is `true`.
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.:
@@ -0,0 +1,49 @@
---
_comment: Do not remove front matter.
---
## Image operations
Use these functions to determine which operations Hugo supports for a given resource. While Hugo classifies a variety of file types as image resources, its ability to process them or extract metadata varies by format.
- [`reflect.IsImageResource`][]: {{% get-page-desc "/functions/reflect/isimageresource" %}}
- [`reflect.IsImageResourceProcessable`][]: {{% get-page-desc "/functions/reflect/isimageresourceprocessable" %}}
- [`reflect.IsImageResourceWithMeta`][]: {{% get-page-desc "/functions/reflect/isimageresourcewithmeta" %}}
The table below shows the values these functions return for various file formats. Use it to determine which checks are required before calling specific methods in your templates.
|Format|IsImageResource|IsImageResourceProcessable|IsImageResourceWithMeta|
|:-----|:--------------|:-------------------------|:----------------------|
|AVIF |true |**false** |true |
|BMP |true |true |true |
|GIF |true |true |true |
|HEIC |true |**false** |true |
|HEIF |true |**false** |true |
|ICO |true |**false** |**false** |
|JPEG |true |true |true |
|PNG |true |true |true |
|SVG |true |**false** |**false** |
|TIFF |true |true |true |
|WebP |true |true |true |
This contrived example demonstrates how to iterate through resources and use these functions to apply the appropriate handling for each image format.
```go-html-template
{{ range resources.Match "**" }}
{{ if reflect.IsImageResource . }}
{{ if reflect.IsImageResourceProcessable . }}
{{ with .Process "resize 300x webp" }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ end }}
{{ else if reflect.IsImageResourceWithMeta . }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
{{ else }}
<img src="{{ .RelPermalink }}" alt="">
{{ end }}
{{ end }}
{{ end }}
```
[`reflect.IsImageResource`]: /functions/reflect/isimageresource/
[`reflect.IsImageResourceProcessable`]: /functions/reflect/isimageresourceprocessable/
[`reflect.IsImageResourceWithMeta`]: /functions/reflect/isimageresourcewithmeta/
@@ -2,15 +2,17 @@
_comment: Do not remove front matter.
---
Hugo is available in three editions: standard, extended, and extended/deploy. While the standard edition provides core functionality, the extended and extended/deploy editions offer advanced features.
## Editions
Hugo offers a standard edition with core features, plus extended and extended/deploy editions with more. Use the standard edition unless you need the features below.
<!-- TODO Remove the transpiler row somewhere around v0.166.0 -->
Feature|extended edition|extended/deploy edition
:--|:-:|:-:
Encode to the WebP format when [processing images]. You can decode WebP images with any edition.|:heavy_check_mark:|:heavy_check_mark:
[Transpile Sass to CSS] using the embedded LibSass transpiler. You can use the [Dart Sass] transpiler with any edition.|:heavy_check_mark:|:heavy_check_mark:
[Transpile Sass to CSS] via embedded LibSass. Note that embedded LibSass was deprecated in v0.153.0 and will be removed in a future release. Use the [Dart Sass] transpiler instead, which is compatible with any edition.|:heavy_check_mark:|:heavy_check_mark:
Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See&nbsp;[details].|:x:|:heavy_check_mark:
[dart sass]: /functions/css/sass/#dart-sass
[processing images]: /content-management/image-processing/
[transpile sass to css]: /functions/css/sass/
[details]: /host-and-deploy/deploy-with-hugo-deploy/
@@ -12,7 +12,7 @@ Git is required to:
- Use the [Hugo Modules] feature
- Install a theme as a Git submodule
- Access [commit information] from a local Git repository
- Host your site with services such as [CloudCannon], [Cloudflare Pages], [GitHub Pages], [GitLab Pages], and [Netlify]
- Host your site on [CI/CD](g) platforms such as [Cloudflare][], [GitHub Pages][], [GitLab Pages][], [Netlify][], [Render][], or [Vercel][]
Go is required to:
@@ -27,16 +27,17 @@ Please refer to the relevant documentation for installation instructions:
- [Go][go install]
- [Dart Sass][dart sass install]
[cloudcannon]: https://cloudcannon.com/
[cloudflare pages]: https://pages.cloudflare.com/
[Cloudflare]: /host-and-deploy/host-on-cloudflare/
[commit information]: /methods/page/GitInfo
[dart sass install]: /functions/css/sass/#dart-sass
[dart sass]: https://sass-lang.com/dart-sass
[git install]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
[git]: https://git-scm.com/
[github pages]: https://pages.github.com/
[gitlab pages]: https://docs.gitlab.com/ee/user/project/pages/
[GitHub Pages]: /host-and-deploy/host-on-github-pages/
[GitLab Pages]: /host-and-deploy/host-on-gitlab-pages/
[go install]: https://go.dev/doc/install
[go]: https://go.dev/
[hugo modules]: /hugo-modules/
[netlify]: https://www.netlify.com/
[Netlify]: /host-and-deploy/host-on-netlify/
[Render]: /host-and-deploy/host-on-render/
[Vercel]: /host-and-deploy/host-on-vercel/
@@ -7,7 +7,7 @@ _comment: Do not remove front matter.
To build the extended or extended/deploy edition from source you must:
1. Install [Git]
1. Install [Go] version 1.24.0 or later
1. Install [Go] version 1.25.0 or later
1. Install a C compiler, either [GCC] or [Clang]
1. Update your `PATH` environment variable as described in the [Go documentation]
@@ -2,7 +2,7 @@
_comment: Do not remove front matter.
---
In this site configuration we enable rendering of [emoji shortcodes], and add emoji shortcodes before (pre) and after (post) each menu entry:
In this project configuration we enable rendering of [emoji shortcodes], and add emoji shortcodes before (pre) and after (post) each menu entry:
{{< code-toggle file=hugo >}}
enableEmoji = true
@@ -0,0 +1,9 @@
---
_comment: Do not remove front matter.
---
To use this method you must first select a specific [output format](g) from a page's [`OutputFormats`][] collection using the [`Get`][] or [`Canonical`][] methods.
[`Canonical`]: /methods/page/outputformats/#canonical
[`Get`]: /methods/page/outputformats/#get
[`OutputFormats`]: /methods/page/outputformats/
@@ -53,8 +53,8 @@ When you visit page-2:
- The `Prev` method points to page-3
- The `Next` method points to page-1
To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [site configuration], or use the [`Next`] and [`Prev`] methods on a `Pages` object for more flexibility.
To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [project configuration], or use the [`Next`] and [`Prev`] methods on a `Pages` object for more flexibility.
[site configuration]: /configuration/page/
[project configuration]: /configuration/page/
[`Next`]: /methods/pages/prev
[`Prev`]: /methods/pages/prev
@@ -53,9 +53,9 @@ When you visit page-2:
- The `PrevInSection` method points to page-3
- The `NextInSection` method points to page-1
To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [site configuration], or use the [`Next`] and [`Prev`] methods on a `Pages` object for more flexibility.
To reverse the meaning of _next_ and _previous_ you can change the sort direction in your [project configuration], or use the [`Next`] and [`Prev`] methods on a `Pages` object for more flexibility.
[site configuration]: /configuration/page/
[project configuration]: /configuration/page/
[`Next`]: /methods/pages/prev
[`Prev`]: /methods/pages/prev
@@ -1,35 +0,0 @@
---
_comment: Do not remove front matter.
---
### Get IDENTIFIER
(`any`) Returns the `OutputFormat` object with the given identifier.
### MediaType
(`media.Type`) Returns the media type of the output format.
### MediaType.MainType
(`string`) Returns the main type of the output format's media type.
### MediaType.SubType
(`string`) Returns the subtype of the current format's media type.
### Name
(`string`) Returns the output identifier of the output format.
### Permalink
(`string`) Returns the permalink of the page generated by the current output format.
### Rel
(`string`) Returns the `rel` value of the output format, either the default or as defined in the site configuration.
### RelPermalink
(`string`) Returns the relative permalink of the page generated by the current output format.
@@ -2,35 +2,72 @@
_comment: Do not remove front matter.
---
## Process specification
## Processing specification
The process specification is a space-delimited, case-insensitive list of one or more of the following in any sequence:
The processing specification is a space-delimited, case-insensitive list containing one or more of the following options in any sequence:
action
: Applicable to the [`Process`](/methods/resource/process) method only. Specify zero or one of `crop`, `fill`, `fit`, or `resize`. If you specify an action you must also provide dimensions.
dimensions
: Provide width _or_ height when using the [`Resize`](/methods/resource/resize) method, else provide both width _and_ height. See&nbsp;[details](/content-management/image-processing/#dimensions).
: Specify one of `crop`, `fill`, `fit`, or `resize`. This is applicable to the [`Process`][] method and the [`images.Process`][] filter. If you specify an action, you must also provide dimensions.
anchor
: Use with the [`Crop`](/methods/resource/crop) and [`Fill`](/methods/resource/fill) methods. Specify zero or one of `TopLeft`, `Top`, `TopRight`, `Left`, `Center`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`, or `Smart`. Default is `Smart`. See&nbsp;[details](/content-management/image-processing/#anchor).
rotation
: Typically specify zero or one of `r90`, `r180`, or `r270`. Also supports arbitrary rotation angles. See&nbsp;[details](/content-management/image-processing/#rotation).
target format
: Specify zero or one of `gif`, `jpeg`, `png`, `tiff`, or `webp`. See&nbsp;[details](/content-management/image-processing/#target-format).
quality
: Applicable to JPEG and WebP images. Optionally specify `qN` where `N` is an integer in the range [0, 100]. Default is `75`. See&nbsp;[details](/content-management/image-processing/#quality).
hint
: Applicable to WebP images and equivalent to the `-preset` flag for the [`cwebp`] encoder. Specify zero or one of `drawing`, `icon`, `photo`, `picture`, or `text`. Default is `photo`. See&nbsp;[details](/content-management/image-processing/#hint).
[`cwebp`]: https://developers.google.com/speed/webp/docs/cwebp
: The focal point used when cropping or filling an image. Valid options include `TopLeft`, `Top`, `TopRight`, `Left`, `Center`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`, or `Smart`. The `Smart` option utilizes the [`muesli/smartcrop`][] package to identify the most interesting area of the image. This defaults to the [`anchor`][] parameter in your project configuration.
background color
: When converting a PNG or WebP with transparency to a format that does not support transparency, optionally specify a background color using a 3-digit or a 6-digit hexadecimal color code. Default is `#ffffff` (white). See&nbsp;[details](/content-management/image-processing/#background-color).
: The background color used when converting transparent images to formats that do not support transparency, such as PNG to JPEG. This color also fills the empty space created when rotating an image by a non-orthogonal angle if the space is not transparent and a background color is not specified in the processing specification. The value must be an RGB [hexadecimal color][]. This defaults to the [`bgColor`][] parameter in your project configuration.
compression
: {{< new-in 0.153.5 />}}
: The encoding strategy used for the image. Options are `lossy` or `lossless`. Note that `lossless` is only supported by the WebP format. This defaults to the [`compression`][] parameter in your project configuration.
dimensions
: The dimensions of the resulting image, in pixels. The format is `WIDTHxHEIGHT` where `WIDTH` and `HEIGHT` are whole numbers. When resizing an image, you may specify only the width (such as `600x`) or only the height (such as `x400`) for proportional scaling. Specifying both width and height when resizing an image may result in non-proportional scaling. When cropping, fitting, or filling, you must provide both width and height such as `600x400`.
format
: The format of the resulting image. Valid options include `bmp`, `gif`, `jpeg`, `png`, `tiff`, or `webp`. This defaults to the format of the source image.
hint
: The encoding preset used when processing WebP images, equivalent to the `-preset` flag for the [`cwebp`][] CLI. Valid options include `drawing`, `icon`, `photo`, `picture`, or `text`. This defaults to the [`hint`][] parameter in your project configuration.
Value|Example
:--|:--
`drawing`|Hand or line drawing with high-contrast details
`icon`|Small colorful image
`photo`|Outdoor photograph with natural lighting
`picture`|Indoor photograph such as a portrait
`text`|Image that is primarily text
quality
: The visual fidelity of the image, applicable to JPEG and WebP formats when using `lossy` compression. The format is `qQUALITY` where `QUALITY` is a whole number between `1` and `100`, inclusive. Lower numbers prioritize smaller file size, while higher numbers prioritize visual clarity. This defaults to the [`quality`][] parameter in your project configuration.
resampling filter
: Typically specify zero or one of `Box`, `Lanczos`, `CatmullRom`, `MitchellNetravali`, `Linear`, or `NearestNeighbor`. Other resampling filters are available. See&nbsp;[details](/content-management/image-processing/#resampling-filter).
: The algorithm used to calculate new pixels when resizing, fitting, or filling an image. Common options include `box`, `lanczos`, `catmullRom`, `mitchellNetravali`, `linear`, or `nearestNeighbor`. This defaults to the [`resampleFilter`][] parameter in your project configuration.
Filter|Description
:--|:--
`box`|Simple and fast averaging filter appropriate for downscaling
`lanczos`|High-quality resampling filter for photographic images yielding sharp results
`catmullRom`|Sharp cubic filter that is faster than the Lanczos filter while providing similar results
`mitchellNetravali`|Cubic filter that produces smoother results with less ringing artifacts than CatmullRom
`linear`|Bilinear resampling filter, produces smooth output, faster than cubic filters
`nearestNeighbor`|Fastest resampling filter, no antialiasing
Refer to the [source documentation][] for a complete list of available resampling filters. If you wish to improve image quality at the expense of performance, you may wish to experiment with the alternative filters.
rotation
: The number of whole degrees to rotate an image counter-clockwise. The format is `rDEGREES` where `DEGREES` is a whole number. Hugo performs rotation before any other transformations, so your [target dimensions](#dimensions) and any [anchor](#anchor) should refer to the image orientation after rotation. Use `r90`, `r180`, or `r270` for orthogonal rotations, or arbitrary angles such as `r45`. To rotate clockwise, use a negative number such as `r-45`. To automatically rotate an image based on its Exif orientation tag, use the [`images.AutoOrient`][] filter instead of manual rotation.
Rotating by non-orthogonal values increases the image extents to fit the rotated corners. For formats supporting alpha channels such as PNG or WebP, this resulting empty space is transparent by default. If the target format does not support transparency such as JPEG, or if you explicitly specify a [background color](#background-color) in the processing specification, the space is filled. If a color is required but not specified in the processing string, it defaults to the [`bgColor`][] parameter in your project configuration.
[`anchor`]: /configuration/imaging/#anchor
[`bgcolor`]: /configuration/imaging/#bgcolor
[`compression`]: /configuration/imaging/#compression
[`cwebp`]: https://developers.google.com/speed/webp/docs/cwebp
[`muesli/smartcrop`]: https://github.com/muesli/smartcrop
[`hint`]: /configuration/imaging/#hint
[`images.AutoOrient`]: /functions/images/autoorient/
[`images.Process`]: /functions/images/process/
[`Process`]: /methods/resource/process
[`quality`]: /configuration/imaging/#quality
[`resampleFilter`]: /configuration/imaging/#resamplefilter
[hexadecimal color]: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
[source documentation]: https://github.com/disintegration/imaging#image-resizing
@@ -6,7 +6,7 @@ Before we can use a `Taxonomy` method, we need to capture a `Taxonomy` object.
## Capture a Taxonomy object
Consider this site configuration:
Consider this project configuration:
{{< code-toggle file=hugo >}}
[taxonomies]
+4 -10
View File
@@ -44,18 +44,12 @@ _comment: Do not remove front matter.
: The `slug` as defined in front matter, else the `title` as defined in front matter, else the automatic title. Hugo generates titles automatically for section, taxonomy, and term pages that are not backed by a file.
`:filename`
: The content's file name without extension, applicable to the `page` page kind.
{{< deprecated-in v0.144.0 >}}
The `:filename` token has been deprecated. Use `:contentbasename` instead.
{{< /deprecated-in >}}
: {{< deprecated-in v0.144.0 />}}
: Use `:contentbasename` instead.
`:slugorfilename`
: The `slug` as defined in front matter, else the content's file name without extension, applicable to the `page` page kind.
{{< deprecated-in v0.144.0 >}}
The `:slugorfilename` token has been deprecated. Use `:slugorcontentbasename` instead.
{{< /deprecated-in >}}
: {{< deprecated-in v0.144.0 />}}
: Use `:slugorcontentbasename` instead.
`:contentbasename`
: {{< new-in 0.144.0 />}}
@@ -2,7 +2,7 @@
_comment: Do not remove front matter.
---
By default, Hugo will throw an error and fail the build if it cannot resolve the path. You can change this to a warning in your site configuration, and specify a URL to return when the path cannot be resolved.
By default, Hugo will throw an error and fail the build if it cannot resolve the path. You can change this to a warning in your project configuration, and specify a URL to return when the path cannot be resolved.
{{< code-toggle file=hugo >}}
refLinksErrorLevel = 'warning'
@@ -4,8 +4,6 @@ _comment: Do not remove front matter.
## PageInner details
{{< new-in 0.125.0 />}}
The primary use case for `PageInner` is to resolve links and [page resources](g) relative to an included `Page`. For example, create an "include" shortcode to compose a page from multiple content files, while preserving a global context for footnotes and the table of contents:
```go-html-template {file="layouts/_shortcodes/include.html" copy=true}
+10 -6
View File
@@ -15,7 +15,7 @@ weight: 20
: Localize your project for each language and region, including translations, images, dates, currencies, numbers, percentages, and collation sequence. Hugo's multilingual framework supports single-host and multihost configurations.
[Output formats]
: Render each page of your site to one or more output formats, with granular control by page kind, section, and path. While HTML is the default output format, you can add JSON, RSS, CSV, and more. For example, create a REST API to access content.
: Render each page of your project to one or more output formats, with granular control by page kind, section, and path. While HTML is the default output format, you can add JSON, RSS, CSV, and more. For example, create a REST API to access content.
[Templates]
: Create templates using variables, functions, and methods to transform your content, resources, and data into a published page. While HTML templates are the most common, you can create templates for any output format.
@@ -24,10 +24,10 @@ weight: 20
: Reduce development time and cost by using one of the hundreds of themes contributed by the Hugo community. Themes are available for corporate sites, documentation projects, image portfolios, landing pages, personal and professional blogs, resumes, CVs, and more.
[Modules]
: Reduce development time and cost by creating or importing packaged combinations of archetypes, assets, content, data, templates, translation tables, static files, or configuration settings. A module may serve as the basis for a new site, or to augment an existing site.
: Reduce development time and cost by creating or importing packaged combinations of archetypes, assets, content, data, templates, translation tables, static files, or configuration settings. A module may serve as the basis for a new project, or to augment an existing project.
[Privacy]
: Configure your site to help comply with regional privacy regulations.
: Configure your project to help comply with regional privacy regulations.
[Security]
: Hugo's security model is based on the premise that template and configuration authors are trusted, but content authors are not. This model enables generation of HTML output safe against code injection. Other protections prevent "shelling out" to arbitrary applications, limit access to specific environment variables, prevent connections to arbitrary remote data sources, and more.
@@ -60,8 +60,11 @@ weight: 20
## Content management
[Multidimensional content model]
: Generate pages across any combination of language, version, and role from a single source. This allows a single piece of content to be published to multiple [sites](g) within your project, removing the need to duplicate files for different audiences or versions.
[Content adapters]
: Create content adapters to dynamically add content when building your site. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML.
: Create content adapters to dynamically add content when building your project. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML.
[Taxonomies]
: Classify content to establish simple or complex logical relationships between pages. For example, create an authors taxonomy, and assign one or more authors to each page. Among other uses, the taxonomy system provides an inverted, weighted index to render a list of related pages, ordered by relevance.
@@ -78,7 +81,7 @@ weight: 20
## Asset pipelines
[Image processing]
: Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data.
: Convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract metadata.
[JavaScript bundling]
: Transpile TypeScript and JSX to JavaScript, bundle, tree shake, minify, create source maps, and perform SRI hashing.
@@ -95,7 +98,7 @@ weight: 20
: Reduce build time and cost by rendering a _partial_ template once then cache the result, either globally or within a given context. For example, cache the result of an asset pipeline to prevent reprocessing on every rendered page.
[Segmentation]
: Reduce build time and cost by partitioning your sites into segments. For example, render the home page and the "news section" every hour, and render the entire site once a week.
: Reduce build time and cost by partitioning your sites into segments. For example, render the home page and the "news section" every hour, and render the entire project once a week.
[Minification]
: Minify HTML, CSS, and JavaScript to reduce file size, bandwidth consumption, and loading times.
@@ -120,6 +123,7 @@ weight: 20
[Syntax highlighting]: /content-management/syntax-highlighting/
[Shortcodes]: /content-management/shortcodes/
[Multidimensional content model]: /quick-reference/glossary/#sites-matrix
[Content adapters]: /content-management/content-adapters/
[Taxonomies]: /content-management/taxonomies/
[Data]: /content-management/data-sources/
+1 -1
View File
@@ -1,7 +1,7 @@
---
title: Command line interface
linkTitle: CLI
description: Use the command line interface (CLI) to manage your site.
description: Use the command line interface (CLI) to manage your project.
categories: []
keywords: []
weight: 10
+6 -7
View File
@@ -5,11 +5,11 @@ url: /commands/hugo/
---
## hugo
Build your site
Build your project
### Synopsis
hugo is the main command, used to build your Hugo site.
hugo is the main command, used to build your Hugo project.
Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.
@@ -68,17 +68,16 @@ hugo [flags]
### SEE ALSO
* [hugo build](/commands/hugo_build/) - Build your site
* [hugo build](/commands/hugo_build/) - Build your project
* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell
* [hugo config](/commands/hugo_config/) - Display site configuration
* [hugo config](/commands/hugo_config/) - Display project configuration
* [hugo convert](/commands/hugo_convert/) - Convert front matter to another format
* [hugo deploy](/commands/hugo_deploy/) - Deploy your site to a cloud provider
* [hugo deploy](/commands/hugo_deploy/) - Deploy your project to a cloud provider
* [hugo env](/commands/hugo_env/) - Display version and environment info
* [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles
* [hugo import](/commands/hugo_import/) - Import a site from another system
* [hugo import](/commands/hugo_import/) - Import a project from another system
* [hugo list](/commands/hugo_list/) - List content
* [hugo mod](/commands/hugo_mod/) - Manage modules
* [hugo new](/commands/hugo_new/) - Create new content
* [hugo server](/commands/hugo_server/) - Start the embedded web server
* [hugo version](/commands/hugo_version/) - Display version
+3 -4
View File
@@ -5,11 +5,11 @@ url: /commands/hugo_build/
---
## hugo build
Build your site
Build your project
### Synopsis
build is the main command, used to build your Hugo site.
build is the main command, used to build your Hugo project.
Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.
@@ -68,5 +68,4 @@ hugo build [flags]
### SEE ALSO
* [hugo](/commands/hugo/) - Build your site
* [hugo](/commands/hugo/) - Build your project

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