mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-26 16:28:52 +00:00
Compare commits
125 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 518eefee9b | |||
| 3aa22b0942 | |||
| 40c3d8233d | |||
| 7ff5ec734c | |||
| 3937ab24d0 | |||
| 9c57af1351 | |||
| d240a705d6 | |||
| bbcc2a7973 | |||
| 98ba786f2f | |||
| 6f42cfbc9b | |||
| a84beee429 | |||
| 65893efd8d | |||
| c0d9bebacc | |||
| 3e2f1cdfdb | |||
| 0a5b870281 | |||
| bba6996e15 | |||
| 94e2c276a8 | |||
| 90d397b142 | |||
| 61e6c730dd | |||
| e4f6b9eef8 | |||
| bb147f91ee | |||
| 266d46dccc | |||
| e77b2ad8fd | |||
| 9487acf6ab | |||
| 84b31721bf | |||
| b8ba33ca95 | |||
| f967212b72 | |||
| 1b4c423667 | |||
| 1e9a0b93e3 | |||
| cfc8d315b4 | |||
| dd6e2c8724 | |||
| 762417617c | |||
| 29bdbde19c | |||
| 6a4a3ab8f8 | |||
| 36f6f987a9 | |||
| 18a9ca7d7a | |||
| b6c8dfa9dc | |||
| 621ea42f3c | |||
| 4ef5720141 | |||
| 34e83789f7 | |||
| 4d3ebe4d21 | |||
| b5c0383bda | |||
| 4217fee4b0 | |||
| fad57964aa | |||
| 10da2bd765 | |||
| 01241d5dc9 | |||
| 8e61f1fe12 | |||
| f37412a575 | |||
| 21a4a9acd7 | |||
| 7a4a4790e5 | |||
| 54065b7ef8 | |||
| e333836f49 | |||
| cc7bfeea32 | |||
| 32eb1a8ad4 | |||
| 32af02cd3e | |||
| 189453612e | |||
| 5273a884d4 | |||
| 6334948515 | |||
| 75259636c8 | |||
| 0df9f3510f | |||
| 302e6a726b | |||
| 202fe0d45c | |||
| 843ffeb48d | |||
| bff5d19121 | |||
| da370d30de | |||
| 6bd328c584 | |||
| 766a2e7868 | |||
| 13e1617557 | |||
| 463e440c7a | |||
| 0a5fd8ebb8 | |||
| e57dcd3795 | |||
| eaf5ace30d | |||
| 9ad26b69ad | |||
| f47193669d | |||
| 013c8cfb25 | |||
| e25db38467 | |||
| 7766fc6241 | |||
| 4a48facef4 | |||
| 0c7b1a3f26 | |||
| 970b887ba1 | |||
| b9b95e5aec | |||
| 84c8426f32 | |||
| a03a245f0c | |||
| 5a81a3a4cf | |||
| 61317821e4 | |||
| 6142bc701c | |||
| e6574cf7a7 | |||
| 05417512bd | |||
| 81426998b8 | |||
| 6def5a1ba9 | |||
| bc98e7a80d | |||
| c745a3e108 | |||
| 9d1d8c8899 | |||
| 84d7a108e8 | |||
| 325a0dba63 | |||
| d70f828e2b | |||
| c7feb15d10 | |||
| 363ab48a24 | |||
| 80f0595311 | |||
| b39b249623 | |||
| d799c045fd | |||
| 95666fc5a4 | |||
| 620fc87b56 | |||
| 8b2124e7c3 | |||
| 5fec7829b1 | |||
| 927d1ec6c1 | |||
| be93d5218b | |||
| a1cb15e1cf | |||
| 673a4d00eb | |||
| 31db7edf6d | |||
| 5857b60cbc | |||
| 7d0039b86d | |||
| 07983e04e2 | |||
| 5c491409d3 | |||
| 75b219db89 | |||
| ad4f63c92f | |||
| 53202314ab | |||
| 2fce0bac03 | |||
| 179aea11ac | |||
| b3d87dd0fd | |||
| 61a286595e | |||
| 6a0e04241a | |||
| 1bd7ac7ed9 | |||
| 41cb880f9a | |||
| df44ee1353 |
+4
-4
@@ -2,8 +2,8 @@
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
ARG GO_VERSION="1.23.2"
|
||||
ARG ALPINE_VERSION="3.20"
|
||||
ARG GO_VERSION="1.24"
|
||||
ARG ALPINE_VERSION="3.22"
|
||||
ARG DART_SASS_VERSION="1.79.3"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
|
||||
@@ -19,7 +19,7 @@ RUN apk add clang lld
|
||||
COPY --from=xx / /
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apk add musl-dev gcc g++
|
||||
RUN xx-apk add musl-dev gcc g++
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=withdeploy .
|
||||
@@ -72,7 +72,7 @@ RUN mkdir -p /var/hugo/bin /cache && \
|
||||
adduser -Sg hugo -u 1000 -h /var/hugo hugo && \
|
||||
chown -R hugo: /var/hugo /cache && \
|
||||
# For the Hugo's Git integration to work.
|
||||
runuser -u hugo -- git config --global --add safe.directory /project && \
|
||||
runuser -u hugo -- git config --global --add safe.directory /project && \
|
||||
# See https://github.com/gohugoio/hugo/issues/9810
|
||||
runuser -u hugo -- git config --global core.quotepath false
|
||||
|
||||
|
||||
@@ -65,9 +65,11 @@ See the [features] section of the documentation for a comprehensive summary of H
|
||||
|
||||
<p> </p>
|
||||
<p float="left">
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
|
||||
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
|
||||
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
|
||||
|
||||
<a href="https://pinme.eth.limo/?s=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/logo-pinme.svg" width="200" alt="PinMe."></a>
|
||||
</p>
|
||||
|
||||
## Editions
|
||||
|
||||
@@ -962,7 +962,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
lrl.Logf("no page to navigate to, force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
} else if len(otherChanges) > 0 {
|
||||
} else if len(otherChanges) > 0 || len(cssChanges) > 0 {
|
||||
if len(otherChanges) == 1 {
|
||||
// Allow single changes to be refreshed without a full page reload.
|
||||
pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(otherChanges[0]), false)
|
||||
@@ -972,6 +972,9 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
lrl.Logf("force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
} else {
|
||||
lrl.Logf("force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
|
||||
if len(cssChanges) > 0 {
|
||||
|
||||
+5
-9
@@ -76,10 +76,8 @@ Ensure you run this within the root directory of your site.`,
|
||||
&simpleCommand{
|
||||
name: "site",
|
||||
use: "site [path]",
|
||||
short: "Create a new site (skeleton)",
|
||||
long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
short: "Create a new site",
|
||||
long: `Create a new site at the specified path.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("path needs to be provided")
|
||||
@@ -124,11 +122,9 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
&simpleCommand{
|
||||
name: "theme",
|
||||
use: "theme [name]",
|
||||
short: "Create a new theme (skeleton)",
|
||||
long: `Create a new theme (skeleton) called [name] in ./themes.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
according to your needs.`,
|
||||
short: "Create a new theme",
|
||||
long: `Create a new theme with the specified name in the ./themes directory.
|
||||
This generates a functional theme including template examples and sample content.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("theme name needs to be provided")
|
||||
|
||||
+1
-1
@@ -627,7 +627,7 @@ func (c *serverCommand) setServerInfoInConfig() error {
|
||||
panic("no server ports set")
|
||||
}
|
||||
return c.withConfE(func(conf *commonConfig) error {
|
||||
for i, language := range conf.configs.Languages {
|
||||
for i, language := range conf.configs.LanguagesDefaultFirst {
|
||||
isMultihost := conf.configs.IsMultihost
|
||||
var serverPort int
|
||||
if isMultihost {
|
||||
|
||||
@@ -15,6 +15,7 @@ package collections
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -77,6 +78,7 @@ func TestAppend(t *testing.T) {
|
||||
{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
|
||||
{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
|
||||
{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
|
||||
{[]string{"a", "b"}, []any{}, []string{"a", "b"}},
|
||||
} {
|
||||
|
||||
result, err := Append(test.start, test.addend...)
|
||||
@@ -146,3 +148,66 @@ func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) {
|
||||
c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"})
|
||||
c.Assert(slice, qt.DeepEquals, []string{"d", "b"})
|
||||
}
|
||||
|
||||
func TestIndirect(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
type testStruct struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
var (
|
||||
nilPtr *testStruct
|
||||
nilIface interface{} = nil
|
||||
nonNilIface interface{} = &testStruct{Field: "hello"}
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input any
|
||||
wantKind reflect.Kind
|
||||
wantNil bool
|
||||
}{
|
||||
{
|
||||
name: "nil pointer",
|
||||
input: nilPtr,
|
||||
wantKind: reflect.Ptr,
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "nil interface",
|
||||
input: nilIface,
|
||||
wantKind: reflect.Invalid,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "non-nil pointer to struct",
|
||||
input: &testStruct{Field: "abc"},
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "non-nil interface holding pointer",
|
||||
input: nonNilIface,
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "plain value",
|
||||
input: testStruct{Field: "xyz"},
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := reflect.ValueOf(tt.input)
|
||||
got, isNil := indirect(v)
|
||||
|
||||
c.Assert(got.Kind(), qt.Equals, tt.wantKind)
|
||||
c.Assert(isNil, qt.Equals, tt.wantNil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,3 +136,37 @@ func TestSortedStringSlice(t *testing.T) {
|
||||
c.Assert(s.Count("z"), qt.Equals, 0)
|
||||
c.Assert(s.Count("a"), qt.Equals, 1)
|
||||
}
|
||||
|
||||
func TestStringSliceToInterfaceSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in []string
|
||||
want []any
|
||||
}{
|
||||
{
|
||||
name: "empty slice",
|
||||
in: []string{},
|
||||
want: []any{},
|
||||
},
|
||||
{
|
||||
name: "single element",
|
||||
in: []string{"hello"},
|
||||
want: []any{"hello"},
|
||||
},
|
||||
{
|
||||
name: "multiple elements",
|
||||
in: []string{"a", "b", "c"},
|
||||
want: []any{"a", "b", "c"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := StringSliceToInterfaceSlice(tt.in)
|
||||
c.Assert(got, qt.DeepEquals, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestNewStack(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
|
||||
c.Assert(s, qt.IsNotNil)
|
||||
}
|
||||
|
||||
func TestStackBasic(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 0)
|
||||
|
||||
s.Push(1)
|
||||
s.Push(2)
|
||||
s.Push(3)
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 3)
|
||||
|
||||
top, ok := s.Peek()
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
c.Assert(top, qt.Equals, 3)
|
||||
|
||||
popped, ok := s.Pop()
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
c.Assert(popped, qt.Equals, 3)
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 2)
|
||||
|
||||
_, _ = s.Pop()
|
||||
_, _ = s.Pop()
|
||||
_, ok = s.Pop()
|
||||
|
||||
c.Assert(ok, qt.Equals, false)
|
||||
}
|
||||
|
||||
func TestStackDrain(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[string]()
|
||||
s.Push("a")
|
||||
s.Push("b")
|
||||
|
||||
got := s.Drain()
|
||||
|
||||
c.Assert(got, qt.DeepEquals, []string{"a", "b"})
|
||||
c.Assert(s.Len(), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestStackDrainMatching(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
s.Push(1)
|
||||
s.Push(2)
|
||||
s.Push(3)
|
||||
s.Push(4)
|
||||
|
||||
got := s.DrainMatching(func(v int) bool { return v%2 == 0 })
|
||||
|
||||
c.Assert(got, qt.DeepEquals, []int{4, 2})
|
||||
c.Assert(s.Drain(), qt.DeepEquals, []int{1, 3})
|
||||
}
|
||||
@@ -24,6 +24,7 @@ const (
|
||||
WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html"
|
||||
WarnGoldmarkRawHTML = "warning-goldmark-raw-html"
|
||||
WarnPartialSuperfluousPrefix = "warning-partial-superfluous-prefix"
|
||||
WarnHomePageIsLeafBundle = "warning-home-page-is-leaf-bundle"
|
||||
)
|
||||
|
||||
// Field/method names with special meaning.
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 146,
|
||||
PatchLevel: 7,
|
||||
Suffix: "",
|
||||
Minor: 149,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
+19
-13
@@ -120,15 +120,20 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot int) {
|
||||
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot, numDots int, isLast bool) {
|
||||
if p.posContainerHigh != -1 {
|
||||
return
|
||||
}
|
||||
mayHaveLang := p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
|
||||
mayHaveLang := numDots > 1 && p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
|
||||
mayHaveLang = mayHaveLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
|
||||
mayHaveOutputFormat := component == files.ComponentFolderLayouts
|
||||
mayHaveKind := p.posIdentifierKind == -1 && mayHaveOutputFormat
|
||||
mayHaveLayout := component == files.ComponentFolderLayouts
|
||||
var mayHaveLayout bool
|
||||
if p.pathType == TypeShortcode {
|
||||
mayHaveLayout = !isLast && component == files.ComponentFolderLayouts
|
||||
} else {
|
||||
mayHaveLayout = component == files.ComponentFolderLayouts
|
||||
}
|
||||
|
||||
var found bool
|
||||
var high int
|
||||
@@ -167,7 +172,6 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
|
||||
if langFound {
|
||||
p.identifiersKnown = append(p.identifiersKnown, id)
|
||||
p.posIdentifierLanguage = len(p.identifiersKnown) - 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,19 +238,24 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
||||
p.s = s
|
||||
slashCount := 0
|
||||
lastDot := 0
|
||||
lastSlashIdx := strings.LastIndex(s, "/")
|
||||
numDots := strings.Count(s[lastSlashIdx+1:], ".")
|
||||
if strings.Contains(s, "/_shortcodes/") {
|
||||
p.pathType = TypeShortcode
|
||||
}
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
|
||||
switch c {
|
||||
case '.':
|
||||
pp.parseIdentifier(component, s, p, i, lastDot)
|
||||
pp.parseIdentifier(component, s, p, i, lastDot, numDots, false)
|
||||
lastDot = i
|
||||
case '/':
|
||||
slashCount++
|
||||
if p.posContainerHigh == -1 {
|
||||
if lastDot > 0 {
|
||||
pp.parseIdentifier(component, s, p, i, lastDot)
|
||||
pp.parseIdentifier(component, s, p, i, lastDot, numDots, true)
|
||||
}
|
||||
p.posContainerHigh = i + 1
|
||||
} else if p.posContainerLow == -1 {
|
||||
@@ -282,10 +291,9 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
||||
p.pathType = TypeContentData
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if component == files.ComponentFolderLayouts {
|
||||
if p.pathType < TypeMarkup && component == files.ComponentFolderLayouts {
|
||||
if p.posIdentifierBaseof != -1 {
|
||||
p.pathType = TypeBaseof
|
||||
} else {
|
||||
@@ -301,12 +309,10 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
||||
}
|
||||
|
||||
if p.pathType == TypeShortcode && p.posIdentifierLayout != -1 {
|
||||
// myshortcode or myshortcode.html, no layout.
|
||||
if len(p.identifiersKnown) <= 2 {
|
||||
id := p.identifiersKnown[p.posIdentifierLayout]
|
||||
if id.Low == p.posContainerHigh {
|
||||
// First identifier is shortcode name.
|
||||
p.posIdentifierLayout = -1
|
||||
} else {
|
||||
// First is always the name.
|
||||
p.posIdentifierLayout--
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -434,12 +434,12 @@ func TestParseLayouts(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"Layout multiple",
|
||||
"/maylayout.list.section.no.html",
|
||||
"/mylayout.list.section.no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Layout(), qt.Equals, "maylayout")
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "maylayout"})
|
||||
c.Assert(p.Layout(), qt.Equals, "mylayout")
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "mylayout"})
|
||||
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
|
||||
c.Assert(p.Base(), qt.Equals, "/maylayout.html")
|
||||
c.Assert(p.Base(), qt.Equals, "/mylayout.html")
|
||||
c.Assert(p.Lang(), qt.Equals, "no")
|
||||
},
|
||||
},
|
||||
@@ -487,7 +487,8 @@ func TestParseLayouts(t *testing.T) {
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Base(), qt.Equals, "/_shortcodes/myshortcode.html")
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list", "myshortcode"})
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list"})
|
||||
c.Assert(p.Layout(), qt.Equals, "list")
|
||||
c.Assert(p.PathNoIdentifier(), qt.Equals, "/_shortcodes/myshortcode")
|
||||
c.Assert(p.PathBeforeLangAndOutputFormatAndExt(), qt.Equals, "/_shortcodes/myshortcode.list")
|
||||
c.Assert(p.Lang(), qt.Equals, "")
|
||||
@@ -563,11 +564,30 @@ func TestParseLayouts(t *testing.T) {
|
||||
c.Assert(p.Type(), qt.Equals, TypePartial)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Shortcode lang in root",
|
||||
"/_shortcodes/no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Lang(), qt.Equals, "")
|
||||
c.Assert(p.NameNoIdentifier(), qt.Equals, "no")
|
||||
},
|
||||
},
|
||||
{
|
||||
"Shortcode lang layout",
|
||||
"/_shortcodes/myshortcode.no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Lang(), qt.Equals, "no")
|
||||
c.Assert(p.Layout(), qt.Equals, "")
|
||||
c.Assert(p.NameNoIdentifier(), qt.Equals, "myshortcode")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c.Run(test.name, func(c *qt.C) {
|
||||
if test.name != "Baseof" {
|
||||
if test.name != "Shortcode lang layout" {
|
||||
// return
|
||||
}
|
||||
test.assert(c, testParser.Parse(files.ComponentFolderLayouts, test.path))
|
||||
|
||||
@@ -17,7 +17,6 @@ package terminal
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
@@ -41,10 +40,6 @@ func PrintANSIColors(f *os.File) bool {
|
||||
// IsTerminal return true if the file descriptor is terminal and the TERM
|
||||
// environment variable isn't a dumb one.
|
||||
func IsTerminal(f *os.File) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
return false
|
||||
}
|
||||
|
||||
fd := f.Fd()
|
||||
return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd))
|
||||
}
|
||||
|
||||
+128
-33
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,7 +33,6 @@ import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/common/urls"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/privacy"
|
||||
@@ -42,6 +42,7 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib/segments"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||
"github.com/gohugoio/hugo/markup/markup_config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/minifiers"
|
||||
@@ -146,7 +147,7 @@ type Config struct {
|
||||
|
||||
// The cascade configuration section contains the top level front matter cascade configuration options,
|
||||
// a slice of page matcher and params to apply to those pages.
|
||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
|
||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]] `mapstructure:"-"`
|
||||
|
||||
// The segments defines segments for the site. Used for partial/segmented builds.
|
||||
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
|
||||
@@ -399,7 +400,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
hugo.DeprecateWithLogger("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0", logger.Logger())
|
||||
c.Pagination.PagerSize = c.Paginate
|
||||
}
|
||||
|
||||
if c.PaginatePath != "" {
|
||||
hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger())
|
||||
c.Pagination.Path = c.PaginatePath
|
||||
@@ -410,12 +410,10 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
hugo.DeprecateWithLogger("site 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())
|
||||
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())
|
||||
c.Privacy.X.Simple = c.Privacy.Twitter.Simple
|
||||
@@ -436,6 +434,45 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger())
|
||||
}
|
||||
|
||||
// Legacy render hook values.
|
||||
alternativeDetails := fmt.Sprintf(
|
||||
"Set to %q if previous value was false, or set to %q if previous value was true.",
|
||||
gc.RenderHookUseEmbeddedNever,
|
||||
gc.RenderHookUseEmbeddedFallback,
|
||||
)
|
||||
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())
|
||||
if *c.Markup.Goldmark.RenderHooks.Image.EnableDefault {
|
||||
c.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
|
||||
} else {
|
||||
c.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedNever
|
||||
}
|
||||
}
|
||||
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())
|
||||
if *c.Markup.Goldmark.RenderHooks.Link.EnableDefault {
|
||||
c.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
|
||||
} else {
|
||||
c.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedNever
|
||||
}
|
||||
}
|
||||
|
||||
// Validate render hook configuration.
|
||||
renderHookUseEmbeddedModes := []string{
|
||||
gc.RenderHookUseEmbeddedAlways,
|
||||
gc.RenderHookUseEmbeddedAuto,
|
||||
gc.RenderHookUseEmbeddedFallback,
|
||||
gc.RenderHookUseEmbeddedNever,
|
||||
}
|
||||
if !slices.Contains(renderHookUseEmbeddedModes, c.Markup.Goldmark.RenderHooks.Image.UseEmbedded) {
|
||||
return fmt.Errorf("site config markup.goldmark.renderHooks.image 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 must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
|
||||
}
|
||||
|
||||
c.C = &ConfigCompiled{
|
||||
Timeout: timeout,
|
||||
BaseURL: baseURL,
|
||||
@@ -776,7 +813,7 @@ type Configs struct {
|
||||
}
|
||||
|
||||
func (c *Configs) Validate(logger loggers.Logger) error {
|
||||
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
|
||||
c.Base.Cascade.Config.Range(func(p page.PageMatcher, cfg page.PageMatcherParamsConfig) bool {
|
||||
page.CheckCascadePattern(logger, p)
|
||||
return true
|
||||
})
|
||||
@@ -800,30 +837,58 @@ func (c *Configs) IsZero() bool {
|
||||
|
||||
func (c *Configs) Init() error {
|
||||
var languages langs.Languages
|
||||
defaultContentLanguage := c.Base.DefaultContentLanguage
|
||||
for k, v := range c.LanguageConfigMap {
|
||||
|
||||
var langKeys []string
|
||||
var hasEn bool
|
||||
|
||||
const en = "en"
|
||||
|
||||
for k := range c.LanguageConfigMap {
|
||||
langKeys = append(langKeys, k)
|
||||
if k == en {
|
||||
hasEn = true
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the LanguageConfigSlice by language weight (if set) or lang.
|
||||
sort.Slice(langKeys, func(i, j int) bool {
|
||||
ki := langKeys[i]
|
||||
kj := langKeys[j]
|
||||
lki := c.LanguageConfigMap[ki]
|
||||
lkj := c.LanguageConfigMap[kj]
|
||||
li := lki.Languages[ki]
|
||||
lj := lkj.Languages[kj]
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return ki < kj
|
||||
})
|
||||
|
||||
// See issue #13646.
|
||||
defaultConfigLanguageFallback := en
|
||||
if !hasEn {
|
||||
// Pick the first one.
|
||||
defaultConfigLanguageFallback = langKeys[0]
|
||||
}
|
||||
|
||||
if c.Base.DefaultContentLanguage == "" {
|
||||
c.Base.DefaultContentLanguage = defaultConfigLanguageFallback
|
||||
}
|
||||
|
||||
for _, k := range langKeys {
|
||||
v := c.LanguageConfigMap[k]
|
||||
if v.DefaultContentLanguage == "" {
|
||||
v.DefaultContentLanguage = defaultConfigLanguageFallback
|
||||
}
|
||||
c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
|
||||
languageConf := v.Languages[k]
|
||||
language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
|
||||
language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
languages = append(languages, language)
|
||||
}
|
||||
|
||||
// Sort the sites by language weight (if set) or lang.
|
||||
sort.Slice(languages, func(i, j int) bool {
|
||||
li := languages[i]
|
||||
lj := languages[j]
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return li.Lang < lj.Lang
|
||||
})
|
||||
|
||||
for _, l := range languages {
|
||||
c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang])
|
||||
}
|
||||
|
||||
// Filter out disabled languages.
|
||||
var n int
|
||||
for _, l := range languages {
|
||||
@@ -836,12 +901,12 @@ func (c *Configs) Init() error {
|
||||
|
||||
var languagesDefaultFirst langs.Languages
|
||||
for _, l := range languages {
|
||||
if l.Lang == defaultContentLanguage {
|
||||
if l.Lang == c.Base.DefaultContentLanguage {
|
||||
languagesDefaultFirst = append(languagesDefaultFirst, l)
|
||||
}
|
||||
}
|
||||
for _, l := range languages {
|
||||
if l.Lang != defaultContentLanguage {
|
||||
if l.Lang != c.Base.DefaultContentLanguage {
|
||||
languagesDefaultFirst = append(languagesDefaultFirst, l)
|
||||
}
|
||||
}
|
||||
@@ -927,17 +992,48 @@ func (c Configs) GetByLang(lang string) config.AllProvider {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Taxonomies: map[string]string{"tag": "tags", "category": "categories"},
|
||||
Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"},
|
||||
RootConfig: RootConfig{
|
||||
Environment: hugo.EnvironmentProduction,
|
||||
TitleCaseStyle: "AP",
|
||||
PluralizeListTitles: true,
|
||||
CapitalizeListTitles: true,
|
||||
StaticDir: []string{"static"},
|
||||
SummaryLength: 70,
|
||||
Timeout: "60s",
|
||||
|
||||
CommonDirs: config.CommonDirs{
|
||||
ArcheTypeDir: "archetypes",
|
||||
ContentDir: "content",
|
||||
ResourceDir: "resources",
|
||||
PublishDir: "public",
|
||||
ThemesDir: "themes",
|
||||
AssetDir: "assets",
|
||||
LayoutDir: "layouts",
|
||||
I18nDir: "i18n",
|
||||
DataDir: "data",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// fromLoadConfigResult creates a new Config from res.
|
||||
func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) {
|
||||
if !res.Cfg.IsSet("languages") {
|
||||
// We need at least one
|
||||
lang := res.Cfg.GetString("defaultContentLanguage")
|
||||
if lang == "" {
|
||||
lang = "en"
|
||||
}
|
||||
res.Cfg.Set("languages", maps.Params{lang: maps.Params{}})
|
||||
}
|
||||
bcfg := res.BaseConfig
|
||||
cfg := res.Cfg
|
||||
|
||||
all := &Config{}
|
||||
all := newDefaultConfig()
|
||||
|
||||
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
|
||||
if err != nil {
|
||||
@@ -947,6 +1043,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
langConfigMap := make(map[string]*Config)
|
||||
|
||||
languagesConfig := cfg.GetStringMap("languages")
|
||||
|
||||
var isMultihost bool
|
||||
|
||||
if err := all.CompileConfig(logger); err != nil {
|
||||
@@ -1022,13 +1119,11 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
|
||||
// Adjust Goldmark config defaults for multilingual, single-host sites.
|
||||
if len(languagesConfig) > 1 && !isMultihost && !clone.Markup.Goldmark.DuplicateResourceFiles {
|
||||
if !clone.Markup.Goldmark.DuplicateResourceFiles {
|
||||
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
|
||||
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
|
||||
}
|
||||
if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
|
||||
clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
|
||||
}
|
||||
if clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
|
||||
clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
|
||||
}
|
||||
if clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
|
||||
clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@ package allconfig_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
)
|
||||
|
||||
@@ -234,3 +237,172 @@ baseURL = "https://example.com"
|
||||
b.Assert(c.IsContentFile("foo.md"), qt.Equals, true)
|
||||
b.Assert(len(s), qt.Equals, 6)
|
||||
}
|
||||
|
||||
func TestMergeDeep(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
theme = ["theme1", "theme2"]
|
||||
_merge = "deep"
|
||||
-- themes/theme1/hugo.toml --
|
||||
[sitemap]
|
||||
filename = 'mysitemap.xml'
|
||||
[services]
|
||||
[services.googleAnalytics]
|
||||
id = 'foo bar'
|
||||
[taxonomies]
|
||||
foo = 'bars'
|
||||
-- themes/theme2/config/_default/hugo.toml --
|
||||
[taxonomies]
|
||||
bar = 'baz'
|
||||
-- layouts/home.html --
|
||||
GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction)
|
||||
b.Assert(base.BaseURL, qt.Equals, "https://example.com")
|
||||
b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml")
|
||||
b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"})
|
||||
|
||||
b.AssertFileContent("public/index.html", "GA ID: foo bar.")
|
||||
}
|
||||
|
||||
func TestMergeDeepBuildStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
title = "Theme 1"
|
||||
_merge = "deep"
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
[[module.imports]]
|
||||
path = "theme1"
|
||||
-- themes/theme1/hugo.toml --
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableIDs = true
|
||||
enable = true
|
||||
-- layouts/home.html --
|
||||
Home.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Title, qt.Equals, "Theme 1")
|
||||
b.Assert(len(base.Module.Imports), qt.Equals, 1)
|
||||
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
|
||||
b.AssertFileExists("/hugo_stats.json", true)
|
||||
}
|
||||
|
||||
func TestMergeDeepBuildStatsTheme(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
_merge = "deep"
|
||||
theme = ["theme1"]
|
||||
-- themes/theme1/hugo.toml --
|
||||
title = "Theme 1"
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableIDs = true
|
||||
enable = true
|
||||
-- layouts/home.html --
|
||||
Home.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Title, qt.Equals, "Theme 1")
|
||||
b.Assert(len(base.Module.Imports), qt.Equals, 1)
|
||||
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
|
||||
b.AssertFileExists("/hugo_stats.json", true)
|
||||
}
|
||||
|
||||
func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
[languages]
|
||||
[languages.nn]
|
||||
weight = 20
|
||||
[languages.sv]
|
||||
weight = 10
|
||||
[languages.sv.taxonomies]
|
||||
tag = "taggar"
|
||||
-- layouts/all.html --
|
||||
All.
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
|
||||
}
|
||||
|
||||
func TestDefaultConfigEnvDisableLanguagesIssue13707(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLanguages = []
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
[languages.sv]
|
||||
weight = 3
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) {
|
||||
conf.Environ = []string{`HUGO_DISABLELANGUAGES=sv nn`}
|
||||
}))
|
||||
|
||||
b.Assert(len(b.H.Sites), qt.Equals, 1)
|
||||
}
|
||||
|
||||
// Issue 13535
|
||||
// We changed enablement of the embedded link and image render hooks from
|
||||
// booleans to enums in v0.148.0.
|
||||
func TestLegacyEmbeddedRenderHookEnablement(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
[markup.goldmark.renderHooks.image]
|
||||
#KEY_VALUE
|
||||
|
||||
[markup.goldmark.renderHooks.link]
|
||||
#KEY_VALUE
|
||||
`
|
||||
f := strings.ReplaceAll(files, "#KEY_VALUE", "enableDefault = false")
|
||||
b := hugolib.Test(t, f)
|
||||
c := b.H.Configs.Base.Markup.Goldmark.RenderHooks
|
||||
b.Assert(c.Link.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedNever)
|
||||
b.Assert(c.Image.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedNever)
|
||||
|
||||
f = strings.ReplaceAll(files, "#KEY_VALUE", "enableDefault = true")
|
||||
b = hugolib.Test(t, f)
|
||||
c = b.H.Configs.Base.Markup.Goldmark.RenderHooks
|
||||
b.Assert(c.Link.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedFallback)
|
||||
b.Assert(c.Image.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedFallback)
|
||||
}
|
||||
|
||||
@@ -249,14 +249,18 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "sitemap",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key))
|
||||
if p.p.IsSet(d.key) {
|
||||
p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key))
|
||||
}
|
||||
return err
|
||||
},
|
||||
},
|
||||
"taxonomies": {
|
||||
key: "taxonomies",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
if p.p.IsSet(d.key) {
|
||||
p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -306,15 +310,17 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
}
|
||||
|
||||
// Validate defaultContentLanguage.
|
||||
var found bool
|
||||
for lang := range p.c.Languages {
|
||||
if lang == p.c.DefaultContentLanguage {
|
||||
found = true
|
||||
break
|
||||
if p.c.DefaultContentLanguage != "" {
|
||||
var found bool
|
||||
for lang := range p.c.Languages {
|
||||
if lang == p.c.DefaultContentLanguage {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -324,7 +330,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "cascade",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key))
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(nil, true, p.p.Get(d.key))
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
||||
+40
-83
@@ -159,63 +159,9 @@ func (l configLoader) applyConfigAliases() error {
|
||||
|
||||
func (l configLoader) applyDefaultConfig() error {
|
||||
defaultSettings := maps.Params{
|
||||
"baseURL": "",
|
||||
"cleanDestinationDir": false,
|
||||
"watch": false,
|
||||
"contentDir": "content",
|
||||
"resourceDir": "resources",
|
||||
"publishDir": "public",
|
||||
"publishDirOrig": "public",
|
||||
"themesDir": "themes",
|
||||
"assetDir": "assets",
|
||||
"layoutDir": "layouts",
|
||||
"i18nDir": "i18n",
|
||||
"dataDir": "data",
|
||||
"archetypeDir": "archetypes",
|
||||
"configDir": "config",
|
||||
"staticDir": "static",
|
||||
"buildDrafts": false,
|
||||
"buildFuture": false,
|
||||
"buildExpired": false,
|
||||
"params": maps.Params{},
|
||||
"environment": hugo.EnvironmentProduction,
|
||||
"uglyURLs": false,
|
||||
"verbose": false,
|
||||
"ignoreCache": false,
|
||||
"canonifyURLs": false,
|
||||
"relativeURLs": false,
|
||||
"removePathAccents": false,
|
||||
"titleCaseStyle": "AP",
|
||||
"taxonomies": maps.Params{"tag": "tags", "category": "categories"},
|
||||
"permalinks": maps.Params{},
|
||||
"sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"},
|
||||
"menus": maps.Params{},
|
||||
"disableLiveReload": false,
|
||||
"pluralizeListTitles": true,
|
||||
"capitalizeListTitles": true,
|
||||
"forceSyncStatic": false,
|
||||
"footnoteAnchorPrefix": "",
|
||||
"footnoteReturnLinkContents": "",
|
||||
"newContentEditor": "",
|
||||
"paginate": 0, // Moved into the paginator struct in Hugo v0.128.0.
|
||||
"paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0.
|
||||
"summaryLength": 70,
|
||||
"rssLimit": -1,
|
||||
"sectionPagesMenu": "",
|
||||
"disablePathToLower": false,
|
||||
"hasCJKLanguage": false,
|
||||
"enableEmoji": false,
|
||||
"defaultContentLanguage": "en",
|
||||
"defaultContentLanguageInSubdir": false,
|
||||
"enableMissingTranslationPlaceholders": false,
|
||||
"enableGitInfo": false,
|
||||
"ignoreFiles": make([]string, 0),
|
||||
"disableAliases": false,
|
||||
"debug": false,
|
||||
"disableFastRender": false,
|
||||
"timeout": "60s",
|
||||
"timeZone": "",
|
||||
"enableInlineShortcodes": false,
|
||||
// These dirs are used early/before we build the config struct.
|
||||
"themesDir": "themes",
|
||||
"configDir": "config",
|
||||
}
|
||||
|
||||
l.cfg.SetDefaults(defaultSettings)
|
||||
@@ -287,40 +233,51 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
|
||||
if existing != nil {
|
||||
val, err := metadecoders.Default.UnmarshalStringTo(env.Value, existing)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
val = l.envValToVal(env.Key, val)
|
||||
if owner != nil {
|
||||
owner[nestedKey] = val
|
||||
} else {
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if owner != nil {
|
||||
owner[nestedKey] = val
|
||||
} else {
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
} else {
|
||||
if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
if owner != nil && nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *configLoader) envValToVal(k string, v any) any {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return l.envStringToVal(k, v)
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (l *configLoader) envStringToVal(k, v string) any {
|
||||
switch k {
|
||||
case "disablekinds", "disablelanguages":
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -28,7 +29,6 @@ import (
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cast"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type BaseConfig struct {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -32,3 +33,13 @@ func TestIsValidConfigFileName(t *testing.T) {
|
||||
c.Assert(IsValidConfigFilename(""), qt.Equals, false)
|
||||
c.Assert(IsValidConfigFilename("config.toml.swp"), qt.Equals, false)
|
||||
}
|
||||
|
||||
func TestFromTOMLConfigString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(
|
||||
func() { FromTOMLConfigString("cfg") },
|
||||
qt.PanicMatches,
|
||||
regexp.MustCompile("_stream.toml:.*"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ type AllProvider interface {
|
||||
}
|
||||
|
||||
// We cannot import the media package as that would create a circular dependency.
|
||||
// This interface defineds a sub set of what media.ContentTypes provides.
|
||||
// This interface defines a subset of what media.ContentTypes provides.
|
||||
type ContentTypesProvider interface {
|
||||
IsContentSuffix(suffix string) bool
|
||||
IsContentFile(filename string) bool
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -353,6 +354,90 @@ func TestDefaultConfigProvider(t *testing.T) {
|
||||
|
||||
c.Assert(r.Wait(), qt.IsNil)
|
||||
})
|
||||
|
||||
c.Run("GetBool", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
var k string
|
||||
var v bool
|
||||
|
||||
k, v = "foo", true
|
||||
|
||||
cfg.Set(k, v)
|
||||
c.Assert(cfg.Get(k), qt.Equals, v)
|
||||
c.Assert(cfg.GetBool(k), qt.Equals, v)
|
||||
})
|
||||
|
||||
c.Run("GetParams", func(c *qt.C) {
|
||||
cfg := New()
|
||||
k := "foo"
|
||||
|
||||
cfg.Set(k, maps.Params{k: true})
|
||||
c.Assert(cfg.GetParams(k), qt.DeepEquals, maps.Params{
|
||||
k: true,
|
||||
})
|
||||
|
||||
c.Assert(cfg.GetParams("bar"), qt.IsNil)
|
||||
})
|
||||
|
||||
c.Run("Keys", func(c *qt.C) {
|
||||
cfg := New()
|
||||
k := "foo"
|
||||
k2 := "bar"
|
||||
|
||||
cfg.Set(k, maps.Params{k: struct{}{}})
|
||||
cfg.Set(k2, maps.Params{k2: struct{}{}})
|
||||
|
||||
c.Assert(len(cfg.Keys()), qt.Equals, 2)
|
||||
|
||||
got := cfg.Keys()
|
||||
slices.Sort(got)
|
||||
|
||||
want := []string{k, k2}
|
||||
slices.Sort(want)
|
||||
|
||||
c.Assert(got, qt.DeepEquals, want)
|
||||
})
|
||||
|
||||
c.Run("WalkParams", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("x", maps.Params{})
|
||||
cfg.Set("y", maps.Params{})
|
||||
|
||||
var got []string
|
||||
cfg.WalkParams(func(params ...maps.KeyParams) bool {
|
||||
got = append(got, params[len(params)-1].Key)
|
||||
return false
|
||||
})
|
||||
|
||||
want := []string{"", "x", "y"}
|
||||
slices.Sort(got)
|
||||
slices.Sort(want)
|
||||
|
||||
c.Assert(got, qt.DeepEquals, want)
|
||||
|
||||
cfg = New()
|
||||
cfg.WalkParams(func(params ...maps.KeyParams) bool {
|
||||
return true
|
||||
})
|
||||
|
||||
got = []string{""}
|
||||
want = []string{""}
|
||||
c.Assert(got, qt.DeepEquals, want)
|
||||
})
|
||||
|
||||
c.Run("SetDefaults", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.SetDefaults(maps.Params{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("foo"), qt.Equals, "bar")
|
||||
c.Assert(cfg.Get("bar"), qt.Equals, "baz")
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDefaultConfigProvider(b *testing.B) {
|
||||
|
||||
@@ -101,6 +101,9 @@ func DecodeConfig(cfg config.Provider) (c Config, err error) {
|
||||
|
||||
if c.RSS.Limit == 0 {
|
||||
c.RSS.Limit = cfg.GetInt(rssLimitKey)
|
||||
if c.RSS.Limit == 0 {
|
||||
c.RSS.Limit = -1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
+49
-34
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
@@ -487,7 +488,12 @@ func knownHiddenDirectory(name string) bool {
|
||||
// walkLocal walks the source directory and returns a flat list of files,
|
||||
// using localFile.SlashPath as the map keys.
|
||||
func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, include, exclude glob.Glob, mediaTypes media.Types, mappath func(string) string) (map[string]*localFile, error) {
|
||||
retval := map[string]*localFile{}
|
||||
retval := make(map[string]*localFile)
|
||||
var mu sync.Mutex
|
||||
|
||||
workers := para.New(d.cfg.Workers)
|
||||
g, _ := workers.Start(context.Background())
|
||||
|
||||
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -508,45 +514,54 @@ func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, incl
|
||||
return nil
|
||||
}
|
||||
|
||||
// When a file system is HFS+, its filepath is in NFD form.
|
||||
if runtime.GOOS == "darwin" {
|
||||
path = norm.NFC.String(path)
|
||||
}
|
||||
|
||||
// Check include/exclude matchers.
|
||||
slashpath := filepath.ToSlash(path)
|
||||
if include != nil && !include.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to include\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
if exclude != nil && exclude.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the first matching matcher (if any).
|
||||
var m *deployconfig.Matcher
|
||||
for _, cur := range matchers {
|
||||
if cur.Matches(slashpath) {
|
||||
m = cur
|
||||
break
|
||||
// Process each file in a worker
|
||||
g.Run(func() error {
|
||||
// When a file system is HFS+, its filepath is in NFD form.
|
||||
if runtime.GOOS == "darwin" {
|
||||
path = norm.NFC.String(path)
|
||||
}
|
||||
}
|
||||
// Apply any additional modifications to the local path, to map it to
|
||||
// the remote path.
|
||||
if mappath != nil {
|
||||
slashpath = mappath(slashpath)
|
||||
}
|
||||
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
retval[lf.SlashPath] = lf
|
||||
|
||||
// Check include/exclude matchers.
|
||||
slashpath := filepath.ToSlash(path)
|
||||
if include != nil && !include.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to include\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
if exclude != nil && exclude.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the first matching matcher (if any).
|
||||
var m *deployconfig.Matcher
|
||||
for _, cur := range matchers {
|
||||
if cur.Matches(slashpath) {
|
||||
m = cur
|
||||
break
|
||||
}
|
||||
}
|
||||
// Apply any additional modifications to the local path, to map it to
|
||||
// the remote path.
|
||||
if mappath != nil {
|
||||
slashpath = mappath(slashpath)
|
||||
}
|
||||
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mu.Lock()
|
||||
retval[lf.SlashPath] = lf
|
||||
mu.Unlock()
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -623,7 +623,7 @@ func TestEndToEndSync(t *testing.T) {
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
|
||||
}
|
||||
|
||||
// Initial deployment should sync remote with local.
|
||||
@@ -706,7 +706,7 @@ func TestMaxDeletes(t *testing.T) {
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
|
||||
}
|
||||
|
||||
// Sync remote with local.
|
||||
@@ -836,7 +836,7 @@ func TestIncludeExclude(t *testing.T) {
|
||||
}
|
||||
deployer := &Deployer{
|
||||
localFs: fsTest.fs,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
target: tgt,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
@@ -893,7 +893,7 @@ func TestIncludeExcludeRemoteDelete(t *testing.T) {
|
||||
}
|
||||
deployer := &Deployer{
|
||||
localFs: fsTest.fs,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
@@ -945,7 +945,7 @@ func TestCompression(t *testing.T) {
|
||||
deployer := &Deployer{
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
@@ -1000,7 +1000,7 @@ func TestMatching(t *testing.T) {
|
||||
deployer := &Deployer{
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
@@ -1097,5 +1097,6 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
|
||||
func newDeployer() *Deployer {
|
||||
return &Deployer{
|
||||
logger: loggers.NewDefault(),
|
||||
cfg: deployconfig.DeployConfig{Workers: 2},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
"# cspell: ignore hugo terminology",
|
||||
"# ----------------------------------------------------------------------",
|
||||
"alignx",
|
||||
"aligny",
|
||||
"attrlink",
|
||||
"canonify",
|
||||
"codeowners",
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
**/icons.html
|
||||
|
||||
# These are whitespace sensitive.
|
||||
layouts/_default/_markup/render-code*
|
||||
layouts/_default/_markup/render-table*
|
||||
layouts/shortcodes/glossary-term.html
|
||||
layouts/shortcodes/glossary.html
|
||||
layouts/shortcodes/highlighting-styles.html
|
||||
layouts/shortcodes/list-pages-in-section.html
|
||||
layouts/shortcodes/quick-reference.html
|
||||
layouts/_markup/render-code*
|
||||
layouts/_markup/render-table*
|
||||
layouts/_shortcodes/glossary-term.html
|
||||
layouts/_shortcodes/glossary.html
|
||||
layouts/_shortcodes/highlighting-styles.html
|
||||
layouts/_shortcodes/list-pages-in-section.html
|
||||
layouts/_shortcodes/quick-reference.html
|
||||
|
||||
# No root node.
|
||||
layouts/partials/layouts/head/head.html
|
||||
layouts/_partials/layouts/head/head.html
|
||||
|
||||
# Auto generated.
|
||||
assets/css/components/chroma*.css
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 299 118" height="100%" width="100%" style="enable-background:new 0 0 299 118;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{clip-path:url(#SVGID_2_);}
|
||||
.st1{fill:#414141;}
|
||||
.st2{fill:#3D4DF4;}
|
||||
</style>
|
||||
<g>
|
||||
<defs>
|
||||
<rect id="SVGID_1_" x="0.4" y="0" width="66.5" height="67.8"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_2_">
|
||||
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
<g class="st0">
|
||||
<path class="st1" d="M34.2,28l31.4,18.3c0.9,0.5,1.1,1.6,0.6,2.4c-0.2,0.3-0.4,0.5-0.6,0.6L35.6,66.9c-1.4,0.8-3.1,0.8-4.5,0
|
||||
L1.3,49.6c-0.9-0.5-1.1-1.6-0.6-2.4c0.2-0.3,0.4-0.5,0.6-0.6l23-13.4l7.9,13.6c0.5,0.8,1.5,1.1,2.4,0.7l0.1,0l0.1,0
|
||||
c0.6-0.4,0.9-1.2,0.7-2l-5-15.7l2.5-1.5C33.3,27.8,33.8,27.8,34.2,28z M36,58.6c-1.4-0.8-3.9-0.7-5.5,0.3l-3,1.7l7,4.1l2-1.2
|
||||
l-2-1.1l1-0.6C37.1,60.9,37.3,59.4,36,58.6z M32.1,59.9c0.6-0.3,1.4-0.4,1.8-0.1c0.5,0.3,0.4,0.7-0.2,1.1l-1,0.6l-1.7-1L32.1,59.9
|
||||
z M36.2,55.6l-2,1.2l7,4.1l2-1.2L36.2,55.6z M42.6,51.9l-2,1.2l2.9,1.7l-4.5-0.8L37,55.1l7,4.1l2-1.2L43,56.3l4.5,0.8l2.1-1.2
|
||||
L42.6,51.9z M56.3,43.8l-6,3.5l2.5,1.4l-3.7-0.7L47,49.3l2.9,1.7l-4.5-0.8l-2.1,1.2l7,4.1l2-1.2l-2.9-1.7l4.5,0.8l2.1-1.2
|
||||
l-2.9-1.7l4,0.7l0.2,0.1l2-1.2l4-2.3l-1.7-1l-4,2.3l-1-0.6l4-2.3l-1.7-1l-4,2.3L54,47.1l4-2.3L56.3,43.8z"/>
|
||||
<path class="st2" d="M41.7,0c0.1,0,0.2,0,0.3,0c1,0.2,1.6,1.1,1.5,2.1l-2.3,13.5c3.9,2.5,6.5,6.9,6.5,11.9v0.7l-10.3,0l-2,17.9
|
||||
c-0.1,0.8-0.7,1.4-1.5,1.5l-0.1,0c-1,0.1-1.9-0.6-2-1.5l-2-17.9H19.6v-0.7c0-5,2.6-9.4,6.5-11.9L23.8,2.1c0-0.1,0-0.2,0-0.3
|
||||
c0-1,0.8-1.8,1.8-1.8H41.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st1" d="M277.4,61c-3.7,0-7.1-0.8-10-2.4c-2.8-1.6-5.1-3.9-6.7-6.8c-1.6-2.9-2.4-6.4-2.4-10.3v-0.9
|
||||
c0-4,0.8-7.4,2.4-10.3c1.6-2.9,3.8-5.2,6.6-6.8c2.8-1.6,6.1-2.4,9.9-2.4c3.7,0,6.9,0.8,9.7,2.5c2.7,1.6,4.9,3.9,6.4,6.8
|
||||
c1.5,2.9,2.3,6.3,2.3,10.1v3.3h-27.4c0.1,2.6,1.1,4.7,2.9,6.3c1.8,1.6,4.1,2.4,6.7,2.4c2.7,0,4.7-0.6,5.9-1.7
|
||||
c1.3-1.2,2.2-2.5,2.9-3.9l7.8,4.1c-0.7,1.3-1.7,2.8-3.1,4.3c-1.3,1.5-3.1,2.8-5.3,4C283.6,60.5,280.8,61,277.4,61z M268.2,36.8h17.6
|
||||
c-0.2-2.2-1.1-3.9-2.7-5.2c-1.5-1.3-3.5-2-6-2c-2.6,0-4.6,0.7-6.2,2C269.5,32.9,268.5,34.6,268.2,36.8z"/>
|
||||
<path class="st1" d="M192.9,60V6.8h18.6l9.2,46.4h1.4l9.2-46.4h18.6V60h-9.7V14.1h-1.4L229.6,60h-16.6L204,14.1h-1.4V60H192.9z"/>
|
||||
<path class="st1" d="M146.3,60V22.3h9.4v4.9h1.4c0.6-1.3,1.7-2.6,3.4-3.7c1.7-1.2,4.2-1.8,7.6-1.8c2.9,0,5.5,0.7,7.7,2.1
|
||||
c2.2,1.3,4,3.2,5.2,5.5c1.2,2.3,1.8,5.1,1.8,8.2V60h-9.6V38.2c0-2.8-0.7-5-2.1-6.4c-1.4-1.4-3.3-2.1-5.9-2.1c-2.9,0-5.2,1-6.8,3
|
||||
c-1.6,1.9-2.4,4.6-2.4,8.1V60H146.3z"/>
|
||||
<path class="st1" d="M126.1,60V22.3h9.6V60H126.1z M130.9,17.9c-1.7,0-3.2-0.6-4.4-1.7c-1.2-1.1-1.7-2.6-1.7-4.4
|
||||
c0-1.8,0.6-3.3,1.7-4.4c1.2-1.1,2.7-1.7,4.4-1.7c1.8,0,3.2,0.6,4.4,1.7c1.2,1.1,1.7,2.6,1.7,4.4c0,1.8-0.6,3.3-1.7,4.4
|
||||
C134.2,17.3,132.7,17.9,130.9,17.9z"/>
|
||||
<path class="st1" d="M79.9,60V6.8h21.9c3.3,0,6.3,0.7,8.8,2.1c2.6,1.3,4.6,3.2,6,5.6c1.5,2.4,2.2,5.3,2.2,8.7v1.1
|
||||
c0,3.3-0.8,6.2-2.3,8.7c-1.5,2.4-3.5,4.3-6.1,5.7c-2.5,1.3-5.4,2-8.7,2H89.9V60H79.9z M89.9,31.4h10.9c2.4,0,4.3-0.7,5.8-2
|
||||
c1.5-1.3,2.2-3.1,2.2-5.4v-0.8c0-2.3-0.7-4.1-2.2-5.4c-1.5-1.3-3.4-2-5.8-2H89.9V31.4z"/>
|
||||
<path class="st2" d="M285.3,112V91h13.5v3.6h-9.5v5h8.7v3.6h-8.7v5.2h9.7v3.6H285.3z"/>
|
||||
<path class="st2" d="M268.7,112V91h13.5v3.6h-9.5v5h8.7v3.6h-8.7v5.2h9.7v3.6H268.7z"/>
|
||||
<path class="st2" d="M249.7,112V91h9.1c1.3,0,2.5,0.2,3.4,0.7c1,0.5,1.7,1.1,2.3,1.9c0.5,0.8,0.8,1.8,0.8,3v0.4
|
||||
c0,1.3-0.3,2.3-0.9,3.1c-0.6,0.8-1.3,1.4-2.2,1.7v0.5c0.8,0,1.4,0.3,1.9,0.8c0.4,0.5,0.7,1.2,0.7,2v6.9h-4v-6.3
|
||||
c0-0.5-0.1-0.9-0.4-1.2c-0.2-0.3-0.6-0.4-1.2-0.4h-5.5v7.9H249.7z M253.7,100.4h4.7c0.9,0,1.7-0.2,2.2-0.8c0.5-0.5,0.8-1.2,0.8-2
|
||||
v-0.3c0-0.8-0.3-1.5-0.8-2c-0.5-0.5-1.3-0.8-2.2-0.8h-4.7V100.4z"/>
|
||||
<path class="st2" d="M233.7,112V91h13.2v3.6h-9.2v5.1h8.5v3.6h-8.5v8.7H233.7z"/>
|
||||
<path class="st1" d="M214.3,112V97.1h3.7v1.7h0.5c0.2-0.6,0.6-1,1.1-1.3c0.5-0.3,1.1-0.4,1.8-0.4h1.8v3.4h-1.9c-1,0-1.8,0.3-2.4,0.8
|
||||
c-0.6,0.5-0.9,1.3-0.9,2.3v8.5H214.3z"/>
|
||||
<path class="st1" d="M203,112.4c-1.5,0-2.8-0.3-4-0.9c-1.2-0.6-2.1-1.5-2.8-2.6c-0.7-1.1-1-2.5-1-4.1v-0.5c0-1.6,0.3-3,1-4.1
|
||||
c0.7-1.1,1.6-2,2.8-2.6c1.2-0.6,2.5-0.9,4-0.9c1.5,0,2.8,0.3,4,0.9c1.2,0.6,2.1,1.5,2.8,2.6c0.7,1.1,1,2.5,1,4.1v0.5
|
||||
c0,1.6-0.3,3-1,4.1c-0.7,1.1-1.6,2-2.8,2.6C205.8,112.1,204.5,112.4,203,112.4z M203,109c1.2,0,2.1-0.4,2.9-1.1
|
||||
c0.8-0.8,1.1-1.8,1.1-3.2v-0.3c0-1.4-0.4-2.5-1.1-3.2c-0.7-0.8-1.7-1.1-2.9-1.1c-1.2,0-2.1,0.4-2.9,1.1c-0.8,0.7-1.1,1.8-1.1,3.2
|
||||
v0.3c0,1.4,0.4,2.5,1.1,3.2C200.9,108.7,201.8,109,203,109z"/>
|
||||
<path class="st1" d="M185.8,112v-11.8H182v-3.1h3.8v-2.8c0-1,0.3-1.8,0.9-2.4c0.6-0.6,1.4-0.9,2.4-0.9h3.9v3.1h-2.6
|
||||
c-0.6,0-0.8,0.3-0.8,0.9v2.1h3.9v3.1h-3.9V112H185.8z"/>
|
||||
<path class="st1" d="M155.9,104.6v-0.5c0-1.6,0.3-2.9,0.9-4c0.6-1.1,1.4-2,2.5-2.5c1-0.6,2.2-0.9,3.4-0.9c1.4,0,2.4,0.2,3.1,0.7
|
||||
c0.7,0.5,1.2,1,1.5,1.5h0.5v-1.8h3.7v17.5c0,1-0.3,1.8-0.9,2.4c-0.6,0.6-1.4,0.9-2.4,0.9h-10v-3.3h8.6c0.6,0,0.8-0.3,0.8-0.9v-3.9
|
||||
h-0.5c-0.2,0.3-0.5,0.7-0.8,1c-0.4,0.3-0.8,0.6-1.4,0.8c-0.6,0.2-1.4,0.3-2.3,0.3c-1.2,0-2.3-0.3-3.4-0.9c-1-0.6-1.8-1.4-2.5-2.6
|
||||
C156.2,107.5,155.9,106.1,155.9,104.6z M163.7,108.7c1.2,0,2.1-0.4,2.9-1.1s1.2-1.8,1.2-3.1v-0.3c0-1.4-0.4-2.4-1.2-3.1
|
||||
c-0.8-0.7-1.7-1.1-2.9-1.1c-1.2,0-2.1,0.4-2.9,1.1c-0.8,0.7-1.2,1.8-1.2,3.1v0.3c0,1.3,0.4,2.4,1.2,3.1S162.6,108.7,163.7,108.7z"/>
|
||||
<path class="st1" d="M145.3,112.4c-1.5,0-2.8-0.3-4-0.9c-1.2-0.6-2.1-1.5-2.8-2.6s-1-2.5-1-4.1v-0.5c0-1.6,0.3-3,1-4.1
|
||||
c0.7-1.1,1.6-2,2.8-2.6c1.2-0.6,2.5-0.9,4-0.9s2.8,0.3,4,0.9c1.2,0.6,2.1,1.5,2.8,2.6c0.7,1.1,1,2.5,1,4.1v0.5c0,1.6-0.3,3-1,4.1
|
||||
c-0.7,1.1-1.6,2-2.8,2.6C148.1,112.1,146.8,112.4,145.3,112.4z M145.3,109c1.2,0,2.1-0.4,2.9-1.1c0.8-0.8,1.1-1.8,1.1-3.2v-0.3
|
||||
c0-1.4-0.4-2.5-1.1-3.2c-0.7-0.8-1.7-1.1-2.9-1.1c-1.2,0-2.1,0.4-2.9,1.1c-0.8,0.7-1.1,1.8-1.1,3.2v0.3c0,1.4,0.4,2.5,1.1,3.2
|
||||
C143.2,108.7,144.1,109,145.3,109z"/>
|
||||
<path class="st1" d="M130.3,112V91h3.8v21H130.3z"/>
|
||||
<path class="st1" d="M109.6,112v-3.5h2.8v-14h-2.8V91h10.8c1.3,0,2.4,0.2,3.3,0.7c1,0.4,1.7,1,2.2,1.8c0.5,0.8,0.8,1.7,0.8,2.8v0.3
|
||||
c0,1-0.2,1.8-0.5,2.4c-0.4,0.6-0.8,1.1-1.3,1.4c-0.5,0.3-0.9,0.6-1.4,0.7v0.5c0.4,0.1,0.9,0.3,1.4,0.7c0.5,0.3,1,0.8,1.3,1.4
|
||||
c0.4,0.6,0.6,1.4,0.6,2.4v0.3c0,1.2-0.3,2.2-0.8,3c-0.5,0.8-1.3,1.4-2.2,1.9c-0.9,0.4-2,0.7-3.3,0.7H109.6z M116.3,108.4h3.7
|
||||
c0.9,0,1.6-0.2,2.1-0.6c0.5-0.4,0.8-1,0.8-1.8v-0.3c0-0.8-0.3-1.4-0.8-1.8c-0.5-0.4-1.2-0.6-2.1-0.6h-3.7V108.4z M116.3,99.6h3.7
|
||||
c0.8,0,1.5-0.2,2-0.6c0.5-0.4,0.8-1,0.8-1.7v-0.3c0-0.8-0.3-1.3-0.8-1.7c-0.5-0.4-1.2-0.6-2-0.6h-3.7V99.6z"/>
|
||||
<path class="st1" d="M85.9,118v-3.3H94c0.6,0,0.8-0.3,0.8-0.9V110h-0.5c-0.2,0.3-0.4,0.7-0.8,1c-0.3,0.3-0.8,0.6-1.4,0.8
|
||||
c-0.6,0.2-1.3,0.3-2.2,0.3c-1.2,0-2.2-0.3-3.1-0.8c-0.9-0.5-1.5-1.3-2-2.2c-0.5-0.9-0.7-2-0.7-3.2v-8.9h3.8v8.6c0,1.1,0.3,2,0.8,2.5
|
||||
c0.6,0.6,1.4,0.8,2.4,0.8c1.2,0,2.1-0.4,2.7-1.1c0.6-0.8,1-1.9,1-3.2v-7.6h3.8v17.5c0,1-0.3,1.8-0.9,2.4c-0.6,0.6-1.4,0.9-2.4,0.9
|
||||
H85.9z"/>
|
||||
<path class="st1" d="M72.9,112.4c-1.5,0-2.8-0.3-4-0.9s-2.1-1.5-2.8-2.6s-1-2.5-1-4.1v-0.5c0-1.6,0.3-3,1-4.1c0.7-1.1,1.6-2,2.8-2.6
|
||||
s2.5-0.9,4-0.9c1.5,0,2.8,0.3,4,0.9s2.1,1.5,2.8,2.6c0.7,1.1,1,2.5,1,4.1v0.5c0,1.6-0.3,3-1,4.1s-1.6,2-2.8,2.6
|
||||
S74.4,112.4,72.9,112.4z M72.9,109c1.2,0,2.1-0.4,2.9-1.1c0.8-0.8,1.1-1.8,1.1-3.2v-0.3c0-1.4-0.4-2.5-1.1-3.2
|
||||
c-0.7-0.8-1.7-1.1-2.9-1.1c-1.2,0-2.1,0.4-2.9,1.1c-0.8,0.7-1.1,1.8-1.1,3.2v0.3c0,1.4,0.4,2.5,1.1,3.2
|
||||
C70.8,108.7,71.8,109,72.9,109z"/>
|
||||
<path class="st1" d="M57.9,112V91h3.8v21H57.9z"/>
|
||||
<path class="st1" d="M38.8,118V97.1h3.7v1.8H43c0.3-0.6,0.9-1.1,1.6-1.5c0.7-0.5,1.8-0.7,3.1-0.7c1.2,0,2.3,0.3,3.3,0.9
|
||||
c1,0.6,1.8,1.4,2.5,2.6c0.6,1.1,0.9,2.5,0.9,4.1v0.5c0,1.6-0.3,3-0.9,4.1c-0.6,1.1-1.4,2-2.5,2.6c-1,0.6-2.1,0.9-3.3,0.9
|
||||
c-0.9,0-1.7-0.1-2.3-0.3c-0.6-0.2-1.1-0.5-1.5-0.8c-0.4-0.3-0.7-0.7-0.9-1h-0.5v7.7H38.8z M46.6,109.1c1.2,0,2.1-0.4,2.9-1.1
|
||||
c0.8-0.8,1.2-1.9,1.2-3.3v-0.3c0-1.4-0.4-2.5-1.2-3.3c-0.8-0.8-1.8-1.1-2.9-1.1s-2.1,0.4-2.9,1.1c-0.8,0.7-1.2,1.8-1.2,3.3v0.3
|
||||
c0,1.4,0.4,2.5,1.2,3.3C44.4,108.7,45.4,109.1,46.6,109.1z"/>
|
||||
<path class="st1" d="M28.2,112.4c-1.5,0-2.8-0.3-3.9-0.9c-1.1-0.6-2-1.5-2.6-2.7c-0.6-1.2-0.9-2.5-0.9-4.1v-0.4
|
||||
c0-1.6,0.3-2.9,0.9-4.1c0.6-1.2,1.5-2,2.6-2.7c1.1-0.6,2.4-1,3.9-1c1.5,0,2.7,0.3,3.8,1c1.1,0.6,1.9,1.5,2.5,2.7
|
||||
c0.6,1.1,0.9,2.5,0.9,4v1.3H24.6c0,1,0.4,1.8,1.1,2.5c0.7,0.6,1.6,1,2.6,1c1.1,0,1.8-0.2,2.3-0.7s0.9-1,1.1-1.5l3.1,1.6
|
||||
c-0.3,0.5-0.7,1.1-1.2,1.7c-0.5,0.6-1.2,1.1-2.1,1.6C30.7,112.2,29.6,112.4,28.2,112.4z M24.6,102.8h7c-0.1-0.9-0.4-1.6-1-2.1
|
||||
c-0.6-0.5-1.4-0.8-2.4-0.8c-1,0-1.8,0.3-2.4,0.8C25.1,101.3,24.7,102,24.6,102.8z"/>
|
||||
<path class="st1" d="M0.8,112v-3.5h2.8v-14H0.8V91h8.6c2.8,0,5,0.7,6.4,2.2c1.5,1.4,2.2,3.5,2.2,6.4v4c0,2.8-0.7,4.9-2.2,6.4
|
||||
c-1.5,1.4-3.6,2.1-6.4,2.1H0.8z M7.5,108.4h2c1.6,0,2.8-0.4,3.5-1.3c0.7-0.8,1.1-2,1.1-3.5v-4.2c0-1.5-0.4-2.7-1.1-3.5
|
||||
c-0.7-0.8-1.9-1.3-3.5-1.3h-2V108.4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
@@ -32,13 +32,13 @@ content/
|
||||
|
||||
And these templates:
|
||||
|
||||
```go-html-template {file="layouts/_default/list.html"}
|
||||
```go-html-template {file="layouts/section.html"}
|
||||
{{ range .Pages.ByWeight }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ with .Prev }}
|
||||
<a href="{{ .RelPermalink }}">Previous</a>
|
||||
{{ end }}
|
||||
|
||||
@@ -32,13 +32,13 @@ content/
|
||||
|
||||
And these templates:
|
||||
|
||||
```go-html-template {file="layouts/_default/list.html"}
|
||||
```go-html-template {file="layouts/section.html"}
|
||||
{{ range .Pages.ByWeight }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ with .PrevInSection }}
|
||||
<a href="{{ .RelPermalink }}">Previous</a>
|
||||
{{ end }}
|
||||
|
||||
@@ -32,13 +32,13 @@ content/
|
||||
|
||||
And these templates:
|
||||
|
||||
```go-html-template {file="layouts/_default/list.html"}
|
||||
```go-html-template {file="layouts/section.html"}
|
||||
{{ range .Pages.ByWeight }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ $pages := .CurrentSection.Pages.ByWeight }}
|
||||
|
||||
{{ with $pages.Prev . }}
|
||||
@@ -57,7 +57,7 @@ When you visit page-2:
|
||||
|
||||
To reverse the meaning of _next_ and _previous_ you can chain the [`Reverse`] method to the page collection definition:
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ $pages := .CurrentSection.Pages.ByWeight.Reverse }}
|
||||
|
||||
{{ with $pages.Prev . }}
|
||||
|
||||
@@ -34,7 +34,7 @@ To capture the "genres" `Taxonomy` object from within any template, use the [`Ta
|
||||
|
||||
To capture the "genres" `Taxonomy` object when rendering its page with a taxonomy template, use the [`Terms`] method on the page's [`Data`] object:
|
||||
|
||||
```go-html-template {file="layouts/_default/taxonomy.html"}
|
||||
```go-html-template {file="layouts/taxonomy.html"}
|
||||
{{ $taxonomyObject := .Data.Terms }}
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ _comment: Do not remove front matter.
|
||||
|
||||
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}
|
||||
```go-html-template {file="layouts/_shortcodes/include.html" copy=true}
|
||||
{{ with .Get 0 }}
|
||||
{{ with $.Page.GetPage . }}
|
||||
{{- .RenderShortcodes }}
|
||||
|
||||
@@ -84,7 +84,7 @@ disableKinds
|
||||
: (`[]string`) A slice of page [kinds](g) to disable during the build process, any of `404`, `home`, `page`, `robotstxt`, `rss`, `section`, `sitemap`, `taxonomy`, or `term`.
|
||||
|
||||
disableLanguages
|
||||
: (`[]string]`) A slice of language keys representing the languages to disable during the build process. Although this is functional, consider using the [`disabled`] key under each language instead.
|
||||
: (`[]string`) A slice of language keys representing the languages to disable during the build process. Although this is functional, consider using the [`disabled`] key under each language instead.
|
||||
|
||||
disableLiveReload
|
||||
: (`bool`) Whether to disable automatic live reloading of the browser window. Default is `false`.
|
||||
@@ -123,7 +123,7 @@ ignoreCache
|
||||
: (`bool`) Whether to ignore the cache directory. Default is `false`.
|
||||
|
||||
ignoreFiles
|
||||
: (`[]string]`) A slice of [regular expressions](g) used to exclude specific files from a build. These expressions are matched against the absolute file path and apply to files within the `content`, `data`, and `i18n` directories. For more advanced file exclusion options, see the section on [module mounts].
|
||||
: (`[]string`) A slice of [regular expressions](g) used to exclude specific files from a build. These expressions are matched against the absolute file path and apply to files within the `content`, `data`, and `i18n` directories. For more advanced file exclusion options, see the section on [module mounts].
|
||||
|
||||
ignoreLogs
|
||||
: (`[]string`) A slice of message identifiers corresponding to warnings and errors you wish to suppress. See [`erroridf`] and [`warnidf`].
|
||||
@@ -280,7 +280,7 @@ themesDir
|
||||
: (`string`) The designated directory for themes. Default is `themes`.
|
||||
|
||||
timeout
|
||||
: (`string`) The timeout for generating page content, either as a [duration] or in seconds. This timeout is used to prevent infinite recursion during content generation. You may need to increase this value if your pages take a long time to generate, for example, due to extensive image processing or reliance on remote content. Default is `30s`.
|
||||
: (`string`) The timeout for generating page content, either as a [duration] or in seconds. This timeout is used to prevent infinite recursion during content generation. You may need to increase this value if your pages take a long time to generate, for example, due to extensive image processing or reliance on remote content. Default is `60s`.
|
||||
|
||||
timeZone
|
||||
: (`string`) The time zone used to parse dates without time zone offsets, including front matter date fields and values passed to the [`time.AsTime`] and [`time.Format`] template functions. The list of valid values may be system dependent, but should include `UTC`, `Local`, and any location in the [IANA Time Zone Database]. For example, `America/Los_Angeles` and `Europe/Oslo` are valid time zones.
|
||||
|
||||
@@ -79,6 +79,11 @@ my-project/
|
||||
|
||||
The root configuration keys are {{< root-configuration-keys >}}.
|
||||
|
||||
> [!note]
|
||||
> You must define `cascade` tables in the root configuration file. You cannot define `cascade` tables in a dedicated file. See issue [#12899] for details.
|
||||
|
||||
[#12899]: https://github.com/gohugoio/hugo/issues/12899
|
||||
|
||||
### Omit the root key
|
||||
|
||||
When splitting the configuration by root key, omit the root key in the component file. For example, these are equivalent:
|
||||
@@ -249,7 +254,7 @@ HUGO_FILE_LOG_FORMAT
|
||||
|
||||
HUGO_MEMORYLIMIT
|
||||
: {{< new-in 0.123.0 />}}
|
||||
: (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. Note that The `HUGO_MEMORYLIMIT` is a “best effort” setting. Don't expect Hugo to build a million pages with only 1 GB memory. You can get more information about how this behaves during the build by building with `hugo --logLevel info` and look for the `dynacache` label.
|
||||
: (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. Note that `HUGO_MEMORYLIMIT` is a "best effort" setting. Don't expect Hugo to build a million pages with only 1 GB of memory. You can get more information about how this behaves during the build by building with `hugo --logLevel info` and look for the `dynacache` label.
|
||||
|
||||
HUGO_NUMWORKERMULTIPLIER
|
||||
: (`int`) The number of workers used in parallel processing. Default is the number of logical CPUs.
|
||||
|
||||
@@ -247,7 +247,7 @@ rougify style monokai.sublime > assets/css/syntax.css
|
||||
|
||||
In your base template add a link to the CSS file:
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html"}
|
||||
```go-html-template {file="layouts/baseof.html"}
|
||||
<head>
|
||||
...
|
||||
{{ with resources.Get "css/syntax.css" }}
|
||||
@@ -337,5 +337,5 @@ ordered
|
||||
[superscript]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup
|
||||
[AsciiDoc]: https://asciidoc.org/
|
||||
[Emacs Org Mode]: https://orgmode.org/
|
||||
[Pandoc]: https://www.pandoc.org/
|
||||
[Pandoc]: https://pandoc.org/
|
||||
[reStructuredText]: https://docutils.sourceforge.io/rst.html
|
||||
|
||||
@@ -135,7 +135,7 @@ See [configure outputs] for more information.
|
||||
Create a template to render the output format. Since Atom feeds are lists, you need to create a list template. Consult the [template lookup order] to find the correct template path:
|
||||
|
||||
```text
|
||||
layouts/_default/list.atom.atom
|
||||
layouts/list.atom.atom
|
||||
```
|
||||
|
||||
We leave writing the template code as an exercise for you. Aim for a result similar to the [embedded RSS template].
|
||||
@@ -154,7 +154,7 @@ To access output formats, each `Page` object provides two methods: [`OutputForma
|
||||
|
||||
By default, a `Page` object's [`Permalink`] and [`RelPermalink`] methods return the URL of the [primary output format](g), typically `html`. This behavior remains consistent regardless of the template used.
|
||||
|
||||
For example, in `single.json.json`, you'll see:
|
||||
For example, in `page.json.json`, you'll see:
|
||||
|
||||
```go-html-template
|
||||
{{ .RelPermalink }} → /that-page/
|
||||
@@ -165,7 +165,7 @@ For example, in `single.json.json`, you'll see:
|
||||
|
||||
To make these methods return the URL of the _current_ template's output format, you must set the [`permalinkable`] setting to `true` for that format.
|
||||
|
||||
With `permalinkable` set to true for `json` in the same `single.json.json` template:
|
||||
With `permalinkable` set to true for `json` in the same `page.json.json` template:
|
||||
|
||||
```go-html-template
|
||||
{{ .RelPermalink }} → /that-page/index.json
|
||||
@@ -188,9 +188,9 @@ For example, for section pages:
|
||||
|
||||
Output format|Template path
|
||||
:--|:--
|
||||
`html`|`layouts/_default/section.html.html`
|
||||
`json`|`layouts/_default/section.json.json`
|
||||
`rss`|`layouts/_default/section.rss.xml`
|
||||
`html`|`layouts/section.html.html`
|
||||
`json`|`layouts/section.json.json`
|
||||
`rss`|`layouts/section.rss.xml`
|
||||
|
||||
[`AlternativeOutputFormats`]: /methods/page/alternativeoutputformats/
|
||||
[`OutputFormats`]: /methods/page/outputformats/
|
||||
|
||||
@@ -97,7 +97,7 @@ We've configured the `authors` index with a weight of `2` and the `genres` index
|
||||
|
||||
Then render a list of 5 related reviews with a partial template like this:
|
||||
|
||||
```go-html-template {file="layouts/partials/related.html" copy=true}
|
||||
```go-html-template {file="layouts/_partials/related.html" copy=true}
|
||||
{{ with site.RegularPages.Related . | first 5 }}
|
||||
<p>Related content:</p>
|
||||
<ul>
|
||||
|
||||
@@ -58,7 +58,7 @@ taxonomies:
|
||||
To disable the taxonomy system, use the [`disableKinds`] setting in the root of your site configuration to disable the `taxonomy` and `term` page [kinds](g).
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
disableKinds = ['categories','tags']
|
||||
disableKinds = ['taxonomy','term']
|
||||
{{< /code-toggle >}}
|
||||
|
||||
[`disableKinds`]: /configuration/all/#disablekinds
|
||||
|
||||
@@ -69,7 +69,7 @@ title = 'Headless page'
|
||||
|
||||
To include the content and images on the home page:
|
||||
|
||||
```go-html-template {file="layouts/_default/home.html"}
|
||||
```go-html-template {file="layouts/home.html"}
|
||||
{{ with .Site.GetPage "/headless" }}
|
||||
{{ .Content }}
|
||||
{{ range .Resources.ByType "image" }}
|
||||
@@ -127,7 +127,7 @@ In the front matter above, note that we have set `list` to `local` to include th
|
||||
|
||||
To include the content and images on the home page:
|
||||
|
||||
```go-html-template {file="layouts/_default/home.html"}
|
||||
```go-html-template {file="layouts/home.html"}
|
||||
{{ with .Site.GetPage "/headless" }}
|
||||
{{ range .Pages }}
|
||||
{{ .Content }}
|
||||
@@ -186,7 +186,7 @@ render = 'always'
|
||||
|
||||
To render the glossary:
|
||||
|
||||
```go-html-template {file="layouts/glossary/list.html"}
|
||||
```go-html-template {file="layouts/glossary/section.html"}
|
||||
<dl>
|
||||
{{ range .Pages }}
|
||||
<dt>{{ .Title }}</dt>
|
||||
|
||||
@@ -34,7 +34,7 @@ For many websites, this is enough configuration. However, you also have the opti
|
||||
Disqus has its own [internal template](/templates/embedded/#disqus) available, to render it add the following code where you want comments to appear:
|
||||
|
||||
```go-html-template
|
||||
{{ template "_internal/disqus.html" . }}
|
||||
{{ partial "disqus.html" . }}
|
||||
```
|
||||
|
||||
## Alternatives
|
||||
|
||||
@@ -27,7 +27,7 @@ content/
|
||||
└── _index.md
|
||||
```
|
||||
|
||||
Each content adapter is named _content.gotmpl and uses the same [syntax] as templates in the `layouts` directory. You can use any of the [template functions] within a content adapter, as well as the methods described below.
|
||||
Each content adapter is named `_content.gotmpl` and uses the same [syntax] as templates in the `layouts` directory. You can use any of the [template functions] within a content adapter, as well as the methods described below.
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -71,7 +71,7 @@ Adds a page resource to the site.
|
||||
|
||||
Then retrieve the new page resource with something like:
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ with .Resources.Get "cover.jpg" }}
|
||||
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
|
||||
{{ end }}
|
||||
@@ -90,7 +90,7 @@ Returns the `Site` to which the pages will be added.
|
||||
|
||||
### Store
|
||||
|
||||
Returns a persistent “scratch pad” to store and manipulate data. The main use case for this is to transfer values between executions when [EnableAllLanguages](#enablealllanguages) is set. See [examples](/methods/page/store/).
|
||||
Returns a persistent "scratch pad" to store and manipulate data. The main use case for this is to transfer values between executions when [EnableAllLanguages](#enablealllanguages) is set. See [examples](/methods/page/store/).
|
||||
|
||||
```go-html-template {file="content/books/_content.gotmpl"}
|
||||
{{ .Store.Set "key" "value" }}
|
||||
@@ -99,7 +99,7 @@ Returns a persistent “scratch pad” to store and manipulate data. The main us
|
||||
|
||||
### EnableAllLanguages
|
||||
|
||||
By default, Hugo executes the content adapter for the language defined by the _content.gotmpl file. Use this method to activate the content adapter for all languages.
|
||||
By default, Hugo executes the content adapter for the language defined by the `_content.gotmpl` file. Use this method to activate the content adapter for all languages.
|
||||
|
||||
```go-html-template {file="content/books/_content.gotmpl"}
|
||||
{{ .EnableAllLanguages }}
|
||||
@@ -153,7 +153,7 @@ Key|Description|Required
|
||||
`title`|The resource title.|
|
||||
|
||||
> [!note]
|
||||
> If the `content.value` is a string Hugo creates a new resource. If the `content.value` is a resource, Hugo obtains the value from the existing resource.
|
||||
> When `content.value` is a string, Hugo generates a new resource with a publication path relative to the page. However, if `content.value` is already a resource, Hugo directly uses its value and publishes it relative to the site root. This latter method is more efficient.
|
||||
>
|
||||
> When setting the `path`, Hugo transforms the given string to a logical path. For example, setting `path` to `A B C/cover.jpg` produces a logical path of `/section/a-b-c/cover.jpg`.
|
||||
|
||||
@@ -237,9 +237,9 @@ Create the content adapter.
|
||||
|
||||
### Step 4
|
||||
|
||||
Create a single template to render each book review.
|
||||
Create a page template to render each book review.
|
||||
|
||||
```go-html-template {file="layouts/books/single.html" copy=true}
|
||||
```go-html-template {file="layouts/books/page.html" copy=true}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
|
||||
@@ -338,7 +338,7 @@ content/
|
||||
└── the-hunchback-of-notre-dame.md
|
||||
```
|
||||
|
||||
If the content adapter also creates books/the-hunchback-of-notre-dame, the content of the published page is indeterminate. You can not define the processing order.
|
||||
If the content adapter also creates `books/the-hunchback-of-notre-dame`, the content of the published page is indeterminate. You can not define the processing order.
|
||||
|
||||
To detect page collisions, use the `--printPathWarnings` flag when building your site.
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ Use data sources to augment existing content. For example, create a shortcode to
|
||||
{{</* csv-to-table "pets.csv" */>}}
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/shortcodes/csv-to-table.html"}
|
||||
```go-html-template {file="layouts/_shortcodes/csv-to-table.html"}
|
||||
{{ with $file := .Get 0 }}
|
||||
{{ with resources.Get $file }}
|
||||
{{ with . | transform.Unmarshal }}
|
||||
|
||||
@@ -39,7 +39,7 @@ Will be rendered as:
|
||||
|
||||
Hugo does not provide a built-in template for Mermaid diagrams. Create your own using a [code block render hook]:
|
||||
|
||||
```go-html-template {file="layouts/_default/_markup/render-codeblock-mermaid.html" copy=true}
|
||||
```go-html-template {file="layouts/_markup/render-codeblock-mermaid.html" copy=true}
|
||||
<pre class="mermaid">
|
||||
{{ .Inner | htmlEscape | safeHTML }}
|
||||
</pre>
|
||||
@@ -48,7 +48,7 @@ Hugo does not provide a built-in template for Mermaid diagrams. Create your own
|
||||
|
||||
Then include this snippet at the _bottom_ of your base template, before the closing `body` tag:
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html" copy=true}
|
||||
```go-html-template {file="layouts/baseof.html" copy=true}
|
||||
{{ if .Store.Get "hasMermaid" }}
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
|
||||
|
||||
@@ -75,7 +75,7 @@ Create your content in the [Emacs Org Mode] format preceded by front matter. You
|
||||
|
||||
### AsciiDoc
|
||||
|
||||
Create your content in the [AsciiDoc] format preceded by front matter. Hugo renders AsciiDoc content to HTML using the Asciidoctor executable. You must install Asciidoctor and its dependencies (Ruby) to use the AsciiDoc content format.
|
||||
Create your content in the [AsciiDoc] format preceded by front matter. Hugo renders AsciiDoc content to HTML using the Asciidoctor executable. You must install Asciidoctor and its dependencies (Ruby) to render the AsciiDoc content format.
|
||||
|
||||
You can configure the AsciiDoc renderer in your [site configuration][configure asciidoc].
|
||||
|
||||
@@ -97,7 +97,9 @@ hugo --logLevel info
|
||||
|
||||
### Pandoc
|
||||
|
||||
Create your content in the [Pandoc] format preceded by front matter. Hugo renders Pandoc content to HTML using the Pandoc executable. You must install Pandoc to use the Pandoc content format.
|
||||
Create your content in the [Pandoc] format[^1] preceded by front matter. Hugo renders Pandoc content to HTML using the Pandoc executable. You must install Pandoc to render the Pandoc content format.
|
||||
|
||||
[^1]: This is a derivation of the Markdown format as described by the CommonMark specification.
|
||||
|
||||
Hugo passes these CLI flags when calling the Pandoc executable:
|
||||
|
||||
@@ -105,11 +107,11 @@ Hugo passes these CLI flags when calling the Pandoc executable:
|
||||
--mathjax
|
||||
```
|
||||
|
||||
[Pandoc]: https://pandoc.org/
|
||||
[Pandoc]: https://pandoc.org/MANUAL.html#pandocs-markdown
|
||||
|
||||
### reStructuredText
|
||||
|
||||
Create your content in the [reStructuredText] format preceded by front matter. Hugo renders reStructuredText content to HTML using [Docutils], specifically rst2html. You must install Docutils and its dependencies (Python) to use the reStructuredText content format.
|
||||
Create your content in the [reStructuredText] format preceded by front matter. Hugo renders reStructuredText content to HTML using [Docutils], specifically rst2html. You must install Docutils and its dependencies (Python) to render the reStructuredText content format.
|
||||
|
||||
Hugo passes these CLI flags when calling the rst2html executable:
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ You can add taxonomy terms to the front matter of any these [page kinds](g):
|
||||
|
||||
Access taxonomy terms from a template using the [`Params`] or [`GetTerms`] method on a `Page` object. For example:
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ with .GetTerms "tags" }}
|
||||
<p>Tags</p>
|
||||
<ul>
|
||||
|
||||
@@ -64,7 +64,7 @@ math = true
|
||||
The configuration above enables mathematical rendering on every page unless you set the `math` parameter to `false` in front matter. To enable mathematical rendering as needed, set the `math` parameter to `false` in your site configuration, and set the `math` parameter to `true` in front matter. Use this parameter in your base template as shown in [Step 3].
|
||||
|
||||
> [!note]
|
||||
> The configuration above precludes the use of the `$...$` delimiter pair for inline equations. Although you can add this delimiter pair to the configuration and JavaScript, you will need to double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting.
|
||||
> The configuration above precludes the use of the `$...$` delimiter pair for inline equations. Although you can add this delimiter pair to the configuration and JavaScript, you must double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting.
|
||||
>
|
||||
> See the [inline delimiters](#inline-delimiters) section for details.
|
||||
|
||||
@@ -87,7 +87,7 @@ inline = [['@', '@']]
|
||||
|
||||
Create a partial template to load MathJax or KaTeX. The example below loads MathJax, or you can use KaTeX as described in the [engines](#engines) section.
|
||||
|
||||
```go-html-template {file="layouts/partials/math.html" copy=true}
|
||||
```go-html-template {file="layouts/_partials/math.html" copy=true}
|
||||
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
|
||||
<script>
|
||||
MathJax = {
|
||||
@@ -108,7 +108,7 @@ The delimiters above must match the delimiters in your site configuration.
|
||||
|
||||
Conditionally call the partial template from the base template.
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html"}
|
||||
```go-html-template {file="layouts/baseof.html"}
|
||||
<head>
|
||||
...
|
||||
{{ if .Param "math" }}
|
||||
@@ -161,10 +161,10 @@ math = true
|
||||
|
||||
The configuration, JavaScript, and examples above use the `\(...\)` delimiter pair for inline equations. The `$...$` delimiter pair is a common alternative, but using it may result in unintended formatting if you use the `$` symbol outside of math contexts.
|
||||
|
||||
If you add the `$...$` delimiter pair to your configuration and JavaScript, you must double-escape the `$` when outside of math contexts, regardless of whether mathematical rendering is enabled on the page. For example:
|
||||
If you add the `$...$` delimiter pair to your configuration and JavaScript, you must double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting. For example:
|
||||
|
||||
```text
|
||||
A \\$5 bill _saved_ is a \\$5 bill _earned_.
|
||||
I will give you \\$2 if you can solve $y = x^2$.
|
||||
```
|
||||
|
||||
> [!note]
|
||||
@@ -181,7 +181,7 @@ MathJax and KaTeX are open-source JavaScript display engines. Both engines are f
|
||||
|
||||
To use KaTeX instead of MathJax, replace the partial template from [Step 2] with this:
|
||||
|
||||
```go-html-template {file="layouts/partials/math.html" copy=true}
|
||||
```go-html-template {file="layouts/_partials/math.html" copy=true}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css"
|
||||
|
||||
@@ -11,7 +11,7 @@ aliases: [/extras/menus/]
|
||||
To create a menu for your site:
|
||||
|
||||
1. Define the menu entries
|
||||
1. [Localize] each entry
|
||||
1. [Localize](multilingual/#menus) each entry
|
||||
1. Render the menu with a [template]
|
||||
|
||||
Create multiple menus, either flat or nested. For example, create a main menu for the header, and a separate menu for the footer.
|
||||
|
||||
@@ -114,7 +114,7 @@ If, across the linked bundles, two or more files share the same basename, only o
|
||||
|
||||
To create a list of links to translated content, use a template similar to the following:
|
||||
|
||||
```go-html-template {file="layouts/partials/i18nlist.html"}
|
||||
```go-html-template {file="layouts/_partials/i18nlist.html"}
|
||||
{{ if .IsTranslated }}
|
||||
<h4>{{ i18n "translations" }}</h4>
|
||||
<ul>
|
||||
@@ -127,7 +127,7 @@ To create a list of links to translated content, use a template similar to the f
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
The above can be put in a `partial` (i.e., inside `layouts/partials/`) and included in any template. It will not print anything if there are no translations for a given page.
|
||||
The above can be put in a partial template then included in any template. It will not print anything if there are no translations for a given page.
|
||||
|
||||
The above also uses the [`i18n` function][i18func] described in the next section.
|
||||
|
||||
@@ -135,7 +135,7 @@ The above also uses the [`i18n` function][i18func] described in the next section
|
||||
|
||||
`.AllTranslations` on a `Page` can be used to list all translations, including the page itself. On the home page it can be used to build a language navigator:
|
||||
|
||||
```go-html-template {file="layouts/partials/allLanguages.html"}
|
||||
```go-html-template {file="layouts/_partials/allLanguages.html"}
|
||||
<ul>
|
||||
{{ range $.Site.Home.AllTranslations }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .Language.LanguageName }}</a></li>
|
||||
|
||||
@@ -99,7 +99,7 @@ The [sections] can be nested as deeply as you want. The important thing to under
|
||||
|
||||
### Single pages in sections
|
||||
|
||||
Single content files in each of your sections will be rendered by a [single template]. Here is an example of a single `post` within `posts`:
|
||||
Single content files in each of your sections will be rendered by a [page template]. Here is an example of a single `post` within `posts`:
|
||||
|
||||
```txt
|
||||
path ("posts/my-first-hugo-post.md")
|
||||
@@ -148,4 +148,4 @@ The `url` is the entire URL path, defined by the file path and optionally overri
|
||||
[config]: /configuration/
|
||||
[pretty]: /content-management/urls/#appearance
|
||||
[sections]: /content-management/sections/
|
||||
[single template]: /templates/types/#single
|
||||
[page template]: /templates/types/#page
|
||||
|
||||
@@ -12,7 +12,7 @@ Hugo uses a set of factors to identify a page's related content based on front m
|
||||
|
||||
To list up to 5 related pages (which share the same _date_ or _keyword_ parameters) is as simple as including something similar to this partial in your template:
|
||||
|
||||
```go-html-template {file="layouts/partials/related.html" copy=true}
|
||||
```go-html-template {file="layouts/_partials/related.html" copy=true}
|
||||
{{ with site.RegularPages.Related . | first 5 }}
|
||||
<p>Related content:</p>
|
||||
<ul>
|
||||
|
||||
@@ -63,7 +63,7 @@ With the file structure from the [example above](#overview):
|
||||
|
||||
1. The list page for the articles section includes all articles, regardless of directory structure; none of the subdirectories are sections.
|
||||
1. The articles/2022 and articles/2023 directories do not have list pages; they are not sections.
|
||||
1. The list page for the products section, by default, includes product-1 and product-2, but not their descendant pages. To include descendant pages, use the `RegularPagesRecursive` method instead of the `Pages` method in the list template.
|
||||
1. The list page for the products section, by default, includes product-1 and product-2, but not their descendant pages. To include descendant pages, use the `RegularPagesRecursive` method instead of the `Pages` method in the section template.
|
||||
1. All directories in the products section have list pages; each directory is a section.
|
||||
|
||||
## Template selection
|
||||
@@ -74,15 +74,15 @@ With the file structure from the [example above](#overview):
|
||||
|
||||
Content directory|Section template
|
||||
:--|:--
|
||||
`content/products`|`layouts/products/list.html`
|
||||
`content/products/product-1`|`layouts/products/list.html`
|
||||
`content/products/product-1/benefits`|`layouts/products/list.html`
|
||||
`content/products`|`layouts/products/section.html`
|
||||
`content/products/product-1`|`layouts/products/section.html`
|
||||
`content/products/product-1/benefits`|`layouts/products/section.html`
|
||||
|
||||
Content directory|Single template
|
||||
Content directory|Page template
|
||||
:--|:--
|
||||
`content/products`|`layouts/products/single.html`
|
||||
`content/products/product-1`|`layouts/products/single.html`
|
||||
`content/products/product-1/benefits`|`layouts/products/single.html`
|
||||
`content/products`|`layouts/products/page.html`
|
||||
`content/products/product-1`|`layouts/products/page.html`
|
||||
`content/products/product-1/benefits`|`layouts/products/page.html`
|
||||
|
||||
If you need to use a different template for a subsection, specify `type` and/or `layout` in front matter.
|
||||
|
||||
@@ -98,7 +98,7 @@ The content file (benefit-1.md) has four ancestors: benefits, product-1, product
|
||||
|
||||
For example, use the `.Ancestors` method to render breadcrumb navigation.
|
||||
|
||||
```go-html-template {file="layouts/partials/breadcrumb.html"}
|
||||
```go-html-template {file="layouts/_partials/breadcrumb.html"}
|
||||
<nav aria-label="breadcrumb" class="breadcrumb">
|
||||
<ol>
|
||||
{{ range .Ancestors.Reverse }}
|
||||
|
||||
@@ -22,7 +22,7 @@ Hugo's embedded shortcodes are pre-defined templates within the application. Ref
|
||||
|
||||
Create custom shortcodes to simplify and standardize content creation. For example, the following shortcode template generates an audio player using a [global resource](g):
|
||||
|
||||
```go-html-template {file="layouts/shortcodes/audio.html"}
|
||||
```go-html-template {file="layouts/_shortcodes/audio.html"}
|
||||
{{ with resources.Get (.Get "src") }}
|
||||
<audio controls preload="auto" src="{{ .RelPermalink }}"></audio>
|
||||
{{ end }}
|
||||
@@ -181,7 +181,7 @@ With standard notation, Hugo processes the shortcode separately, merging the out
|
||||
|
||||
By way of example, with this shortcode template:
|
||||
|
||||
```go-html-template {file="layouts/shortcodes/foo.html"}
|
||||
```go-html-template {file="layouts/_shortcodes/foo.html"}
|
||||
{{ .Inner }}
|
||||
```
|
||||
|
||||
|
||||
@@ -108,18 +108,73 @@ categories_weight = 44
|
||||
|
||||
By using taxonomic weight, the same piece of content can appear in different positions in different taxonomies.
|
||||
|
||||
## Add custom metadata to a taxonomy or term
|
||||
## Metadata
|
||||
|
||||
If you need to add custom metadata to your taxonomy terms, you will need to create a page for that term at `/content/<TAXONOMY>/<TERM>/_index.md` and add your metadata in its front matter. Continuing with our 'Actors' example, let's say you want to add a Wikipedia page link to each actor. Your terms pages would be something like this:
|
||||
Display metadata about each term by creating a corresponding branch bundle in the `content` directory.
|
||||
|
||||
{{< code-toggle file=content/actors/bruce-willis/_index.md fm=true >}}
|
||||
title: "Bruce Willis"
|
||||
wikipedia: "https://en.wikipedia.org/wiki/Bruce_Willis"
|
||||
For example, create an "authors" taxonomy:
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
[taxonomies]
|
||||
author = 'authors'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
[content section]: /content-management/sections/
|
||||
[content type]: /content-management/types/
|
||||
[documentation on archetypes]: /content-management/archetypes/
|
||||
[front matter]: /content-management/front-matter/
|
||||
[taxonomy templates]: /templates/types/#taxonomy
|
||||
[site configuration]: /configuration/
|
||||
Then create content with one [branch bundle](g) for each term:
|
||||
|
||||
```text
|
||||
content/
|
||||
└── authors/
|
||||
├── jsmith/
|
||||
│ ├── _index.md
|
||||
│ └── portrait.jpg
|
||||
└── rjones/
|
||||
├── _index.md
|
||||
└── portrait.jpg
|
||||
```
|
||||
|
||||
Then add front matter to each term page:
|
||||
|
||||
{{< code-toggle file=content/authors/jsmith/_index.md fm=true >}}
|
||||
title = "John Smith"
|
||||
affiliation = "University of Chicago"
|
||||
{{< /code-toggle >}}
|
||||
|
||||
Then create a taxonomy template specific to the "authors" taxonomy:
|
||||
|
||||
```go-html-template {file="layouts/authors/taxonomy.html"}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
{{ range .Data.Terms.Alphabetical }}
|
||||
<h2><a href="{{ .Page.RelPermalink }}">{{ .Page.LinkTitle }}</a></h2>
|
||||
<p>Affiliation: {{ .Page.Params.Affiliation }}</p>
|
||||
{{ with .Page.Resources.Get "portrait.jpg" }}
|
||||
{{ with .Fill "100x100" }}
|
||||
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="portrait">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
In the example above we list each author including their affiliation and portrait.
|
||||
|
||||
Or create a term template specific to the "authors" taxonomy:
|
||||
|
||||
```go-html-template {file="layouts/authors/term.html"}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p>Affiliation: {{ .Params.affiliation }}</p>
|
||||
{{ with .Resources.Get "portrait.jpg" }}
|
||||
{{ with .Fill "100x100" }}
|
||||
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="portrait">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ .Content }}
|
||||
{{ range .Pages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
In the example above we display the author including their affiliation and portrait, then a list of associated content.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: Content types
|
||||
description: Hugo is built around content organized in sections.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/content/types]
|
||||
---
|
||||
|
||||
A **content type** is a way to organize your content. Hugo resolves the content type from either the `type` in front matter or, if not set, the first directory in the file path. E.g. `content/blog/my-first-event.md` will be of type `blog` if no `type` is set.
|
||||
|
||||
A content type is used to
|
||||
|
||||
- Determine how the content is rendered. See [Template Lookup Order](/templates/lookup-order/) and [Content Views](/templates/content-view) for more.
|
||||
- Determine which [archetype](/content-management/archetypes/) template to use for new content.
|
||||
@@ -108,7 +108,7 @@ multilingual|`about`|`https://example.org/de/about/`
|
||||
|
||||
{{< new-in 0.131.0 />}}
|
||||
|
||||
You can also usetokens when setting the `url` value. This is typically used in `cascade` sections:
|
||||
You can also use tokens when setting the `url` value. This is typically used in `cascade` sections:
|
||||
|
||||
{{< code-toggle file=content/foo/bar/_index.md fm=true >}}
|
||||
title ="Bar"
|
||||
|
||||
@@ -153,7 +153,7 @@ CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
|
||||
To build and install a specific release:
|
||||
|
||||
```sh
|
||||
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@v0.144.2
|
||||
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@v0.147.1
|
||||
```
|
||||
|
||||
To build and install at the latest commit on the master branch:
|
||||
|
||||
@@ -112,7 +112,7 @@ Yes → Hugo is fast.
|
||||
|
||||
### Function and method descriptions
|
||||
|
||||
Start descriptions in the functions and methods sections with "Returns", of for booelan values, "Reports whether".
|
||||
Start descriptions in the functions and methods sections with "Returns", or for boolean values, "Reports whether".
|
||||
|
||||
### File paths and names
|
||||
|
||||
@@ -152,26 +152,27 @@ This example demonstrates the minimum required front matter fields.
|
||||
|
||||
If quotation marks are required, prefer single quotes to double quotes when possible.
|
||||
|
||||
Seq|Field|Description|Required
|
||||
--:|:--|:--|:--
|
||||
1|`title`|The page title|:heavy_check_mark:|
|
||||
2|`linkTitle`|A short version of the page title||
|
||||
3|`description`|A complete sentence describing the page|:heavy_check_mark:|
|
||||
4|`categories`|An array of terms in the categories taxonomy|:heavy_check_mark: [^1]|
|
||||
5|`keywords`|An array of keywords used to identify related content|:heavy_check_mark: [^1]|
|
||||
6|`publishDate`|Applicable to news items: the publication date||
|
||||
7|`params.altTitle`|An alternate title: used in the "see also" panel if provided||
|
||||
8|`params.functions_and_methods.aliases`|Applicable to function and method pages: an array of alias names||
|
||||
9|`params.functions_and_methods.returnType`|Applicable to function and method pages: the data type returned||
|
||||
10|`params.functions_and_methods.signatures`|Applicable to function and method pages: an array of signatures||
|
||||
11|`params.hide_in_this_section`|Whether to hide the "in this section" panel||
|
||||
12|`params.minversion`|Applicable to the quick start page: the minimum Hugo version required||
|
||||
13|`params.permalink`|Reserved for use by the news content adapter||
|
||||
14|`params.reference (used in glossary term)`|Applicable to glossary entries: a URL for additional information||
|
||||
15|`params.show_publish_date`|Whether to show the `publishDate` when rendering the page||
|
||||
16|`weight`|The page weight||
|
||||
17|`aliases`|Previous URLs used to access this page||
|
||||
18|`expirydate`|The expiration date||
|
||||
Field|Description|Required
|
||||
:--|:--|:--
|
||||
`title`|The page title|:heavy_check_mark:|
|
||||
`linkTitle`|A short version of the page title||
|
||||
`description`|A complete sentence describing the page|:heavy_check_mark:|
|
||||
`categories`|An array of terms in the categories taxonomy|:heavy_check_mark: [^1]|
|
||||
`keywords`|An array of keywords used to identify related content|:heavy_check_mark: [^1]|
|
||||
`publishDate`|Applicable to news items: the publication date||
|
||||
`params.alt_title`|An alternate title: used in the "see also" panel if provided||
|
||||
`params.functions_and_methods.aliases`|Applicable to function and method pages: an array of alias names||
|
||||
`params.functions_and_methods.returnType`|Applicable to function and method pages: the data type returned||
|
||||
`params.functions_and_methods.signatures`|Applicable to function and method pages: an array of signatures||
|
||||
`params.hide_in_this_section`|Whether to hide the "in this section" panel||
|
||||
`params.minversion`|Applicable to the quick start page: the minimum Hugo version required||
|
||||
`params.permalink`|Reserved for use by the news content adapter||
|
||||
`params.reference (used in glossary term)`|Applicable to glossary entries: a URL for additional information||
|
||||
`params.searchable`|Whether to add the content of this page to the search index. The default value is cascaded down from the site configuration; `true` if the page kind is `page`, and `false` if the page kind is one of `home`, `section`, `taxonomy`, or `term`. Add this field to override the default value.||
|
||||
`params.show_publish_date`|Whether to show the `publishDate` when rendering the page||
|
||||
`weight`|The page weight||
|
||||
`aliases`|Previous URLs used to access this page||
|
||||
`expirydate`|The expiration date||
|
||||
|
||||
[^1]: The field is required, but its data is not.
|
||||
|
||||
@@ -185,13 +186,13 @@ If the title in the "See also" sidebar is ambiguous or the same as another page,
|
||||
title = "Long descriptive title"
|
||||
linkTitle = "Short title"
|
||||
[params]
|
||||
altTitle = "Whatever you want"
|
||||
alt_title = "Whatever you want"
|
||||
{{< /code-toggle >}}
|
||||
|
||||
Use of the alternate title is limited to the "See also" sidebar.
|
||||
|
||||
> [!note]
|
||||
> Think carefully before setting the `altTitle`. Use it only when absolutely necessary.
|
||||
> Think carefully before setting the `alt_title`. Use it only when absolutely necessary.
|
||||
|
||||
## Code examples
|
||||
|
||||
@@ -223,10 +224,10 @@ erroneous lexing/highlighting of shortcode calls.
|
||||
```
|
||||
````
|
||||
|
||||
To include a filename header and copy-to-clipboard button:
|
||||
To include a file name header and copy-to-clipboard button:
|
||||
|
||||
````text
|
||||
```go-html-template {file="layouts/partials/foo.html" copy=true}
|
||||
```go-html-template {file="layouts/_partials/foo.html" copy=true}
|
||||
{{ if eq $foo "bar" }}
|
||||
{{ print "foo is bar" }}
|
||||
{{ end }}
|
||||
@@ -236,7 +237,7 @@ To include a filename header and copy-to-clipboard button:
|
||||
To wrap the code block within an initially-opened `details` element using a non-default summary:
|
||||
|
||||
````text
|
||||
```go-html-template {details=true open=true summary="layouts/partials/foo.html" copy=true}
|
||||
```go-html-template {details=true open=true summary="layouts/_partials/foo.html" copy=true}
|
||||
{{ if eq $foo "bar" }}
|
||||
{{ print "foo is bar" }}
|
||||
{{ end }}
|
||||
@@ -427,7 +428,7 @@ Use the [new-in shortcode](#new-in) to indicate a new feature:
|
||||
{{</* new-in 0.144.0 */>}}
|
||||
```
|
||||
|
||||
The "new in" label will be hidden if the specified version is older than a predefined threshold, based on differences in major and minor versions. See [details](https://github.com/gohugoio/hugoDocs/blob/master/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html).
|
||||
The "new in" label will be hidden if the specified version is older than a predefined threshold, based on differences in major and minor versions. See [details](https://github.com/gohugoio/hugoDocs/blob/master/_vendor/github.com/gohugoio/gohugoioTheme/layouts/_shortcodes/new-in.html).
|
||||
|
||||
## Deprecated features
|
||||
|
||||
@@ -514,17 +515,17 @@ Visit the [documentation repository] and create a pull request (PR).
|
||||
|
||||
A project maintainer will review your PR and may request changes. You may delete your branch after the maintainer merges your PR.
|
||||
|
||||
[ATX]: https://spec.commonmark.org/0.30/#atx-headings
|
||||
[ATX]: https://spec.commonmark.org/current/#atx-headings
|
||||
[basic english]: https://simple.wikipedia.org/wiki/Basic_English
|
||||
[basic english]: https://simple.wikipedia.org/wiki/Basic_English
|
||||
[developer documentation style guide]: https://developers.google.com/style
|
||||
[documentation repository]: https://github.com/gohugoio/hugoDocs/
|
||||
[fenced code blocks]: https://spec.commonmark.org/0.30/#fenced-code-blocks
|
||||
[fenced code blocks]: https://spec.commonmark.org/current/#fenced-code-blocks
|
||||
[glossary]: /quick-reference/glossary/
|
||||
[indented code blocks]: https://spec.commonmark.org/0.30/#indented-code-blocks
|
||||
[indented code blocks]: https://spec.commonmark.org/current/#indented-code-blocks
|
||||
[issues]: https://github.com/gohugoio/hugoDocs/issues
|
||||
[list items]: https://spec.commonmark.org/0.30/#list-items
|
||||
[list items]: https://spec.commonmark.org/current/#list-items
|
||||
[project repository]: https://github.com/gohugoio/hugo
|
||||
[raw HTML]: https://spec.commonmark.org/0.30/#raw-html
|
||||
[raw HTML]: https://spec.commonmark.org/current/#raw-html
|
||||
[related content]: /content-management/related-content/
|
||||
[setext]: https://spec.commonmark.org/0.30/#setext-heading
|
||||
[setext]: https://spec.commonmark.org/current/#setext-heading
|
||||
|
||||
@@ -3,6 +3,8 @@ title: Hugo Documentation
|
||||
linkTitle: Docs
|
||||
description: Hugo is the world's fastest static website engine. It's written in Go (aka Golang) and developed by bep, spf13 and friends.
|
||||
layout: list
|
||||
params:
|
||||
searchable: false
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
@@ -12,8 +12,18 @@ aliases: [/functions/shuffle]
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ shuffle (seq 1 2 3) }} → [3 1 2]
|
||||
{{ shuffle (slice "a" "b" "c") }} → [b a c]
|
||||
{{ collections.Shuffle (slice "a" "b" "c") }} → [b a c]
|
||||
```
|
||||
|
||||
The result will vary from one build to the next.
|
||||
|
||||
To render an unordered list of 5 random pages from a page collection:
|
||||
|
||||
```go-html-template
|
||||
<ul>
|
||||
{{ $p := site.RegularPages }}
|
||||
{{ range $p | collections.Shuffle | first 5 }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
```
|
||||
|
||||
@@ -87,7 +87,6 @@ Use any of the following logical operators:
|
||||
: (`bool`) Reports whether the given field value (a slice) contains one or more elements in common with `VALUE`. See [details](/functions/collections/intersect).
|
||||
|
||||
`like`
|
||||
: {{< new-in 0.116.0 />}}
|
||||
: (`bool`) Reports whether the given field value matches the [regular expression](g) specified in `VALUE`. Use the `like` operator to compare `string` values. The `like` operator returns `false` when comparing other data types to the regular expression.
|
||||
|
||||
> [!note]
|
||||
@@ -167,8 +166,6 @@ For example, to return a collection of pages where any of the terms in the "genr
|
||||
|
||||
## Regular expression comparison
|
||||
|
||||
{{< new-in 0.116.0 />}}
|
||||
|
||||
To return a collection of pages where the "author" page parameter begins with either "victor" or "Victor":
|
||||
|
||||
```go-html-template
|
||||
|
||||
@@ -12,26 +12,6 @@ params:
|
||||
|
||||
{{< new-in 0.128.0 />}}
|
||||
|
||||
```go-html-template
|
||||
{{ with resources.Get "sass/main.scss" }}
|
||||
{{ $opts := dict
|
||||
"enableSourceMap" (not hugo.IsProduction)
|
||||
"outputStyle" (cond hugo.IsProduction "compressed" "expanded")
|
||||
"targetPath" "css/main.css"
|
||||
"transpiler" "libsass"
|
||||
}}
|
||||
{{ with . | toCSS $opts }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ with . | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Transpile Sass to CSS using the LibSass transpiler included in Hugo's extended and extended/deploy editions, or [install Dart Sass](#dart-sass) to use the latest features of the Sass language.
|
||||
|
||||
Sass has two forms of syntax: [SCSS] and [indented]. Hugo supports both.
|
||||
@@ -41,53 +21,69 @@ Sass has two forms of syntax: [SCSS] and [indented]. Hugo supports both.
|
||||
|
||||
## Options
|
||||
|
||||
transpiler
|
||||
: (`string`) The transpiler to use, either `libsass` (default) or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass) below.
|
||||
|
||||
targetPath
|
||||
: (`string`) If not set, the transformed resource's target path will be the original path of the asset file with its extension replaced by `.css`.
|
||||
|
||||
vars
|
||||
: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/).
|
||||
|
||||
```scss
|
||||
// LibSass
|
||||
@import "hugo:vars";
|
||||
|
||||
// Dart Sass
|
||||
@use "hugo:vars" as v;
|
||||
```
|
||||
|
||||
outputStyle
|
||||
: (`string`) Output styles available to LibSass include `nested` (default), `expanded`, `compact`, and `compressed`. Output styles available to Dart Sass include `expanded` (default) and `compressed`.
|
||||
|
||||
precision
|
||||
: (`int`) Precision of floating point math. Not applicable to Dart Sass.
|
||||
|
||||
enableSourceMap
|
||||
: (`bool`) Whether to generate a source map. Default is `false`.
|
||||
|
||||
sourceMapIncludeSources
|
||||
: (`bool`) Whether to embed sources in the generated source map. Not applicable to LibSass. Default is `false`.
|
||||
|
||||
includePaths
|
||||
: (`slice`) A slice of paths, relative to the project root, that the transpiler will use when resolving `@use` and `@import` statements.
|
||||
|
||||
```go-html-template
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"targetPath" "css/style.css"
|
||||
"vars" site.Params.styles
|
||||
"enableSourceMap" (not hugo.IsProduction)
|
||||
"includePaths" (slice "node_modules/bootstrap/scss")
|
||||
}}
|
||||
{{ with resources.Get "sass/main.scss" | toCSS $opts | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{ end }}
|
||||
```
|
||||
outputStyle
|
||||
: (`string`) The output style of the resulting CSS. With LibSass, one of `nested` (default), `expanded`, `compact`, or `compressed`. With Dart Sass, either `expanded` (default) or `compressed`.
|
||||
|
||||
precision
|
||||
: (`int`) The precision of floating point math. Applicable to LibSass. Default is `8`.
|
||||
|
||||
silenceDeprecations
|
||||
: (`slice`) {{< new-in 0.139.0 />}} A slice of deprecation IDs to silence. The deprecation IDs are printed to in the warning message, e.g "import" in `WARN Dart Sass: DEPRECATED [import] ...`. This is for Dart Sass only.
|
||||
: {{< new-in 0.139.0 />}}
|
||||
: (`slice`) A slice of deprecation IDs to silence. IDs are enclosed in brackets within Dart Sass warning messages (e.g., `import` in `WARN Dart Sass: DEPRECATED [import]`). Applicable to Dart Sass. Default is `false`.
|
||||
|
||||
silenceDependencyDeprecations
|
||||
: {{< new-in 0.146.0 />}}
|
||||
: (`bool`) Whether to silence deprecation warnings from dependencies, where a dependency is considered any file transitively imported through a load path. This does not apply to `@warn` or `@debug` rules.Default is `false`.
|
||||
|
||||
sourceMapIncludeSources
|
||||
: (`bool`) Whether to embed sources in the generated source map. Applicable to Dart Sass. Default is `false`.
|
||||
|
||||
targetPath
|
||||
: (`string`) The publish path for the transformed resource, relative to the[`publishDir`]. If unset, the target path defaults to the asset's original path with a `.css` extension.
|
||||
|
||||
transpiler
|
||||
: (`string`) The transpiler to use, either `libsass` or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass). Default is `libsass`.
|
||||
|
||||
vars
|
||||
: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/).
|
||||
|
||||
```scss
|
||||
// LibSass
|
||||
@import "hugo:vars";
|
||||
|
||||
// Dart Sass
|
||||
@use "hugo:vars" as v;
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```go-html-template {copy=true}
|
||||
{{ with resources.Get "sass/main.scss" }}
|
||||
{{ $opts := dict
|
||||
"enableSourceMap" hugo.IsDevelopment
|
||||
"outputStyle" (cond hugo.IsDevelopment "expanded" "compressed")
|
||||
"targetPath" "css/main.css"
|
||||
"transpiler" "dartsass"
|
||||
"vars" site.Params.styles
|
||||
"includePaths" (slice "node_modules/bootstrap/scss")
|
||||
}}
|
||||
{{ with . | toCSS $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 }}
|
||||
```
|
||||
|
||||
## Dart Sass
|
||||
|
||||
@@ -117,10 +113,13 @@ macOS|Homebrew|[brew.sh]|`brew install sass/sass/sass`
|
||||
Windows|Chocolatey|[chocolatey.org]|`choco install sass`
|
||||
Windows|Scoop|[scoop.sh]|`scoop install sass`
|
||||
|
||||
You may also install [prebuilt binaries] for Linux, macOS, and Windows.
|
||||
You may also install [prebuilt binaries] for Linux, macOS, and Windows. You must install the prebuilt binary outside of your project directory and ensure its path is included in your system's PATH environment variable.
|
||||
|
||||
Run `hugo env` to list the active transpilers.
|
||||
|
||||
> [!note]
|
||||
> If you build Hugo from source and run `mage test -v`, the test will fail if you install Dart Sass as a Snap package. This is due to the Snap package's strict confinement model.
|
||||
|
||||
### Installing in a production environment
|
||||
|
||||
For [CI/CD](g) deployments (e.g., GitHub Pages, GitLab Pages, Netlify, etc.) you must edit the workflow to install Dart Sass before Hugo builds the site[^2]. Some providers allow you to use one of the package managers above, or you can download and extract one of the prebuilt binaries.
|
||||
@@ -136,16 +135,14 @@ To install Dart Sass for your builds on GitHub Pages, add this step to the GitHu
|
||||
run: sudo snap install dart-sass
|
||||
```
|
||||
|
||||
If you are using GitHub Pages for the first time with your repository, GitHub provides a [starter workflow] for Hugo that includes Dart Sass. This is the simplest way to get started.
|
||||
|
||||
#### GitLab Pages
|
||||
|
||||
To install Dart Sass for your builds on GitLab Pages, the `.gitlab-ci.yml` file should look something like this:
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
HUGO_VERSION: 0.144.2
|
||||
DART_SASS_VERSION: 1.85.0
|
||||
HUGO_VERSION: 0.147.9
|
||||
DART_SASS_VERSION: 1.89.2
|
||||
GIT_DEPTH: 0
|
||||
GIT_STRATEGY: clone
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
@@ -178,8 +175,8 @@ To install Dart Sass for your builds on Netlify, the `netlify.toml` file should
|
||||
|
||||
```toml
|
||||
[build.environment]
|
||||
HUGO_VERSION = "0.144.2"
|
||||
DART_SASS_VERSION = "1.85.0"
|
||||
HUGO_VERSION = "0.147.9"
|
||||
DART_SASS_VERSION = "1.89.2"
|
||||
NODE_VERSION = "22"
|
||||
TZ = "America/Los_Angeles"
|
||||
|
||||
@@ -194,34 +191,6 @@ command = """\
|
||||
"""
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
To transpile with Dart Sass, set `transpiler` to `dartsass` in the options map passed to `css.Sass`. For example:
|
||||
|
||||
```go-html-template
|
||||
{{ with resources.Get "sass/main.scss" }}
|
||||
{{ $opts := dict
|
||||
"enableSourceMap" (not hugo.IsProduction)
|
||||
"outputStyle" (cond hugo.IsProduction "compressed" "expanded")
|
||||
"targetPath" "css/main.css"
|
||||
"transpiler" "dartsass"
|
||||
}}
|
||||
{{ with . | toCSS $opts }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ with . | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
If you build Hugo from source and run `mage test -v`, the test will fail if you install Dart Sass as a Snap package. This is due to the Snap package's strict confinement model.
|
||||
|
||||
[brew.sh]: https://brew.sh/
|
||||
[chocolatey.org]: https://community.chocolatey.org/packages/sass
|
||||
[dart sass]: https://sass-lang.com/dart-sass
|
||||
@@ -232,3 +201,4 @@ If you build Hugo from source and run `mage test -v`, the test will fail if you
|
||||
[snap package]: /installation/linux/#snap
|
||||
[snapcraft.io]: https://snapcraft.io/dart-sass
|
||||
[starter workflow]: https://github.com/actions/starter-workflows/blob/main/pages/hugo.yml
|
||||
[`publishDir`]: /configuration/all/#publishdir
|
||||
|
||||
@@ -18,8 +18,10 @@ Use the `css.TailwindCSS` function to process your Tailwind CSS files. This func
|
||||
1. Compile those utility classes into standard CSS.
|
||||
1. Generate an optimized CSS output file.
|
||||
|
||||
> [!caution]
|
||||
> Tailwind CSS v4.0 and later requires a relatively [modern browser](https://tailwindcss.com/docs/compatibility#browser-support) to render correctly.
|
||||
> [!note]
|
||||
> Use this function with Tailwind CSS v4.0 and later, which require a relatively [modern browser] to render correctly.
|
||||
|
||||
[modern browser]: https://tailwindcss.com/docs/compatibility#browser-support
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -27,11 +29,12 @@ Use the `css.TailwindCSS` function to process your Tailwind CSS files. This func
|
||||
|
||||
Install the Tailwind CSS CLI v4.0 or later:
|
||||
|
||||
```sh
|
||||
```sh {copy=true}
|
||||
npm install --save-dev tailwindcss @tailwindcss/cli
|
||||
```
|
||||
|
||||
The TailwindCSS CLI is also available as a [standalone executable] if you want to use it without installing Node.js.
|
||||
The Tailwind CSS CLI is also available as a [standalone executable]. You must install it outside of your project directory and ensure its path is included in your system's `PATH` environment variable.
|
||||
|
||||
|
||||
[standalone executable]: https://github.com/tailwindlabs/tailwindcss/releases/latest
|
||||
|
||||
@@ -40,21 +43,23 @@ The TailwindCSS CLI is also available as a [standalone executable] if you want t
|
||||
Add this to your site configuration:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
[[module.mounts]]
|
||||
source = "hugo_stats.json"
|
||||
target = "assets/notwatching/hugo_stats.json"
|
||||
disableWatch = true
|
||||
[build.buildStats]
|
||||
enable = true
|
||||
[[build.cachebusters]]
|
||||
source = "assets/notwatching/hugo_stats\\.json"
|
||||
target = "css"
|
||||
[[build.cachebusters]]
|
||||
source = "(postcss|tailwind)\\.config\\.js"
|
||||
target = "css"
|
||||
[build]
|
||||
[build.buildStats]
|
||||
enable = true
|
||||
[[build.cachebusters]]
|
||||
source = 'assets/notwatching/hugo_stats\.json'
|
||||
target = 'css'
|
||||
[[build.cachebusters]]
|
||||
source = '(postcss|tailwind)\.config\.js'
|
||||
target = 'css'
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = 'assets'
|
||||
target = 'assets'
|
||||
[[module.mounts]]
|
||||
disableWatch = true
|
||||
source = 'hugo_stats.json'
|
||||
target = 'assets/notwatching/hugo_stats.json'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
### Step 3
|
||||
@@ -72,20 +77,15 @@ Tailwind CSS respects `.gitignore` files. This means that if `hugo_stats.json` i
|
||||
|
||||
Create a partial template to process the CSS with the Tailwind CSS CLI:
|
||||
|
||||
```go-html-template {file="layouts/partials/css.html" copy=true}
|
||||
{{ with (templates.Defer (dict "key" "global")) }}
|
||||
{{ with resources.Get "css/main.css" }}
|
||||
{{ $opts := dict
|
||||
"minify" hugo.IsProduction
|
||||
"inlineImports" true
|
||||
}}
|
||||
{{ with . | css.TailwindCSS $opts }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ with . | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
```go-html-template {file="layouts/_partials/css.html" copy=true}
|
||||
{{ with resources.Get "css/main.css" }}
|
||||
{{ $opts := dict "minify" (not hugo.IsDevelopment) }}
|
||||
{{ with . | css.TailwindCSS $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 }}
|
||||
@@ -94,33 +94,16 @@ Create a partial template to process the CSS with the Tailwind CSS CLI:
|
||||
|
||||
### Step 5
|
||||
|
||||
Call the partial template from your base template:
|
||||
Call the partial template from your base template, deferring template execution until after all sites and output formats have been rendered:
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html"}
|
||||
```go-html-template {file="layouts/baseof.html" copy=true}
|
||||
<head>
|
||||
...
|
||||
{{ partialCached "css.html" . }}
|
||||
{{ with (templates.Defer (dict "key" "global")) }}
|
||||
{{ partial "css.html" . }}
|
||||
{{ end }}
|
||||
...
|
||||
<head>
|
||||
```
|
||||
|
||||
### Step 6
|
||||
|
||||
Optionally create a `tailwind.config.js` file in the root of your project as shown below. This is necessary if you use the [Tailwind CSS IntelliSense
|
||||
extension] for Visual Studio Code.
|
||||
|
||||
[Tailwind CSS IntelliSense
|
||||
extension]: https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss
|
||||
|
||||
```js {file="tailwind.config.js" copy=true}
|
||||
/*
|
||||
This file is present to satisfy a requirement of the Tailwind CSS IntelliSense
|
||||
extension for Visual Studio Code.
|
||||
|
||||
https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss
|
||||
|
||||
The rest of this file is intentionally empty.
|
||||
*/
|
||||
</head>
|
||||
```
|
||||
|
||||
## Options
|
||||
@@ -131,8 +114,9 @@ minify
|
||||
optimize
|
||||
: (`bool`) Whether to optimize the output without minifying. Default is `false`.
|
||||
|
||||
inlineImports
|
||||
: (`bool`) Whether to enable inlining of `@import` statements. Inlining is performed recursively, but currently once only per file. It is not possible to import the same file in different scopes (root, media query, etc.). Note that this import routine does not care about the CSS specification, so you can have `@import` statements anywhere in the file. Default is `false`.
|
||||
disableInlineImports
|
||||
: {{< new-in 0.147.4 />}}
|
||||
: (`bool`) Whether to disable inlining of `@import` statements. Inlining is performed recursively, but currently once only per file. It is not possible to import the same file in different scopes (root, media query, etc.). Note that this import routine does not care about the CSS specification, so you can have `@import` statements anywhere in the file. Default is `false`.
|
||||
|
||||
skipInlineImportsNotFound
|
||||
: (`bool`) Whether to allow the build process to continue despite unresolved import statements, preserving the original import declarations. It is important to note that the inline importer does not process URL-based imports or those with media queries, and these will remain unaltered even when this option is disabled. Default is `false`.
|
||||
|
||||
@@ -18,7 +18,7 @@ The timer starts when you instantiate it, and stops when you call its `Stop` met
|
||||
|
||||
```go-html-template
|
||||
{{ $t := debug.Timer "TestSqrt" }}
|
||||
{{ range seq 2000 }}
|
||||
{{ range 2000 }}
|
||||
{{ $f := math.Sqrt . }}
|
||||
{{ end }}
|
||||
{{ $t.Stop }}
|
||||
|
||||
@@ -66,7 +66,7 @@ To customize rendering, override Hugo's [embedded code block render hook] for Go
|
||||
|
||||
By way of example, let's create a code block render hook to render GoAT diagrams as `figure` elements with an optional caption.
|
||||
|
||||
```go-html-template {file="layouts/_default/_markup/render-codeblock-goat.html"}
|
||||
```go-html-template {file="layouts/_markup/render-codeblock-goat.html"}
|
||||
{{ $caption := or .Attributes.caption "" }}
|
||||
{{ $class := or .Attributes.class "diagram" }}
|
||||
{{ $id := or .Attributes.id (printf "diagram-%d" (add 1 .Ordinal)) }}
|
||||
|
||||
@@ -30,7 +30,7 @@ When a `Page` object is not in context, you can use the global `page` function:
|
||||
|
||||
## Explanation
|
||||
|
||||
Hugo almost always passes a `Page` as the data context into the top-level template (e.g., `single.html`). The one exception is the multihost sitemap template. This means that you can access the current page with the `.` in the template.
|
||||
Hugo almost always passes a `Page` as the data context into the top-level template (e.g., `baseof.html`). The one exception is the multihost sitemap template. This means that you can access the current page with the `.` in the template.
|
||||
|
||||
But when you are deeply nested inside of a [content view](g), [partial](g), or [render hook](g), it is not always practical or possible to access the `Page` object.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ and then executing it in place:
|
||||
```
|
||||
The typical use is to define a set of root templates that are then customized by redefining the block templates within.
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html"}
|
||||
```go-html-template {file="layouts/baseof.html"}
|
||||
<body>
|
||||
<main>
|
||||
{{ block "main" . }}
|
||||
@@ -33,14 +33,14 @@ The typical use is to define a set of root templates that are then customized by
|
||||
</body>
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_default/list.html"}
|
||||
```go-html-template {file="layouts/section.html"}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
|
||||
@@ -28,7 +28,7 @@ Use with the [`partial`] function:
|
||||
```go-html-template
|
||||
{{ partial "inline/foo.html" (dict "answer" 42) }}
|
||||
|
||||
{{ define "partials/inline/foo.html" }}
|
||||
{{ define "_partials/inline/foo.html" }}
|
||||
{{ printf "The answer is %v." .answer }}
|
||||
{{ end }}
|
||||
```
|
||||
@@ -43,8 +43,21 @@ Use with the [`template`] function:
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> Only [template comments] are allowed outside of the `define` and `end` statements. Avoid placing any other text, including HTML comments, outside of these boundaries. Doing so will cause rendering issues, potentially resulting in a blank page. See the example below.
|
||||
|
||||
```go-html-template {file="layouts/do-not-do-this.html"}
|
||||
<div>This div element broke your template.</div>
|
||||
{{ define "main" }}
|
||||
<h2>{{ .Title }}</h2>
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
<!-- An HTML comment will break your template too. -->
|
||||
```
|
||||
|
||||
{{% include "/_common/functions/go-template/text-template.md" %}}
|
||||
|
||||
[`block`]: /functions/go-template/block/
|
||||
[`template`]: /functions/go-template/block/
|
||||
[`partial`]: /functions/partials/include/
|
||||
|
||||
{{% include "/_common/functions/go-template/text-template.md" %}}
|
||||
[template comments]: /templates/introduction/#comments
|
||||
|
||||
@@ -11,7 +11,7 @@ params:
|
||||
aliases: [/functions/range]
|
||||
---
|
||||
|
||||
{{% include "/_common/functions/truthy-falsy.md" %}}
|
||||
The collection may be a slice, a map, or an integer.
|
||||
|
||||
```go-html-template
|
||||
{{ $s := slice "foo" "bar" "baz" }}
|
||||
@@ -40,19 +40,22 @@ Within a range block:
|
||||
|
||||
At the top of a page template, the [context](g) (the dot) is a `Page` object. Within the `range` block, the context is bound to each successive element.
|
||||
|
||||
With this contrived example that uses the [`seq`] function to generate a slice of integers:
|
||||
With this contrived example:
|
||||
|
||||
```go-html-template
|
||||
{{ range seq 3 }}
|
||||
{{ .Title }}
|
||||
{{ $s := slice "foo" "bar" "baz" }}
|
||||
{{ range $s }}
|
||||
{{ .Title }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Hugo will throw an error:
|
||||
|
||||
can't evaluate field Title in type int
|
||||
```text
|
||||
can't evaluate field Title in type int
|
||||
```
|
||||
|
||||
The error occurs because we are trying to use the `.Title` method on an integer instead of a `Page` object. Within the `range` block, if we want to render the page title, we need to get the context passed into the template.
|
||||
The error occurs because we are trying to use the `.Title` method on a string instead of a `Page` object. Within the `range` block, if we want to render the page title, we need to get the context passed into the template.
|
||||
|
||||
> [!note]
|
||||
> Use the `$` to get the context passed into the template.
|
||||
@@ -60,15 +63,18 @@ The error occurs because we are trying to use the `.Title` method on an integer
|
||||
This template will render the page title three times:
|
||||
|
||||
```go-html-template
|
||||
{{ range seq 3 }}
|
||||
{{ $.Title }}
|
||||
{{ $s := slice "foo" "bar" "baz" }}
|
||||
{{ range $s }}
|
||||
{{ $.Title }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
> [!note]
|
||||
> Gaining a thorough understanding of context is critical for anyone writing template code.
|
||||
|
||||
## Array or slice of scalars
|
||||
## Examples
|
||||
|
||||
### Slice of scalars
|
||||
|
||||
This template code:
|
||||
|
||||
@@ -121,7 +127,7 @@ Is rendered to:
|
||||
<p>2: baz</p>
|
||||
```
|
||||
|
||||
## Array or slice of maps
|
||||
### Slice of maps
|
||||
|
||||
This template code:
|
||||
|
||||
@@ -144,7 +150,7 @@ Is rendered to:
|
||||
<p>Joey is 24</p>
|
||||
```
|
||||
|
||||
## Array or slice of pages
|
||||
### Slice of pages
|
||||
|
||||
This template code:
|
||||
|
||||
@@ -162,7 +168,7 @@ Is rendered to:
|
||||
<h2><a href="/articles/article-1/">Article 1</a></h2>
|
||||
```
|
||||
|
||||
## Maps
|
||||
### Maps
|
||||
|
||||
This template code:
|
||||
|
||||
@@ -182,9 +188,32 @@ Is rendered to:
|
||||
|
||||
Unlike ranging over an array or slice, Hugo sorts by key when ranging over a map.
|
||||
|
||||
### Integers
|
||||
|
||||
{{< new-in 0.123.0 />}}
|
||||
|
||||
Ranging over a positive integer `n` executes the block `n` times, with the context starting at zero and incrementing by one in each iteration.
|
||||
|
||||
```go-html-template
|
||||
{{ $s := slice }}
|
||||
{{ range 1 }}
|
||||
{{ $s = $s | append . }}
|
||||
{{ end }}
|
||||
{{ $s }} → [0]
|
||||
```
|
||||
|
||||
```go-html-template
|
||||
{{ $s := slice }}
|
||||
{{ range 3 }}
|
||||
{{ $s = $s | append . }}
|
||||
{{ end }}
|
||||
{{ $s }} → [0 1 2]
|
||||
```
|
||||
|
||||
Ranging over a non-positive integer executes the block zero times.
|
||||
|
||||
{{% include "/_common/functions/go-template/text-template.md" %}}
|
||||
|
||||
[`break`]: /functions/go-template/break/
|
||||
[`continue`]: /functions/go-template/continue/
|
||||
[`else`]: /functions/go-template/else/
|
||||
[`seq`]: /functions/collections/seq/
|
||||
|
||||
@@ -23,7 +23,7 @@ A `return` statement without a value returns an empty string of type `template.H
|
||||
|
||||
By way of example, let's create a partial template that _renders_ HTML, describing whether the given number is odd or even:
|
||||
|
||||
```go-html-template {file="layouts/partials/odd-or-even.html"}
|
||||
```go-html-template {file="layouts/_partials/odd-or-even.html"}
|
||||
{{ if math.ModBool . 2 }}
|
||||
<p>{{ . }} is even</p>
|
||||
{{ else }}
|
||||
@@ -39,7 +39,7 @@ When called, the partial renders HTML:
|
||||
|
||||
Instead of rendering HTML, let's create a partial that _returns_ a boolean value, reporting whether the given number is even:
|
||||
|
||||
```go-html-template {file="layouts/partials/is-even.html"}
|
||||
```go-html-template {file="layouts/_partials/is-even.html"}
|
||||
{{ return math.ModBool . 2 }}
|
||||
```
|
||||
|
||||
@@ -60,8 +60,6 @@ Hugo renders:
|
||||
<p>42 is even</p>
|
||||
```
|
||||
|
||||
See additional examples in the [partial templates] section.
|
||||
|
||||
## Usage
|
||||
|
||||
> [!note]
|
||||
@@ -71,7 +69,7 @@ A partial that returns a value must contain only one `return` statement, placed
|
||||
|
||||
For example:
|
||||
|
||||
```go-html-template {file="layouts/partials/is-even.html"}
|
||||
```go-html-template {file="layouts/_partials/is-even.html"}
|
||||
{{ $result := false }}
|
||||
{{ if math.ModBool . 2 }}
|
||||
{{ $result = "even" }}
|
||||
@@ -84,7 +82,7 @@ For example:
|
||||
> [!note]
|
||||
> The construct below is incorrect; it contains more than one `return` statement.
|
||||
|
||||
```go-html-template {file="layouts/partials/do-not-do-this.html"}
|
||||
```go-html-template {file="layouts/_partials/do-not-do-this.html"}
|
||||
{{ if math.ModBool . 2 }}
|
||||
{{ return "even" }}
|
||||
{{ else }}
|
||||
@@ -92,5 +90,4 @@ For example:
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
[partial templates]: /templates/partial/#returning-a-value-from-a-partial
|
||||
[text/template package]: https://pkg.go.dev/text/template
|
||||
|
||||
@@ -10,16 +10,7 @@ params:
|
||||
signatures: ['template NAME [CONTEXT]']
|
||||
---
|
||||
|
||||
Use the `template` function to execute [embedded templates]. For example:
|
||||
|
||||
```go-html-template
|
||||
{{ range (.Paginate .Pages).Pages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ end }}
|
||||
{{ template "_internal/pagination.html" . }}
|
||||
```
|
||||
|
||||
You can also use the `template` function to execute a defined template:
|
||||
Use the `template` function to execute a defined template:
|
||||
|
||||
```go-html-template
|
||||
{{ template "foo" (dict "answer" 42) }}
|
||||
@@ -29,18 +20,23 @@ You can also use the `template` function to execute a defined template:
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
The example above can be rewritten using an [inline partial] template:
|
||||
The example above can be rewritten using an inline partial template:
|
||||
|
||||
```go-html-template
|
||||
{{ partial "inline/foo.html" (dict "answer" 42) }}
|
||||
|
||||
{{ define "partials/inline/foo.html" }}
|
||||
{{ define "_partials/inline/foo.html" }}
|
||||
{{ printf "The answer is %v." .answer }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
The key distinctions between the preceding two examples are:
|
||||
|
||||
1. Inline partials are globally scoped. That means that an inline partial defined in _one_ template may be called from _any_ template.
|
||||
2. Leveraging the [`partialCached`] function when calling an inline partial allows for performance optimization through result caching.
|
||||
3. An inline partial can [`return`] a value of any data type instead of rendering a string.
|
||||
|
||||
{{% include "/_common/functions/go-template/text-template.md" %}}
|
||||
|
||||
[`partial`]: /functions/partials/include/
|
||||
[inline partial]: /templates/partial/#inline-partials
|
||||
[embedded templates]: /templates/embedded/
|
||||
[`partialCached`]: /functions/partials/includecached/
|
||||
[`return`]: /functions/go-template/return/
|
||||
|
||||
@@ -11,5 +11,5 @@ params:
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ hugo.Generator }} → <meta name="generator" content="Hugo 0.144.2">
|
||||
{{ hugo.Generator }} → <meta name="generator" content="Hugo 0.147.9">
|
||||
```
|
||||
|
||||
@@ -11,5 +11,5 @@ params:
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ hugo.Version }} → 0.144.2
|
||||
{{ hugo.Version }} → 0.147.9
|
||||
```
|
||||
|
||||
@@ -68,7 +68,7 @@ Specify `level`, `scale`, and `targetDir` as needed to achieve the desired resul
|
||||
|
||||
To include a QR code that points to the `Permalink` of the current page:
|
||||
|
||||
```go-html-template {file="layouts/_default/single.html"}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ with images.QR .Permalink }}
|
||||
<img
|
||||
src="{{ .RelPermalink }}"
|
||||
|
||||
@@ -18,6 +18,10 @@ alignx
|
||||
: {{< new-in 0.141.0 />}}
|
||||
: (`string`) The horizontal alignment of the text relative to the horizontal offset, one of `left`, `center`, or `right`. Default is `left`.
|
||||
|
||||
aligny
|
||||
: {{< new-in 0.147.0 />}}
|
||||
: (`string`) The vertical alignment of the text relative to the vertical offset, one of `top`, `center`, or `bottom`. Default is `top`.
|
||||
|
||||
color
|
||||
: (`string`) The font color, either a 3-digit or 6-digit hexadecimal color code. Default is `#ffffff` (white).
|
||||
|
||||
@@ -71,6 +75,7 @@ Create the filter, centering the text horizontally and vertically:
|
||||
{{ with $r = resources.Get $imagePath }}
|
||||
{{ $opts := dict
|
||||
"alignx" "center"
|
||||
"aligny" "center"
|
||||
"color" "#fbfaf5"
|
||||
"font" $font
|
||||
"linespacing" 8
|
||||
|
||||
@@ -20,18 +20,18 @@ The `js.Build` function uses the [evanw/esbuild] package to:
|
||||
|
||||
```go-html-template
|
||||
{{ with resources.Get "js/main.js" }}
|
||||
{{ $opts := dict
|
||||
"minify" hugo.IsProduction
|
||||
"sourceMap" (cond hugo.IsProduction "" "external")
|
||||
{{$opts := dict
|
||||
"minify" (not hugo.IsDevelopment)
|
||||
"sourceMap" (cond hugo.IsDevelopment "external" "")
|
||||
"targetPath" "js/main.js"
|
||||
}}
|
||||
{{ with . | js.Build $opts }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ if hugo.IsDevelopment }}
|
||||
<script src="{{ .RelPermalink }}"></script>
|
||||
{{ else }}
|
||||
{{ with . | fingerprint }}
|
||||
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<script src="{{ .RelPermalink }}"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -24,6 +24,6 @@ A more practical example is to fill in the missing translations from the other l
|
||||
```sh
|
||||
{{ $pages := .Site.RegularPages }}
|
||||
{{ range .Site.Home.Translations }}
|
||||
{{ $pages = $pages | lang.Merge .Site.RegularPages }}
|
||||
{{ $pages = $pages | lang.Merge .Site.RegularPages }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
@@ -12,14 +12,14 @@ params:
|
||||
|
||||
The counter is global for both monolingual and multilingual sites, and its initial value for each build is 1.
|
||||
|
||||
```go-html-template
|
||||
{{ warnf "single.html called %d times" math.Counter }}
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ warnf "page.html called %d times" math.Counter }}
|
||||
```
|
||||
|
||||
```sh
|
||||
WARN single.html called 1 times
|
||||
WARN single.html called 2 times
|
||||
WARN single.html called 3 times
|
||||
```text
|
||||
WARN page.html called 1 times
|
||||
WARN page.html called 2 times
|
||||
WARN page.html called 3 times
|
||||
```
|
||||
|
||||
Use this function to:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: math.MaxInt64
|
||||
description: Returns the maximum value for a signed 64-bit integer.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: int64
|
||||
signatures: [math.MaxInt64]
|
||||
---
|
||||
|
||||
{{< new-in 0.147.3 />}}
|
||||
|
||||
```go-html-template
|
||||
{{ math.MaxInt64 }} → 9223372036854775807
|
||||
```
|
||||
|
||||
This function is helpful for simulating a loop that continues indefinitely until a break condition is met. For example:
|
||||
|
||||
```go-html-template
|
||||
{{ range math.MaxInt64 }}
|
||||
{{ if eq . 42 }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
@@ -39,7 +39,7 @@ To inspect the data structure:
|
||||
To list the GET and POST operations for each of the API paths:
|
||||
|
||||
```go-html-template
|
||||
{{ range $path, $details := $api.Paths }}
|
||||
{{ range $path, $details := $api.Paths.Map }}
|
||||
<p>{{ $path }}</p>
|
||||
<dl>
|
||||
{{ with $details.Get }}
|
||||
@@ -54,6 +54,11 @@ To list the GET and POST operations for each of the API paths:
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> The unmarshaled data structure is created with [`kin-openapi`](https://github.com/getkin/kin-openapi). Many fields are structs or pointers (not maps), and therefore require accessors or other methods for indexing and iteration.
|
||||
> For example, prior to [`kin-openapi` v0.122.0](https://github.com/getkin/kin-openapi#v01220) / [Hugo v0.121.0](https://github.com/gohugoio/hugo/releases/tag/v0.121.0), `Paths` was a map (so `.Paths` was iterable) and it is now a pointer (and requires the `.Paths.Map` accessor, as in the example above).
|
||||
> See the [`kin-openapi` godoc for OpenAPI 3](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3) for full type definitions.
|
||||
|
||||
Hugo renders this to:
|
||||
|
||||
```html
|
||||
|
||||
@@ -19,7 +19,7 @@ In this example we have three partial templates:
|
||||
|
||||
```text
|
||||
layouts/
|
||||
└── partials/
|
||||
└── _partials/
|
||||
├── average.html
|
||||
├── breadcrumbs.html
|
||||
└── footer.html
|
||||
|
||||
@@ -16,7 +16,7 @@ Without a [`return`] statement, the `partialCached` function returns a string of
|
||||
The `partialCached` function can offer significant performance gains for complex templates that don't need to be re-rendered on every invocation.
|
||||
|
||||
> [!note]
|
||||
> Each Site (or language) has its own `partialCached` cache, so each site will execute a partial once.
|
||||
> Each site (or language) has its own `partialCached` cache, so each site will execute a partial once.
|
||||
>
|
||||
> Hugo renders pages in parallel, and will render the partial more than once with concurrent calls to the `partialCached` function. After Hugo caches the rendered partial, new pages entering the build pipeline will use the cached result.
|
||||
|
||||
@@ -28,7 +28,7 @@ Here is the simplest usage:
|
||||
|
||||
Pass additional arguments to `partialCached` to create variants of the cached partial. For example, if you have a complex partial that should be identical when rendered for pages within the same section, use a variant based on section so that the partial is only rendered once per section:
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html"}
|
||||
```go-html-template {file="layouts/baseof.html"}
|
||||
{{ partialCached "footer.html" . .Section }}
|
||||
```
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ Let's say you need to publish a file named "site.json" in the root of your `publ
|
||||
|
||||
```json
|
||||
{
|
||||
"build_date": "2025-01-16T19:14:41-08:00",
|
||||
"hugo_version": "0.141.0",
|
||||
"last_modified": "2025-01-16T19:14:46-08:00"
|
||||
"build_date": "2025-05-03T19:14:41-08:00",
|
||||
"hugo_version": "0.147.9",
|
||||
"last_modified": "2025-05-03T19:14:46-08:00"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: templates.Current
|
||||
description: Returns information about the currently executing template.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: tpl.CurrentTemplateInfo
|
||||
signatures: [templates.Current]
|
||||
---
|
||||
|
||||
> [!note]
|
||||
> This function is experimental and subject to change.
|
||||
|
||||
{{< new-in 0.146.0 />}}
|
||||
|
||||
The `templates.Current` function provides introspection capabilities, allowing you to access details about the currently executing templates. This is useful for debugging complex template hierarchies and understanding the flow of execution during rendering.
|
||||
|
||||
## Methods
|
||||
|
||||
Ancestors
|
||||
: (`tpl.CurrentTemplateInfos`) Returns a slice containing information about each template in the current execution chain, starting from the parent of the current template and going up towards the initial template called. It excludes any base template applied via `define` and `block`. You can chain the `Reverse` method to this result to get the slice in chronological execution order.
|
||||
|
||||
Base
|
||||
: (`tpl.CurrentTemplateInfoCommonOps`) Returns an object representing the base template that was applied to the current template, if any. This may be `nil`.
|
||||
|
||||
Filename
|
||||
: (`string`) Returns the absolute path of the current template. This will be empty for embedded templates.
|
||||
|
||||
Name
|
||||
: (`string`) Returns the name of the current template. This is usually the path relative to the layouts directory.
|
||||
|
||||
Parent
|
||||
: (`tpl.CurrentTemplateInfo`) Returns an object representing the parent of the current template, if any. This may be `nil`.
|
||||
|
||||
## Examples
|
||||
|
||||
The examples below help visualize template execution and require a `debug` parameter set to `true` in your site configuration:
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
[params]
|
||||
debug = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
### Boundaries
|
||||
|
||||
To visually mark where a template begins and ends execution:
|
||||
|
||||
```go-html-template {file="layouts/page.html"}
|
||||
{{ define "main" }}
|
||||
{{ if site.Params.debug }}
|
||||
<div class="debug">[entering {{ templates.Current.Filename }}]</div>
|
||||
{{ end }}
|
||||
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
|
||||
{{ if site.Params.debug }}
|
||||
<div class="debug">[leaving {{ templates.Current.Filename }}]</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### Call stack
|
||||
|
||||
To display the chain of templates that led to the current one, create a partial template that iterates through its ancestors:
|
||||
|
||||
```go-html-template {file="layouts/_partials/template-call-stack.html" copy=true}
|
||||
{{ with templates.Current }}
|
||||
<div class="debug">
|
||||
{{ range .Ancestors }}
|
||||
{{ .Filename }}<br>
|
||||
{{ with .Base }}
|
||||
{{ .Filename }}<br>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Then call the partial from any template:
|
||||
|
||||
```go-html-template {file="layouts/_partials/footer/copyright.html" copy=true}
|
||||
{{ if site.Params.debug }}
|
||||
{{ partial "template-call-stack.html" . }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
The rendered template stack would look something like this:
|
||||
|
||||
```text
|
||||
/home/user/project/layouts/_partials/footer/copyright.html
|
||||
/home/user/project/themes/foo/layouts/_partials/footer.html
|
||||
/home/user/project/layouts/page.html
|
||||
/home/user/project/themes/foo/layouts/baseof.html
|
||||
```
|
||||
|
||||
To reverse the order of the entries, chain the `Reverse` method to the `Ancestors` method:
|
||||
|
||||
```go-html-template {file="layouts/_partials/template-call-stack.html" copy=true}
|
||||
{{ with templates.Current }}
|
||||
<div class="debug">
|
||||
{{ range .Ancestors.Reverse }}
|
||||
{{ with .Base }}
|
||||
{{ .Filename }}<br>
|
||||
{{ end }}
|
||||
{{ .Filename }}<br>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### VS Code
|
||||
|
||||
To render links that, when clicked, will open the template in Microsoft Visual Studio Code, create a partial template with anchor elements that use the `vscode` URI scheme:
|
||||
|
||||
```go-html-template {file="layouts/_partials/template-open-in-vs-code.html" copy=true}
|
||||
{{ with templates.Current.Parent }}
|
||||
<div class="debug">
|
||||
<a href="vscode://file/{{ .Filename }}">{{ .Name }}</a>
|
||||
{{ with .Base }}
|
||||
<a href="vscode://file/{{ .Filename }}">{{ .Name }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Then call the partial from any template:
|
||||
|
||||
```go-html-template {file="layouts/page.html" copy=true}
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
|
||||
{{ if site.Params.debug }}
|
||||
{{ partial "template-open-in-vs-code.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
Use the same approach to render the entire call stack as links:
|
||||
|
||||
```go-html-template {file="layouts/_partials/template-call-stack.html" copy=true}
|
||||
{{ with templates.Current }}
|
||||
<div class="debug">
|
||||
{{ range .Ancestors }}
|
||||
<a href="vscode://file/{{ .Filename }}">{{ .Filename }}</a><br>
|
||||
{{ with .Base }}
|
||||
<a href="vscode://file/{{ .Filename }}">{{ .Filename }}</a><br>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
```
|
||||
@@ -14,33 +14,34 @@ aliases: [/functions/templates.defer]
|
||||
{{< new-in 0.128.0 />}}
|
||||
|
||||
> [!note]
|
||||
> This feature is meant to be used in the main page layout files/templates, and has undefined behavior when used from shortcodes, partials or render hook templates. See [this issue](https://github.com/gohugoio/hugo/issues/13492#issuecomment-2734700391) for more info.
|
||||
> This feature should only be used in the main page template, typically `layouts/baseof.html`. Using it in shortcodes, partials, or render hook templates may lead to unpredictable results. For further details, please refer to [this issue].
|
||||
|
||||
[this issue]: https://github.com/gohugoio/hugo/issues/13492#issuecomment-2734700391
|
||||
|
||||
In some rare use cases, you may need to defer the execution of a template until after all sites and output formats have been rendered. One such example could be [TailwindCSS](/functions/css/tailwindcss/) using the output of [hugo_stats.json](/configuration/build/) to determine which classes and other HTML identifiers are being used in the final output:
|
||||
|
||||
```go-html-template
|
||||
{{ with (templates.Defer (dict "key" "global")) }}
|
||||
{{ $t := debug.Timer "tailwindcss" }}
|
||||
{{ with resources.Get "css/styles.css" }}
|
||||
{{ $opts := dict
|
||||
"inlineImports" true
|
||||
"optimize" hugo.IsProduction
|
||||
}}
|
||||
{{ with . | css.TailwindCSS $opts }}
|
||||
{{ if hugo.IsDevelopment }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" />
|
||||
{{ else }}
|
||||
{{ with . | minify | fingerprint }}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{{ .RelPermalink }}"
|
||||
integrity="{{ .Data.Integrity }}"
|
||||
crossorigin="anonymous" />
|
||||
{{ end }}
|
||||
```go-html-template {file="layouts/baseof.html" copy=true}
|
||||
<head>
|
||||
...
|
||||
{{ with (templates.Defer (dict "key" "global")) }}
|
||||
{{ partial "css.html" . }}
|
||||
{{ end }}
|
||||
...
|
||||
</head>
|
||||
```
|
||||
|
||||
```go-html-template {file="layouts/_partials/css.html" copy=true}
|
||||
{{ with resources.Get "css/main.css" }}
|
||||
{{ $opts := dict "minify" (not hugo.IsDevelopment) }}
|
||||
{{ with . | css.TailwindCSS $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 }}
|
||||
{{ $t.Stop }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
@@ -52,19 +53,23 @@ In some rare use cases, you may need to defer the execution of a template until
|
||||
For the above to work well when running the server (or `hugo -w`), you want to have a configuration similar to this:
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
[build]
|
||||
[build.buildStats]
|
||||
enable = true
|
||||
[[build.cachebusters]]
|
||||
source = 'assets/notwatching/hugo_stats\.json'
|
||||
target = 'css'
|
||||
[[build.cachebusters]]
|
||||
source = '(postcss|tailwind)\.config\.js'
|
||||
target = 'css'
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = "hugo_stats.json"
|
||||
target = "assets/notwatching/hugo_stats.json"
|
||||
disableWatch = true
|
||||
[build.buildStats]
|
||||
enable = true
|
||||
[[build.cachebusters]]
|
||||
source = "assets/notwatching/hugo_stats\\.json"
|
||||
target = "styles\\.css"
|
||||
[[build.cachebusters]]
|
||||
source = "(postcss|tailwind)\\.config\\.js"
|
||||
target = "css"
|
||||
[[module.mounts]]
|
||||
source = 'assets'
|
||||
target = 'assets'
|
||||
[[module.mounts]]
|
||||
disableWatch = true
|
||||
source = 'hugo_stats.json'
|
||||
target = 'assets/notwatching/hugo_stats.json'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
## Options
|
||||
|
||||
@@ -17,7 +17,7 @@ Use the `templates.Exists` function with dynamic template paths:
|
||||
|
||||
```go-html-template
|
||||
{{ $partialPath := printf "headers/%s.html" .Type }}
|
||||
{{ if templates.Exists ( printf "partials/%s" $partialPath ) }}
|
||||
{{ if templates.Exists ( printf "_partials/%s" $partialPath ) }}
|
||||
{{ partial $partialPath . }}
|
||||
{{ else }}
|
||||
{{ partial "headers/default.html" . }}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: time.In
|
||||
description: Returns the given date/time as represented in the specified IANA time zone.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: time.Time
|
||||
signatures: [time.In TIMEZONE INPUT]
|
||||
---
|
||||
|
||||
{{< new-in 0.146.0 />}}
|
||||
|
||||
The `time.In` function returns the given date/time as represented in the specified [IANA](g) time zone.
|
||||
|
||||
- If the time zone is an empty string or `UTC`, the time is returned in [UTC](g).
|
||||
- If the time zone is `Local`, the time is returned in the system's local time zone.
|
||||
- Otherwise, the time zone must be a valid IANA [time zone name].
|
||||
|
||||
[time zone name]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||
|
||||
```go-html-template
|
||||
{{ $layout := "2006-01-02T15:04:05-07:00" }}
|
||||
{{ $t := time.AsTime "2025-03-31T14:45:00-00:00" }}
|
||||
|
||||
{{ $t | time.In "America/Denver" | time.Format $layout }} → 2025-03-31T08:45:00-06:00
|
||||
{{ $t | time.In "Australia/Adelaide" | time.Format $layout }} → 2025-04-01T01:15:00+10:30
|
||||
{{ $t | time.In "Europe/Oslo" | time.Format $layout }} → 2025-03-31T16:45:00+02:00
|
||||
```
|
||||
@@ -6,7 +6,7 @@ keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: types.Result[template.HTML]
|
||||
returnType: template.HTML
|
||||
signatures: ['transform.ToMath INPUT [OPTIONS]']
|
||||
aliases: [/functions/tomath]
|
||||
---
|
||||
@@ -58,12 +58,25 @@ minRuleThickness
|
||||
: (`float`) The minimum thickness of the fraction lines in `em`. Default is `0.04`.
|
||||
|
||||
output
|
||||
: (`string`). Determines the markup language of the output, one of `html`, `mathml`, or `htmlAndMathml`. Default is `mathml`.
|
||||
: (`string`) Determines the markup language of the output, one of `html`, `mathml`, or `htmlAndMathml`. Default is `mathml`.
|
||||
|
||||
With `html` and `htmlAndMathml` you must include the KaTeX style sheet within the `head` element of your base template.
|
||||
|
||||
```html
|
||||
<link href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css" rel="stylesheet">
|
||||
|
||||
strict
|
||||
: {{< new-in 0.147.6 />}}
|
||||
: (`string`) Controls how KaTeX handles LaTeX features that offer convenience but aren't officially supported, one of `error`, `ignore`, or `warn`. Default is `error`.
|
||||
|
||||
- `error`: Throws an error when convenient, unsupported LaTeX features are encountered.
|
||||
- `ignore`: Allows convenient, unsupported LaTeX features without any feedback.
|
||||
- `warn`: {{< new-in 0.147.7 />}} Emits a warning when convenient, unsupported LaTeX features are encountered.
|
||||
|
||||
: The `newLineInDisplayMode` error code, which flags the use of `\\`
|
||||
or `\newline` in display mode outside an array or tabular environment, is
|
||||
intentionally designed not to throw an error, despite this behavior
|
||||
being questionable.
|
||||
|
||||
throwOnError
|
||||
: (`bool`) Whether to throw a `ParseError` when KaTeX encounters an unsupported command or invalid LaTeX. Default is `true`.
|
||||
@@ -96,13 +109,13 @@ inline = [['\(', '\)']]
|
||||
{{< /code-toggle >}}
|
||||
|
||||
> [!note]
|
||||
> The configuration above precludes the use of the `$...$` delimiter pair for inline equations. Although you can add this delimiter pair to the configuration, you will need to double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting.
|
||||
> The configuration above precludes the use of the `$...$` delimiter pair for inline equations. Although you can add this delimiter pair to the configuration, you must double-escape the `$` symbol when used outside of math contexts to avoid unintended formatting.
|
||||
|
||||
### Step 2
|
||||
|
||||
Create a [passthrough render hook] to capture and render the LaTeX markup.
|
||||
|
||||
```go-html-template {file="layouts/_default/_markup/render-passthrough.html" copy=true}
|
||||
```go-html-template {file="layouts/_markup/render-passthrough.html" copy=true}
|
||||
{{- $opts := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") }}
|
||||
{{- with try (transform.ToMath .Inner $opts) }}
|
||||
{{- with .Err }}
|
||||
@@ -118,11 +131,11 @@ Create a [passthrough render hook] to capture and render the LaTeX markup.
|
||||
|
||||
In your base template, conditionally include the KaTeX CSS within the head element.
|
||||
|
||||
```go-html-template {file="layouts/_default/baseof.html" copy=true}
|
||||
```go-html-template {file="layouts/baseof.html" copy=true}
|
||||
<head>
|
||||
{{ $noop := .WordCount }}
|
||||
{{ if .Page.Store.Get "hasMath" }}
|
||||
<link href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css" rel="stylesheet">
|
||||
{{ end }}
|
||||
</head>
|
||||
```
|
||||
|
||||
@@ -114,12 +114,14 @@ A remote resource is a file on a remote server, accessible via HTTP or HTTPS.
|
||||
>
|
||||
> `{{ $data = .Content | transform.Unmarshal }}`
|
||||
|
||||
## Options
|
||||
## Working with CSV
|
||||
|
||||
### Options
|
||||
|
||||
When unmarshaling a CSV file, provide an optional map of options.
|
||||
|
||||
delimiter
|
||||
: (`string`) The delimiter used, default is `,`.
|
||||
: (`string`) The delimiter used. Default is `,`.
|
||||
|
||||
comment
|
||||
: (`string`) The comment character used in the CSV. If set, lines beginning with the comment character without preceding whitespace are ignored.
|
||||
@@ -128,8 +130,85 @@ lazyQuotes
|
||||
: {{< new-in 0.122.0 />}}
|
||||
: (`bool`) Whether to allow a quote in an unquoted field, or to allow a non-doubled quote in a quoted field. Default is `false`.
|
||||
|
||||
targetType
|
||||
: {{< new-in 0.146.7 />}}
|
||||
: (`string`) The target data type, either `slice` or `map`. Default is `slice`.
|
||||
|
||||
### Examples
|
||||
|
||||
The examples below use this CSV file:
|
||||
|
||||
```csv
|
||||
"name","type","breed","age"
|
||||
"Spot","dog","Collie",3
|
||||
"Rover","dog","Boxer",5
|
||||
"Felix","cat","Calico",7
|
||||
```
|
||||
|
||||
To render an HTML table from a CSV file:
|
||||
|
||||
```go-html-template
|
||||
{{ $csv := "a;b;c" | transform.Unmarshal (dict "delimiter" ";") }}
|
||||
{{ $data := slice }}
|
||||
{{ $file := "pets.csv" }}
|
||||
{{ with or (.Resources.Get $file) (resources.Get $file) }}
|
||||
{{ $opts := dict "targetType" "slice" }}
|
||||
{{ $data = transform.Unmarshal $opts . }}
|
||||
{{ end }}
|
||||
|
||||
{{ with $data }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{{ range index . 0 }}
|
||||
<th>{{ . }}</th>
|
||||
{{ end }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range . | after 1 }}
|
||||
<tr>
|
||||
{{ range . }}
|
||||
<td>{{ . }}</td>
|
||||
{{ end }}
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
To extract a subset of the data, or to sort the data, unmarshal to a map instead of a slice:
|
||||
|
||||
```go-html-template
|
||||
{{ $data := dict }}
|
||||
{{ $file := "pets.csv" }}
|
||||
{{ with or (.Resources.Get $file) (resources.Get $file) }}
|
||||
{{ $opts := dict "targetType" "map" }}
|
||||
{{ $data = transform.Unmarshal $opts . }}
|
||||
{{ end }}
|
||||
|
||||
{{ with sort (where $data "type" "dog") "name" "asc" }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>type</th>
|
||||
<th>breed</th>
|
||||
<th>age</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range . }}
|
||||
<tr>
|
||||
<td>{{ .name }}</td>
|
||||
<td>{{ .type }}</td>
|
||||
<td>{{ .breed }}</td>
|
||||
<td>{{ .age }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
## Working with XML
|
||||
|
||||
@@ -31,7 +31,7 @@ For example:
|
||||
|
||||
When using `transform.XMLEscape` in a template rendered by Go's [html/template] package, declare the string to be safe HTML to avoid double escaping. For example, in an RSS template:
|
||||
|
||||
```xml {file="layouts/_default/rss.xml"}
|
||||
```xml {file="layouts/rss.xml"}
|
||||
<description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
|
||||
```
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ This course covers the basics of using the Hugo static site generator. Work your
|
||||
1. [Content Organization](https://www.giraffeacademy.com/static-site-generators/hugo/content-organization/)
|
||||
1. [Front Matter](https://www.giraffeacademy.com/static-site-generators/hugo/front-matter/)
|
||||
1. [Archetypes](https://www.giraffeacademy.com/static-site-generators/hugo/archetypes/)
|
||||
1. [Shortcodes](https://www.giraffeacademy.com/static-site-generators/hugo/archetypes/)
|
||||
1. [Shortcodes](https://www.giraffeacademy.com/static-site-generators/hugo/shortcodes/)
|
||||
1. [Taxonomies](https://www.giraffeacademy.com/static-site-generators/hugo/taxonomies/)
|
||||
1. [Template Basics](https://www.giraffeacademy.com/static-site-generators/hugo/introduction-to-templates/)
|
||||
1. [List Page Templates](https://www.giraffeacademy.com/static-site-generators/hugo/list-page-templates/)
|
||||
|
||||
@@ -44,9 +44,9 @@ version: 1
|
||||
env:
|
||||
variables:
|
||||
# Application versions
|
||||
DART_SASS_VERSION: 1.85.0
|
||||
GO_VERSION: 1.23.3
|
||||
HUGO_VERSION: 0.144.2
|
||||
DART_SASS_VERSION: 1.89.2
|
||||
GO_VERSION: 1.24.2
|
||||
HUGO_VERSION: 0.147.9
|
||||
# Time zone
|
||||
TZ: America/Los_Angeles
|
||||
# Cache
|
||||
|
||||
@@ -51,9 +51,11 @@ git remote add origin https://codeberg.org/<YourUsername>/pages.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
## Automated deployment
|
||||
## Automated deployment using Woodpecker CI
|
||||
|
||||
In order to automatically deploy your Hugo website, you need to have or [request] access to Codeberg's CI, as well as add a `.woodpecker.yaml` file in the root of your project. A template and additional instructions are available in the official [examples repository].
|
||||
There are two methods you can use to deploy your Hugo website to Codeberg automatically. These are: Woodpecker CI and Forgejo Actions.
|
||||
|
||||
To use Codeberg's Woodpecker CI, you need to have or [request] access to it, as well as add a `.woodpecker.yaml` file in the root of your project. A template and additional instructions are available in the official [examples repository].
|
||||
|
||||
[request]: https://codeberg.org/Codeberg-e.V./requests/issues/new?template=ISSUE_TEMPLATE%2fWoodpecker-CI.yaml
|
||||
[examples repository]: https://codeberg.org/Codeberg-CI/examples/src/branch/main/Hugo/.woodpecker.yaml
|
||||
@@ -74,7 +76,178 @@ git remote add origin https://codeberg.org/<YourUsername>/<YourWebsite>.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
Your project will then be built and deployed by Codeberg's CI.
|
||||
Your project will then be built and deployed by Codeberg's Woodpecker CI.
|
||||
|
||||
## Automated deployment using Forgejo Actions
|
||||
|
||||
The other way to deploy your website to Codeberg pages automatically is to make use of Forgejo Actions. Actions need a _runner_ to work, and Codeberg has [great documentation] on how to set one up yourself. However, Codeberg provides a [handful of humble runners] themselves (they say this feature is in "open alpha"), which actually seem powerful enough to build at least relatively simple websites.
|
||||
|
||||
[great documentation]: https://docs.codeberg.org/ci/actions/
|
||||
[handful of humble runners]: https://codeberg.org/actions/meta
|
||||
|
||||
To deploy your website this way, you don't need to request any access. All you need to do is enable actions in your repository settings (see the documentation link above) and add a workflow configuration file, for example, `hugo.yaml`, to the `.forgejo/workflows/` directory in your website's source repository.
|
||||
|
||||
Two examples of such a file are provided below.
|
||||
|
||||
The first file should work for automatically building your website from the source branch (`main` in this case) and committing the result to the target branch (`pages`). Without changes, this file should make your built website accessible under `https://<YourUsername>.codeberg.page/<YourWebsiteRepositoryName>/`:
|
||||
|
||||
```yaml {file=".forgejo/workflows/hugo.yaml" copy=true}
|
||||
name: Deploy Hugo site to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches:
|
||||
# If you want to build from a different branch, change it here.
|
||||
- main
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# You can find the list of available runners on https://codeberg.org/actions/meta, or run one yourself.
|
||||
runs-on: codeberg-tiny-lazy
|
||||
container:
|
||||
# Specify "hugomods/hugo:exts" if you want to always use the latest version of Hugo for building.
|
||||
image: "hugomods/hugo:exts-0.147.9"
|
||||
steps:
|
||||
- name: Clone the repository
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Generate static files with Hugo
|
||||
env:
|
||||
# For maximum backward compatibility with Hugo modules
|
||||
HUGO_ENVIRONMENT: production
|
||||
HUGO_ENV: production
|
||||
run: |
|
||||
hugo \
|
||||
--gc \
|
||||
--minify
|
||||
- name: Upload generated files
|
||||
uses: https://code.forgejo.org/actions/upload-artifact@v3
|
||||
with:
|
||||
name: Generated files
|
||||
path: public/
|
||||
deploy:
|
||||
needs: [ build ]
|
||||
runs-on: codeberg-tiny-lazy
|
||||
steps:
|
||||
- name: Clone the repository
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Checkout the target branch and clean it up
|
||||
# If you want to commit to a branch other than "pages", change the two references below, as well as the reference in the last step.
|
||||
run: |
|
||||
git checkout pages || git switch --orphan pages && \
|
||||
rm -Rfv $(ls -A | egrep -v '^(\.git|LICENSE)$')
|
||||
- name: Download generated files
|
||||
uses: https://code.forgejo.org/actions/download-artifact@v3
|
||||
with:
|
||||
name: Generated files
|
||||
- name: Publish the website
|
||||
run: |
|
||||
git config user.email codeberg-ci && \
|
||||
git config user.name "Codeberg CI" && \
|
||||
git add . && \
|
||||
git commit --allow-empty --message "Codeberg build for ${GITHUB_SHA}" && \
|
||||
git push origin pages
|
||||
```
|
||||
|
||||
The second file implements a more complex scenario: having your website sources in one repository and the resulting static website in another repository (in this case, `pages`). If you want Codeberg to make your website available at the root of your pages subdomain (`https://<YourUsername>.codeberg.page/`), you have to push that website to the default branch of your repository named `pages`.
|
||||
|
||||
Since this action involves more than one repository, it will require a bit more preparation:
|
||||
1. Create the target repository. Name it `pages`.
|
||||
2. Generate a new SSH key. *Do not* use any of your own SSH keys for this, but generate one for this specific task only. On Linux, BSD, and, likely, other operating systems, you can open a terminal emulator and run the following command to generate the key:
|
||||
```shell
|
||||
ssh-keygen -f pagesbuild -P ""
|
||||
```
|
||||
This will generate two files in your current directory: `pagesbuild` (private key) and `pagesbuild.pub` (public key).
|
||||
3. Add the newly generated public key as a deploy key to your `pages` repository: navigate to its Settings, click on "Deploy keys" in the left menu, click the "Add deploy key" button, give it a name (e.g. "Actions deploy key"), paste the contents of the **public** key file (`pagesbuild.pub`) to the Content field, tick the "Enable write access" checkbox, then submit the form.
|
||||
4. Navigate back to your source repository settings, expand the "Actions" menu and click on "Secrets". Then click "Add Secret", enter "DEPLOY_KEY" as the secret name and paste the contents of the newly generated **private** key file (`pagesbuild`) into the Value field.
|
||||
5. Navigate to the "Variables" submenu of the "Actions" menu and add the following variables:
|
||||
|
||||
| Name | Value |
|
||||
|---------------------|----------------------------------------------------------------------------------|
|
||||
| `TARGET_REPOSITORY` | `<YourUsername>/pages` |
|
||||
| `TARGET_BRANCH` | `main` (enter the default branch name of the `pages` repo here) |
|
||||
| `SSH_KNOWN_HOSTS` | (paste the output you get by running `ssh-keyscan codeberg.org` in the terminal) |
|
||||
|
||||
Once you've done all of the above, commit the following file to your repository as `.forgejo/workflows/hugo.yaml`. As you can see, the `deploy` job of this workflow is slightly different from the file above:
|
||||
|
||||
```yaml {file=".forgejo/workflows/hugo.yaml" copy=true}
|
||||
name: Deploy Hugo site to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches:
|
||||
# If you want to build from a different branch, change it here.
|
||||
- main
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: codeberg-tiny-lazy
|
||||
container:
|
||||
# Specify "hugomods/hugo:exts" if you want to always use the latest version of Hugo for building.
|
||||
image: "hugomods/hugo:exts-0.147.9"
|
||||
steps:
|
||||
- name: Clone the repository
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Generate static files with Hugo
|
||||
env:
|
||||
# For maximum backward compatibility with Hugo modules
|
||||
HUGO_ENVIRONMENT: production
|
||||
HUGO_ENV: production
|
||||
run: |
|
||||
hugo \
|
||||
--gc \
|
||||
--minify \
|
||||
--source ${PWD} \
|
||||
--destination ${PWD}/public/
|
||||
- name: Upload generated files
|
||||
uses: https://code.forgejo.org/actions/upload-artifact@v3
|
||||
with:
|
||||
name: Generated files
|
||||
path: public/
|
||||
deploy:
|
||||
needs: [ build ]
|
||||
runs-on: codeberg-tiny-lazy
|
||||
steps:
|
||||
- name: Clone the repository
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ vars.TARGET_REPOSITORY }}
|
||||
ref: ${{ vars.TARGET_BRANCH }}
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||
ssh-known-hosts: ${{ vars.SSH_KNOWN_HOSTS }}
|
||||
- name: Remove all files
|
||||
run: |
|
||||
rm -Rfv $(ls -A | egrep -v '^(\.git|LICENSE)$')
|
||||
- name: Download generated files
|
||||
uses: https://code.forgejo.org/actions/download-artifact@v3
|
||||
with:
|
||||
name: Generated files
|
||||
- name: Commit and push the website
|
||||
run: |
|
||||
git config user.email codeberg-ci && \
|
||||
git config user.name "Codeberg CI" && \
|
||||
git add -v . && \
|
||||
git commit -v --allow-empty --message "Codeberg build for ${GITHUB_SHA}" && \
|
||||
git push -v origin ${{ vars.TARGET_BRANCH }}
|
||||
```
|
||||
|
||||
Once you commit one of the two files to your website source repository, you should see your first automated build firing up pretty soon. You can also trigger it manually by navigating to the **Actions** section of your repository web page, choosing **hugo.yaml** on the left and clicking on **Run workflow**.
|
||||
|
||||
## Other resources
|
||||
|
||||
|
||||
@@ -107,7 +107,8 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: 0.145.0
|
||||
DART_SASS_VERSION: 1.89.2
|
||||
HUGO_VERSION: 0.147.9
|
||||
HUGO_ENVIRONMENT: production
|
||||
TZ: America/Los_Angeles
|
||||
steps:
|
||||
@@ -116,7 +117,11 @@ jobs:
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
- name: Install Dart Sass
|
||||
run: sudo snap install dart-sass
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/dart-sass.tar.gz https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz \
|
||||
&& tar -xf ${{ runner.temp }}/dart-sass.tar.gz --directory ${{ runner.temp }} \
|
||||
&& mv ${{ runner.temp }}/dart-sass/ /usr/local/bin \
|
||||
&& echo "/usr/local/bin/dart-sass" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -136,6 +141,8 @@ jobs:
|
||||
key: hugo-${{ github.run_id }}
|
||||
restore-keys:
|
||||
hugo-
|
||||
- name: Configure Git
|
||||
run: git config core.quotepath false
|
||||
- name: Build with Hugo
|
||||
run: |
|
||||
hugo \
|
||||
|
||||
@@ -23,17 +23,18 @@ Define your [CI/CD](g) jobs by creating a `.gitlab-ci.yml` file in the root of y
|
||||
|
||||
```yaml {file=".gitlab-ci.yml" copy=true}
|
||||
variables:
|
||||
DART_SASS_VERSION: 1.85.0
|
||||
DART_SASS_VERSION: 1.89.2
|
||||
GIT_DEPTH: 0
|
||||
GIT_STRATEGY: clone
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
HUGO_VERSION: 0.144.2
|
||||
NODE_VERSION: 23.x
|
||||
HUGO_VERSION: 0.147.9
|
||||
NODE_VERSION: 22.x
|
||||
TZ: America/Los_Angeles
|
||||
image:
|
||||
name: golang:1.23.4-bookworm
|
||||
name: golang:1.24.2-bookworm
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
script:
|
||||
# Install brotli
|
||||
- apt-get update
|
||||
@@ -53,6 +54,8 @@ pages:
|
||||
- apt-get install -y nodejs
|
||||
# Install Node.js dependencies
|
||||
- "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
|
||||
# Configure Git
|
||||
- git config core.quotepath false
|
||||
# Build
|
||||
- hugo --gc --minify --baseURL ${CI_PAGES_URL}
|
||||
# Compress
|
||||
|
||||
@@ -113,21 +113,23 @@ Create a new file named netlify.toml in the root of your project directory. In i
|
||||
|
||||
```toml {file="netlify.toml"}
|
||||
[build.environment]
|
||||
HUGO_VERSION = "0.144.2"
|
||||
GO_VERSION = "1.24.2"
|
||||
HUGO_VERSION = "0.147.9"
|
||||
NODE_VERSION = "22"
|
||||
TZ = "America/Los_Angeles"
|
||||
|
||||
[build]
|
||||
publish = "public"
|
||||
command = "hugo --gc --minify"
|
||||
command = "git config core.quotepath false && hugo --gc --minify"
|
||||
```
|
||||
|
||||
If your site requires Dart Sass to transpile Sass to CSS, the configuration file should look something like this:
|
||||
|
||||
```toml {file="netlify.toml"}
|
||||
[build.environment]
|
||||
HUGO_VERSION = "0.144.2"
|
||||
DART_SASS_VERSION = "1.85.0"
|
||||
DART_SASS_VERSION = "1.89.2"
|
||||
GO_VERSION = "1.24.2"
|
||||
HUGO_VERSION = "0.147.9"
|
||||
NODE_VERSION = "22"
|
||||
TZ = "America/Los_Angeles"
|
||||
|
||||
@@ -138,6 +140,7 @@ command = """\
|
||||
tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
|
||||
rm dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
|
||||
export PATH=/opt/build/repo/dart-sass:$PATH && \
|
||||
git config core.quotepath false && \
|
||||
hugo --gc --minify \
|
||||
"""
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Host on Render
|
||||
description: Host your on Render.
|
||||
description: Host your site on Render.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-render/]
|
||||
|
||||
@@ -30,17 +30,33 @@ To install the extended edition of Hugo:
|
||||
sudo snap install hugo
|
||||
```
|
||||
|
||||
To enable or revoke access to removable media:
|
||||
To control automatic updates:
|
||||
|
||||
```sh
|
||||
# disable automatic updates
|
||||
sudo snap refresh --hold hugo
|
||||
|
||||
# enable automatic updates
|
||||
sudo snap refresh --unhold hugo
|
||||
```
|
||||
|
||||
To control access to removable media:
|
||||
|
||||
```sh
|
||||
# allow access
|
||||
sudo snap connect hugo:removable-media
|
||||
|
||||
# revoke access
|
||||
sudo snap disconnect hugo:removable-media
|
||||
```
|
||||
|
||||
To enable or revoke access to SSH keys:
|
||||
To control access to SSH keys:
|
||||
|
||||
```sh
|
||||
# allow access
|
||||
sudo snap connect hugo:ssh-keys
|
||||
|
||||
# revoke access
|
||||
sudo snap disconnect hugo:ssh-keys
|
||||
```
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ weight = 20
|
||||
|
||||
With this template code:
|
||||
|
||||
```go-html-template {file="layouts/partials/menu.html"}
|
||||
```go-html-template {file="layouts/_partials/menu.html"}
|
||||
<ul>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
@@ -83,7 +83,7 @@ In the above note that the `href` attribute of the second `anchor` element is bl
|
||||
|
||||
With this template code:
|
||||
|
||||
```go-html-template {file="layouts/partials/menu.html"}
|
||||
```go-html-template {file="layouts/_partials/menu.html"}
|
||||
<ul>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ or .URL .PageRef }}">{{ .Name }}</a></li>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user