From 34373407b7778d66e2aaf009c6faeda5a1c2a866 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Mon, 9 Dec 2024 10:58:52 -0800 Subject: [PATCH 001/374] markup/goldmark: Fix blockquote render hook text parsing Fixes #12913 Fixes #13119 --- markup/goldmark/blockquotes/blockquotes.go | 23 +++- .../blockquotes_integration_test.go | 126 +++++++++++++++--- 2 files changed, 126 insertions(+), 23 deletions(-) diff --git a/markup/goldmark/blockquotes/blockquotes.go b/markup/goldmark/blockquotes/blockquotes.go index f9d518850..064200d5e 100644 --- a/markup/goldmark/blockquotes/blockquotes.go +++ b/markup/goldmark/blockquotes/blockquotes.go @@ -74,7 +74,7 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N ordinal := ctx.GetAndIncrementOrdinal(ast.KindBlockquote) typ := typeRegular - alert := resolveBlockQuoteAlert(string(text)) + alert := resolveBlockQuoteAlert(text) if alert.typ != "" { typ = typeAlert } @@ -85,10 +85,21 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N } if typ == typeAlert { - // Trim preamble:

[!NOTE]
\n but preserve leading paragraph. - // We could possibly complicate this by moving this to the parser, but - // keep it simple for now. - text = "

" + text[strings.Index(text, "\n")+1:] + // Parse the blockquote content to determine the alert text. The alert + // text begins after the first newline, but we need to add an opening p + // tag if the first line of the blockquote content does not have a + // closing p tag. At some point we might want to move this to the + // parser. + before, after, found := strings.Cut(strings.TrimSpace(text), "\n") + if found { + if strings.HasSuffix(before, "

") { + text = after + } else { + text = "

" + after + } + } else { + text = "" + } } bqctx := &blockquoteContext{ @@ -165,7 +176,7 @@ func resolveBlockQuoteAlert(s string) blockQuoteAlert { m := blockQuoteAlertRe.FindStringSubmatch(s) if len(m) == 4 { title := strings.TrimSpace(m[3]) - title = strings.TrimRight(title, "

") + title = strings.TrimSuffix(title, "

") return blockQuoteAlert{ typ: strings.ToLower(m[1]), sign: m[2], diff --git a/markup/goldmark/blockquotes/blockquotes_integration_test.go b/markup/goldmark/blockquotes/blockquotes_integration_test.go index 1f671df2b..93fe5b27d 100644 --- a/markup/goldmark/blockquotes/blockquotes_integration_test.go +++ b/markup/goldmark/blockquotes/blockquotes_integration_test.go @@ -48,10 +48,9 @@ Content: {{ .Content }} title: "p1" --- -> [!NOTE] +> [!NOTE] > This is a note with some whitespace after the alert type. - > [!TIP] > This is a tip. @@ -64,29 +63,26 @@ title: "p1" > This is a tip with attributes. {class="foo bar" id="baz"} -> [!NOTE] +> [!NOTE] > Note triggering showing the position. {showpos="true"} - -> [!nOtE] +> [!nOtE] > Mixed case alert type. - - ` b := hugolib.Test(t, files) b.AssertFileContentExact("public/p1/index.html", - "Blockquote Alert: |

This is a note with some whitespace after the alert type.

\n|alert|", + "Blockquote Alert: |

This is a note with some whitespace after the alert type.

|alert|", "Blockquote Alert: |

This is a tip.

", - "Blockquote Alert: |

This is a caution with some whitespace before the alert type.

\n|alert|", + "Blockquote Alert: |

This is a caution with some whitespace before the alert type.

|alert|", "Blockquote: |

A regular blockquote.

\n|regular|", - "Blockquote Alert Attributes: |

This is a tip with attributes.

\n|map[class:foo bar id:baz]|", - filepath.FromSlash("/content/p1.md:20:3"), - "Blockquote Alert Page: |

This is a tip with attributes.

\n|p1|p1|", + "Blockquote Alert Attributes: |

This is a tip with attributes.

|map[class:foo bar id:baz]|", + filepath.FromSlash("/content/p1.md:19:3"), + "Blockquote Alert Page: |

This is a tip with attributes.

|p1|p1|", // Issue 12767. - "Blockquote Alert: |

Mixed case alert type.

\n|alert", + "Blockquote Alert: |

Mixed case alert type.

|alert", ) } @@ -142,17 +138,113 @@ title: "Home" {{ .Content }} -- layouts/_default/_markup/render-blockquote.html -- AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|AlertSign: {{ .AlertSign | safeHTML }}|Text: {{ .Text }}| - + ` b := hugolib.Test(t, files) b.AssertFileContentExact("public/index.html", "AlertType: tip|AlertTitle: Callouts can have custom titles|AlertSign: |", "AlertType: tip|AlertTitle: Title-only callout|AlertSign: |", - "AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text:

Yes! In a foldable callout, the contents are hidden when the callout is collapsed

\n|", - "AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|Text:

Yes! In a foldable callout, the contents are hidden when the callout is collapsed

\n|", - "AlertType: danger|AlertTitle: |AlertSign: |Text:

Do not approach or handle without protective gear.

\n|", + "AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text:

Yes! In a foldable callout, the contents are hidden when the callout is collapsed

|", + "AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|Text:

Yes! In a foldable callout, the contents are hidden when the callout is collapsed

|", + "AlertType: danger|AlertTitle: |AlertSign: |Text:

Do not approach or handle without protective gear.

|", "AlertTitle: Can callouts be nested?|", "AlertTitle: You can even use multiple layers of nesting.|", ) } + +// Issue 12913 +// Issue 13119 +func TestBlockquoteRenderHookTextParsing(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- layouts/_default/_markup/render-blockquote.html -- +AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|Text: {{ .Text }}| +-- content/_index.md -- +--- +title: home +--- + +> [!one] + +> [!two] title + +> [!three] +> line 1 + +> [!four] title +> line 1 + +> [!five] +> line 1 +> line 2 + +> [!six] title +> line 1 +> line 2 + +> [!seven] +> - list item + +> [!eight] title +> - list item + +> [!nine] +> line 1 +> - list item + +> [!ten] title +> line 1 +> - list item + +> [!eleven] +> line 1 +> - list item +> +> line 2 + +> [!twelve] title +> line 1 +> - list item +> +> line 2 + +> [!thirteen] +> ![alt](a.jpg) + +> [!fourteen] title +> ![alt](a.jpg) + +> [!fifteen] _title_ + +> [!sixteen] _title_ +> line one + +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", + "AlertType: one|AlertTitle: |Text: |", + "AlertType: two|AlertTitle: title|Text: |", + "AlertType: three|AlertTitle: |Text:

line 1

|", + "AlertType: four|AlertTitle: title|Text:

line 1

|", + "AlertType: five|AlertTitle: |Text:

line 1\nline 2

|", + "AlertType: six|AlertTitle: title|Text:

line 1\nline 2

|", + "AlertType: seven|AlertTitle: |Text: |", + "AlertType: eight|AlertTitle: title|Text: |", + "AlertType: nine|AlertTitle: |Text:

line 1

\n|", + "AlertType: ten|AlertTitle: title|Text:

line 1

\n|", + "AlertType: eleven|AlertTitle: |Text:

line 1

\n\n

line 2

|", + "AlertType: twelve|AlertTitle: title|Text:

line 1

\n\n

line 2

|", + "AlertType: thirteen|AlertTitle: |Text:

\"alt\"

|", + "AlertType: fourteen|AlertTitle: title|Text:

\"alt\"

|", + "AlertType: fifteen|AlertTitle: title|Text: |", + "AlertType: sixteen|AlertTitle: title|Text:

line one

|", + ) +} From 7b69218489afe77ac15faa773ec968ad599a0214 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:15:38 +0000 Subject: [PATCH 002/374] build(deps): bump golang.org/x/net from 0.31.0 to 0.32.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.31.0 to 0.32.0. - [Commits](https://github.com/golang/net/compare/v0.31.0...v0.32.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index b95bf8001..523027d11 100644 --- a/go.mod +++ b/go.mod @@ -77,9 +77,9 @@ require ( golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 golang.org/x/image v0.22.0 golang.org/x/mod v0.22.0 - golang.org/x/net v0.31.0 - golang.org/x/sync v0.9.0 - golang.org/x/text v0.20.0 + golang.org/x/net v0.32.0 + golang.org/x/sync v0.10.0 + golang.org/x/text v0.21.0 golang.org/x/tools v0.27.0 google.golang.org/api v0.206.0 gopkg.in/yaml.v2 v2.4.0 @@ -153,9 +153,9 @@ require ( go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.30.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28 // indirect diff --git a/go.sum b/go.sum index 5a3c99bf8..d995f8266 100644 --- a/go.sum +++ b/go.sum @@ -504,8 +504,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -590,8 +590,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -618,8 +618,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -667,8 +667,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -687,8 +687,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 5ab38de36387062a8ca6c1d18c6a773fa248519c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 10 Dec 2024 13:33:27 +0100 Subject: [PATCH 003/374] Fix Sass imports on the form index.{scss,sass} Fixes #13123 --- .../dartsass/dartsass_integration_test.go | 26 ++++++++++--------- .../tocss/dartsass/transform.go | 1 + .../tocss/scss/scss_integration_test.go | 26 ++++++++++--------- .../resource_transformers/tocss/scss/tocss.go | 1 + 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/resources/resource_transformers/tocss/dartsass/dartsass_integration_test.go b/resources/resource_transformers/tocss/dartsass/dartsass_integration_test.go index 5d1b89eaf..293fdfbcf 100644 --- a/resources/resource_transformers/tocss/dartsass/dartsass_integration_test.go +++ b/resources/resource_transformers/tocss/dartsass/dartsass_integration_test.go @@ -577,23 +577,25 @@ disableKinds = ['page','section','rss','sitemap','taxonomy','term'] [[module.mounts]] source = 'assets' target = 'assets' - -[[module.imports]] -path = "github.com/gohugoio/hugoTestModule2" - -[[module.imports.mounts]] +[[module.mounts]] source = "miscellaneous/sass" target = "assets/sass" --- go.mod -- -module hugo-github-issue-12849 -- layouts/index.html -- {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }} {{ (resources.Get "sass/main.scss" | toCSS $opts).Content }} -- assets/sass/main.scss -- -@use "foo"; // directory with index file from OS file system -@use "bar"; // directory with index file from module mount --- assets/sass/foo/_index.scss -- -.foo {color: red;} +@use "foo1"; // directory with _index file from OS file system +@use "bar1"; // directory with _index file from module mount +@use "foo2"; // directory with index file from OS file system +@use "bar2"; // directory with index file from module mount +-- assets/sass/foo1/_index.scss -- +.foo1 {color: red;} +-- miscellaneous/sass/bar1/_index.scss -- +.bar1 {color: blue;} +-- assets/sass/foo2/index.scss -- +.foo2 {color: red;} +-- miscellaneous/sass/bar2/index.scss -- +.bar2 {color: blue;} ` b := hugolib.NewIntegrationTestBuilder( @@ -603,7 +605,7 @@ module hugo-github-issue-12849 TxtarString: files, }).Build() - b.AssertFileContent("public/index.html", ".foo{color:red}.bar{color:green}") + b.AssertFileContent("public/index.html", ".foo1{color:red}.bar1{color:blue}.foo2{color:red}.bar2{color:blue}") } func TestIgnoreDeprecationWarnings(t *testing.T) { diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go index c5f97abff..0d7b57062 100644 --- a/resources/resource_transformers/tocss/dartsass/transform.go +++ b/resources/resource_transformers/tocss/dartsass/transform.go @@ -170,6 +170,7 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) { "_%s.sass", "%s.sass", "_%s.css", "%s.css", "%s/_index.scss", "%s/_index.sass", + "%s/index.scss", "%s/index.sass", } } diff --git a/resources/resource_transformers/tocss/scss/scss_integration_test.go b/resources/resource_transformers/tocss/scss/scss_integration_test.go index 10309ad20..0154a4634 100644 --- a/resources/resource_transformers/tocss/scss/scss_integration_test.go +++ b/resources/resource_transformers/tocss/scss/scss_integration_test.go @@ -432,23 +432,25 @@ disableKinds = ['page','section','rss','sitemap','taxonomy','term'] [[module.mounts]] source = 'assets' target = 'assets' - -[[module.imports]] -path = "github.com/gohugoio/hugoTestModule2" - -[[module.imports.mounts]] +[[module.mounts]] source = "miscellaneous/sass" target = "assets/sass" --- go.mod -- -module hugo-github-issue-12849 -- layouts/index.html -- {{ $opts := dict "transpiler" "libsass" "outputStyle" "compressed" }} {{ (resources.Get "sass/main.scss" | toCSS $opts).Content }} -- assets/sass/main.scss -- -@import "foo"; // directory with index file from OS file system -@import "bar"; // directory with index file from module mount --- assets/sass/foo/_index.scss -- -.foo {color: red;} +@import "foo1"; // directory with _index file from OS file system +@import "bar1"; // directory with _index file from module mount +@import "foo2"; // directory with index file from OS file system +@import "bar2"; // directory with index file from module mount +-- assets/sass/foo1/_index.scss -- +.foo1 {color: red;} +-- miscellaneous/sass/bar1/_index.scss -- +.bar1 {color: blue;} +-- assets/sass/foo2/index.scss -- +.foo2 {color: red;} +-- miscellaneous/sass/bar2/index.scss -- +.bar2 {color: blue;} ` b := hugolib.NewIntegrationTestBuilder( @@ -458,5 +460,5 @@ module hugo-github-issue-12849 TxtarString: files, }).Build() - b.AssertFileContent("public/index.html", ".foo{color:red}.bar{color:green}") + b.AssertFileContent("public/index.html", ".foo1{color:red}.bar1{color:blue}.foo2{color:red}.bar2{color:blue}") } diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go index 4b9c51ce0..89e95b2f8 100644 --- a/resources/resource_transformers/tocss/scss/tocss.go +++ b/resources/resource_transformers/tocss/scss/tocss.go @@ -109,6 +109,7 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx "_%s.scss", "%s.scss", "_%s.sass", "%s.sass", "%s/_index.scss", "%s/_index.sass", + "%s/index.scss", "%s/index.sass", } } From e477373487abcccdbed95688e37aa74b9b8fc198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 11 Dec 2024 09:53:33 +0100 Subject: [PATCH 004/374] Squashed 'docs/' changes from 227aab619..8390a4a3a 8390a4a3a Add Void Linux installation instructions d6099aae8 Update link to PostCSS plugins 25dad4693 netlify: Hugo 0.139.4 2b1fa118c Fix typo 3ef1eb505 Update hosting-on-aws-amplify c0f6d35d6 Fix typo aa54d4301 Correct directory name 98aa26bdb Improve instructions for hosting with AWS Amplify a07638a80 Add new-in badges 6ad018055 netlify: Hugo 0.139.3 1050835d6 Update title of hugo.Store page ebbd2e851 Clarify the shortcode Ordinal method b7716ed95 Revise code block render hook for Mermaid diagrams f1da9b6ea netlify: Hugo 0.139.2 d8ac9f428 Downgrade the Go toolchain in go.mod to a slightly older Go version 254b3c4f2 netlify: Hugo 0.139.1 03e666038 Add hugo.Store, site.Store and Shortcode.Store 157e8983b Update Anchorize.md 59fa9f214 Document the PageRef menu entry method bda544cce docs(transform.Unmarshal): match lang attribute to title language in examples 1985886bd Adjust front matter of shared Markdown snippets da5bd70d1 Fix typo 431b65d6b Update theme b63ef69f5 Update style guidance d50ed3422 Remove old new-in badges 12bfb9933 Update docs.yaml 0b936cacd netlify: Hugo 0.139.0 ab7668b4d dartsass: Add silenceDeprecations option 154df9bfc Merge commit '838bd312b1a287bb33962ad478dbc54737654f35' efa80477c docs: Regen CLI docs ad99e4a7a docs: Regenerate CLI docs git-subtree-dir: docs git-subtree-split: 8390a4a3ac36094f4cf47170af23b636608c420e --- .../gohugoioTheme/layouts/news/list.html | 9 +- .../gohugoioTheme/layouts/news/list.xml | 11 +- .../layouts/partials/related.html | 2 +- _vendor/modules.txt | 3 +- content/en/commands/hugo.md | 2 - content/en/commands/hugo_build.md | 2 - content/en/commands/hugo_completion.md | 2 - content/en/commands/hugo_completion_bash.md | 2 - content/en/commands/hugo_completion_fish.md | 2 - .../en/commands/hugo_completion_powershell.md | 2 - content/en/commands/hugo_completion_zsh.md | 2 - content/en/commands/hugo_config.md | 2 - content/en/commands/hugo_config_mounts.md | 2 - content/en/commands/hugo_convert.md | 2 - content/en/commands/hugo_convert_toJSON.md | 2 - content/en/commands/hugo_convert_toTOML.md | 2 - content/en/commands/hugo_convert_toYAML.md | 2 - content/en/commands/hugo_deploy.md | 2 - content/en/commands/hugo_env.md | 2 - content/en/commands/hugo_gen.md | 2 - content/en/commands/hugo_gen_chromastyles.md | 2 - content/en/commands/hugo_gen_doc.md | 2 - content/en/commands/hugo_gen_man.md | 2 - content/en/commands/hugo_import.md | 2 - content/en/commands/hugo_import_jekyll.md | 2 - content/en/commands/hugo_list.md | 2 - content/en/commands/hugo_list_all.md | 2 - content/en/commands/hugo_list_drafts.md | 2 - content/en/commands/hugo_list_expired.md | 2 - content/en/commands/hugo_list_future.md | 2 - content/en/commands/hugo_list_published.md | 2 - content/en/commands/hugo_mod.md | 2 - content/en/commands/hugo_mod_clean.md | 2 - content/en/commands/hugo_mod_get.md | 2 - content/en/commands/hugo_mod_graph.md | 2 - content/en/commands/hugo_mod_init.md | 2 - content/en/commands/hugo_mod_npm.md | 2 - content/en/commands/hugo_mod_npm_pack.md | 2 - content/en/commands/hugo_mod_tidy.md | 2 - content/en/commands/hugo_mod_vendor.md | 2 - content/en/commands/hugo_mod_verify.md | 2 - content/en/commands/hugo_new.md | 2 - content/en/commands/hugo_new_content.md | 2 - content/en/commands/hugo_new_site.md | 2 - content/en/commands/hugo_new_theme.md | 2 - content/en/commands/hugo_server.md | 3 +- content/en/commands/hugo_server_trust.md | 2 - content/en/commands/hugo_version.md | 2 - .../content-management/_common/page-kinds.md | 2 +- content/en/content-management/diagrams.md | 2 +- content/en/contribute/documentation.md | 4 +- content/en/functions/_common/glob-patterns.md | 2 +- .../_common/go-html-template-package.md | 2 +- content/en/functions/_common/locales.md | 2 +- .../functions/_common/regular-expressions.md | 2 +- .../functions/_common/time-layout-string.md | 2 +- content/en/functions/css/PostCSS.md | 2 +- content/en/functions/css/Sass.md | 3 + .../en/functions/fmt/_common/fmt-layout.md | 2 +- .../go-template/_common/text-template.md | 2 +- .../go-template/_common/truthy-falsy.md | 2 +- content/en/functions/hugo/Store.md | 125 +++++++++++++++ .../images/_common/apply-image-filter.md | 2 +- .../_common/postcss-windows-warning.md | 2 +- .../_common/parsable-date-time-strings.md | 2 +- content/en/functions/transform/Unmarshal.md | 10 +- content/en/functions/urls/Anchorize.md | 4 +- .../urls/_common/anchorize-vs-urlize.md | 2 +- .../hosting-on-aws-amplify.md | 46 ------ .../amplify-step-05.png | Bin 0 -> 21326 bytes .../amplify-step-06.png | Bin 0 -> 48097 bytes .../amplify-step-07.png | Bin 0 -> 19282 bytes .../amplify-step-08.png | Bin 0 -> 81416 bytes .../amplify-step-09.png | Bin 0 -> 22619 bytes .../amplify-step-11.png | Bin 0 -> 61021 bytes .../hosting-on-aws-amplify/index.md | 151 ++++++++++++++++++ .../hosting-on-github/index.md | 17 +- .../hosting-on-netlify/index.md | 28 ++-- content/en/hugo-modules/use-modules.md | 2 - content/en/hugo-pipes/postprocess.md | 2 +- .../en/hugo-pipes/transpile-sass-to-css.md | 2 +- .../en/installation/_common/01-editions.md | 2 +- .../installation/_common/02-prerequisites.md | 2 +- .../_common/03-prebuilt-binaries.md | 2 +- .../_common/04-build-from-source.md | 2 +- content/en/installation/_common/homebrew.md | 2 +- content/en/installation/linux.md | 10 ++ content/en/methods/menu-entry/PageRef.md | 120 ++++++++++++++ .../en/methods/menu-entry/_common/pre-post.md | 2 +- content/en/methods/page/Ancestors.md | 2 - content/en/methods/page/GitInfo.md | 1 + content/en/methods/page/HasMenuCurrent.md | 4 + content/en/methods/page/IsMenuCurrent.md | 4 + content/en/methods/page/Store.md | 3 + .../page/_common/definition-of-section.md | 2 +- .../en/methods/page/_common/next-and-prev.md | 2 +- .../nextinsection-and-previnsection.md | 2 +- .../page/_common/output-format-definition.md | 2 +- .../page/_common/output-format-methods.md | 2 +- .../methods/page/_common/scratch-methods.md | 2 +- .../methods/pages/_common/group-sort-order.md | 2 +- .../en/methods/pages/_common/next-and-prev.md | 2 +- .../_common/global-page-remote-resources.md | 2 +- .../resource/_common/processing-spec.md | 2 +- content/en/methods/shortcode/Ordinal.md | 4 + content/en/methods/shortcode/Scratch.md | 12 +- content/en/methods/shortcode/Store.md | 29 ++++ content/en/methods/site/Store.md | 126 +++++++++++++++ .../taxonomy/_common/get-a-taxonomy-object.md | 2 +- .../ordered-taxonomy-element-methods.md | 2 +- content/en/render-hooks/_common/pageinner.md | 2 +- content/en/render-hooks/code-blocks.md | 2 +- .../en/templates/_common/filter-sort-group.md | 2 +- content/en/templates/introduction.md | 2 +- data/docs.yaml | 51 ++---- go.mod | 4 +- go.sum | 6 +- netlify.toml | 2 +- 118 files changed, 709 insertions(+), 251 deletions(-) create mode 100644 content/en/functions/hugo/Store.md delete mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify.md create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-05.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-06.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-07.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-08.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-09.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-11.png create mode 100644 content/en/hosting-and-deployment/hosting-on-aws-amplify/index.md create mode 100644 content/en/methods/menu-entry/PageRef.md create mode 100644 content/en/methods/shortcode/Store.md create mode 100644 content/en/methods/site/Store.md diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.html index a41e45a2c..113e63973 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.html @@ -22,8 +22,15 @@ {{ $releases = where $releases "draft" false }} {{ $releases = where $releases "prerelease" false }} {{ range $releases | first 20 }} + {{ $publishDate := .published_at | time.AsTime }} + + {{/* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}} + {{ if eq .name "v0.138.0" }} + {{ $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }} + {{ end }} + {{ $ctx := dict - "Date" (.published_at | time.AsTime) + "Date" $publishDate "Title" (printf "Release %s" .name) "Permalink" .html_url "Section" "news" diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.xml b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.xml index 40bca59eb..26b1217d0 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.xml +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.xml @@ -21,14 +21,21 @@ {{- $releases = where $releases "draft" false }} {{- $releases = where $releases "prerelease" false }} {{- range $releases | first 20 }} + {{- $publishDate := .published_at | time.AsTime }} + + {{- /* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}} + {{- if eq .name "v0.138.0" }} + {{- $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }} + {{- end }} + {{- $summary := printf "Hugo %s was released on %s. See [release notes](%s) for details." .tag_name - (.published_at | time.AsTime | time.Format "2 Jan 2006") + ($publishDate | time.AsTime | time.Format "2 Jan 2006") .html_url }} {{- $ctx := dict - "PublishDate" (.published_at | time.AsTime) + "PublishDate" $publishDate "Title" (printf "Release %s" .name) "Permalink" .html_url "Section" "news" diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html index ff7435668..53995a486 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html @@ -16,7 +16,7 @@

{{ $heading }}

{{- end }} diff --git a/_vendor/modules.txt b/_vendor/modules.txt index 0a5950790..5ff5b9db1 100644 --- a/_vendor/modules.txt +++ b/_vendor/modules.txt @@ -1 +1,2 @@ -# github.com/gohugoio/gohugoioTheme v0.0.0-20241105120803-6c6e5fb8f8af +# github.com/gohugoio/gohugoioTheme v0.0.0-20241119115653-b92d27ede3e1 + diff --git a/content/en/commands/hugo.md b/content/en/commands/hugo.md index c23b3e4ba..ef0bca9a5 100644 --- a/content/en/commands/hugo.md +++ b/content/en/commands/hugo.md @@ -33,7 +33,6 @@ hugo [flags] --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") -c, --contentDir string filesystem path to content directory - --debug debug output -d, --destination string filesystem path to write files to --disableKinds strings disable different kind of pages (home, RSS etc.) --enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages @@ -64,7 +63,6 @@ hugo [flags] -t, --theme strings themes to use (located in /themes/THEMENAME/) --themesDir string filesystem path to themes directory --trace file write trace to file (not useful in general) - -v, --verbose verbose output -w, --watch watch filesystem for changes and recreate as needed ``` diff --git a/content/en/commands/hugo_build.md b/content/en/commands/hugo_build.md index c0abecfa9..582cbe511 100644 --- a/content/en/commands/hugo_build.md +++ b/content/en/commands/hugo_build.md @@ -33,7 +33,6 @@ hugo build [flags] --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") -c, --contentDir string filesystem path to content directory - --debug debug output -d, --destination string filesystem path to write files to --disableKinds strings disable different kind of pages (home, RSS etc.) --enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages @@ -64,7 +63,6 @@ hugo build [flags] -t, --theme strings themes to use (located in /themes/THEMENAME/) --themesDir string filesystem path to themes directory --trace file write trace to file (not useful in general) - -v, --verbose verbose output -w, --watch watch filesystem for changes and recreate as needed ``` diff --git a/content/en/commands/hugo_completion.md b/content/en/commands/hugo_completion.md index 171018fee..96f53742a 100644 --- a/content/en/commands/hugo_completion.md +++ b/content/en/commands/hugo_completion.md @@ -25,7 +25,6 @@ See each sub-command's help for details on how to use the generated script. --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -34,7 +33,6 @@ See each sub-command's help for details on how to use the generated script. -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_completion_bash.md b/content/en/commands/hugo_completion_bash.md index bface97c6..60973415f 100644 --- a/content/en/commands/hugo_completion_bash.md +++ b/content/en/commands/hugo_completion_bash.md @@ -48,7 +48,6 @@ hugo completion bash --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -57,7 +56,6 @@ hugo completion bash -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_completion_fish.md b/content/en/commands/hugo_completion_fish.md index 3a9cf0df2..92bbd6c22 100644 --- a/content/en/commands/hugo_completion_fish.md +++ b/content/en/commands/hugo_completion_fish.md @@ -39,7 +39,6 @@ hugo completion fish [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -48,7 +47,6 @@ hugo completion fish [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_completion_powershell.md b/content/en/commands/hugo_completion_powershell.md index 593573cee..f01442920 100644 --- a/content/en/commands/hugo_completion_powershell.md +++ b/content/en/commands/hugo_completion_powershell.md @@ -36,7 +36,6 @@ hugo completion powershell [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -45,7 +44,6 @@ hugo completion powershell [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_completion_zsh.md b/content/en/commands/hugo_completion_zsh.md index c227c6125..142c53103 100644 --- a/content/en/commands/hugo_completion_zsh.md +++ b/content/en/commands/hugo_completion_zsh.md @@ -50,7 +50,6 @@ hugo completion zsh [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -59,7 +58,6 @@ hugo completion zsh [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_config.md b/content/en/commands/hugo_config.md index 96f84a531..8e8d745d2 100644 --- a/content/en/commands/hugo_config.md +++ b/content/en/commands/hugo_config.md @@ -34,7 +34,6 @@ hugo config [command] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -43,7 +42,6 @@ hugo config [command] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_config_mounts.md b/content/en/commands/hugo_config_mounts.md index 6fa30c016..5af4819a2 100644 --- a/content/en/commands/hugo_config_mounts.md +++ b/content/en/commands/hugo_config_mounts.md @@ -28,7 +28,6 @@ hugo config mounts [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -37,7 +36,6 @@ hugo config mounts [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_convert.md b/content/en/commands/hugo_convert.md index 53d4d992c..aeaa37766 100644 --- a/content/en/commands/hugo_convert.md +++ b/content/en/commands/hugo_convert.md @@ -27,7 +27,6 @@ See convert's subcommands toJSON, toTOML and toYAML for more information. --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ See convert's subcommands toJSON, toTOML and toYAML for more information. -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_convert_toJSON.md b/content/en/commands/hugo_convert_toJSON.md index 9546788b5..40403193c 100644 --- a/content/en/commands/hugo_convert_toJSON.md +++ b/content/en/commands/hugo_convert_toJSON.md @@ -28,7 +28,6 @@ hugo convert toJSON [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo convert toJSON [flags] [args] -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory --unsafe enable less safe operations, please backup first - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_convert_toTOML.md b/content/en/commands/hugo_convert_toTOML.md index 3bd703f60..53ab82651 100644 --- a/content/en/commands/hugo_convert_toTOML.md +++ b/content/en/commands/hugo_convert_toTOML.md @@ -28,7 +28,6 @@ hugo convert toTOML [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo convert toTOML [flags] [args] -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory --unsafe enable less safe operations, please backup first - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_convert_toYAML.md b/content/en/commands/hugo_convert_toYAML.md index 563375486..efb63a4e2 100644 --- a/content/en/commands/hugo_convert_toYAML.md +++ b/content/en/commands/hugo_convert_toYAML.md @@ -28,7 +28,6 @@ hugo convert toYAML [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo convert toYAML [flags] [args] -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory --unsafe enable less safe operations, please backup first - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_deploy.md b/content/en/commands/hugo_deploy.md index a6fc53658..fce1e5422 100644 --- a/content/en/commands/hugo_deploy.md +++ b/content/en/commands/hugo_deploy.md @@ -38,7 +38,6 @@ hugo deploy [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -47,7 +46,6 @@ hugo deploy [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_env.md b/content/en/commands/hugo_env.md index 50a7bc92f..e216be416 100644 --- a/content/en/commands/hugo_env.md +++ b/content/en/commands/hugo_env.md @@ -27,7 +27,6 @@ hugo env [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo env [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_gen.md b/content/en/commands/hugo_gen.md index 20d829178..97cdbdb9d 100644 --- a/content/en/commands/hugo_gen.md +++ b/content/en/commands/hugo_gen.md @@ -23,7 +23,6 @@ Generate documentation for your project using Hugo's documentation engine, inclu --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -32,7 +31,6 @@ Generate documentation for your project using Hugo's documentation engine, inclu -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_gen_chromastyles.md b/content/en/commands/hugo_gen_chromastyles.md index 1c0ac7235..49cde6bb9 100644 --- a/content/en/commands/hugo_gen_chromastyles.md +++ b/content/en/commands/hugo_gen_chromastyles.md @@ -33,7 +33,6 @@ hugo gen chromastyles [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -42,7 +41,6 @@ hugo gen chromastyles [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_gen_doc.md b/content/en/commands/hugo_gen_doc.md index 5d2fffa4f..180dc4f95 100644 --- a/content/en/commands/hugo_gen_doc.md +++ b/content/en/commands/hugo_gen_doc.md @@ -33,7 +33,6 @@ hugo gen doc [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -42,7 +41,6 @@ hugo gen doc [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_gen_man.md b/content/en/commands/hugo_gen_man.md index f05b06298..f33342c54 100644 --- a/content/en/commands/hugo_gen_man.md +++ b/content/en/commands/hugo_gen_man.md @@ -30,7 +30,6 @@ hugo gen man [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo gen man [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_import.md b/content/en/commands/hugo_import.md index 7de28e4cb..b20b58599 100644 --- a/content/en/commands/hugo_import.md +++ b/content/en/commands/hugo_import.md @@ -25,7 +25,6 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -34,7 +33,6 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_import_jekyll.md b/content/en/commands/hugo_import_jekyll.md index 33c91d24c..14c57cc44 100644 --- a/content/en/commands/hugo_import_jekyll.md +++ b/content/en/commands/hugo_import_jekyll.md @@ -30,7 +30,6 @@ hugo import jekyll [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo import jekyll [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list.md b/content/en/commands/hugo_list.md index 070a44d84..726fe51a9 100644 --- a/content/en/commands/hugo_list.md +++ b/content/en/commands/hugo_list.md @@ -25,7 +25,6 @@ List requires a subcommand, e.g. hugo list drafts --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -34,7 +33,6 @@ List requires a subcommand, e.g. hugo list drafts -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list_all.md b/content/en/commands/hugo_list_all.md index 5dd29904e..59846733d 100644 --- a/content/en/commands/hugo_list_all.md +++ b/content/en/commands/hugo_list_all.md @@ -27,7 +27,6 @@ hugo list all [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo list all [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list_drafts.md b/content/en/commands/hugo_list_drafts.md index 4dff70886..5f3bcd617 100644 --- a/content/en/commands/hugo_list_drafts.md +++ b/content/en/commands/hugo_list_drafts.md @@ -27,7 +27,6 @@ hugo list drafts [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo list drafts [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list_expired.md b/content/en/commands/hugo_list_expired.md index 7b874a105..c010d1159 100644 --- a/content/en/commands/hugo_list_expired.md +++ b/content/en/commands/hugo_list_expired.md @@ -27,7 +27,6 @@ hugo list expired [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo list expired [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list_future.md b/content/en/commands/hugo_list_future.md index f558acd52..888784dcd 100644 --- a/content/en/commands/hugo_list_future.md +++ b/content/en/commands/hugo_list_future.md @@ -27,7 +27,6 @@ hugo list future [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo list future [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_list_published.md b/content/en/commands/hugo_list_published.md index 9fc75694b..b8ec0e7b2 100644 --- a/content/en/commands/hugo_list_published.md +++ b/content/en/commands/hugo_list_published.md @@ -27,7 +27,6 @@ hugo list published [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo list published [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod.md b/content/en/commands/hugo_mod.md index d4e305ff1..2c07b89cf 100644 --- a/content/en/commands/hugo_mod.md +++ b/content/en/commands/hugo_mod.md @@ -34,7 +34,6 @@ See https://gohugo.io/hugo-modules/ for more information. --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -43,7 +42,6 @@ See https://gohugo.io/hugo-modules/ for more information. -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_clean.md b/content/en/commands/hugo_mod_clean.md index 80a983a66..7df51059f 100644 --- a/content/en/commands/hugo_mod_clean.md +++ b/content/en/commands/hugo_mod_clean.md @@ -34,7 +34,6 @@ hugo mod clean [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -43,7 +42,6 @@ hugo mod clean [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_get.md b/content/en/commands/hugo_mod_get.md index de6b01a24..f4ca6069a 100644 --- a/content/en/commands/hugo_mod_get.md +++ b/content/en/commands/hugo_mod_get.md @@ -58,7 +58,6 @@ hugo mod get [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -67,7 +66,6 @@ hugo mod get [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_graph.md b/content/en/commands/hugo_mod_graph.md index f20e26b6f..5b5a14d5d 100644 --- a/content/en/commands/hugo_mod_graph.md +++ b/content/en/commands/hugo_mod_graph.md @@ -35,7 +35,6 @@ hugo mod graph [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -44,7 +43,6 @@ hugo mod graph [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_init.md b/content/en/commands/hugo_mod_init.md index 1d7bc4ac0..bf9651b59 100644 --- a/content/en/commands/hugo_mod_init.md +++ b/content/en/commands/hugo_mod_init.md @@ -39,7 +39,6 @@ hugo mod init [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -48,7 +47,6 @@ hugo mod init [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_npm.md b/content/en/commands/hugo_mod_npm.md index eeb684e51..a013fb91f 100644 --- a/content/en/commands/hugo_mod_npm.md +++ b/content/en/commands/hugo_mod_npm.md @@ -27,7 +27,6 @@ hugo mod npm [command] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo mod npm [command] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_npm_pack.md b/content/en/commands/hugo_mod_npm_pack.md index 47d3e28b9..8f8738280 100644 --- a/content/en/commands/hugo_mod_npm_pack.md +++ b/content/en/commands/hugo_mod_npm_pack.md @@ -42,7 +42,6 @@ hugo mod npm pack [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -51,7 +50,6 @@ hugo mod npm pack [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_tidy.md b/content/en/commands/hugo_mod_tidy.md index be0f92657..c15ddb3c8 100644 --- a/content/en/commands/hugo_mod_tidy.md +++ b/content/en/commands/hugo_mod_tidy.md @@ -28,7 +28,6 @@ hugo mod tidy [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -37,7 +36,6 @@ hugo mod tidy [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_vendor.md b/content/en/commands/hugo_mod_vendor.md index d33b2a673..ae112a36a 100644 --- a/content/en/commands/hugo_mod_vendor.md +++ b/content/en/commands/hugo_mod_vendor.md @@ -34,7 +34,6 @@ hugo mod vendor [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -43,7 +42,6 @@ hugo mod vendor [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_mod_verify.md b/content/en/commands/hugo_mod_verify.md index c5e755e3c..63dd28ce8 100644 --- a/content/en/commands/hugo_mod_verify.md +++ b/content/en/commands/hugo_mod_verify.md @@ -33,7 +33,6 @@ hugo mod verify [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -42,7 +41,6 @@ hugo mod verify [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_new.md b/content/en/commands/hugo_new.md index cfe6cc1cd..4d3021b44 100644 --- a/content/en/commands/hugo_new.md +++ b/content/en/commands/hugo_new.md @@ -30,7 +30,6 @@ Ensure you run this within the root directory of your site. --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ Ensure you run this within the root directory of your site. -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_new_content.md b/content/en/commands/hugo_new_content.md index c1e3ffa30..a8d2100f9 100644 --- a/content/en/commands/hugo_new_content.md +++ b/content/en/commands/hugo_new_content.md @@ -42,7 +42,6 @@ hugo new content [path] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -51,7 +50,6 @@ hugo new content [path] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_new_site.md b/content/en/commands/hugo_new_site.md index 9df879103..cc0e63013 100644 --- a/content/en/commands/hugo_new_site.md +++ b/content/en/commands/hugo_new_site.md @@ -31,7 +31,6 @@ hugo new site [path] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -40,7 +39,6 @@ hugo new site [path] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_new_theme.md b/content/en/commands/hugo_new_theme.md index 6ab98f6b9..a79978c4a 100644 --- a/content/en/commands/hugo_new_theme.md +++ b/content/en/commands/hugo_new_theme.md @@ -30,7 +30,6 @@ hugo new theme [name] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -39,7 +38,6 @@ hugo new theme [name] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_server.md b/content/en/commands/hugo_server.md index b79b7f374..a1c77d36f 100644 --- a/content/en/commands/hugo_server.md +++ b/content/en/commands/hugo_server.md @@ -54,6 +54,7 @@ hugo server [command] [flags] --noChmod don't sync permission mode of files --noHTTPCache prevent HTTP caching --noTimes don't sync modification time of files + -O, --openBrowser open the site in a browser after server startup --panicOnWarning panic on first WARNING log --poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes -p, --port int port on which the server will listen (default 1313) @@ -80,7 +81,6 @@ hugo server [command] [flags] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -89,7 +89,6 @@ hugo server [command] [flags] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_server_trust.md b/content/en/commands/hugo_server_trust.md index fb242d88c..1a904e845 100644 --- a/content/en/commands/hugo_server_trust.md +++ b/content/en/commands/hugo_server_trust.md @@ -24,7 +24,6 @@ hugo server trust [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -33,7 +32,6 @@ hugo server trust [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/commands/hugo_version.md b/content/en/commands/hugo_version.md index bbc961093..b1a6b971e 100644 --- a/content/en/commands/hugo_version.md +++ b/content/en/commands/hugo_version.md @@ -27,7 +27,6 @@ hugo version [flags] [args] --clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00 --config string config file (default is hugo.yaml|json|toml) --configDir string config dir (default "config") - --debug debug output -d, --destination string filesystem path to write files to -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern @@ -36,7 +35,6 @@ hugo version [flags] [args] -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from --themesDir string filesystem path to themes directory - -v, --verbose verbose output ``` ### SEE ALSO diff --git a/content/en/content-management/_common/page-kinds.md b/content/en/content-management/_common/page-kinds.md index 07a53e8e6..8f10dcd79 100644 --- a/content/en/content-management/_common/page-kinds.md +++ b/content/en/content-management/_common/page-kinds.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- | Kind | Description | Example | diff --git a/content/en/content-management/diagrams.md b/content/en/content-management/diagrams.md index 8851034c6..f9f3cd30f 100644 --- a/content/en/content-management/diagrams.md +++ b/content/en/content-management/diagrams.md @@ -52,7 +52,7 @@ Hugo does not provide a built-in template for Mermaid diagrams. Create your own {{< code file=layouts/_default/_markup/render-codeblock-mermaid.html >}}
-  {{- .Inner | safeHTML }}
+  {{- .Inner | htmlEscape | safeHTML }}
 
{{ .Page.Store.Set "hasMermaid" true }} {{< /code >}} diff --git a/content/en/contribute/documentation.md b/content/en/contribute/documentation.md index 580d0b0e2..d4e65c9e0 100644 --- a/content/en/contribute/documentation.md +++ b/content/en/contribute/documentation.md @@ -37,7 +37,9 @@ Please follow these guidelines: ### Style -Although we do not strictly adhere to the [Microsoft Writing Style Guide], it is an excellent resource for questions related to style, grammar, and voice. +Please adhere to Google's [developer documentation style guide]. + +[developer documentation style guide]: https://developers.google.com/style #### Terminology diff --git a/content/en/functions/_common/glob-patterns.md b/content/en/functions/_common/glob-patterns.md index 3b0813f6f..d3092dece 100644 --- a/content/en/functions/_common/glob-patterns.md +++ b/content/en/functions/_common/glob-patterns.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Path|Pattern|Match diff --git a/content/en/functions/_common/go-html-template-package.md b/content/en/functions/_common/go-html-template-package.md index b622f2b76..57992ea66 100644 --- a/content/en/functions/_common/go-html-template-package.md +++ b/content/en/functions/_common/go-html-template-package.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo uses Go's [text/template] and [html/template] packages. diff --git a/content/en/functions/_common/locales.md b/content/en/functions/_common/locales.md index fd8415781..42d008776 100644 --- a/content/en/functions/_common/locales.md +++ b/content/en/functions/_common/locales.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- {{% note %}} diff --git a/content/en/functions/_common/regular-expressions.md b/content/en/functions/_common/regular-expressions.md index 48e020ac6..58f81a2ee 100644 --- a/content/en/functions/_common/regular-expressions.md +++ b/content/en/functions/_common/regular-expressions.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- When specifying the regular expression, use a raw [string literal] (backticks) instead of an interpreted string literal (double quotes) to simplify the syntax. With an interpreted string literal you must escape backslashes. diff --git a/content/en/functions/_common/time-layout-string.md b/content/en/functions/_common/time-layout-string.md index 827dc9894..3664eaef2 100644 --- a/content/en/functions/_common/time-layout-string.md +++ b/content/en/functions/_common/time-layout-string.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Format a `time.Time` value based on [Go's reference time]: diff --git a/content/en/functions/css/PostCSS.md b/content/en/functions/css/PostCSS.md index 750971c10..8d4143575 100644 --- a/content/en/functions/css/PostCSS.md +++ b/content/en/functions/css/PostCSS.md @@ -124,6 +124,6 @@ module.exports = { ``` [node.js]: https://nodejs.org/en/download -[postcss plugins]: https://www.postcss.parts/ +[postcss plugins]: https://postcss.org/docs/postcss-plugins [supported file name]: https://github.com/postcss/postcss-load-config#usage [transpile to CSS]: /functions/css/sass/ diff --git a/content/en/functions/css/Sass.md b/content/en/functions/css/Sass.md index 328037bb9..793c0c1ac 100644 --- a/content/en/functions/css/Sass.md +++ b/content/en/functions/css/Sass.md @@ -86,6 +86,9 @@ includePaths {{ end }} ``` +silenceDeprecations +: (`slice`) {{< new-in 0.139.0 >}} A slice of deprecation IDs to silence. The deprecation IDs are printed to in the warning message, e.g "import" in `WARN Dart Sass: DEPRECATED [import] ...`. This is for Dart Sass only. + ## Dart Sass The extended version of Hugo includes [LibSass] to transpile Sass to CSS. In 2020, the Sass team deprecated LibSass in favor of [Dart Sass]. diff --git a/content/en/functions/fmt/_common/fmt-layout.md b/content/en/functions/fmt/_common/fmt-layout.md index ff69ce5e4..09a9ee867 100644 --- a/content/en/functions/fmt/_common/fmt-layout.md +++ b/content/en/functions/fmt/_common/fmt-layout.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- The documentation for Go's [fmt] package describes the structure and content of the format string. diff --git a/content/en/functions/go-template/_common/text-template.md b/content/en/functions/go-template/_common/text-template.md index 71718c3fd..4b934c1e9 100644 --- a/content/en/functions/go-template/_common/text-template.md +++ b/content/en/functions/go-template/_common/text-template.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- See Go's [text/template] documentation for more information. diff --git a/content/en/functions/go-template/_common/truthy-falsy.md b/content/en/functions/go-template/_common/truthy-falsy.md index c41bb6561..e15e58d61 100644 --- a/content/en/functions/go-template/_common/truthy-falsy.md +++ b/content/en/functions/go-template/_common/truthy-falsy.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- The falsy values are `false`, `0`, any `nil` pointer or interface value, any array, slice, map, or string of length zero, and zero `time.Time` values. diff --git a/content/en/functions/hugo/Store.md b/content/en/functions/hugo/Store.md new file mode 100644 index 000000000..b0503a5ff --- /dev/null +++ b/content/en/functions/hugo/Store.md @@ -0,0 +1,125 @@ +--- +title: hugo.Store +description: Returns a global, persistent "scratch pad" to store and manipulate data. +categories: [] +keywords: [] +action: + related: + - methods/page/store + - methods/site/store + - functions/collections/NewScratch + returnType: maps.Scratch + signatures: [hugo.Store] +toc: true +--- + +{{< new-in 0.139.0 >}} + +The global `hugo.Store` function creates a persistent [scratch pad] to store and manipulate data. To create a locally scoped, use the [`newScratch`] function. + +[`Scratch`]: /functions/hugo/scratch/ +[`newScratch`]: /functions/collections/newscratch/ +[scratch pad]: /getting-started/glossary/#scratch-pad + +## Methods + +###### Set + +Sets the value of a given key. + +```go-html-template +{{ hugo.Store.Set "greeting" "Hello" }} +``` + +###### Get + +Gets the value of a given key. + +```go-html-template +{{ hugo.Store.Set "greeting" "Hello" }} +{{ hugo.Store.Get "greeting" }} → Hello +``` + +###### Add + +Adds a given value to existing value(s) of the given key. + +For single values, `Add` accepts values that support Go's `+` operator. If the first `Add` for a key is an array or slice, the following adds will be appended to that list. + +```go-html-template +{{ hugo.Store.Set "greeting" "Hello" }} +{{ hugo.Store.Add "greeting" "Welcome" }} +{{ hugo.Store.Get "greeting" }} → HelloWelcome +``` + +```go-html-template +{{ hugo.Store.Set "total" 3 }} +{{ hugo.Store.Add "total" 7 }} +{{ hugo.Store.Get "total" }} → 10 +``` + +```go-html-template +{{ hugo.Store.Set "greetings" (slice "Hello") }} +{{ hugo.Store.Add "greetings" (slice "Welcome" "Cheers") }} +{{ hugo.Store.Get "greetings" }} → [Hello Welcome Cheers] +``` + +###### SetInMap + +Takes a `key`, `mapKey` and `value` and adds a map of `mapKey` and `value` to the given `key`. + +```go-html-template +{{ hugo.Store.SetInMap "greetings" "english" "Hello" }} +{{ hugo.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ hugo.Store.Get "greetings" }} → map[english:Hello french:Bonjour] +``` + +###### DeleteInMap + +Takes a `key` and `mapKey` and removes the map of `mapKey` from the given `key`. + +```go-html-template +{{ hugo.Store.SetInMap "greetings" "english" "Hello" }} +{{ hugo.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ hugo.Store.DeleteInMap "greetings" "english" }} +{{ hugo.Store.Get "greetings" }} → map[french:Bonjour] +``` + +###### GetSortedMapValues + +Returns an array of values from `key` sorted by `mapKey`. + +```go-html-template +{{ hugo.Store.SetInMap "greetings" "english" "Hello" }} +{{ hugo.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ hugo.Store.GetSortedMapValues "greetings" }} → [Hello Bonjour] +``` + +###### Delete + +Removes the given key. + +```go-html-template +{{ hugo.Store.Set "greeting" "Hello" }} +{{ hugo.Store.Delete "greeting" }} +``` + +## Determinate values + +The `Store` method is often used to set scratch pad values within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are indeterminate until Hugo renders the page content. + +If you need to access a scratch pad value from a parent template, and the parent template has not yet rendered the page content, you can trigger content rendering by assigning the returned value to a [noop] variable: + +[noop]: /getting-started/glossary/#noop + +```go-html-template +{{ $noop := .Content }} +{{ hugo.Store.Get "mykey" }} +``` + +You can also trigger content rendering with the `ContentWithoutSummary`, `FuzzyWordCount`, `Len`, `Plain`, `PlainWords`, `ReadingTime`, `Summary`, `Truncated`, and `WordCount` methods. For example: + +```go-html-template +{{ $noop := .WordCount }} +{{ hugo.Store.Get "mykey" }} +``` diff --git a/content/en/functions/images/_common/apply-image-filter.md b/content/en/functions/images/_common/apply-image-filter.md index 15eddb485..08e08238f 100644 --- a/content/en/functions/images/_common/apply-image-filter.md +++ b/content/en/functions/images/_common/apply-image-filter.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Apply the filter using the [`images.Filter`] function: diff --git a/content/en/functions/resources/_common/postcss-windows-warning.md b/content/en/functions/resources/_common/postcss-windows-warning.md index 1b72e74db..e2d97850b 100644 --- a/content/en/functions/resources/_common/postcss-windows-warning.md +++ b/content/en/functions/resources/_common/postcss-windows-warning.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- If you are a Windows user, and the path to your project contains a space, you must place the PostCSS configuration within the package.json file. See [this example] and issue [#7333]. diff --git a/content/en/functions/time/_common/parsable-date-time-strings.md b/content/en/functions/time/_common/parsable-date-time-strings.md index 6d1633a6f..92842767e 100644 --- a/content/en/functions/time/_common/parsable-date-time-strings.md +++ b/content/en/functions/time/_common/parsable-date-time-strings.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Format|Time zone diff --git a/content/en/functions/transform/Unmarshal.md b/content/en/functions/transform/Unmarshal.md index 998152eb2..960332c44 100644 --- a/content/en/functions/transform/Unmarshal.md +++ b/content/en/functions/transform/Unmarshal.md @@ -230,7 +230,7 @@ Let's add a `lang` attribute to the `title` nodes of our RSS feed, and a namespa en-US - The Hunchback of Notre Dame + The Hunchback of Notre Dame Written by Victor Hugo 9780140443530 https://example.org/books/the-hunchback-of-notre-dame/ @@ -238,7 +238,7 @@ Let's add a `lang` attribute to the `title` nodes of our RSS feed, and a namespa https://example.org/books/the-hunchback-of-notre-dame/ - Les Misérables + Les Misérables Written by Victor Hugo 9780451419439 https://example.org/books/les-miserables/ @@ -266,7 +266,7 @@ Each item node looks like this: "pubDate": "Mon, 09 Oct 2023 09:27:12 -0700", "title": { "#text": "The Hunchback of Notre Dame", - "-lang": "fr" + "-lang": "en" } } ``` @@ -290,8 +290,8 @@ Hugo renders this to: ```html
    -
  • The Hunchback of Notre Dame (fr) 9780140443530
  • -
  • Les Misérables (en) 9780451419439
  • +
  • The Hunchback of Notre Dame (en) 9780140443530
  • +
  • Les Misérables (fr) 9780451419439
``` diff --git a/content/en/functions/urls/Anchorize.md b/content/en/functions/urls/Anchorize.md index f3939675a..d8866ae05 100644 --- a/content/en/functions/urls/Anchorize.md +++ b/content/en/functions/urls/Anchorize.md @@ -28,10 +28,10 @@ This controls the behavior of the `anchorize` function and the generation of hea Set `autoHeadingIDType` to one of: github -: Compatible with GitHub. This is the default, and strongly recommended. +: Compatible with GitHub. This is the default. github-ascii -: Similar to the "github" setting, but removes non-ASCII characters. +: Similar to the `github` setting, but removes non-ASCII characters. blackfriday : Provided for backwards compatibility with Hugo v0.59.1 and earlier. This option will be removed in a future release. diff --git a/content/en/functions/urls/_common/anchorize-vs-urlize.md b/content/en/functions/urls/_common/anchorize-vs-urlize.md index 718c14098..710a3c592 100644 --- a/content/en/functions/urls/_common/anchorize-vs-urlize.md +++ b/content/en/functions/urls/_common/anchorize-vs-urlize.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- The [`anchorize`] and [`urlize`] functions are similar: diff --git a/content/en/hosting-and-deployment/hosting-on-aws-amplify.md b/content/en/hosting-and-deployment/hosting-on-aws-amplify.md deleted file mode 100644 index 43d75312a..000000000 --- a/content/en/hosting-and-deployment/hosting-on-aws-amplify.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Host on AWS Amplify -description: Develop and deploy a cloud-powered web app with AWS Amplify. -categories: [hosting and deployment] -keywords: [hosting,amplify] -menu: - docs: - parent: hosting-and-deployment -toc: true ---- - -In this guide we'll walk through how to deploy and host your Hugo site using the [AWS Amplify Console](https://console.amplify.aws). - -AWS Amplify is a combination of client library, CLI toolchain, and a Console for continuous deployment and hosting. The Amplify CLI and library allow developers to get up & running with full-stack cloud-powered applications with features like authentication, storage, serverless GraphQL or REST APIs, analytics, Lambda functions, & more. The Amplify Console provides continuous deployment and hosting for modern web apps (single page apps and static site generators). Continuous deployment allows developers to deploy updates to their web app on every code commit to their Git repository. Hosting includes features such as globally available CDNs, easy custom domain setup + HTTPS, feature branch deployments, and password protection. - -## Pre-requisites - -* [Sign up for an AWS Account](https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation). There are no upfront charges or any term commitments to create an AWS account and signing up gives you immediate access to the AWS Free Tier. -* You have an account with GitHub, GitLab, or Bitbucket. -* You have completed the [Quick Start] or have a Hugo website you are ready to deploy and share with the world. - -## Hosting - -1. Log in to the [AWS Amplify Console](https://console.aws.amazon.com/amplify/home) and choose Get Started under Deploy. - ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png) - -1. Connect a branch from your GitHub, Bitbucket, GitLab, or AWS CodeCommit repository. Connecting your repository allows Amplify to deploy updates on every code commit to a branch. - ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif) - -1. Accept the default build settings. The Amplify Console automatically detects your Hugo build settings and output directory. - ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png) - -1. Review your changes and then choose **Save and deploy**. The Amplify Console will pull code from your repository, build changes to the backend and frontend, and deploy your build artifacts at `https://master.unique-id.amplifyapp.com`. Bonus: Screenshots of your app on different devices to find layout issues. - -## Using a newer version of Hugo - -If you need to use a different, perhaps newer, version of Hugo than the version currently supported by AWS Amplify: - -1. Visit the [AWS Amplify Console](https://console.aws.amazon.com/amplify/home), and click the app you would like to modify -1. In the side navigation bar, Under App Settings, click **Build settings** -1. On the Build settings page, near the bottom, there is a section called **Build image settings**. Click **Edit** -1. Under **Live package updates**, click **Add package version override** -1. From the selection, click **Hugo** and ensure the version field says `latest` -1. Click **Save** to save the changes. - -[Quick Start]: /getting-started/quick-start/ diff --git a/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-05.png b/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-05.png new file mode 100644 index 0000000000000000000000000000000000000000..bb98d974a804d9948054c8d1be72159db078f703 GIT binary patch literal 21326 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 E0cRUsu>b%7 literal 0 HcmV?d00001 diff --git a/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-08.png b/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-08.png new file mode 100644 index 0000000000000000000000000000000000000000..55e80e7100b0d8f977d9f149a7a56ed333fcb77c GIT binary patch literal 81416 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%0Djg&Jsw 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 OrZUnq)2-BTj{YCtQI?7T literal 0 HcmV?d00001 diff --git a/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-11.png b/content/en/hosting-and-deployment/hosting-on-aws-amplify/amplify-step-11.png new file mode 100644 index 0000000000000000000000000000000000000000..e147edfb9d23005de2ff5b5d3815f2ec9f2c2a45 GIT binary patch literal 61021 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 +version: 1 +env: + variables: + # Application versions + DART_SASS_VERSION: 1.81.0 + GO_VERSION: 1.23.3 + HUGO_VERSION: 0.139.3 + # Time zone + TZ: America/Los_Angeles + # Cache + HUGO_CACHEDIR: ${PWD}/.hugo + NPM_CONFIG_CACHE: ${PWD}/.npm +frontend: + phases: + preBuild: + commands: + # Install Dart Sass + - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz + - sudo tar -C /usr/local/bin -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz + - rm dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz + - export PATH=/usr/local/bin/dart-sass:$PATH + + # Install Go + - curl -LJO https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz + - sudo tar -C /usr/local -xf go${GO_VERSION}.linux-amd64.tar.gz + - rm go${GO_VERSION}.linux-amd64.tar.gz + - export PATH=/usr/local/go/bin:$PATH + + # Install Hugo + - curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz + - sudo tar -C /usr/local/bin -xf hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz + - rm hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz + - export PATH=/usr/local/bin:$PATH + + # Check installed versions + - go version + - hugo version + - node -v + - npm -v + - sass --embedded --version + + # Install Node.JS dependencies + - "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci --prefer-offline || true" + + # https://github.com/gohugoio/hugo/issues/9810 + - git config --add core.quotepath false + build: + commands: + - hugo --gc --minify + artifacts: + baseDirectory: public + files: + - '**/*' + cache: + paths: + - ${HUGO_CACHEDIR}/**/* + - ${NPM_CONFIG_CACHE}/**/* +{{< /code >}} + +Step 3 +: Commit and push the change to your GitHub repository. + +```sh +git add -A +git commit -m "Create amplify.yml" +git push +``` + +Step 4 +: Log in to your AWS account, navigate to the [Amplify Console], then press the **Deploy an app** button. + +[Amplify Console]: https://console.aws.amazon.com/amplify/apps + +Step 5 +: Choose a source code provider, then press the **Next** button. + + ![screen capture](amplify-step-05.png) + +Step 6 +: Authorize AWS Amplify to access your GitHub account. + + ![screen capture](amplify-step-06.png) + +Step 7 +: Select your personal account or relevant organization. + + ![screen capture](amplify-step-07.png) + +Step 8 +: Authorize access to one or more repositories. + + ![screen capture](amplify-step-08.png) + +Step 9 +: Select a repository and branch, then press the **Next** button. + + ![screen capture](amplify-step-09.png) + +Step 10 +: On the "App settings" page, scroll to the bottom then press the **Next** button. Amplify reads the `amplify.yml` file you created in Steps 1-3 instead of using the values on this page. + +Step 11 +: On the "Review" page, scroll to the bottom then press the **Save and deploy** button. + +Step 12 +: When your site has finished deploying, press the **Visit deployed URL** button to view your published site. + + ![screen capture](amplify-step-11.png) diff --git a/content/en/hosting-and-deployment/hosting-on-github/index.md b/content/en/hosting-and-deployment/hosting-on-github/index.md index 28c5ff841..45adeda1f 100644 --- a/content/en/hosting-and-deployment/hosting-on-github/index.md +++ b/content/en/hosting-and-deployment/hosting-on-github/index.md @@ -12,6 +12,8 @@ aliases: [/tutorials/github-pages-blog/] ## Prerequisites +Please complete the following tasks before continuing: + 1. [Create a GitHub account] 2. [Install Git] 3. [Create a Hugo site] and test it locally with `hugo server`. @@ -53,10 +55,11 @@ Step 4 {style="max-width: 280px"} Step 5 -: Create an empty file in your local repository. +: Create a file named `hugo.yaml` in a directory named `.github/workflows`. ```text -.github/workflows/hugo.yaml +mkdir -p .github/workflows +touch hugo.yaml ``` Step 6 @@ -144,7 +147,13 @@ jobs: {{< /code >}} Step 7 -: Commit the change to your local repository with a commit message of something like "Add workflow", and push to GitHub. +: Commit and push the change to your GitHub repository. + +```sh +git add -A +git commit -m "Create hugo.yaml" +git push +``` Step 8 : From GitHub's main menu, choose **Actions**. You will see something like this: @@ -181,7 +190,7 @@ You may remove this step if your site, themes, and modules do not transpile Sass [Dart Sass]: /hugo-pipes/transpile-sass-to-css/#dart-sass -## Additional resources +## Other resources - [Learn more about GitHub Actions](https://docs.github.com/en/actions) - [Caching dependencies to speed up workflows](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows) diff --git a/content/en/hosting-and-deployment/hosting-on-netlify/index.md b/content/en/hosting-and-deployment/hosting-on-netlify/index.md index a63451062..361a40e80 100644 --- a/content/en/hosting-and-deployment/hosting-on-netlify/index.md +++ b/content/en/hosting-and-deployment/hosting-on-netlify/index.md @@ -11,11 +11,13 @@ toc: true ## Prerequisites +Please complete the following tasks before continuing: + 1. [Create a Netlify account] 2. [Install Git] 3. [Create a Hugo site] and test it locally with `hugo server` 4. Commit the changes to your local repository -4. Push the local repository to your [GitHub], [GitLab], or [Bitbucket] account +5. Push the local repository to your [GitHub], [GitLab], or [Bitbucket] account [Bitbucket]: https://bitbucket.org/product [Create a Hugo site]: /getting-started/quick-start/ @@ -34,64 +36,64 @@ Step 1 Step 2 : Select your deployment method. -![screen capture](netlify-step-02.png) + ![screen capture](netlify-step-02.png) Step 3 : Authorize Netlify to connect with your GitHub account by pressing the **Authorize Netlify** button. -![screen capture](netlify-step-03.png) + ![screen capture](netlify-step-03.png) Step 4 : Press the **Configure Netlify on GitHub** button. -![screen capture](netlify-step-04.png) + ![screen capture](netlify-step-04.png) Step 5 : Install the Netlify app by selecting your GitHub account. -![screen capture](netlify-step-05.png) + ![screen capture](netlify-step-05.png) Step 6 : Press the **Install** button. -![screen capture](netlify-step-06.png) + ![screen capture](netlify-step-06.png) Step 7 : Click on the site's repository from the list. -![screen capture](netlify-step-07.png) + ![screen capture](netlify-step-07.png) Step 8 : Set the site name and branch from which to deploy. -![screen capture](netlify-step-08.png) + ![screen capture](netlify-step-08.png) Step 9 : Define the build settings, press the **Add environment variables** button, then press the **New variable** button. -![screen capture](netlify-step-09.png) + ![screen capture](netlify-step-09.png) Step 10 : Create a new environment variable named `HUGO_VERSION` and set the value to the [latest version]. [latest version]: https://github.com/gohugoio/hugo/releases/latest -![screen capture](netlify-step-10.png) + ![screen capture](netlify-step-10.png) Step 11 : Press the "Deploy my new site" button at the bottom of the page. -![screen capture](netlify-step-11.png) + ![screen capture](netlify-step-11.png) Step 12 : At the bottom of the screen, wait for the deploy to complete, then click on the deploy log entry. -![screen capture](netlify-step-12.png) + ![screen capture](netlify-step-12.png) Step 13 : Press the **Open production deploy** button to view the live site. -![screen capture](netlify-step-13.png) + ![screen capture](netlify-step-13.png) ## Configuration file diff --git a/content/en/hugo-modules/use-modules.md b/content/en/hugo-modules/use-modules.md index 913e4f775..d14379a10 100644 --- a/content/en/hugo-modules/use-modules.md +++ b/content/en/hugo-modules/use-modules.md @@ -133,8 +133,6 @@ Also see the [CLI Doc](/commands/hugo_mod_clean/). ## Module workspaces -{{< new-in 0.109.0 >}} - Workspace support was added in [Go 1.18](https://go.dev/blog/get-familiar-with-workspaces) and Hugo got solid support for it in the `v0.109.0` version. A common use case for a workspace is to simplify local development of a site with its theme modules. diff --git a/content/en/hugo-pipes/postprocess.md b/content/en/hugo-pipes/postprocess.md index cf99049fe..57c71e8ab 100755 --- a/content/en/hugo-pipes/postprocess.md +++ b/content/en/hugo-pipes/postprocess.md @@ -88,7 +88,7 @@ HUGO_ENVIRONMENT : The value e.g. set with `hugo -e production` (defaults to `production` for `hugo` and `development` for `hugo server`). HUGO_PUBLISHDIR -: {{< new-in 0.109.0 >}} The absolute path to the publish directory (the `public` directory). Note that the value will always point to a directory on disk even when running `hugo server` in memory mode. If you write to this folder from PostCSS when running the server, you could run the server with one of these flags: +: The absolute path to the publish directory (the `public` directory). Note that the value will always point to a directory on disk even when running `hugo server` in memory mode. If you write to this folder from PostCSS when running the server, you could run the server with one of these flags: ```sh hugo server --renderToDisk diff --git a/content/en/hugo-pipes/transpile-sass-to-css.md b/content/en/hugo-pipes/transpile-sass-to-css.md index 67ef4b7cd..886488ca6 100644 --- a/content/en/hugo-pipes/transpile-sass-to-css.md +++ b/content/en/hugo-pipes/transpile-sass-to-css.md @@ -42,7 +42,7 @@ transpiler 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 {{< new-in 0.109.0 >}} +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 diff --git a/content/en/installation/_common/01-editions.md b/content/en/installation/_common/01-editions.md index 7a80a522e..b6fc23c74 100644 --- a/content/en/installation/_common/01-editions.md +++ b/content/en/installation/_common/01-editions.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo is available in three editions: standard, extended, and extended/deploy. While the standard edition provides core functionality, the extended and extended/deploy editions offer advanced features. diff --git a/content/en/installation/_common/02-prerequisites.md b/content/en/installation/_common/02-prerequisites.md index ea6eaa8fe..65ec2a12f 100644 --- a/content/en/installation/_common/02-prerequisites.md +++ b/content/en/installation/_common/02-prerequisites.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## Prerequisites diff --git a/content/en/installation/_common/03-prebuilt-binaries.md b/content/en/installation/_common/03-prebuilt-binaries.md index 55f7cb85b..34411cddd 100644 --- a/content/en/installation/_common/03-prebuilt-binaries.md +++ b/content/en/installation/_common/03-prebuilt-binaries.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## Prebuilt binaries diff --git a/content/en/installation/_common/04-build-from-source.md b/content/en/installation/_common/04-build-from-source.md index 6829c50ce..b3039d18a 100644 --- a/content/en/installation/_common/04-build-from-source.md +++ b/content/en/installation/_common/04-build-from-source.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## Build from source diff --git a/content/en/installation/_common/homebrew.md b/content/en/installation/_common/homebrew.md index 6cd7a524f..14f48174e 100644 --- a/content/en/installation/_common/homebrew.md +++ b/content/en/installation/_common/homebrew.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ### Homebrew diff --git a/content/en/installation/linux.md b/content/en/installation/linux.md index 2403bf47b..14479d004 100644 --- a/content/en/installation/linux.md +++ b/content/en/installation/linux.md @@ -192,6 +192,16 @@ sudo eopkg install hugo [Solus]: https://getsol.us/ +### Void Linux + +To install the extended edition of Hugo on [Void Linux]: + +```sh +sudo xbps-install -S hugo +``` + +[Void Linux]: https://voidlinux.org/ + {{% include "installation/_common/04-build-from-source.md" %}} ## Comparison diff --git a/content/en/methods/menu-entry/PageRef.md b/content/en/methods/menu-entry/PageRef.md new file mode 100644 index 000000000..5d99d628c --- /dev/null +++ b/content/en/methods/menu-entry/PageRef.md @@ -0,0 +1,120 @@ +--- +title: PageRef +description: Returns the `pageRef` property of the given menu entry. +categories: [] +keywords: [] +action: + related: + - /methods/menu-entry/URL + returnType: string + signatures: [MENUENTRY.PageRef] +toc: true +--- + +The use case for this method is rare. + +In almost also scenarios you should use the [`URL`] method instead. + +## Explanation + +If you specify a `pageRef` property when [defining a menu entry] in your site configuration, Hugo looks for a matching page when rendering the entry. + +If a matching page is found: + +- The [`URL`] method returns the page's relative permalink +- The [`Page`] method returns the corresponding `Page` object +- The [`HasMenuCurrent`] and [`IsMenuCurrent`] methods on a `Page` object return the expected values + +If a matching page is not found: + +- The [`URL`] method returns the entry's `url` property if set, else an empty string +- The [`Page`] method returns nil +- The [`HasMenuCurrent`] and [`IsMenuCurrent`] methods on a `Page` object return `false` + +{{% note %}} +In almost also scenarios you should use the [`URL`] method instead. + +[`URL`]: /methods/menu-entry/url/ +{{% /note %}} + +[defining a menu entry]: /content-management/menus/#define-in-site-configuration +[`Page`]: /methods/menu-entry/page/ +[`URL`]: /methods/menu-entry/url/ +[`IsMenuCurrent`]: /methods/page/ismenucurrent/ +[`HasMenuCurrent`]: /methods/page/hasmenucurrent/ +[`RelPermalink`]: /methods/page/relpermalink/ + +## Example + +This example is contrived. + +{{% note %}} +In almost also scenarios you should use the [`URL`] method instead. + +[`URL`]: /methods/menu-entry/url/ +{{% /note %}} + + +Consider this content structure: + +```text +content/ +├── products.md +└── _index.md +``` + +And this menu definition: + +{{< code-toggle file=hugo >}} +[[menus.main]] +name = 'Products' +pageRef = '/products' +weight = 10 +[[menus.main]] +name = 'Services' +pageRef = '/services' +weight = 20 +{{< /code-toggle >}} + +With this template code: + +{{< code file=layouts/partials/menu.html >}} +
    + {{ range .Site.Menus.main }} +
  • {{ .Name }}
  • + {{ end }} +
+{{< /code >}} + +Hugo render this HTML: + +```html + +``` + +In the above note that the `href` attribute of the second `anchor` element is blank because Hugo was unable to find the "services" page. + +With this template code: + + +{{< code file=layouts/partials/menu.html >}} +
    + {{ range .Site.Menus.main }} +
  • {{ .Name }}
  • + {{ end }} +
+{{< /code >}} + +Hugo renders this HTML: + +```html + +``` + +In the above note that Hugo populates the `href` attribute of the second `anchor` element with the `pageRef` property as defined in the site configuration because the template code falls back to the `PageRef` method. diff --git a/content/en/methods/menu-entry/_common/pre-post.md b/content/en/methods/menu-entry/_common/pre-post.md index fbfce062a..da3d584d1 100644 --- a/content/en/methods/menu-entry/_common/pre-post.md +++ b/content/en/methods/menu-entry/_common/pre-post.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- In this site configuration we enable rendering of [emoji shortcodes], and add emoji shortcodes before (pre) and after (post) each menu entry: diff --git a/content/en/methods/page/Ancestors.md b/content/en/methods/page/Ancestors.md index 3bf36d9aa..da5cf2ca3 100644 --- a/content/en/methods/page/Ancestors.md +++ b/content/en/methods/page/Ancestors.md @@ -16,8 +16,6 @@ action: signatures: [PAGE.Ancestors] --- -{{< new-in 0.109.0 >}} - {{% include "methods/page/_common/definition-of-section.md" %}} With this content structure: diff --git a/content/en/methods/page/GitInfo.md b/content/en/methods/page/GitInfo.md index c54a77371..a05916d79 100644 --- a/content/en/methods/page/GitInfo.md +++ b/content/en/methods/page/GitInfo.md @@ -141,6 +141,7 @@ Some providers perform deep clones by default, others allow you to configure the Hosting service | Default clone depth | Configurable :-- | :-- | :-- +AWS Amplify | Deep | N/A Cloudflare Pages | Shallow | Yes [^CFP] DigitalOcean App Platform | Deep | N/A GitHub Pages | Shallow | Yes [^GHP] diff --git a/content/en/methods/page/HasMenuCurrent.md b/content/en/methods/page/HasMenuCurrent.md index a092f22c0..24ce462c9 100644 --- a/content/en/methods/page/HasMenuCurrent.md +++ b/content/en/methods/page/HasMenuCurrent.md @@ -28,4 +28,8 @@ If the `Page` object associated with the menu entry is a section, this method al See [menu templates] for a complete example. +{{% note %}} +When using this method you must either define the menu entry in front matter, or specify a `pageRef` property when defining the menu entry in your site configuration. +{{% /note %}} + [menu templates]: /templates/menu/#example diff --git a/content/en/methods/page/IsMenuCurrent.md b/content/en/methods/page/IsMenuCurrent.md index 8a939b1a1..a16be51ee 100644 --- a/content/en/methods/page/IsMenuCurrent.md +++ b/content/en/methods/page/IsMenuCurrent.md @@ -26,4 +26,8 @@ aliases: [/functions/ismenucurrent] See [menu templates] for a complete example. +{{% note %}} +When using this method you must either define the menu entry in front matter, or specify a `pageRef` property when defining the menu entry in your site configuration. +{{% /note %}} + [menu templates]: /templates/menu/#example diff --git a/content/en/methods/page/Store.md b/content/en/methods/page/Store.md index a81fa71a3..484c27fd5 100644 --- a/content/en/methods/page/Store.md +++ b/content/en/methods/page/Store.md @@ -1,11 +1,14 @@ --- title: Store +linktitle: PAGE.Store description: Returns a persistent "scratch pad" on the given page to store and manipulate data. categories: [] keywords: [] action: related: - methods/page/scratch + - methods/site/store + - functions/hugo/store - functions/collections/NewScratch returnType: maps.Scratch signatures: [PAGE.Store] diff --git a/content/en/methods/page/_common/definition-of-section.md b/content/en/methods/page/_common/definition-of-section.md index 7dc600789..79e22b10d 100644 --- a/content/en/methods/page/_common/definition-of-section.md +++ b/content/en/methods/page/_common/definition-of-section.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- A _section_ is a top-level content directory, or any content directory with an _index.md file. diff --git a/content/en/methods/page/_common/next-and-prev.md b/content/en/methods/page/_common/next-and-prev.md index 0d1436e11..c71392c8f 100644 --- a/content/en/methods/page/_common/next-and-prev.md +++ b/content/en/methods/page/_common/next-and-prev.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo determines the _next_ and _previous_ page by sorting the site's collection of regular pages according to this sorting hierarchy: diff --git a/content/en/methods/page/_common/nextinsection-and-previnsection.md b/content/en/methods/page/_common/nextinsection-and-previnsection.md index 6c558b69e..f2126eaee 100644 --- a/content/en/methods/page/_common/nextinsection-and-previnsection.md +++ b/content/en/methods/page/_common/nextinsection-and-previnsection.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo determines the _next_ and _previous_ page by sorting the current section's regular pages according to this sorting hierarchy: diff --git a/content/en/methods/page/_common/output-format-definition.md b/content/en/methods/page/_common/output-format-definition.md index d21211a3d..df89447f7 100644 --- a/content/en/methods/page/_common/output-format-definition.md +++ b/content/en/methods/page/_common/output-format-definition.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo generates one or more files per page when building a site. For example, when rendering home, [section], [taxonomy], and [term] pages, Hugo generates an HTML file and an RSS file. Both HTML and RSS are built-in _output formats_. Create multiple output formats, and control generation based on [page kind], or by enabling one or more output formats for one or more pages. See [details]. diff --git a/content/en/methods/page/_common/output-format-methods.md b/content/en/methods/page/_common/output-format-methods.md index 5e7111fe5..5390e7b46 100644 --- a/content/en/methods/page/_common/output-format-methods.md +++ b/content/en/methods/page/_common/output-format-methods.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Get IDENTIFIER diff --git a/content/en/methods/page/_common/scratch-methods.md b/content/en/methods/page/_common/scratch-methods.md index b2650cdde..92a97cdd5 100644 --- a/content/en/methods/page/_common/scratch-methods.md +++ b/content/en/methods/page/_common/scratch-methods.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## Methods diff --git a/content/en/methods/pages/_common/group-sort-order.md b/content/en/methods/pages/_common/group-sort-order.md index bb5be82f6..e2997a1bd 100644 --- a/content/en/methods/pages/_common/group-sort-order.md +++ b/content/en/methods/pages/_common/group-sort-order.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- For the optional sort order, specify either `asc` for ascending order, or `desc` for descending order. diff --git a/content/en/methods/pages/_common/next-and-prev.md b/content/en/methods/pages/_common/next-and-prev.md index e0d05de84..540783992 100644 --- a/content/en/methods/pages/_common/next-and-prev.md +++ b/content/en/methods/pages/_common/next-and-prev.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Hugo determines the _next_ and _previous_ page by sorting the page collection according to this sorting hierarchy: diff --git a/content/en/methods/resource/_common/global-page-remote-resources.md b/content/en/methods/resource/_common/global-page-remote-resources.md index 4ea4d1b87..5df2963e3 100644 --- a/content/en/methods/resource/_common/global-page-remote-resources.md +++ b/content/en/methods/resource/_common/global-page-remote-resources.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- {{% note %}} diff --git a/content/en/methods/resource/_common/processing-spec.md b/content/en/methods/resource/_common/processing-spec.md index b12a21d3a..395217328 100644 --- a/content/en/methods/resource/_common/processing-spec.md +++ b/content/en/methods/resource/_common/processing-spec.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## Process specification diff --git a/content/en/methods/shortcode/Ordinal.md b/content/en/methods/shortcode/Ordinal.md index 6f3580d0f..41393fa22 100644 --- a/content/en/methods/shortcode/Ordinal.md +++ b/content/en/methods/shortcode/Ordinal.md @@ -11,6 +11,10 @@ action: The `Ordinal` method returns the zero-based ordinal of the shortcode in relation to its parent. If the parent is the page itself, the ordinal represents the position of this shortcode in the page content. +{{% note %}} +Hugo increments the ordinal with each shortcode call, regardless of the specific shortcode type. This means that the ordinal value is tracked sequentially across all shortcodes within a given page. +{{% /note %}} + This method is useful for, among other things, assigning unique element IDs when a shortcode is called two or more times from the same page. For example: {{< code file=content/about.md lang=md >}} diff --git a/content/en/methods/shortcode/Scratch.md b/content/en/methods/shortcode/Scratch.md index fcfc99d53..811d9f9ee 100644 --- a/content/en/methods/shortcode/Scratch.md +++ b/content/en/methods/shortcode/Scratch.md @@ -10,7 +10,17 @@ action: signatures: [SHORTCODE.Scratch] --- -The `Scratch` method within a shortcode creates a [scratch pad] to store and manipulate data. The scratch pad is scoped to the shortcode, and is reset on server rebuilds. +{{% deprecated-in 0.139.0 %}} +Use the [`SHORTCODE.Store`] method instead. + +This is a soft deprecation. This method will be removed in a future release, but the removal date has not been established. Although Hugo will not emit a warning if you continue to use this method, you should begin using `SHORTCODE.Store` as soon as possible. + +Beginning with v0.139.0 the `SHORTCODE.Scratch` method is aliased to `SHORTCODE.Store`. + +[`SHORTCODE.Store`]: /methods/shortcode/store/ +{{% /deprecated-in %}} + +The `Scratch` method within a shortcode creates a [scratch pad] to store and manipulate data. The scratch pad is scoped to the shortcode. {{% note %}} With the introduction of the [`newScratch`] function, and the ability to [assign values to template variables] after initialization, the `Scratch` method within a shortcode is obsolete. diff --git a/content/en/methods/shortcode/Store.md b/content/en/methods/shortcode/Store.md new file mode 100644 index 000000000..f7be8c8d0 --- /dev/null +++ b/content/en/methods/shortcode/Store.md @@ -0,0 +1,29 @@ +--- +title: Store +description: Returns a "Store pad" scoped to the shortcode to store and manipulate data. +categories: [] +keywords: [] +action: + related: + - functions/collections/NewScratch + - methods/page/Store + - methods/site/Store + - functions/hugo/Store + returnType: maps.Store + signatures: [SHORTCODE.Store] +--- + +{{< new-in 0.139.0 >}} + +The `Store` method within a shortcode creates a [scratch pad] to store and manipulate data. The scratch pad is scoped to the shortcode. + +{{% note %}} +With the introduction of the [`newScratch`] function, and the ability to [assign values to template variables] after initialization, the `Store` method within a shortcode is mostly obsolete. + +[assign values to template variables]: https://go.dev/doc/go1.11#text/template +[`newScratch`]: /functions/collections/newScratch/ +{{% /note %}} + +[Store pad]: /getting-started/glossary/#scratch-pad + +{{% include "methods/page/_common/scratch-methods.md" %}} diff --git a/content/en/methods/site/Store.md b/content/en/methods/site/Store.md new file mode 100644 index 000000000..1c54007ab --- /dev/null +++ b/content/en/methods/site/Store.md @@ -0,0 +1,126 @@ +--- +title: Store +linktitle: site.Store +description: Returns a persistent "scratch pad" on the given site to store and manipulate data. +categories: [] +keywords: [] +action: + related: + - methods/page/store + - functions/hugo/store + - functions/collections/NewScratch + returnType: maps.Scratch + signatures: [site.Store] +toc: true +--- + +{{< new-in 0.139.0 >}} + +The `Store` method on a `Site` object creates a persistent [scratch pad] to store and manipulate data. To create a locally scoped scratch pad that is not attached to a `Site` object, use the [`newScratch`] function. + +[`Scratch`]: /methods/site/scratch/ +[`newScratch`]: /functions/collections/newscratch/ +[scratch pad]: /getting-started/glossary/#scratch-pad + +## Methods + +###### Set + +Sets the value of a given key. + +```go-html-template +{{ site.Store.Set "greeting" "Hello" }} +``` + +###### Get + +Gets the value of a given key. + +```go-html-template +{{ site.Store.Set "greeting" "Hello" }} +{{ site.Store.Get "greeting" }} → Hello +``` + +###### Add + +Adds a given value to existing value(s) of the given key. + +For single values, `Add` accepts values that support Go's `+` operator. If the first `Add` for a key is an array or slice, the following adds will be appended to that list. + +```go-html-template +{{ site.Store.Set "greeting" "Hello" }} +{{ site.Store.Add "greeting" "Welcome" }} +{{ site.Store.Get "greeting" }} → HelloWelcome +``` + +```go-html-template +{{ site.Store.Set "total" 3 }} +{{ site.Store.Add "total" 7 }} +{{ site.Store.Get "total" }} → 10 +``` + +```go-html-template +{{ site.Store.Set "greetings" (slice "Hello") }} +{{ site.Store.Add "greetings" (slice "Welcome" "Cheers") }} +{{ site.Store.Get "greetings" }} → [Hello Welcome Cheers] +``` + +###### SetInMap + +Takes a `key`, `mapKey` and `value` and adds a map of `mapKey` and `value` to the given `key`. + +```go-html-template +{{ site.Store.SetInMap "greetings" "english" "Hello" }} +{{ site.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ site.Store.Get "greetings" }} → map[english:Hello french:Bonjour] +``` + +###### DeleteInMap + +Takes a `key` and `mapKey` and removes the map of `mapKey` from the given `key`. + +```go-html-template +{{ site.Store.SetInMap "greetings" "english" "Hello" }} +{{ site.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ site.Store.DeleteInMap "greetings" "english" }} +{{ site.Store.Get "greetings" }} → map[french:Bonjour] +``` + +###### GetSortedMapValues + +Returns an array of values from `key` sorted by `mapKey`. + +```go-html-template +{{ site.Store.SetInMap "greetings" "english" "Hello" }} +{{ site.Store.SetInMap "greetings" "french" "Bonjour" }} +{{ site.Store.GetSortedMapValues "greetings" }} → [Hello Bonjour] +``` + +###### Delete + +Removes the given key. + +```go-html-template +{{ site.Store.Set "greeting" "Hello" }} +{{ site.Store.Delete "greeting" }} +``` + +## Determinate values + +The `Store` method is often used to set scratch pad values within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are indeterminate until Hugo renders the page content. + +If you need to access a scratch pad value from a parent template, and the parent template has not yet rendered the page content, you can trigger content rendering by assigning the returned value to a [noop] variable: + +[noop]: /getting-started/glossary/#noop + +```go-html-template +{{ $noop := .Content }} +{{ site.Store.Get "mykey" }} +``` + +You can also trigger content rendering with the `ContentWithoutSummary`, `FuzzyWordCount`, `Len`, `Plain`, `PlainWords`, `ReadingTime`, `Summary`, `Truncated`, and `WordCount` methods. For example: + +```go-html-template +{{ $noop := .WordCount }} +{{ site.Store.Get "mykey" }} +``` diff --git a/content/en/methods/taxonomy/_common/get-a-taxonomy-object.md b/content/en/methods/taxonomy/_common/get-a-taxonomy-object.md index ef78c8eae..928ec085a 100644 --- a/content/en/methods/taxonomy/_common/get-a-taxonomy-object.md +++ b/content/en/methods/taxonomy/_common/get-a-taxonomy-object.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- Before we can use a `Taxonomy` method, we need to capture a `Taxonomy` object. diff --git a/content/en/methods/taxonomy/_common/ordered-taxonomy-element-methods.md b/content/en/methods/taxonomy/_common/ordered-taxonomy-element-methods.md index 7201ad318..a23c26ef5 100644 --- a/content/en/methods/taxonomy/_common/ordered-taxonomy-element-methods.md +++ b/content/en/methods/taxonomy/_common/ordered-taxonomy-element-methods.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- An ordered taxonomy is a slice, where each element is an object that contains the term and a slice of its weighted pages. diff --git a/content/en/render-hooks/_common/pageinner.md b/content/en/render-hooks/_common/pageinner.md index de1316cba..1d467aeef 100644 --- a/content/en/render-hooks/_common/pageinner.md +++ b/content/en/render-hooks/_common/pageinner.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- ## PageInner details diff --git a/content/en/render-hooks/code-blocks.md b/content/en/render-hooks/code-blocks.md index d322cb88d..f43df06c7 100755 --- a/content/en/render-hooks/code-blocks.md +++ b/content/en/render-hooks/code-blocks.md @@ -122,7 +122,7 @@ For example, to create a code block render hook to render [Mermaid] diagrams: {{< code file=layouts/_default/_markup/render-codeblock-mermaid.html copy=true >}}
-  {{- .Inner | safeHTML }}
+  {{- .Inner | htmlEscape | safeHTML }}
 
{{ .Page.Store.Set "hasMermaid" true }} {{< /code >}} diff --git a/content/en/templates/_common/filter-sort-group.md b/content/en/templates/_common/filter-sort-group.md index 89cc095f5..24bbee96e 100644 --- a/content/en/templates/_common/filter-sort-group.md +++ b/content/en/templates/_common/filter-sort-group.md @@ -1,5 +1,5 @@ --- -# Do not remove front matter. +_comment: Do not remove front matter. --- {{% note %}} diff --git a/content/en/templates/introduction.md b/content/en/templates/introduction.md index e5650149a..f8f50dfaa 100644 --- a/content/en/templates/introduction.md +++ b/content/en/templates/introduction.md @@ -167,7 +167,7 @@ Whitespace includes spaces, horizontal tabs, carriage returns, and newlines. ### Pipes -Within a template action you may [pipe] a value to function or method. The piped value becomes the final argument to the function or method. For example, these are equivalent: +Within a template action you may [pipe] a value to a function or method. The piped value becomes the final argument to the function or method. For example, these are equivalent: [pipe]: /getting-started/glossary/#pipeline diff --git a/data/docs.yaml b/data/docs.yaml index 8d97c21e0..4999bfc0b 100644 --- a/data/docs.yaml +++ b/data/docs.yaml @@ -2440,17 +2440,6 @@ tpl: treating values as key-value pairs. The number of values must be even. The keys can be string slices, which will create the needed nested structure. Examples: [] - EchoParam: - Aliases: - - echoParam - Args: - - c - - k - Description: |- - EchoParam returns the value in the collection c with key k if is set; otherwise, it returns an - empty string. - Deprecated: Use the index function instead. - Examples: [] First: Aliases: - first @@ -3119,6 +3108,11 @@ tpl: Args: null Description: "" Examples: null + Store: + Aliases: null + Args: null + Description: "" + Examples: null Version: Aliases: null Args: null @@ -3384,11 +3378,6 @@ tpl: Args: null Description: "" Examples: null - NumFmt: - Aliases: null - Args: null - Description: "" - Examples: null Translate: Aliases: - i18n @@ -4002,11 +3991,6 @@ tpl: Args: null Description: "" Examples: null - DisqusShortname: - Aliases: null - Args: null - Description: "" - Examples: null ForEeachIdentityByName: Aliases: null Args: null @@ -4017,11 +4001,6 @@ tpl: Args: null Description: "" Examples: null - GoogleAnalytics: - Aliases: null - Args: null - Description: "" - Examples: null Home: Aliases: null Args: null @@ -4037,11 +4016,6 @@ tpl: Args: null Description: "" Examples: null - IsServer: - Aliases: null - Args: null - Description: "" - Examples: null Key: Aliases: null Args: null @@ -4102,11 +4076,6 @@ tpl: Args: null Description: "" Examples: null - RSSLink: - Aliases: null - Args: null - Description: "" - Examples: null RegularPages: Aliases: null Args: null @@ -4132,6 +4101,11 @@ tpl: Args: null Description: "" Examples: null + Store: + Aliases: null + Args: null + Description: "" + Examples: null Taxonomies: Aliases: null Args: null @@ -4457,6 +4431,11 @@ tpl: Examples: - - '{{ "aabbaa" | strings.TrimRight "a" }}' - aabb + TrimSpace: + Aliases: null + Args: null + Description: "" + Examples: null TrimSuffix: Aliases: null Args: diff --git a/go.mod b/go.mod index a9d63af19..5e909630f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/gohugoio/hugoDocs -go 1.16 +go 1.22.0 -require github.com/gohugoio/gohugoioTheme v0.0.0-20241105120803-6c6e5fb8f8af // indirect +require github.com/gohugoio/gohugoioTheme v0.0.0-20241119115653-b92d27ede3e1 // indirect diff --git a/go.sum b/go.sum index 5c0e10736..e1863b60e 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,2 @@ -github.com/gohugoio/gohugoioTheme v0.0.0-20241105040910-e9dac9458255 h1:kaSc7cVAifWPRzmECr7il0YXgXBM+H2ZrGcNnb03S8k= -github.com/gohugoio/gohugoioTheme v0.0.0-20241105040910-e9dac9458255/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20241105120803-6c6e5fb8f8af h1:H8Oa4AEJs2yz8w1Gq9hEGBJNukkKo05OAaIEsHMd63k= -github.com/gohugoio/gohugoioTheme v0.0.0-20241105120803-6c6e5fb8f8af/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= +github.com/gohugoio/gohugoioTheme v0.0.0-20241119115653-b92d27ede3e1 h1:d6XNQ4QYvJGIE8vMejUFTD89AWaPDywcLivzWpEU0qE= +github.com/gohugoio/gohugoioTheme v0.0.0-20241119115653-b92d27ede3e1/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= diff --git a/netlify.toml b/netlify.toml index 5079ac44d..995057a75 100644 --- a/netlify.toml +++ b/netlify.toml @@ -3,7 +3,7 @@ command = "hugo --gc --minify" [build.environment] - HUGO_VERSION = "0.138.0" + HUGO_VERSION = "0.139.4" [context.production.environment] HUGO_ENV = "production" From 5f897868c07f0b227a1a316d42d404ea44b2fc4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 08:26:45 +0000 Subject: [PATCH 005/374] build(deps): bump github.com/hairyhenderson/go-codeowners Bumps [github.com/hairyhenderson/go-codeowners](https://github.com/hairyhenderson/go-codeowners) from 0.6.1 to 0.7.0. - [Release notes](https://github.com/hairyhenderson/go-codeowners/releases) - [Changelog](https://github.com/hairyhenderson/go-codeowners/blob/main/CHANGELOG.md) - [Commits](https://github.com/hairyhenderson/go-codeowners/compare/v0.6.1...v0.7.0) --- updated-dependencies: - dependency-name: github.com/hairyhenderson/go-codeowners dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 523027d11..32de8796f 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 github.com/google/go-cmp v0.6.0 github.com/gorilla/websocket v1.5.3 - github.com/hairyhenderson/go-codeowners v0.6.1 + github.com/hairyhenderson/go-codeowners v0.7.0 github.com/jdkato/prose v1.2.1 github.com/kylelemons/godebug v1.1.0 github.com/kyokomi/emoji/v2 v2.2.13 diff --git a/go.sum b/go.sum index d995f8266..758ea0c62 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,6 @@ github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= -github.com/bep/godartsass/v2 v2.3.1 h1:Lh9v0IxAaB+toyZrZ+XYNYcGXI1PzGzvZ4u8tRWTnLw= -github.com/bep/godartsass/v2 v2.3.1/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo= github.com/bep/godartsass/v2 v2.3.2 h1:meuc76J1C1soSCAnlnJRdGqJ5S4m6/GW+8hmOe9tOog= github.com/bep/godartsass/v2 v2.3.2/go.mod h1:Qe5WOS9nVJy7G0jHssXPd3c+Pqk/f7+Tm6k/vahbVgs= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= @@ -326,8 +324,8 @@ github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hairyhenderson/go-codeowners v0.6.1 h1:2OLPpLWFMxkCf9hkYzOexnCGD+kj853OqeoKq7S+9us= -github.com/hairyhenderson/go-codeowners v0.6.1/go.mod h1:RFWbGcjlXhRKNezt7AQHmJucY0alk4osN0+RKOsIAa8= +github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= +github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -445,8 +443,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tdewolff/minify/v2 v2.20.37 h1:Q97cx4STXCh1dlWDlNHZniE8BJ2EBL0+2b0n92BJQhw= github.com/tdewolff/minify/v2 v2.20.37/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU= github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw= @@ -852,8 +850,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 989b299b55d3fb48f8447ea69e85e26ee9dc7795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 11 Dec 2024 13:14:49 +0100 Subject: [PATCH 006/374] Remove some old and unused shell scripts --- bench.sh | 37 ------------------------------------- benchSite.sh | 12 ------------ benchbep.sh | 1 - bepdock.sh | 1 - 4 files changed, 51 deletions(-) delete mode 100755 bench.sh delete mode 100755 benchSite.sh delete mode 100755 benchbep.sh delete mode 100755 bepdock.sh diff --git a/bench.sh b/bench.sh deleted file mode 100755 index c6a20a7e3..000000000 --- a/bench.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# allow user to override go executable by running as GOEXE=xxx make ... -GOEXE="${GOEXE-go}" - -# Convenience script to -# - For a given branch -# - Run benchmark tests for a given package -# - Do the same for master -# - then compare the two runs with benchcmp - -benchFilter=".*" - -if (( $# < 2 )); - then - echo "USAGE: ./bench.sh (and (regexp, optional))" - exit 1 -fi - - - -if [ $# -eq 3 ]; then - benchFilter=$3 -fi - - -BRANCH=$1 -PACKAGE=$2 - -git checkout $BRANCH -"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-$BRANCH.txt - -git checkout master -"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-master.txt - - -benchcmp /tmp/bench-$PACKAGE-master.txt /tmp/bench-$PACKAGE-$BRANCH.txt diff --git a/benchSite.sh b/benchSite.sh deleted file mode 100755 index aae21231c..000000000 --- a/benchSite.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# allow user to override go executable by running as GOEXE=xxx make ... -GOEXE="${GOEXE-go}" - -# Send in a regexp matching the benchmarks you want to run, i.e. './benchSite.sh "YAML"'. -# Note the quotes, which will be needed for more complex expressions. -# The above will run all variations, but only for front matter YAML. - -echo "Running with BenchmarkSiteBuilding/${1}" - -"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -count 3 -cpuprofile cpu.prof diff --git a/benchbep.sh b/benchbep.sh deleted file mode 100755 index efd616c88..000000000 --- a/benchbep.sh +++ /dev/null @@ -1 +0,0 @@ -gobench -package=./hugolib -bench="BenchmarkSiteNew/Deep_content_tree" \ No newline at end of file diff --git a/bepdock.sh b/bepdock.sh deleted file mode 100755 index a7ac0c639..000000000 --- a/bepdock.sh +++ /dev/null @@ -1 +0,0 @@ -docker run --rm --mount type=bind,source="$(pwd)",target=/hugo -w /hugo -i -t bepsays/ci-goreleaser:1.11-2 /bin/bash \ No newline at end of file From 947e4e66b504f28f2eb5974452a87c688d73c92b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:51:24 +0000 Subject: [PATCH 007/374] build(deps): bump golang.org/x/tools from 0.27.0 to 0.28.0 Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 32de8796f..5d419bd58 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( golang.org/x/net v0.32.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 - golang.org/x/tools v0.27.0 + golang.org/x/tools v0.28.0 google.golang.org/api v0.206.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 758ea0c62..af2b64e39 100644 --- a/go.sum +++ b/go.sum @@ -744,8 +744,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 6be253000f98260ab1e5b70f8ba0af147555e7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 12 Dec 2024 10:38:55 +0100 Subject: [PATCH 008/374] Upgrade to Go 1.23.4 Closes #13130 --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ab2dd4ce..849d08ad4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ parameters: defaults: &defaults resource_class: large docker: - - image: bepsays/ci-hugoreleaser:1.22300.20200 + - image: bepsays/ci-hugoreleaser:1.22300.20400 environment: &buildenv GOMODCACHE: /root/project/gomodcache version: 2 @@ -58,7 +58,7 @@ jobs: environment: <<: [*buildenv] docker: - - image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20200 + - image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20400 steps: - *restore-cache - &attach-workspace From 157d86414d43f6801e2a6996108f67d28679eac5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:27:00 +0000 Subject: [PATCH 009/374] build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5d419bd58..c2582e28a 100644 --- a/go.mod +++ b/go.mod @@ -153,7 +153,7 @@ require ( go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect - golang.org/x/crypto v0.30.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/time v0.8.0 // indirect diff --git a/go.sum b/go.sum index af2b64e39..d393396e2 100644 --- a/go.sum +++ b/go.sum @@ -502,8 +502,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From e293e7ca6dcc34cded7eb90a644b5c720c2179cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 10 Dec 2024 16:22:08 +0100 Subject: [PATCH 010/374] Add js.Batch Fixes #12626 Closes #7499 Closes #9978 Closes #12879 Closes #13113 Fixes #13116 --- commands/hugobuilder.go | 6 +- commands/server.go | 6 +- common/herrors/errors.go | 15 + common/herrors/file_error.go | 2 +- common/hreflect/helpers.go | 21 + common/hreflect/helpers_test.go | 13 + common/maps/cache.go | 7 +- common/paths/url.go | 130 +- common/types/closer.go | 7 + config/allconfig/configlanguage.go | 4 +- config/configProvider.go | 2 +- deps/deps.go | 51 +- hugolib/content_map_page.go | 6 + hugolib/hugo_sites.go | 4 + hugolib/hugo_sites_build.go | 5 +- hugolib/hugo_sites_build_errors_test.go | 9 +- hugolib/integrationtest_builder.go | 25 +- hugolib/page.go | 4 + hugolib/pages_capture.go | 18 +- hugolib/pagesfromdata/pagesfromgotmpl.go | 7 +- hugolib/rebuild_test.go | 16 +- hugolib/site.go | 6 +- identity/identity.go | 11 +- internal/js/esbuild/batch-esm-runner.gotmpl | 20 + internal/js/esbuild/batch.go | 1437 +++++++++++++++++ internal/js/esbuild/batch_integration_test.go | 686 ++++++++ internal/js/esbuild/build.go | 236 +++ .../js/esbuild/helpers.go | 5 +- internal/js/esbuild/options.go | 375 +++++ internal/js/esbuild/options_test.go | 219 +++ internal/js/esbuild/resolve.go | 315 ++++ internal/js/esbuild/resolve_test.go | 86 + internal/js/esbuild/sourcemap.go | 80 + lazy/init.go | 2 +- lazy/once.go | 10 +- media/mediaType.go | 12 +- resources/image.go | 5 + resources/page/page.go | 3 +- resources/resource.go | 54 + resources/resource/resource_helpers.go | 3 +- resources/resource/resources.go | 184 ++- .../resource/resources_integration_test.go | 105 ++ resources/resource/resources_test.go | 122 ++ resources/resource_factories/create/create.go | 19 +- resources/resource_metadata.go | 4 + resources/resource_test.go | 18 + resources/resource_transformers/js/build.go | 228 +-- .../js/js_integration_test.go | 28 +- resources/resource_transformers/js/options.go | 461 ------ .../resource_transformers/js/options_test.go | 241 --- .../resource_transformers/js/transform.go | 68 + .../tocss/dartsass/transform.go | 4 +- resources/transform.go | 15 + tpl/debug/debug.go | 4 +- tpl/fmt/fmt.go | 3 +- tpl/js/init.go | 5 +- tpl/js/js.go | 57 +- tpl/partials/partials.go | 3 +- .../_hugo/build/js/batch-esm-runner.gotmpl | 16 + tpl/tplimpl/template.go | 8 +- tpl/tplimpl/template_funcs.go | 7 +- 61 files changed, 4520 insertions(+), 1003 deletions(-) create mode 100644 internal/js/esbuild/batch-esm-runner.gotmpl create mode 100644 internal/js/esbuild/batch.go create mode 100644 internal/js/esbuild/batch_integration_test.go create mode 100644 internal/js/esbuild/build.go rename resources/resource_transformers/js/build_test.go => internal/js/esbuild/helpers.go (79%) create mode 100644 internal/js/esbuild/options.go create mode 100644 internal/js/esbuild/options_test.go create mode 100644 internal/js/esbuild/resolve.go create mode 100644 internal/js/esbuild/resolve_test.go create mode 100644 internal/js/esbuild/sourcemap.go create mode 100644 resources/resource/resources_integration_test.go create mode 100644 resources/resource/resources_test.go delete mode 100644 resources/resource_transformers/js/options.go delete mode 100644 resources/resource_transformers/js/options_test.go create mode 100644 resources/resource_transformers/js/transform.go create mode 100644 tpl/tplimpl/embedded/templates/_hugo/build/js/batch-esm-runner.gotmpl diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index 51493938d..dcc5c099b 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -920,7 +920,11 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, changed := c.changeDetector.changed() if c.changeDetector != nil { - lrl.Logf("build changed %d files", len(changed)) + if len(changed) >= 10 { + lrl.Logf("build changed %d files", len(changed)) + } else { + lrl.Logf("build changed %d files: %q", len(changed), changed) + } if len(changed) == 0 { // Nothing has changed. return diff --git a/commands/server.go b/commands/server.go index c2fee68b2..c4a8ddd29 100644 --- a/commands/server.go +++ b/commands/server.go @@ -32,6 +32,7 @@ import ( "path" "path/filepath" "regexp" + "sort" "strconv" "strings" "sync" @@ -210,16 +211,17 @@ func (f *fileChangeDetector) changed() []string { } } - return f.filterIrrelevant(c) + return f.filterIrrelevantAndSort(c) } -func (f *fileChangeDetector) filterIrrelevant(in []string) []string { +func (f *fileChangeDetector) filterIrrelevantAndSort(in []string) []string { var filtered []string for _, v := range in { if !f.irrelevantRe.MatchString(v) { filtered = append(filtered, v) } } + sort.Strings(filtered) return filtered } diff --git a/common/herrors/errors.go b/common/herrors/errors.go index 40833e55c..c7ee90dd0 100644 --- a/common/herrors/errors.go +++ b/common/herrors/errors.go @@ -133,6 +133,21 @@ func IsNotExist(err error) bool { return false } +// IsExist returns true if the error is a file exists error. +// Unlike os.IsExist, this also considers wrapped errors. +func IsExist(err error) bool { + if os.IsExist(err) { + return true + } + + // os.IsExist does not consider wrapped errors. + if os.IsExist(errors.Unwrap(err)) { + return true + } + + return false +} + var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`) const deferredPrefix = "__hdeferred/" diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 007a06b48..c8a48823e 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -384,7 +384,7 @@ func extractPosition(e error) (pos text.Position) { case godartsass.SassError: span := v.Span start := span.Start - filename, _ := paths.UrlToFilename(span.Url) + filename, _ := paths.UrlStringToFilename(span.Url) pos.Filename = filename pos.Offset = start.Offset pos.ColumnNumber = start.Column diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index 5113a3886..fc83165c7 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -223,6 +223,27 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) { return time.Time{}, false } +// ToSliceAny converts the given value to a slice of any if possible. +func ToSliceAny(v any) ([]any, bool) { + if v == nil { + return nil, false + } + switch vv := v.(type) { + case []any: + return vv, true + default: + vvv := reflect.ValueOf(v) + if vvv.Kind() == reflect.Slice { + out := make([]any, vvv.Len()) + for i := 0; i < vvv.Len(); i++ { + out[i] = vvv.Index(i).Interface() + } + return out, true + } + } + return nil, false +} + func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value { fn := v.MethodByName(name) var args []reflect.Value diff --git a/common/hreflect/helpers_test.go b/common/hreflect/helpers_test.go index 27b774337..119722261 100644 --- a/common/hreflect/helpers_test.go +++ b/common/hreflect/helpers_test.go @@ -50,6 +50,19 @@ func TestIsContextType(t *testing.T) { c.Assert(IsContextType(reflect.TypeOf(valueCtx)), qt.IsTrue) } +func TestToSliceAny(t *testing.T) { + c := qt.New(t) + + checkOK := func(in any, expected []any) { + out, ok := ToSliceAny(in) + c.Assert(ok, qt.Equals, true) + c.Assert(out, qt.DeepEquals, expected) + } + + checkOK([]any{1, 2, 3}, []any{1, 2, 3}) + checkOK([]int{1, 2, 3}, []any{1, 2, 3}) +} + func BenchmarkIsContextType(b *testing.B) { type k string b.Run("value", func(b *testing.B) { diff --git a/common/maps/cache.go b/common/maps/cache.go index cdc31a684..72c07e7ca 100644 --- a/common/maps/cache.go +++ b/common/maps/cache.go @@ -113,11 +113,14 @@ func (c *Cache[K, T]) set(key K, value T) { } // ForEeach calls the given function for each key/value pair in the cache. -func (c *Cache[K, T]) ForEeach(f func(K, T)) { +// If the function returns false, the iteration stops. +func (c *Cache[K, T]) ForEeach(f func(K, T) bool) { c.RLock() defer c.RUnlock() for k, v := range c.m { - f(k, v) + if !f(k, v) { + return + } } } diff --git a/common/paths/url.go b/common/paths/url.go index 75b4b644a..867f68baf 100644 --- a/common/paths/url.go +++ b/common/paths/url.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import ( "net/url" "path" "path/filepath" + "runtime" "strings" ) @@ -159,31 +160,6 @@ func Uglify(in string) string { return path.Clean(in) } -// UrlToFilename converts the URL s to a filename. -// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned. -func UrlToFilename(s string) (string, bool) { - u, err := url.ParseRequestURI(s) - if err != nil { - return filepath.FromSlash(s), false - } - - p := u.Path - - if p == "" { - p, _ = url.QueryUnescape(u.Opaque) - return filepath.FromSlash(p), true - } - - p = filepath.FromSlash(p) - - if u.Host != "" { - // C:\data\file.txt - p = strings.ToUpper(u.Host) + ":" + p - } - - return p, true -} - // URLEscape escapes unicode letters. func URLEscape(uri string) string { // escape unicode letters @@ -193,3 +169,105 @@ func URLEscape(uri string) string { } return u.String() } + +// TrimExt trims the extension from a path.. +func TrimExt(in string) string { + return strings.TrimSuffix(in, path.Ext(in)) +} + +// From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45 +func UrlFromFilename(filename string) (*url.URL, error) { + if !filepath.IsAbs(filename) { + return nil, fmt.Errorf("filepath must be absolute") + } + + // If filename has a Windows volume name, convert the volume to a host and prefix + // per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/. + if vol := filepath.VolumeName(filename); vol != "" { + if strings.HasPrefix(vol, `\\`) { + filename = filepath.ToSlash(filename[2:]) + i := strings.IndexByte(filename, '/') + + if i < 0 { + // A degenerate case. + // \\host.example.com (without a share name) + // becomes + // file://host.example.com/ + return &url.URL{ + Scheme: "file", + Host: filename, + Path: "/", + }, nil + } + + // \\host.example.com\Share\path\to\file + // becomes + // file://host.example.com/Share/path/to/file + return &url.URL{ + Scheme: "file", + Host: filename[:i], + Path: filepath.ToSlash(filename[i:]), + }, nil + } + + // C:\path\to\file + // becomes + // file:///C:/path/to/file + return &url.URL{ + Scheme: "file", + Path: "/" + filepath.ToSlash(filename), + }, nil + } + + // /path/to/file + // becomes + // file:///path/to/file + return &url.URL{ + Scheme: "file", + Path: filepath.ToSlash(filename), + }, nil +} + +// UrlToFilename converts the URL s to a filename. +// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned. +func UrlStringToFilename(s string) (string, bool) { + u, err := url.ParseRequestURI(s) + if err != nil { + return filepath.FromSlash(s), false + } + + p := u.Path + + if p == "" { + p, _ = url.QueryUnescape(u.Opaque) + return filepath.FromSlash(p), false + } + + if runtime.GOOS != "windows" { + return p, true + } + + if len(p) == 0 || p[0] != '/' { + return filepath.FromSlash(p), false + } + + p = filepath.FromSlash(p) + + if len(u.Host) == 1 { + // file://c/Users/... + return strings.ToUpper(u.Host) + ":" + p, true + } + + if u.Host != "" && u.Host != "localhost" { + if filepath.VolumeName(u.Host) != "" { + return "", false + } + return `\\` + u.Host + p, true + } + + if vol := filepath.VolumeName(p[1:]); vol == "" || strings.HasPrefix(vol, `\\`) { + return "", false + } + + return p[1:], true +} diff --git a/common/types/closer.go b/common/types/closer.go index 2844b1986..9f8875a8a 100644 --- a/common/types/closer.go +++ b/common/types/closer.go @@ -19,6 +19,13 @@ type Closer interface { Close() error } +// CloserFunc is a convenience type to create a Closer from a function. +type CloserFunc func() error + +func (f CloserFunc) Close() error { + return f() +} + type CloseAdder interface { Add(Closer) } diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go index 38d2309ef..deec61449 100644 --- a/config/allconfig/configlanguage.go +++ b/config/allconfig/configlanguage.go @@ -137,11 +137,11 @@ func (c ConfigLanguage) Watching() bool { return c.m.Base.Internal.Watch } -func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager { +func (c ConfigLanguage) NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager { if !c.Watching() { return identity.NopManager } - return identity.NewManager(name) + return identity.NewManager(name, opts...) } func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider { diff --git a/config/configProvider.go b/config/configProvider.go index ee6691cf1..5bda2c55a 100644 --- a/config/configProvider.go +++ b/config/configProvider.go @@ -58,7 +58,7 @@ type AllProvider interface { BuildDrafts() bool Running() bool Watching() bool - NewIdentityManager(name string) identity.Manager + NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager FastRenderMode() bool PrintUnusedTemplates() bool EnableMissingTranslationPlaceholders() bool diff --git a/deps/deps.go b/deps/deps.go index 4389036cb..ca0f5ee3b 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "os" "path/filepath" "sort" "strings" @@ -47,6 +48,9 @@ type Deps struct { // The templates to use. This will usually implement the full tpl.TemplateManager. tmplHandlers *tpl.TemplateHandlers + // The template funcs. + TmplFuncMap map[string]any + // The file systems to use. Fs *hugofs.Fs `json:"-"` @@ -83,10 +87,13 @@ type Deps struct { Metrics metrics.Provider // BuildStartListeners will be notified before a build starts. - BuildStartListeners *Listeners + BuildStartListeners *Listeners[any] // BuildEndListeners will be notified after a build finishes. - BuildEndListeners *Listeners + BuildEndListeners *Listeners[any] + + // OnChangeListeners will be notified when something changes. + OnChangeListeners *Listeners[identity.Identity] // Resources that gets closed when the build is done or the server shuts down. BuildClosers *types.Closers @@ -154,17 +161,21 @@ func (d *Deps) Init() error { } if d.BuildStartListeners == nil { - d.BuildStartListeners = &Listeners{} + d.BuildStartListeners = &Listeners[any]{} } if d.BuildEndListeners == nil { - d.BuildEndListeners = &Listeners{} + d.BuildEndListeners = &Listeners[any]{} } if d.BuildClosers == nil { d.BuildClosers = &types.Closers{} } + if d.OnChangeListeners == nil { + d.OnChangeListeners = &Listeners[identity.Identity]{} + } + if d.Metrics == nil && d.Conf.TemplateMetrics() { d.Metrics = metrics.NewProvider(d.Conf.TemplateMetricsHints()) } @@ -268,6 +279,23 @@ func (d *Deps) Compile(prototype *Deps) error { return nil } +// MkdirTemp returns a temporary directory path that will be cleaned up on exit. +func (d Deps) MkdirTemp(pattern string) (string, error) { + filename, err := os.MkdirTemp("", pattern) + if err != nil { + return "", err + } + d.BuildClosers.Add( + types.CloserFunc( + func() error { + return os.RemoveAll(filename) + }, + ), + ) + + return filename, nil +} + type globalErrHandler struct { logger loggers.Logger @@ -306,15 +334,16 @@ func (e *globalErrHandler) StopErrorCollector() { } // Listeners represents an event listener. -type Listeners struct { +type Listeners[T any] struct { sync.Mutex // A list of funcs to be notified about an event. - listeners []func() + // If the return value is true, the listener will be removed. + listeners []func(...T) bool } // Add adds a function to a Listeners instance. -func (b *Listeners) Add(f func()) { +func (b *Listeners[T]) Add(f func(...T) bool) { if b == nil { return } @@ -324,12 +353,16 @@ func (b *Listeners) Add(f func()) { } // Notify executes all listener functions. -func (b *Listeners) Notify() { +func (b *Listeners[T]) Notify(vs ...T) { b.Lock() defer b.Unlock() + temp := b.listeners[:0] for _, notify := range b.listeners { - notify() + if !notify(vs...) { + temp = append(temp, notify) + } } + b.listeners = temp } // ResourceProvider is used to create and refresh, and clone resources needed. diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index a336c8489..eda6f769e 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1754,6 +1754,11 @@ func (sa *sitePagesAssembler) assembleResources() error { mt = rs.rc.ContentMediaType } + var filename string + if rs.fi != nil { + filename = rs.fi.Meta().Filename + } + rd := resources.ResourceSourceDescriptor{ OpenReadSeekCloser: rs.opener, Path: rs.path, @@ -1762,6 +1767,7 @@ func (sa *sitePagesAssembler) assembleResources() error { TargetBasePaths: targetBasePaths, BasePathRelPermalink: targetPaths.SubResourceBaseLink, BasePathTargetPath: baseTarget, + SourceFilenameOrPath: filename, NameNormalized: relPath, NameOriginal: relPathOriginal, MediaType: mt, diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index a5186fd44..792d6a990 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -111,6 +111,10 @@ func (h *HugoSites) ShouldSkipFileChangeEvent(ev fsnotify.Event) bool { return h.skipRebuildForFilenames[ev.Name] } +func (h *HugoSites) Close() error { + return h.Deps.Close() +} + func (h *HugoSites) isRebuild() bool { return h.buildCounter.Load() > 0 } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index dd548be51..5346e2e6b 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -520,8 +520,9 @@ func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error { }, }) - de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) { + de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) bool { g.Enqueue(filename) + return true }) return g.Wait() @@ -1058,6 +1059,8 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } + h.Deps.OnChangeListeners.Notify(changed.Changes()...) + if err := h.resolveAndClearStateForIdentities(ctx, l, cacheBusterOr, changed.Drain()); err != nil { return err } diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 71afe6767..1298d7f4f 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -554,8 +554,6 @@ toc line 3 toc line 4 - - ` t.Run("base template", func(t *testing.T) { @@ -569,7 +567,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/baseof.html:4:6"`)) + b.Assert(err.Error(), qt.Contains, `baseof.html:4:6`) }) t.Run("index template", func(t *testing.T) { @@ -583,7 +581,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:3:7"`)) + b.Assert(err.Error(), qt.Contains, `index.html:3:7"`) }) t.Run("partial from define", func(t *testing.T) { @@ -597,8 +595,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:7:8": execute of template failed`)) - b.Assert(err.Error(), qt.Contains, `execute of template failed: template: partials/toc.html:2:8: executing "partials/toc.html"`) + b.Assert(err.Error(), qt.Contains, `toc.html:2:8"`) }) } diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index 637e5dee0..bc83d65b3 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -69,6 +69,13 @@ func TestOptDebug() TestOpt { } } +// TestOptInfo will enable info logging in integration tests. +func TestOptInfo() TestOpt { + return func(c *IntegrationTestConfig) { + c.LogLevel = logg.LevelInfo + } +} + // TestOptWarn will enable warn logging in integration tests. func TestOptWarn() TestOpt { return func(c *IntegrationTestConfig) { @@ -90,6 +97,13 @@ func TestOptWithNFDOnDarwin() TestOpt { } } +// TestOptWithOSFs enables the real file system. +func TestOptWithOSFs() TestOpt { + return func(c *IntegrationTestConfig) { + c.NeedsOsFS = true + } +} + // TestOptWithWorkingDir allows setting any config optiona as a function al option. func TestOptWithConfig(fn func(c *IntegrationTestConfig)) TestOpt { return func(c *IntegrationTestConfig) { @@ -284,8 +298,9 @@ func (s *IntegrationTestBuilder) negate(match string) (string, bool) { func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...string) { s.Helper() content := strings.TrimSpace(s.FileContent(filename)) + for _, m := range matches { - cm := qt.Commentf("File: %s Match %s", filename, m) + cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content) lines := strings.Split(m, "\n") for _, match := range lines { match = strings.TrimSpace(match) @@ -313,7 +328,8 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches s.Helper() content := s.FileContent(filename) for _, m := range matches { - s.Assert(content, qt.Contains, m, qt.Commentf(m)) + cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content) + s.Assert(content, qt.Contains, m, cm) } } @@ -450,6 +466,11 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder { return s } +func (s *IntegrationTestBuilder) Close() { + s.Helper() + s.Assert(s.H.Close(), qt.IsNil) +} + func (s *IntegrationTestBuilder) LogString() string { return s.lastBuildLog } diff --git a/hugolib/page.go b/hugolib/page.go index e4c841966..83f0c6e25 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -143,6 +143,10 @@ func (p *pageState) GetDependencyManagerForScope(scope int) identity.Manager { } } +func (p *pageState) GetDependencyManagerForScopesAll() []identity.Manager { + return []identity.Manager{p.dependencyManager, p.dependencyManagerOutput} +} + func (p *pageState) Key() string { return "page-" + strconv.FormatUint(p.pid, 10) } diff --git a/hugolib/pages_capture.go b/hugolib/pages_capture.go index 96c2c0f96..f175895c4 100644 --- a/hugolib/pages_capture.go +++ b/hugolib/pages_capture.go @@ -143,13 +143,29 @@ func (c *pagesCollector) Collect() (collectErr error) { s.pageMap.cfg.isRebuild = true } + var hasStructuralChange bool + for _, id := range c.ids { + if id.isStructuralChange() { + hasStructuralChange = true + break + } + } + for _, id := range c.ids { if id.p.IsLeafBundle() { collectErr = c.collectDir( id.p, false, func(fim hugofs.FileMetaInfo) bool { - return true + if hasStructuralChange { + return true + } + fimp := fim.Meta().PathInfo + if fimp == nil { + return true + } + + return fimp.Path() == id.p.Path() }, ) } else if id.p.IsBranchBundle() { diff --git a/hugolib/pagesfromdata/pagesfromgotmpl.go b/hugolib/pagesfromdata/pagesfromgotmpl.go index fd7213bd9..2ee273718 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl.go @@ -245,10 +245,11 @@ func (b *BuildState) resolveDeletedPaths() { return } var paths []string - b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) { + b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) bool { if _, found := b.sourceInfosCurrent.Get(k); !found { paths = append(paths, k) } + return true }) b.DeletedPaths = paths @@ -287,6 +288,10 @@ func (p *PagesFromTemplate) GetDependencyManagerForScope(scope int) identity.Man return p.DependencyManager } +func (p *PagesFromTemplate) GetDependencyManagerForScopesAll() []identity.Manager { + return []identity.Manager{p.DependencyManager} +} + func (p *PagesFromTemplate) Execute(ctx context.Context) (BuildInfo, error) { defer func() { p.buildState.PrepareNextBuild() diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go index ff1f8267e..42e1b8ed5 100644 --- a/hugolib/rebuild_test.go +++ b/hugolib/rebuild_test.go @@ -98,6 +98,18 @@ My Other Text: {{ $r.Content }}|{{ $r.Permalink }}| ` +func TestRebuildEditLeafBundleHeaderOnly(t *testing.T) { + b := TestRunning(t, rebuildFilesSimple) + b.AssertFileContent("public/mysection/mysectionbundle/index.html", + "My Section Bundle Content Content.") + + b.EditFileReplaceAll("content/mysection/mysectionbundle/index.md", "My Section Bundle Content.", "My Section Bundle Content Edited.").Build() + b.AssertFileContent("public/mysection/mysectionbundle/index.html", + "My Section Bundle Content Edited.") + b.AssertRenderCountPage(2) // home (rss) + bundle. + b.AssertRenderCountContent(1) +} + func TestRebuildEditTextFileInLeafBundle(t *testing.T) { b := TestRunning(t, rebuildFilesSimple) b.AssertFileContent("public/mysection/mysectionbundle/index.html", @@ -962,7 +974,7 @@ Single. {{ partial "head.html" . }}$ RelPermalink: {{ $js.RelPermalink }}| ` - b := TestRunning(t, files) + b := TestRunning(t, files, TestOptOsFs()) b.AssertFileContent("public/p1/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") b.AssertFileContent("public/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") @@ -998,7 +1010,7 @@ Base. {{ partial "common/head.html" . }}$ RelPermalink: {{ $js.RelPermalink }}| ` - b := TestRunning(t, files) + b := TestRunning(t, files, TestOptOsFs()) b.AssertFileContent("public/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") diff --git a/hugolib/site.go b/hugolib/site.go index f6dc89a77..7ec70eb6c 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -1493,7 +1493,11 @@ func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, } if err = s.Tmpl().ExecuteWithContext(ctx, templ, w, d); err != nil { - return fmt.Errorf("render of %q failed: %w", name, err) + filename := name + if p, ok := d.(*pageState); ok { + filename = p.String() + } + return fmt.Errorf("render of %q failed: %w", filename, err) } return } diff --git a/identity/identity.go b/identity/identity.go index d106eb1fc..e38a8f0f2 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -82,9 +82,8 @@ func FirstIdentity(v any) Identity { var result Identity = Anonymous WalkIdentitiesShallow(v, func(level int, id Identity) bool { result = id - return true + return result != Anonymous }) - return result } @@ -146,6 +145,7 @@ func (d DependencyManagerProviderFunc) GetDependencyManager() Manager { // DependencyManagerScopedProvider provides a manager for dependencies with a given scope. type DependencyManagerScopedProvider interface { GetDependencyManagerForScope(scope int) Manager + GetDependencyManagerForScopesAll() []Manager } // ForEeachIdentityProvider provides a way iterate over identities. @@ -308,11 +308,13 @@ type identityManager struct { func (im *identityManager) AddIdentity(ids ...Identity) { im.mu.Lock() + defer im.mu.Unlock() for _, id := range ids { if id == nil || id == Anonymous { continue } + if _, found := im.ids[id]; !found { if im.onAddIdentity != nil { im.onAddIdentity(id) @@ -320,7 +322,6 @@ func (im *identityManager) AddIdentity(ids ...Identity) { im.ids[id] = true } } - im.mu.Unlock() } func (im *identityManager) AddIdentityForEach(ids ...ForEeachIdentityProvider) { @@ -355,6 +356,10 @@ func (im *identityManager) GetDependencyManagerForScope(int) Manager { return im } +func (im *identityManager) GetDependencyManagerForScopesAll() []Manager { + return []Manager{im} +} + func (im *identityManager) String() string { return fmt.Sprintf("IdentityManager(%s)", im.name) } diff --git a/internal/js/esbuild/batch-esm-runner.gotmpl b/internal/js/esbuild/batch-esm-runner.gotmpl new file mode 100644 index 000000000..3193b4c30 --- /dev/null +++ b/internal/js/esbuild/batch-esm-runner.gotmpl @@ -0,0 +1,20 @@ +{{ range $i, $e := .Scripts -}} + {{ if eq .Export "*" }} + {{- printf "import %s as Script%d from %q;" .Export $i .Import -}} + {{ else -}} + {{- printf "import { %s as Script%d } from %q;" .Export $i .Import -}} + {{ end -}} +{{ end -}} +{{ range $i, $e := .Runners }} + {{- printf "import { %s as Run%d } from %q;" .Export $i .Import -}} +{{ end -}} +{{ if .Runners -}} + let group = { id: "{{ $.ID }}", scripts: [] } + {{ range $i, $e := .Scripts -}} + group.scripts.push({{ .RunnerJSON $i }}); + {{ end -}} + {{ range $i, $e := .Runners -}} + {{ $id := printf "Run%d" $i }} + {{ $id }}(group); + {{ end -}} +{{ end -}} diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go new file mode 100644 index 000000000..43e2da444 --- /dev/null +++ b/internal/js/esbuild/batch.go @@ -0,0 +1,1437 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package esbuild provides functions for building JavaScript resources. +package esbuild + +import ( + "bytes" + "context" + _ "embed" + "encoding/json" + "fmt" + "path" + "path/filepath" + "reflect" + "sort" + "strings" + "sync" + "sync/atomic" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/cache/dynacache" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/lazy" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/resources/resource_factories/create" + "github.com/gohugoio/hugo/tpl" + "github.com/mitchellh/mapstructure" + "github.com/spf13/afero" + "github.com/spf13/cast" +) + +var _ Batcher = (*batcher)(nil) + +const ( + NsBatch = "_hugo-js-batch" + + propsKeyImportContext = "importContext" + propsResoure = "resource" +) + +//go:embed batch-esm-runner.gotmpl +var runnerTemplateStr string + +var _ BatchPackage = (*Package)(nil) + +var _ buildToucher = (*optsHolder[scriptOptions])(nil) + +var ( + _ buildToucher = (*scriptGroup)(nil) + _ isBuiltOrTouchedProvider = (*scriptGroup)(nil) +) + +func NewBatcherClient(deps *deps.Deps) (*BatcherClient, error) { + c := &BatcherClient{ + d: deps, + buildClient: NewBuildClient(deps.BaseFs.Assets, deps.ResourceSpec), + createClient: create.New(deps.ResourceSpec), + bundlesCache: maps.NewCache[string, BatchPackage](), + } + + deps.BuildEndListeners.Add(func(...any) bool { + c.bundlesCache.Reset() + return false + }) + + return c, nil +} + +func (o optionsMap[K, C]) ByKey() optionsGetSetters[K, C] { + var values []optionsGetSetter[K, C] + for _, v := range o { + values = append(values, v) + } + + sort.Slice(values, func(i, j int) bool { + return values[i].Key().String() < values[j].Key().String() + }) + + return values +} + +func (o *opts[K, C]) Compiled() C { + o.h.checkCompileErr() + return o.h.compiled +} + +func (os optionsGetSetters[K, C]) Filter(predicate func(K) bool) optionsGetSetters[K, C] { + var a optionsGetSetters[K, C] + for _, v := range os { + if predicate(v.Key()) { + a = append(a, v) + } + } + return a +} + +func (o *optsHolder[C]) IdentifierBase() string { + return o.optionsID +} + +func (o *opts[K, C]) Key() K { + return o.key +} + +func (o *opts[K, C]) Reset() { + mu := o.once.ResetWithLock() + defer mu.Unlock() + o.h.resetCounter++ +} + +func (o *opts[K, C]) Get(id uint32) OptionsSetter { + var b *optsHolder[C] + o.once.Do(func() { + b = o.h + b.setBuilt(id) + }) + return b +} + +func (o *opts[K, C]) GetIdentity() identity.Identity { + return o.h +} + +func (o *optsHolder[C]) SetOptions(m map[string]any) string { + o.optsSetCounter++ + o.optsPrev = o.optsCurr + o.optsCurr = m + o.compiledPrev = o.compiled + o.compiled, o.compileErr = o.compiled.compileOptions(m, o.defaults) + o.checkCompileErr() + return "" +} + +// ValidateBatchID validates the given ID according to some very +func ValidateBatchID(id string, isTopLevel bool) error { + if id == "" { + return fmt.Errorf("id must be set") + } + // No Windows slashes. + if strings.Contains(id, "\\") { + return fmt.Errorf("id must not contain backslashes") + } + + // Allow forward slashes in top level IDs only. + if !isTopLevel && strings.Contains(id, "/") { + return fmt.Errorf("id must not contain forward slashes") + } + + return nil +} + +func newIsBuiltOrTouched() isBuiltOrTouched { + return isBuiltOrTouched{ + built: make(buildIDs), + touched: make(buildIDs), + } +} + +func newOpts[K any, C optionsCompiler[C]](key K, optionsID string, defaults defaultOptionValues) *opts[K, C] { + return &opts[K, C]{ + key: key, + h: &optsHolder[C]{ + optionsID: optionsID, + defaults: defaults, + isBuiltOrTouched: newIsBuiltOrTouched(), + }, + } +} + +// BatchPackage holds a group of JavaScript resources. +type BatchPackage interface { + Groups() map[string]resource.Resources +} + +// Batcher is used to build JavaScript packages. +type Batcher interface { + Build(context.Context) (BatchPackage, error) + Config(ctx context.Context) OptionsSetter + Group(ctx context.Context, id string) BatcherGroup +} + +// BatcherClient is a client for building JavaScript packages. +type BatcherClient struct { + d *deps.Deps + + once sync.Once + runnerTemplate tpl.Template + + createClient *create.Client + buildClient *BuildClient + + bundlesCache *maps.Cache[string, BatchPackage] +} + +// New creates a new Batcher with the given ID. +// This will be typically created once and reused across rebuilds. +func (c *BatcherClient) New(id string) (Batcher, error) { + var initErr error + c.once.Do(func() { + // We should fix the initialization order here (or use the Go template package directly), but we need to wait + // for the Hugo templates to be ready. + tmpl, err := c.d.TextTmpl().Parse("batch-esm-runner", runnerTemplateStr) + if err != nil { + initErr = err + return + } + c.runnerTemplate = tmpl + }) + + if initErr != nil { + return nil, initErr + } + + dependencyManager := c.d.Conf.NewIdentityManager("jsbatch_" + id) + configID := "config_" + id + + b := &batcher{ + id: id, + scriptGroups: make(map[string]*scriptGroup), + dependencyManager: dependencyManager, + client: c, + configOptions: newOpts[scriptID, configOptions]( + scriptID(configID), + configID, + defaultOptionValues{}, + ), + } + + c.d.BuildEndListeners.Add(func(...any) bool { + b.reset() + return false + }) + + idFinder := identity.NewFinder(identity.FinderConfig{}) + + c.d.OnChangeListeners.Add(func(ids ...identity.Identity) bool { + for _, id := range ids { + if r := idFinder.Contains(id, b.dependencyManager, 50); r > 0 { + b.staleVersion.Add(1) + return false + } + + sp, ok := id.(identity.DependencyManagerScopedProvider) + if !ok { + continue + } + idms := sp.GetDependencyManagerForScopesAll() + + for _, g := range b.scriptGroups { + g.forEachIdentity(func(id2 identity.Identity) bool { + bt, ok := id2.(buildToucher) + if !ok { + return false + } + for _, id3 := range idms { + // This handles the removal of the only source for a script group (e.g. all shortcodes in a contnt page). + // Note the very shallow search. + if r := idFinder.Contains(id2, id3, 0); r > 0 { + bt.setTouched(b.buildCount) + return false + } + } + return false + }) + } + } + + return false + }) + + return b, nil +} + +func (c *BatcherClient) buildBatchGroup(ctx context.Context, t *batchGroupTemplateContext) (resource.Resource, string, error) { + var buf bytes.Buffer + + if err := c.d.Tmpl().ExecuteWithContext(ctx, c.runnerTemplate, &buf, t); err != nil { + return nil, "", err + } + + s := paths.AddLeadingSlash(t.keyPath + ".js") + r, err := c.createClient.FromString(s, buf.String()) + if err != nil { + return nil, "", err + } + + return r, s, nil +} + +// BatcherGroup is a group of scripts and instances. +type BatcherGroup interface { + Instance(sid, iid string) OptionsSetter + Runner(id string) OptionsSetter + Script(id string) OptionsSetter +} + +// OptionsSetter is used to set options for a batch, script or instance. +type OptionsSetter interface { + SetOptions(map[string]any) string +} + +// Package holds a group of JavaScript resources. +type Package struct { + id string + b *batcher + + groups map[string]resource.Resources +} + +func (p *Package) Groups() map[string]resource.Resources { + return p.groups +} + +type batchGroupTemplateContext struct { + keyPath string + ID string + Runners []scriptRunnerTemplateContext + Scripts []scriptBatchTemplateContext +} + +type batcher struct { + mu sync.Mutex + id string + buildCount uint32 + staleVersion atomic.Uint32 + scriptGroups scriptGroups + + client *BatcherClient + dependencyManager identity.Manager + + configOptions optionsGetSetter[scriptID, configOptions] + + // The last successfully built package. + // If this is non-nil and not stale, we can reuse it (e.g. on server rebuilds) + prevBuild *Package +} + +// Build builds the batch if not already built or if it's stale. +func (b *batcher) Build(ctx context.Context) (BatchPackage, error) { + key := dynacache.CleanKey(b.id + ".js") + p, err := b.client.bundlesCache.GetOrCreate(key, func() (BatchPackage, error) { + return b.build(ctx) + }) + if err != nil { + return nil, fmt.Errorf("failed to build JS batch %q: %w", b.id, err) + } + return p, nil +} + +func (b *batcher) Config(ctx context.Context) OptionsSetter { + return b.configOptions.Get(b.buildCount) +} + +func (b *batcher) Group(ctx context.Context, id string) BatcherGroup { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + b.mu.Lock() + defer b.mu.Unlock() + + group, found := b.scriptGroups[id] + if !found { + idm := b.client.d.Conf.NewIdentityManager("jsbatch_" + id) + b.dependencyManager.AddIdentity(idm) + + group = &scriptGroup{ + id: id, b: b, + isBuiltOrTouched: newIsBuiltOrTouched(), + dependencyManager: idm, + scriptsOptions: make(optionsMap[scriptID, scriptOptions]), + instancesOptions: make(optionsMap[instanceID, paramsOptions]), + runnersOptions: make(optionsMap[scriptID, scriptOptions]), + } + b.scriptGroups[id] = group + } + + group.setBuilt(b.buildCount) + + return group +} + +func (b *batcher) isStale() bool { + if b.staleVersion.Load() > 0 { + return true + } + + if b.removeNotSet() { + return true + } + + if b.configOptions.isStale() { + return true + } + + for _, v := range b.scriptGroups { + if v.isStale() { + return true + } + } + + return false +} + +func (b *batcher) build(ctx context.Context) (BatchPackage, error) { + b.mu.Lock() + defer b.mu.Unlock() + defer func() { + b.staleVersion.Store(0) + b.buildCount++ + }() + + if b.prevBuild != nil { + if !b.isStale() { + return b.prevBuild, nil + } + } + + p, err := b.doBuild(ctx) + if err != nil { + return nil, err + } + + b.prevBuild = p + + return p, nil +} + +func (b *batcher) doBuild(ctx context.Context) (*Package, error) { + type importContext struct { + name string + resourceGetter resource.ResourceGetter + scriptOptions scriptOptions + dm identity.Manager + } + + state := struct { + importResource *maps.Cache[string, resource.Resource] + resultResource *maps.Cache[string, resource.Resource] + importerImportContext *maps.Cache[string, importContext] + pathGroup *maps.Cache[string, string] + }{ + importResource: maps.NewCache[string, resource.Resource](), + resultResource: maps.NewCache[string, resource.Resource](), + importerImportContext: maps.NewCache[string, importContext](), + pathGroup: maps.NewCache[string, string](), + } + + // Entry points passed to ESBuid. + var entryPoints []string + addResource := func(group, pth string, r resource.Resource, isResult bool) { + state.pathGroup.Set(paths.TrimExt(pth), group) + state.importResource.Set(pth, r) + if isResult { + state.resultResource.Set(pth, r) + } + entryPoints = append(entryPoints, pth) + } + + for k, v := range b.scriptGroups { + keyPath := k + var runners []scriptRunnerTemplateContext + for _, vv := range v.runnersOptions.ByKey() { + runnerKeyPath := keyPath + "_" + vv.Key().String() + runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix) + runners = append(runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath}) + addResource(k, runnerImpPath, vv.Compiled().Resource, false) + } + + t := &batchGroupTemplateContext{ + keyPath: keyPath, + ID: v.id, + Runners: runners, + } + + instances := v.instancesOptions.ByKey() + + for _, vv := range v.scriptsOptions.ByKey() { + keyPath := keyPath + "_" + vv.Key().String() + opts := vv.Compiled() + impPath := path.Join(PrefixHugoVirtual, opts.Dir(), keyPath+opts.Resource.MediaType().FirstSuffix.FullSuffix) + impCtx := opts.ImportContext + + state.importerImportContext.Set(impPath, importContext{ + name: keyPath, + resourceGetter: impCtx, + scriptOptions: opts, + dm: v.dependencyManager, + }) + + bt := scriptBatchTemplateContext{ + opts: vv, + Import: impPath, + } + state.importResource.Set(bt.Import, vv.Compiled().Resource) + predicate := func(k instanceID) bool { + return k.scriptID == vv.Key() + } + for _, vvv := range instances.Filter(predicate) { + bt.Instances = append(bt.Instances, scriptInstanceBatchTemplateContext{opts: vvv}) + } + + t.Scripts = append(t.Scripts, bt) + } + + r, s, err := b.client.buildBatchGroup(ctx, t) + if err != nil { + return nil, fmt.Errorf("failed to build JS batch: %w", err) + } + + state.importerImportContext.Set(s, importContext{ + name: s, + resourceGetter: nil, + dm: v.dependencyManager, + }) + + addResource(v.id, s, r, true) + } + + mediaTypes := b.client.d.ResourceSpec.MediaTypes() + + externalOptions := b.configOptions.Compiled().Options + if externalOptions.Format == "" { + externalOptions.Format = "esm" + } + if externalOptions.Format != "esm" { + return nil, fmt.Errorf("only esm format is currently supported") + } + + jsOpts := Options{ + ExternalOptions: externalOptions, + InternalOptions: InternalOptions{ + DependencyManager: b.dependencyManager, + Splitting: true, + ImportOnResolveFunc: func(imp string, args api.OnResolveArgs) string { + var importContextPath string + if args.Kind == api.ResolveEntryPoint { + importContextPath = args.Path + } else { + importContextPath = args.Importer + } + importContext, importContextFound := state.importerImportContext.Get(importContextPath) + + // We want to track the dependencies closest to where they're used. + dm := b.dependencyManager + if importContextFound { + dm = importContext.dm + } + + if r, found := state.importResource.Get(imp); found { + dm.AddIdentity(identity.FirstIdentity(r)) + return imp + } + + if importContext.resourceGetter != nil { + resolved := ResolveResource(imp, importContext.resourceGetter) + if resolved != nil { + resolvePath := resources.InternalResourceTargetPath(resolved) + dm.AddIdentity(identity.FirstIdentity(resolved)) + imp := PrefixHugoVirtual + resolvePath + state.importResource.Set(imp, resolved) + state.importerImportContext.Set(imp, importContext) + return imp + + } + } + return "" + }, + ImportOnLoadFunc: func(args api.OnLoadArgs) string { + imp := args.Path + + if r, found := state.importResource.Get(imp); found { + content, err := r.(resource.ContentProvider).Content(ctx) + if err != nil { + panic(err) + } + return cast.ToString(content) + } + return "" + }, + ImportParamsOnLoadFunc: func(args api.OnLoadArgs) json.RawMessage { + if importContext, found := state.importerImportContext.Get(args.Path); found { + if !importContext.scriptOptions.IsZero() { + return importContext.scriptOptions.Params + } + } + return nil + }, + ErrorMessageResolveFunc: func(args api.Message) *ErrorMessageResolved { + if loc := args.Location; loc != nil { + path := strings.TrimPrefix(loc.File, NsHugoImportResolveFunc+":") + if r, found := state.importResource.Get(path); found { + sourcePath := resources.InternalResourceSourcePathBestEffort(r) + + var contentr hugio.ReadSeekCloser + if cp, ok := r.(hugio.ReadSeekCloserProvider); ok { + contentr, _ = cp.ReadSeekCloser() + } + return &ErrorMessageResolved{ + Content: contentr, + Path: sourcePath, + Message: args.Text, + } + + } + + } + return nil + }, + ResolveSourceMapSource: func(s string) string { + if r, found := state.importResource.Get(s); found { + if ss := resources.InternalResourceSourcePath(r); ss != "" { + return ss + } + return PrefixHugoMemory + s + } + return "" + }, + EntryPoints: entryPoints, + }, + } + + result, err := b.client.buildClient.Build(jsOpts) + if err != nil { + return nil, fmt.Errorf("failed to build JS bundle: %w", err) + } + + groups := make(map[string]resource.Resources) + + createAndAddResource := func(targetPath, group string, o api.OutputFile, mt media.Type) error { + var sourceFilename string + if r, found := state.importResource.Get(targetPath); found { + sourceFilename = resources.InternalResourceSourcePathBestEffort(r) + } + targetPath = path.Join(b.id, targetPath) + + rd := resources.ResourceSourceDescriptor{ + LazyPublish: true, + OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { + return hugio.NewReadSeekerNoOpCloserFromBytes(o.Contents), nil + }, + MediaType: mt, + TargetPath: targetPath, + SourceFilenameOrPath: sourceFilename, + } + r, err := b.client.d.ResourceSpec.NewResource(rd) + if err != nil { + return err + } + + groups[group] = append(groups[group], r) + + return nil + } + + outDir := b.client.d.AbsPublishDir + + createAndAddResources := func(o api.OutputFile) (bool, error) { + p := paths.ToSlashPreserveLeading(strings.TrimPrefix(o.Path, outDir)) + ext := path.Ext(p) + mt, _, found := mediaTypes.GetBySuffix(ext) + if !found { + return false, nil + } + + group, found := state.pathGroup.Get(paths.TrimExt(p)) + + if !found { + return false, nil + } + + if err := createAndAddResource(p, group, o, mt); err != nil { + return false, err + } + + return true, nil + } + + for _, o := range result.OutputFiles { + handled, err := createAndAddResources(o) + if err != nil { + return nil, err + } + + if !handled { + // Copy to destination. + p := strings.TrimPrefix(o.Path, outDir) + targetFilename := filepath.Join(b.id, p) + fs := b.client.d.BaseFs.PublishFs + if err := fs.MkdirAll(filepath.Dir(targetFilename), 0o777); err != nil { + return nil, fmt.Errorf("failed to create dir %q: %w", targetFilename, err) + } + + if err := afero.WriteFile(fs, targetFilename, o.Contents, 0o666); err != nil { + return nil, fmt.Errorf("failed to write to %q: %w", targetFilename, err) + } + } + } + + p := &Package{ + id: path.Join(NsBatch, b.id), + b: b, + groups: groups, + } + + return p, nil +} + +func (b *batcher) removeNotSet() bool { + // We already have the lock. + var removed bool + currentBuildID := b.buildCount + for k, v := range b.scriptGroups { + if !v.isBuilt(currentBuildID) && v.isTouched(currentBuildID) { + // Remove entire group. + removed = true + delete(b.scriptGroups, k) + continue + } + if v.removeTouchedButNotSet() { + removed = true + } + if v.removeNotSet() { + removed = true + } + } + + return removed +} + +func (b *batcher) reset() { + b.mu.Lock() + defer b.mu.Unlock() + b.configOptions.Reset() + for _, v := range b.scriptGroups { + v.Reset() + } +} + +type buildIDs map[uint32]bool + +func (b buildIDs) Has(buildID uint32) bool { + return b[buildID] +} + +func (b buildIDs) Set(buildID uint32) { + b[buildID] = true +} + +type buildToucher interface { + setTouched(buildID uint32) +} + +type configOptions struct { + Options ExternalOptions +} + +func (s configOptions) isStaleCompiled(prev configOptions) bool { + return false +} + +func (s configOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (configOptions, error) { + config, err := DecodeExternalOptions(m) + if err != nil { + return configOptions{}, err + } + + return configOptions{ + Options: config, + }, nil +} + +type defaultOptionValues struct { + defaultExport string +} + +type instanceID struct { + scriptID scriptID + instanceID string +} + +func (i instanceID) String() string { + return i.scriptID.String() + "_" + i.instanceID +} + +type isBuiltOrTouched struct { + built buildIDs + touched buildIDs +} + +func (i isBuiltOrTouched) setBuilt(id uint32) { + i.built.Set(id) +} + +func (i isBuiltOrTouched) isBuilt(id uint32) bool { + return i.built.Has(id) +} + +func (i isBuiltOrTouched) setTouched(id uint32) { + i.touched.Set(id) +} + +func (i isBuiltOrTouched) isTouched(id uint32) bool { + return i.touched.Has(id) +} + +type isBuiltOrTouchedProvider interface { + isBuilt(uint32) bool + isTouched(uint32) bool +} + +type key interface { + comparable + fmt.Stringer +} + +type optionsCompiler[C any] interface { + isStaleCompiled(C) bool + compileOptions(map[string]any, defaultOptionValues) (C, error) +} + +type optionsGetSetter[K, C any] interface { + isBuiltOrTouchedProvider + identity.IdentityProvider + // resource.StaleInfo + + Compiled() C + Key() K + Reset() + + Get(uint32) OptionsSetter + isStale() bool + currPrev() (map[string]any, map[string]any) +} + +type optionsGetSetters[K key, C any] []optionsGetSetter[K, C] + +type optionsMap[K key, C any] map[K]optionsGetSetter[K, C] + +type opts[K any, C optionsCompiler[C]] struct { + key K + h *optsHolder[C] + once lazy.OnceMore +} + +type optsHolder[C optionsCompiler[C]] struct { + optionsID string + + defaults defaultOptionValues + + // Keep track of one generation so we can detect changes. + // Note that most of this tracking is performed on the options/map level. + compiled C + compiledPrev C + compileErr error + + resetCounter uint32 + optsSetCounter uint32 + optsCurr map[string]any + optsPrev map[string]any + + isBuiltOrTouched +} + +type paramsOptions struct { + Params json.RawMessage +} + +func (s paramsOptions) isStaleCompiled(prev paramsOptions) bool { + return false +} + +func (s paramsOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (paramsOptions, error) { + v := struct { + Params map[string]any + }{} + + if err := mapstructure.WeakDecode(m, &v); err != nil { + return paramsOptions{}, err + } + + paramsJSON, err := json.Marshal(v.Params) + if err != nil { + return paramsOptions{}, err + } + + return paramsOptions{ + Params: paramsJSON, + }, nil +} + +type scriptBatchTemplateContext struct { + opts optionsGetSetter[scriptID, scriptOptions] + Import string + Instances []scriptInstanceBatchTemplateContext +} + +func (s *scriptBatchTemplateContext) Export() string { + return s.opts.Compiled().Export +} + +func (c scriptBatchTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + Instances []scriptInstanceBatchTemplateContext `json:"instances"` + }{ + ID: c.opts.Key().String(), + Instances: c.Instances, + }) +} + +func (b scriptBatchTemplateContext) RunnerJSON(i int) string { + script := fmt.Sprintf("Script%d", i) + + v := struct { + ID string `json:"id"` + + // Read-only live JavaScript binding. + Binding string `json:"binding"` + Instances []scriptInstanceBatchTemplateContext `json:"instances"` + }{ + b.opts.Key().String(), + script, + b.Instances, + } + + bb, err := json.Marshal(v) + if err != nil { + panic(err) + } + s := string(bb) + + // Remove the quotes to make it a valid JS object. + s = strings.ReplaceAll(s, fmt.Sprintf("%q", script), script) + + return s +} + +type scriptGroup struct { + mu sync.Mutex + id string + b *batcher + isBuiltOrTouched + dependencyManager identity.Manager + + scriptsOptions optionsMap[scriptID, scriptOptions] + instancesOptions optionsMap[instanceID, paramsOptions] + runnersOptions optionsMap[scriptID, scriptOptions] +} + +// For internal use only. +func (b *scriptGroup) GetDependencyManager() identity.Manager { + return b.dependencyManager +} + +// For internal use only. +func (b *scriptGroup) IdentifierBase() string { + return b.id +} + +func (s *scriptGroup) Instance(sid, id string) OptionsSetter { + if err := ValidateBatchID(sid, false); err != nil { + panic(err) + } + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + + iid := instanceID{scriptID: scriptID(sid), instanceID: id} + if v, found := s.instancesOptions[iid]; found { + return v.Get(s.b.buildCount) + } + + fullID := "instance_" + s.key() + "_" + iid.String() + + s.instancesOptions[iid] = newOpts[instanceID, paramsOptions]( + iid, + fullID, + defaultOptionValues{}, + ) + + return s.instancesOptions[iid].Get(s.b.buildCount) +} + +func (g *scriptGroup) Reset() { + for _, v := range g.scriptsOptions { + v.Reset() + } + for _, v := range g.instancesOptions { + v.Reset() + } + for _, v := range g.runnersOptions { + v.Reset() + } +} + +func (s *scriptGroup) Runner(id string) OptionsSetter { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + sid := scriptID(id) + if v, found := s.runnersOptions[sid]; found { + return v.Get(s.b.buildCount) + } + + runnerIdentity := "runner_" + s.key() + "_" + id + + // A typical signature for a runner would be: + // export default function Run(scripts) {} + // The user can override the default export in the templates. + + s.runnersOptions[sid] = newOpts[scriptID, scriptOptions]( + sid, + runnerIdentity, + defaultOptionValues{ + defaultExport: "default", + }, + ) + + return s.runnersOptions[sid].Get(s.b.buildCount) +} + +func (s *scriptGroup) Script(id string) OptionsSetter { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + sid := scriptID(id) + if v, found := s.scriptsOptions[sid]; found { + return v.Get(s.b.buildCount) + } + + scriptIdentity := "script_" + s.key() + "_" + id + + s.scriptsOptions[sid] = newOpts[scriptID, scriptOptions]( + sid, + scriptIdentity, + defaultOptionValues{ + defaultExport: "*", + }, + ) + + return s.scriptsOptions[sid].Get(s.b.buildCount) +} + +func (s *scriptGroup) isStale() bool { + for _, v := range s.scriptsOptions { + if v.isStale() { + return true + } + } + + for _, v := range s.instancesOptions { + if v.isStale() { + return true + } + } + + for _, v := range s.runnersOptions { + if v.isStale() { + return true + } + } + + return false +} + +func (v *scriptGroup) forEachIdentity( + f func(id identity.Identity) bool, +) bool { + if f(v) { + return true + } + for _, vv := range v.instancesOptions { + if f(vv.GetIdentity()) { + return true + } + } + + for _, vv := range v.scriptsOptions { + if f(vv.GetIdentity()) { + return true + } + } + + for _, vv := range v.runnersOptions { + if f(vv.GetIdentity()) { + return true + } + } + + return false +} + +func (s *scriptGroup) key() string { + return s.b.id + "_" + s.id +} + +func (g *scriptGroup) removeNotSet() bool { + currentBuildID := g.b.buildCount + if !g.isBuilt(currentBuildID) { + // This group was never accessed in this build. + return false + } + var removed bool + + if g.instancesOptions.isBuilt(currentBuildID) { + // A new instance has been set in this group for this build. + // Remove any instance that has not been set in this build. + for k, v := range g.instancesOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.instancesOptions, k) + removed = true + } + } + + if g.runnersOptions.isBuilt(currentBuildID) { + // A new runner has been set in this group for this build. + // Remove any runner that has not been set in this build. + for k, v := range g.runnersOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.runnersOptions, k) + removed = true + } + } + + if g.scriptsOptions.isBuilt(currentBuildID) { + // A new script has been set in this group for this build. + // Remove any script that has not been set in this build. + for k, v := range g.scriptsOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.scriptsOptions, k) + + // Also remove any instance with this ID. + for kk := range g.instancesOptions { + if kk.scriptID == k { + delete(g.instancesOptions, kk) + } + } + removed = true + } + } + + return removed +} + +func (g *scriptGroup) removeTouchedButNotSet() bool { + currentBuildID := g.b.buildCount + var removed bool + for k, v := range g.instancesOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.instancesOptions, k) + removed = true + } + } + for k, v := range g.runnersOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.runnersOptions, k) + removed = true + } + } + for k, v := range g.scriptsOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.scriptsOptions, k) + removed = true + + // Also remove any instance with this ID. + for kk := range g.instancesOptions { + if kk.scriptID == k { + delete(g.instancesOptions, kk) + } + } + } + + } + return removed +} + +type scriptGroups map[string]*scriptGroup + +func (s scriptGroups) Sorted() []*scriptGroup { + var a []*scriptGroup + for _, v := range s { + a = append(a, v) + } + sort.Slice(a, func(i, j int) bool { + return a[i].id < a[j].id + }) + return a +} + +type scriptID string + +func (s scriptID) String() string { + return string(s) +} + +type scriptInstanceBatchTemplateContext struct { + opts optionsGetSetter[instanceID, paramsOptions] +} + +func (c scriptInstanceBatchTemplateContext) ID() string { + return c.opts.Key().instanceID +} + +func (c scriptInstanceBatchTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + Params json.RawMessage `json:"params"` + }{ + ID: c.opts.Key().instanceID, + Params: c.opts.Compiled().Params, + }) +} + +type scriptOptions struct { + // The script to build. + Resource resource.Resource + + // The import context to use. + // Note that we will always fall back to the resource's own import context. + ImportContext resource.ResourceGetter + + // The export name to use for this script's group's runners (if any). + // If not set, the default export will be used. + Export string + + // Params marshaled to JSON. + Params json.RawMessage +} + +func (o *scriptOptions) Dir() string { + return path.Dir(resources.InternalResourceTargetPath(o.Resource)) +} + +func (s scriptOptions) IsZero() bool { + return s.Resource == nil +} + +func (s scriptOptions) isStaleCompiled(prev scriptOptions) bool { + if prev.IsZero() { + return false + } + + // All but the ImportContext are checked at the options/map level. + i1nil, i2nil := prev.ImportContext == nil, s.ImportContext == nil + if i1nil && i2nil { + return false + } + if i1nil || i2nil { + return true + } + // On its own this check would have too many false positives, but combined with the other checks it should be fine. + // We cannot do equality checking here. + if !prev.ImportContext.(resource.IsProbablySameResourceGetter).IsProbablySameResourceGetter(s.ImportContext) { + return true + } + + return false +} + +func (s scriptOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (scriptOptions, error) { + v := struct { + Resource resource.Resource + ImportContext any + Export string + Params map[string]any + }{} + + if err := mapstructure.WeakDecode(m, &v); err != nil { + panic(err) + } + + var paramsJSON []byte + if v.Params != nil { + var err error + paramsJSON, err = json.Marshal(v.Params) + if err != nil { + panic(err) + } + } + + if v.Export == "" { + v.Export = defaults.defaultExport + } + + compiled := scriptOptions{ + Resource: v.Resource, + Export: v.Export, + ImportContext: resource.NewCachedResourceGetter(v.ImportContext), + Params: paramsJSON, + } + + if compiled.Resource == nil { + return scriptOptions{}, fmt.Errorf("resource not set") + } + + return compiled, nil +} + +type scriptRunnerTemplateContext struct { + opts optionsGetSetter[scriptID, scriptOptions] + Import string +} + +func (s *scriptRunnerTemplateContext) Export() string { + return s.opts.Compiled().Export +} + +func (c scriptRunnerTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + }{ + ID: c.opts.Key().String(), + }) +} + +func (o optionsMap[K, C]) isBuilt(id uint32) bool { + for _, v := range o { + if v.isBuilt(id) { + return true + } + } + + return false +} + +func (o *opts[K, C]) isBuilt(id uint32) bool { + return o.h.isBuilt(id) +} + +func (o *opts[K, C]) isStale() bool { + if o.h.isStaleOpts() { + return true + } + if o.h.compiled.isStaleCompiled(o.h.compiledPrev) { + return true + } + return false +} + +func (o *optsHolder[C]) isStaleOpts() bool { + if o.optsSetCounter == 1 && o.resetCounter > 0 { + return false + } + isStale := func() bool { + if len(o.optsCurr) != len(o.optsPrev) { + return true + } + for k, v := range o.optsPrev { + vv, found := o.optsCurr[k] + if !found { + return true + } + if strings.EqualFold(k, propsKeyImportContext) { + // This is checked later. + } else if si, ok := vv.(resource.StaleInfo); ok { + if si.StaleVersion() > 0 { + return true + } + } else { + if !reflect.DeepEqual(v, vv) { + return true + } + } + } + return false + }() + + return isStale +} + +func (o *opts[K, C]) isTouched(id uint32) bool { + return o.h.isTouched(id) +} + +func (o *optsHolder[C]) checkCompileErr() { + if o.compileErr != nil { + panic(o.compileErr) + } +} + +func (o *opts[K, C]) currPrev() (map[string]any, map[string]any) { + return o.h.optsCurr, o.h.optsPrev +} + +func init() { + // We don't want any dependencies/change tracking on the top level Package, + // we want finer grained control via Package.Group. + var p any = &Package{} + if _, ok := p.(identity.Identity); ok { + panic("esbuid.Package should not implement identity.Identity") + } + if _, ok := p.(identity.DependencyManagerProvider); ok { + panic("esbuid.Package should not implement identity.DependencyManagerProvider") + } +} diff --git a/internal/js/esbuild/batch_integration_test.go b/internal/js/esbuild/batch_integration_test.go new file mode 100644 index 000000000..07f99ee4e --- /dev/null +++ b/internal/js/esbuild/batch_integration_test.go @@ -0,0 +1,686 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package js provides functions for building JavaScript resources +package esbuild_test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/bep/logg" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/internal/js/esbuild" +) + +// Used to test misc. error situations etc. +const jsBatchFilesTemplate = ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import './styles.css'; +import * as params from '@params'; +import * as foo from 'mylib'; +console.log("Hello, Main!"); +console.log("params.p1", params.p1); +export default function Main() {}; +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/shortcodes/hdx.html -- +{{ $path := .Get "r" }} +{{ $r := or (.Page.Resources.Get $path) (resources.Get $path) }} +{{ $batch := (js.Batch "mybatch") }} +{{ $scriptID := $path | anchorize }} +{{ $instanceID := .Ordinal | string }} +{{ $group := .Page.RelPermalink | anchorize }} +{{ $params := .Params | default dict }} +{{ $export := .Get "export" | default "default" }} +{{ with $batch.Group $group }} + {{ with .Runner "create-elements" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script $scriptID }} + {{ .SetOptions (dict + "resource" $r + "export" $export + "importContext" (slice $.Page) + ) + }} + {{ end }} + {{ with .Instance $scriptID $instanceID }} + {{ .SetOptions (dict "params" $params) }} + {{ end }} +{{ end }} +hdx-instance: {{ $scriptID }}: {{ $instanceID }}| +-- layouts/_default/baseof.html -- +Base. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ block "main" . }}Main{{ end }} +End. +-- layouts/_default/single.html -- +{{ define "main" }} +==> Single Template Content: {{ .Content }}$ +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} +{{ end }} +-- layouts/index.html -- +{{ define "main" }} +Home. +{{ end }} +-- content/p1/index.md -- +--- +title: "P1" +--- + +Some content. + +{{< hdx r="p1script.js" myparam="p1-param-1" >}} +{{< hdx r="p1script.js" myparam="p1-param-2" >}} + +-- content/p1/p1script.js -- +console.log("P1 Script"); + + +` + +// Just to verify that the above file setup works. +func TestBatchTemplateOKBuild(t *testing.T) { + b := hugolib.Test(t, jsBatchFilesTemplate, hugolib.TestOptWithOSFs()) + b.AssertPublishDir("mybatch/mygroup.js", "mybatch/mygroup.css") +} + +func TestBatchRemoveAllInGroup(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + + b.AssertFileContent("public/p1/index.html", "p1: /mybatch/p1.js") + + b.EditFiles("content/p1/index.md", ` +--- +title: "P1" +--- +Empty. +`) + b.Build() + + b.AssertFileContent("public/p1/index.html", "! p1: /mybatch/p1.js") + + // Add one script back. + b.EditFiles("content/p1/index.md", ` +--- +title: "P1" +--- + +{{< hdx r="p1script.js" myparam="p1-param-1-new" >}} +`) + b.Build() + + b.AssertFileContent("public/mybatch/p1.js", + "p1-param-1-new", + "p1script.js") +} + +func TestBatchEditInstance(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js", "Instance 1") + b.EditFileReplaceAll("layouts/_default/single.html", "Instance 1", "Instance 1 Edit").Build() + b.AssertFileContent("public/mybatch/mygroup.js", "Instance 1 Edit") +} + +func TestBatchEditScriptParam(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main") + b.EditFileReplaceAll("layouts/_default/single.html", "param-p1-main", "param-p1-main-edited").Build() + b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main-edited") +} + +func TestBatchErrorScriptResourceNotSet(t *testing.T) { + files := strings.Replace(jsBatchFilesTemplate, `(resources.Get "js/main.js")`, `(resources.Get "js/doesnotexist.js")`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `error calling SetOptions: resource not set`) +} + +func TestBatchSlashInBatchID(t *testing.T) { + files := strings.ReplaceAll(jsBatchFilesTemplate, `"mybatch"`, `"my/batch"`) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNil) + b.AssertPublishDir("my/batch/mygroup.js") +} + +func TestBatchSourceMaps(t *testing.T) { + filesTemplate := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import * as foo from 'mylib'; +console.log("Hello, Main!"); +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/shortcodes/hdx.html -- +{{ $path := .Get "r" }} +{{ $r := or (.Page.Resources.Get $path) (resources.Get $path) }} +{{ $batch := (js.Batch "mybatch") }} +{{ $scriptID := $path | anchorize }} +{{ $instanceID := .Ordinal | string }} +{{ $group := .Page.RelPermalink | anchorize }} +{{ $params := .Params | default dict }} +{{ $export := .Get "export" | default "default" }} +{{ with $batch.Group $group }} + {{ with .Runner "create-elements" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script $scriptID }} + {{ .SetOptions (dict + "resource" $r + "export" $export + "importContext" (slice $.Page) + ) + }} + {{ end }} + {{ with .Instance $scriptID $instanceID }} + {{ .SetOptions (dict "params" $params) }} + {{ end }} +{{ end }} +hdx-instance: {{ $scriptID }}: {{ $instanceID }}| +-- layouts/_default/baseof.html -- +Base. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ block "main" . }}Main{{ end }} +End. +-- layouts/_default/single.html -- +{{ define "main" }} +==> Single Template Content: {{ .Content }}$ +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} +{{ end }} +-- layouts/index.html -- +{{ define "main" }} +Home. +{{ end }} +-- content/p1/index.md -- +--- +title: "P1" +--- + +Some content. + +{{< hdx r="p1script.js" myparam="p1-param-1" >}} +{{< hdx r="p1script.js" myparam="p1-param-2" >}} + +-- content/p1/p1script.js -- +import * as foo from 'mylib'; +console.lg("Foo", foo); +console.log("P1 Script"); +export default function P1Script() {}; + + +` + files := strings.Replace(filesTemplate, `"sourceMap" ""`, `"sourceMap" "linked"`, 1) + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js.map", "main.js", "! ns-hugo") + b.AssertFileContent("public/mybatch/mygroup.js", "sourceMappingURL=mygroup.js.map") + b.AssertFileContent("public/mybatch/p1.js", "sourceMappingURL=p1.js.map") + b.AssertFileContent("public/mybatch/mygroup_run_runner.js", "sourceMappingURL=mygroup_run_runner.js.map") + b.AssertFileContent("public/mybatch/chunk-UQKPPNA6.js", "sourceMappingURL=chunk-UQKPPNA6.js.map") + + checkMap := func(p string, expectLen int) { + s := b.FileContent(p) + sources := esbuild.SourcesFromSourceMap(s) + b.Assert(sources, qt.HasLen, expectLen) + + // Check that all source files exist. + for _, src := range sources { + filename, ok := paths.UrlStringToFilename(src) + b.Assert(ok, qt.IsTrue) + _, err := os.Stat(filename) + b.Assert(err, qt.IsNil) + } + } + + checkMap("public/mybatch/mygroup.js.map", 1) + checkMap("public/mybatch/p1.js.map", 1) + checkMap("public/mybatch/mygroup_run_runner.js.map", 0) + checkMap("public/mybatch/chunk-UQKPPNA6.js.map", 1) +} + +func TestBatchErrorRunnerResourceNotSet(t *testing.T) { + files := strings.Replace(jsBatchFilesTemplate, `(resources.Get "js/runner.js")`, `(resources.Get "js/doesnotexist.js")`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `resource not set`) +} + +func TestBatchErrorScriptResourceInAssetsSyntaxError(t *testing.T) { + // Introduce JS syntax error in assets/js/main.js + files := strings.Replace(jsBatchFilesTemplate, `console.log("Hello, Main!");`, `console.log("Hello, Main!"`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`assets/js/main.js:5:0": Expected ")" but found "console"`)) +} + +func TestBatchErrorScriptResourceInBundleSyntaxError(t *testing.T) { + // Introduce JS syntax error in content/p1/p1script.js + files := strings.Replace(jsBatchFilesTemplate, `console.log("P1 Script");`, `console.log("P1 Script"`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`/content/p1/p1script.js:3:0": Expected ")" but found end of file`)) +} + +func TestBatch(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term"] +disableLiveReload = true +baseURL = "https://example.com" +-- package.json -- +{ + "devDependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + } +} +-- assets/js/shims/react.js -- +-- assets/js/shims/react-dom.js -- +module.exports = window.ReactDOM; +module.exports = window.React; +-- content/mybundle/index.md -- +--- +title: "My Bundle" +--- +-- content/mybundle/mybundlestyles.css -- +@import './foo.css'; +@import './bar.css'; +@import './otherbundlestyles.css'; + +.mybundlestyles { + background-color: blue; +} +-- content/mybundle/bundlereact.jsx -- +import * as React from "react"; +import './foo.css'; +import './mybundlestyles.css'; +window.React1 = React; + +let text = 'Click me, too!' + +export default function MyBundleButton() { + return ( + + ) +} + +-- assets/js/reactrunner.js -- +import * as ReactDOM from 'react-dom/client'; +import * as React from 'react'; + +export default function Run(group) { + for (const module of group.scripts) { + for (const instance of module.instances) { + /* This is a convention in this project. */ + let elId = §§${module.id}-${instance.id}§§; + let el = document.getElementById(elId); + if (!el) { + console.warn(§§Element with id ${elId} not found§§); + continue; + } + const root = ReactDOM.createRoot(el); + const reactEl = React.createElement(module.mod, instance.params); + root.render(reactEl); + } + } +} +-- assets/other/otherbundlestyles.css -- +.otherbundlestyles { + background-color: red; +} +-- assets/other/foo.css -- +@import './bar.css'; + +.foo { + background-color: blue; +} +-- assets/other/bar.css -- +.bar { + background-color: red; +} +-- assets/js/button.css -- +button { + background-color: red; +} +-- assets/js/bar.css -- +.bar-assets { + background-color: red; +} +-- assets/js/helper.js -- +import './bar.css' + +export function helper() { + console.log('helper'); +} + +-- assets/js/react1styles_nested.css -- +.react1styles_nested { + background-color: red; +} +-- assets/js/react1styles.css -- +@import './react1styles_nested.css'; +.react1styles { + background-color: red; +} +-- assets/js/react1.jsx -- +import * as React from "react"; +import './button.css' +import './foo.css' +import './react1styles.css' + +window.React1 = React; + +let text = 'Click me' + +export default function MyButton() { + return ( + + ) +} + +-- assets/js/react2.jsx -- +import * as React from "react"; +import { helper } from './helper.js' +import './foo.css' + +window.React2 = React; + +let text = 'Click me, too!' + +export function MyOtherButton() { + return ( + + ) +} +-- assets/js/main1.js -- +import * as React from "react"; +import * as params from '@params'; + +console.log('main1.React', React) +console.log('main1.params.id', params.id) + +-- assets/js/main2.js -- +import * as React from "react"; +import * as params from '@params'; + +console.log('main2.React', React) +console.log('main2.params.id', params.id) + +export default function Main2() {}; + +-- assets/js/main3.js -- +import * as React from "react"; +import * as params from '@params'; +import * as config from '@params/config'; + +console.log('main3.params.id', params.id) +console.log('config.params.id', config.id) + +export default function Main3() {}; + +-- layouts/_default/single.html -- +Single. + +{{ $r := .Resources.GetMatch "*.jsx" }} +{{ $batch := (js.Batch "mybundle") }} +{{ $otherCSS := (resources.Match "/other/*.css").Mount "/other" "." }} + {{ with $batch.Config }} + {{ $shims := dict "react" "js/shims/react.js" "react-dom/client" "js/shims/react-dom.js" }} + {{ .SetOptions (dict + "target" "es2018" + "params" (dict "id" "config") + "shims" $shims + ) + }} +{{ end }} +{{ with $batch.Group "reactbatch" }} + {{ with .Script "r3" }} + {{ .SetOptions (dict + "resource" $r + "importContext" (slice $ $otherCSS) + "params" (dict "id" "r3") + ) + }} + {{ end }} + {{ with .Instance "r3" "r2i1" }} + {{ .SetOptions (dict "title" "r2 instance 1")}} + {{ end }} +{{ end }} +-- layouts/index.html -- +Home. +{{ with (templates.Defer (dict "key" "global")) }} +{{ $batch := (js.Batch "mybundle") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . }} + {{ $k }}: {{ $kk }}: {{ .RelPermalink }} + {{ end }} + {{ end }} +{{ end }} +{{ $myContentBundle := site.GetPage "mybundle" }} +{{ $batch := (js.Batch "mybundle") }} +{{ $otherCSS := (resources.Match "/other/*.css").Mount "/other" "." }} +{{ with $batch.Group "mains" }} + {{ with .Script "main1" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main1.js") + "params" (dict "id" "main1") + ) + }} + {{ end }} + {{ with .Script "main2" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main2.js") + "params" (dict "id" "main2") + ) + }} + {{ end }} + {{ with .Script "main3" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main3.js") + ) + }} + {{ end }} +{{ with .Instance "main1" "m1i1" }}{{ .SetOptions (dict "params" (dict "title" "Main1 Instance 1"))}}{{ end }} +{{ with .Instance "main1" "m1i2" }}{{ .SetOptions (dict "params" (dict "title" "Main1 Instance 2"))}}{{ end }} +{{ end }} +{{ with $batch.Group "reactbatch" }} + {{ with .Runner "reactrunner" }} + {{ .SetOptions ( dict "resource" (resources.Get "js/reactrunner.js") )}} + {{ end }} + {{ with .Script "r1" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/react1.jsx") + "importContext" (slice $myContentBundle $otherCSS) + "params" (dict "id" "r1") + ) + }} + {{ end }} + {{ with .Instance "r1" "i1" }}{{ .SetOptions (dict "params" (dict "title" "Instance 1"))}}{{ end }} + {{ with .Instance "r1" "i2" }}{{ .SetOptions (dict "params" (dict "title" "Instance 2"))}}{{ end }} + {{ with .Script "r2" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/react2.jsx") + "export" "MyOtherButton" + "importContext" $otherCSS + "params" (dict "id" "r2") + ) + }} + {{ end }} + {{ with .Instance "r2" "i1" }}{{ .SetOptions (dict "params" (dict "title" "Instance 2-1"))}}{{ end }} +{{ end }} + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + NeedsOsFS: true, + NeedsNpmInstall: true, + TxtarString: files, + Running: true, + LogLevel: logg.LevelWarn, + // PrintAndKeepTempDir: true, + }).Build() + + b.AssertFileContent("public/index.html", + "mains: 0: /mybundle/mains.js", + "reactbatch: 2: /mybundle/reactbatch.css", + ) + + b.AssertFileContent("public/mybundle/reactbatch.css", + ".bar {", + ) + + // Verify params resolution. + b.AssertFileContent("public/mybundle/mains.js", + ` +var id = "main1"; +console.log("main1.params.id", id); +var id2 = "main2"; +console.log("main2.params.id", id2); + + +# Params from top level config. +var id3 = "config"; +console.log("main3.params.id", void 0); +console.log("config.params.id", id3); +`) + + b.EditFileReplaceAll("content/mybundle/mybundlestyles.css", ".mybundlestyles", ".mybundlestyles-edit").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".mybundlestyles-edit {") + + b.EditFileReplaceAll("assets/other/bar.css", ".bar {", ".bar-edit {").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".bar-edit {") + + b.EditFileReplaceAll("assets/other/bar.css", ".bar-edit {", ".bar-edit2 {").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".bar-edit2 {") +} + +func TestEditBaseofManyTimes(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +disableKinds = ["taxonomy", "term"] +-- layouts/_default/baseof.html -- +Baseof. +{{ block "main" . }}{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Now. {{ now }} +{{ end }} +-- layouts/_default/single.html -- +{{ define "main" }} +Single. +{{ end }} +-- +-- layouts/_default/list.html -- +{{ define "main" }} +List. +{{ end }} +-- content/mybundle/index.md -- +--- +title: "My Bundle" +--- +-- content/_index.md -- +--- +title: "Home" +--- +` + + b := hugolib.TestRunning(t, files) + b.AssertFileContent("public/index.html", "Baseof.") + + for i := 0; i < 100; i++ { + b.EditFileReplaceAll("layouts/_default/baseof.html", "Now", "Now.").Build() + b.AssertFileContent("public/index.html", "Now..") + } +} diff --git a/internal/js/esbuild/build.go b/internal/js/esbuild/build.go new file mode 100644 index 000000000..33b91eafc --- /dev/null +++ b/internal/js/esbuild/build.go @@ -0,0 +1,236 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package esbuild provides functions for building JavaScript resources. +package esbuild + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/text" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources" +) + +// NewBuildClient creates a new BuildClient. +func NewBuildClient(fs *filesystems.SourceFilesystem, rs *resources.Spec) *BuildClient { + return &BuildClient{ + rs: rs, + sfs: fs, + } +} + +// BuildClient is a client for building JavaScript resources using esbuild. +type BuildClient struct { + rs *resources.Spec + sfs *filesystems.SourceFilesystem +} + +// Build builds the given JavaScript resources using esbuild with the given options. +func (c *BuildClient) Build(opts Options) (api.BuildResult, error) { + dependencyManager := opts.DependencyManager + if dependencyManager == nil { + dependencyManager = identity.NopManager + } + + opts.OutDir = c.rs.AbsPublishDir + opts.ResolveDir = c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved + opts.AbsWorkingDir = opts.ResolveDir + opts.TsConfig = c.rs.ResolveJSConfigFile("tsconfig.json") + assetsResolver := newFSResolver(c.rs.Assets.Fs) + + if err := opts.validate(); err != nil { + return api.BuildResult{}, err + } + + if err := opts.compile(); err != nil { + return api.BuildResult{}, err + } + + var err error + opts.compiled.Plugins, err = createBuildPlugins(c.rs, assetsResolver, dependencyManager, opts) + if err != nil { + return api.BuildResult{}, err + } + + if opts.Inject != nil { + // Resolve the absolute filenames. + for i, ext := range opts.Inject { + impPath := filepath.FromSlash(ext) + if filepath.IsAbs(impPath) { + return api.BuildResult{}, fmt.Errorf("inject: absolute paths not supported, must be relative to /assets") + } + + m := assetsResolver.resolveComponent(impPath) + + if m == nil { + return api.BuildResult{}, fmt.Errorf("inject: file %q not found", ext) + } + + opts.Inject[i] = m.Filename + + } + + opts.compiled.Inject = opts.Inject + + } + + result := api.Build(opts.compiled) + + if len(result.Errors) > 0 { + createErr := func(msg api.Message) error { + if msg.Location == nil { + return errors.New(msg.Text) + } + var ( + contentr hugio.ReadSeekCloser + errorMessage string + loc = msg.Location + errorPath = loc.File + err error + ) + + var resolvedError *ErrorMessageResolved + + if opts.ErrorMessageResolveFunc != nil { + resolvedError = opts.ErrorMessageResolveFunc(msg) + } + + if resolvedError == nil { + if errorPath == stdinImporter { + errorPath = opts.StdinSourcePath + } + + errorMessage = msg.Text + + var namespace string + for _, ns := range hugoNamespaces { + if strings.HasPrefix(errorPath, ns) { + namespace = ns + break + } + } + + if namespace != "" { + namespace += ":" + errorMessage = strings.ReplaceAll(errorMessage, namespace, "") + errorPath = strings.TrimPrefix(errorPath, namespace) + contentr, err = hugofs.Os.Open(errorPath) + } else { + var fi os.FileInfo + fi, err = c.sfs.Fs.Stat(errorPath) + if err == nil { + m := fi.(hugofs.FileMetaInfo).Meta() + errorPath = m.Filename + contentr, err = m.Open() + } + } + } else { + contentr = resolvedError.Content + errorPath = resolvedError.Path + errorMessage = resolvedError.Message + } + + if contentr != nil { + defer contentr.Close() + } + + if err == nil { + fe := herrors. + NewFileErrorFromName(errors.New(errorMessage), errorPath). + UpdatePosition(text.Position{Offset: -1, LineNumber: loc.Line, ColumnNumber: loc.Column}). + UpdateContent(contentr, nil) + + return fe + } + + return fmt.Errorf("%s", errorMessage) + } + + var errors []error + + for _, msg := range result.Errors { + errors = append(errors, createErr(msg)) + } + + // Return 1, log the rest. + for i, err := range errors { + if i > 0 { + c.rs.Logger.Errorf("js.Build failed: %s", err) + } + } + + return result, errors[0] + } + + inOutputPathToAbsFilename := opts.ResolveSourceMapSource + opts.ResolveSourceMapSource = func(s string) string { + if inOutputPathToAbsFilename != nil { + if filename := inOutputPathToAbsFilename(s); filename != "" { + return filename + } + } + + if m := assetsResolver.resolveComponent(s); m != nil { + return m.Filename + } + + return "" + } + + for i, o := range result.OutputFiles { + if err := fixOutputFile(&o, func(s string) string { + if s == "" { + return opts.ResolveSourceMapSource(opts.StdinSourcePath) + } + var isNsHugo bool + if strings.HasPrefix(s, "ns-hugo") { + isNsHugo = true + idxColon := strings.Index(s, ":") + s = s[idxColon+1:] + } + + if !strings.HasPrefix(s, PrefixHugoVirtual) { + if !filepath.IsAbs(s) { + s = filepath.Join(opts.OutDir, s) + } + } + + if isNsHugo { + if ss := opts.ResolveSourceMapSource(s); ss != "" { + if strings.HasPrefix(ss, PrefixHugoMemory) { + // File not on disk, mark it for removal from the sources slice. + return "" + } + return ss + } + return "" + } + return s + }); err != nil { + return result, err + } + result.OutputFiles[i] = o + } + + return result, nil +} diff --git a/resources/resource_transformers/js/build_test.go b/internal/js/esbuild/helpers.go similarity index 79% rename from resources/resource_transformers/js/build_test.go rename to internal/js/esbuild/helpers.go index 30a4490ed..b4cb565b8 100644 --- a/resources/resource_transformers/js/build_test.go +++ b/internal/js/esbuild/helpers.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,4 +11,5 @@ // See the License for the specific language governing permissions and // limitations under the License. -package js +// Package esbuild provides functions for building JavaScript resources. +package esbuild diff --git a/internal/js/esbuild/options.go b/internal/js/esbuild/options.go new file mode 100644 index 000000000..16fc0d4bb --- /dev/null +++ b/internal/js/esbuild/options.go @@ -0,0 +1,375 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/identity" + + "github.com/evanw/esbuild/pkg/api" + + "github.com/gohugoio/hugo/media" + "github.com/mitchellh/mapstructure" +) + +var ( + nameTarget = map[string]api.Target{ + "": api.ESNext, + "esnext": api.ESNext, + "es5": api.ES5, + "es6": api.ES2015, + "es2015": api.ES2015, + "es2016": api.ES2016, + "es2017": api.ES2017, + "es2018": api.ES2018, + "es2019": api.ES2019, + "es2020": api.ES2020, + "es2021": api.ES2021, + "es2022": api.ES2022, + "es2023": api.ES2023, + } + + // source names: https://github.com/evanw/esbuild/blob/9eca46464ed5615cb36a3beb3f7a7b9a8ffbe7cf/internal/config/config.go#L208 + nameLoader = map[string]api.Loader{ + "none": api.LoaderNone, + "base64": api.LoaderBase64, + "binary": api.LoaderBinary, + "copy": api.LoaderFile, + "css": api.LoaderCSS, + "dataurl": api.LoaderDataURL, + "default": api.LoaderDefault, + "empty": api.LoaderEmpty, + "file": api.LoaderFile, + "global-css": api.LoaderGlobalCSS, + "js": api.LoaderJS, + "json": api.LoaderJSON, + "jsx": api.LoaderJSX, + "local-css": api.LoaderLocalCSS, + "text": api.LoaderText, + "ts": api.LoaderTS, + "tsx": api.LoaderTSX, + } +) + +// DecodeExternalOptions decodes the given map into ExternalOptions. +func DecodeExternalOptions(m map[string]any) (ExternalOptions, error) { + opts := ExternalOptions{ + SourcesContent: true, + } + + if err := mapstructure.WeakDecode(m, &opts); err != nil { + return opts, err + } + + if opts.TargetPath != "" { + opts.TargetPath = paths.ToSlashTrimLeading(opts.TargetPath) + } + + opts.Target = strings.ToLower(opts.Target) + opts.Format = strings.ToLower(opts.Format) + + return opts, nil +} + +// ErrorMessageResolved holds a resolved error message. +type ErrorMessageResolved struct { + Path string + Message string + Content hugio.ReadSeekCloser +} + +// ExternalOptions holds user facing options for the js.Build template function. +type ExternalOptions struct { + // If not set, the source path will be used as the base target path. + // Note that the target path's extension may change if the target MIME type + // is different, e.g. when the source is TypeScript. + TargetPath string + + // Whether to minify to output. + Minify bool + + // One of "inline", "external", "linked" or "none". + SourceMap string + + SourcesContent bool + + // The language target. + // One of: es2015, es2016, es2017, es2018, es2019, es2020 or esnext. + // Default is esnext. + Target string + + // The output format. + // One of: iife, cjs, esm + // Default is to esm. + Format string + + // External dependencies, e.g. "react". + Externals []string + + // This option allows you to automatically replace a global variable with an import from another file. + // The filenames must be relative to /assets. + // See https://esbuild.github.io/api/#inject + Inject []string + + // User defined symbols. + Defines map[string]any + + // Maps a component import to another. + Shims map[string]string + + // Configuring a loader for a given file type lets you load that file type with an + // import statement or a require call. For example, configuring the .png file extension + // to use the data URL loader means importing a .png file gives you a data URL + // containing the contents of that image + // + // See https://esbuild.github.io/api/#loader + Loaders map[string]string + + // User defined params. Will be marshaled to JSON and available as "@params", e.g. + // import * as params from '@params'; + Params any + + // What to use instead of React.createElement. + JSXFactory string + + // What to use instead of React.Fragment. + JSXFragment string + + // What to do about JSX syntax. + // See https://esbuild.github.io/api/#jsx + JSX string + + // Which library to use to automatically import JSX helper functions from. Only works if JSX is set to automatic. + // See https://esbuild.github.io/api/#jsx-import-source + JSXImportSource string + + // There is/was a bug in WebKit with severe performance issue with the tracking + // of TDZ checks in JavaScriptCore. + // + // Enabling this flag removes the TDZ and `const` assignment checks and + // may improve performance of larger JS codebases until the WebKit fix + // is in widespread use. + // + // See https://bugs.webkit.org/show_bug.cgi?id=199866 + // Deprecated: This no longer have any effect and will be removed. + // TODO(bep) remove. See https://github.com/evanw/esbuild/commit/869e8117b499ca1dbfc5b3021938a53ffe934dba + AvoidTDZ bool +} + +// InternalOptions holds internal options for the js.Build template function. +type InternalOptions struct { + MediaType media.Type + OutDir string + Contents string + SourceDir string + ResolveDir string + AbsWorkingDir string + Metafile bool + + StdinSourcePath string + + DependencyManager identity.Manager + + Stdin bool // Set to true to pass in the entry point as a byte slice. + Splitting bool + TsConfig string + EntryPoints []string + ImportOnResolveFunc func(string, api.OnResolveArgs) string + ImportOnLoadFunc func(api.OnLoadArgs) string + ImportParamsOnLoadFunc func(args api.OnLoadArgs) json.RawMessage + ErrorMessageResolveFunc func(api.Message) *ErrorMessageResolved + ResolveSourceMapSource func(string) string // Used to resolve paths in error source maps. +} + +// Options holds the options passed to Build. +type Options struct { + ExternalOptions + InternalOptions + + compiled api.BuildOptions +} + +func (opts *Options) compile() (err error) { + target, found := nameTarget[opts.Target] + if !found { + err = fmt.Errorf("invalid target: %q", opts.Target) + return + } + + var loaders map[string]api.Loader + if opts.Loaders != nil { + loaders = make(map[string]api.Loader) + for k, v := range opts.Loaders { + loader, found := nameLoader[v] + if !found { + err = fmt.Errorf("invalid loader: %q", v) + return + } + loaders[k] = loader + } + } + + mediaType := opts.MediaType + if mediaType.IsZero() { + mediaType = media.Builtin.JavascriptType + } + + var loader api.Loader + switch mediaType.SubType { + case media.Builtin.JavascriptType.SubType: + loader = api.LoaderJS + case media.Builtin.TypeScriptType.SubType: + loader = api.LoaderTS + case media.Builtin.TSXType.SubType: + loader = api.LoaderTSX + case media.Builtin.JSXType.SubType: + loader = api.LoaderJSX + default: + err = fmt.Errorf("unsupported Media Type: %q", opts.MediaType) + return + } + + var format api.Format + // One of: iife, cjs, esm + switch opts.Format { + case "", "iife": + format = api.FormatIIFE + case "esm": + format = api.FormatESModule + case "cjs": + format = api.FormatCommonJS + default: + err = fmt.Errorf("unsupported script output format: %q", opts.Format) + return + } + + var jsx api.JSX + switch opts.JSX { + case "", "transform": + jsx = api.JSXTransform + case "preserve": + jsx = api.JSXPreserve + case "automatic": + jsx = api.JSXAutomatic + default: + err = fmt.Errorf("unsupported jsx type: %q", opts.JSX) + return + } + + var defines map[string]string + if opts.Defines != nil { + defines = maps.ToStringMapString(opts.Defines) + } + + // By default we only need to specify outDir and no outFile + outDir := opts.OutDir + outFile := "" + var sourceMap api.SourceMap + switch opts.SourceMap { + case "inline": + sourceMap = api.SourceMapInline + case "external": + sourceMap = api.SourceMapExternal + case "linked": + sourceMap = api.SourceMapLinked + case "", "none": + sourceMap = api.SourceMapNone + default: + err = fmt.Errorf("unsupported sourcemap type: %q", opts.SourceMap) + return + } + + sourcesContent := api.SourcesContentInclude + if !opts.SourcesContent { + sourcesContent = api.SourcesContentExclude + } + + opts.compiled = api.BuildOptions{ + Outfile: outFile, + Bundle: true, + Metafile: opts.Metafile, + AbsWorkingDir: opts.AbsWorkingDir, + + Target: target, + Format: format, + Sourcemap: sourceMap, + SourcesContent: sourcesContent, + + Loader: loaders, + + MinifyWhitespace: opts.Minify, + MinifyIdentifiers: opts.Minify, + MinifySyntax: opts.Minify, + + Outdir: outDir, + Splitting: opts.Splitting, + + Define: defines, + External: opts.Externals, + + JSXFactory: opts.JSXFactory, + JSXFragment: opts.JSXFragment, + + JSX: jsx, + JSXImportSource: opts.JSXImportSource, + + Tsconfig: opts.TsConfig, + + EntryPoints: opts.EntryPoints, + } + + if opts.Stdin { + // This makes ESBuild pass `stdin` as the Importer to the import. + opts.compiled.Stdin = &api.StdinOptions{ + Contents: opts.Contents, + ResolveDir: opts.ResolveDir, + Loader: loader, + } + } + return +} + +func (o Options) loaderFromFilename(filename string) api.Loader { + ext := filepath.Ext(filename) + if optsLoaders := o.compiled.Loader; optsLoaders != nil { + if l, found := optsLoaders[ext]; found { + return l + } + } + l, found := extensionToLoaderMap[ext] + if found { + return l + } + return api.LoaderJS +} + +func (opts *Options) validate() error { + if opts.ImportOnResolveFunc != nil && opts.ImportOnLoadFunc == nil { + return fmt.Errorf("ImportOnLoadFunc must be set if ImportOnResolveFunc is set") + } + if opts.ImportOnResolveFunc == nil && opts.ImportOnLoadFunc != nil { + return fmt.Errorf("ImportOnResolveFunc must be set if ImportOnLoadFunc is set") + } + if opts.AbsWorkingDir == "" { + return fmt.Errorf("AbsWorkingDir must be set") + } + return nil +} diff --git a/internal/js/esbuild/options_test.go b/internal/js/esbuild/options_test.go new file mode 100644 index 000000000..ca19717f7 --- /dev/null +++ b/internal/js/esbuild/options_test.go @@ -0,0 +1,219 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "testing" + + "github.com/gohugoio/hugo/media" + + "github.com/evanw/esbuild/pkg/api" + + qt "github.com/frankban/quicktest" +) + +func TestToBuildOptions(t *testing.T) { + c := qt.New(t) + + opts := Options{ + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", + Format: "cjs", + Minify: true, + AvoidTDZ: true, + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + SourcesContent: 1, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "inline", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + SourcesContent: 1, + Sourcemap: api.SourceMapInline, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "inline", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Sourcemap: api.SourceMapInline, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "external", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Sourcemap: api.SourceMapExternal, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + JSX: "automatic", JSXImportSource: "preact", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + JSX: api.JSXAutomatic, + JSXImportSource: "preact", + }) +} + +func TestToBuildOptionsTarget(t *testing.T) { + c := qt.New(t) + + for _, test := range []struct { + target string + expect api.Target + }{ + {"es2015", api.ES2015}, + {"es2016", api.ES2016}, + {"es2017", api.ES2017}, + {"es2018", api.ES2018}, + {"es2019", api.ES2019}, + {"es2020", api.ES2020}, + {"es2021", api.ES2021}, + {"es2022", api.ES2022}, + {"es2023", api.ES2023}, + {"", api.ESNext}, + {"esnext", api.ESNext}, + } { + c.Run(test.target, func(c *qt.C) { + opts := Options{ + ExternalOptions: ExternalOptions{ + Target: test.target, + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled.Target, qt.Equals, test.expect) + }) + } +} + +func TestDecodeExternalOptions(t *testing.T) { + c := qt.New(t) + m := map[string]any{} + opts, err := DecodeExternalOptions(m) + c.Assert(err, qt.IsNil) + c.Assert(opts, qt.DeepEquals, ExternalOptions{ + SourcesContent: true, + }) +} diff --git a/internal/js/esbuild/resolve.go b/internal/js/esbuild/resolve.go new file mode 100644 index 000000000..ac0010da9 --- /dev/null +++ b/internal/js/esbuild/resolve.go @@ -0,0 +1,315 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" + "github.com/spf13/afero" +) + +const ( + NsHugoImport = "ns-hugo-imp" + NsHugoImportResolveFunc = "ns-hugo-imp-func" + nsHugoParams = "ns-hugo-params" + pathHugoConfigParams = "@params/config" + + stdinImporter = "" +) + +var hugoNamespaces = []string{NsHugoImport, NsHugoImportResolveFunc, nsHugoParams} + +const ( + PrefixHugoVirtual = "__hu_v" + PrefixHugoMemory = "__hu_m" +) + +var extensionToLoaderMap = map[string]api.Loader{ + ".js": api.LoaderJS, + ".mjs": api.LoaderJS, + ".cjs": api.LoaderJS, + ".jsx": api.LoaderJSX, + ".ts": api.LoaderTS, + ".tsx": api.LoaderTSX, + ".css": api.LoaderCSS, + ".json": api.LoaderJSON, + ".txt": api.LoaderText, +} + +// This is a common sub-set of ESBuild's default extensions. +// We assume that imports of JSON, CSS etc. will be using their full +// name with extension. +var commonExtensions = []string{".js", ".ts", ".tsx", ".jsx"} + +// ResolveComponent resolves a component using the given resolver. +func ResolveComponent[T any](impPath string, resolve func(string) (v T, found, isDir bool)) (v T, found bool) { + findFirst := func(base string) (v T, found, isDir bool) { + for _, ext := range commonExtensions { + if strings.HasSuffix(impPath, ext) { + // Import of foo.js.js need the full name. + continue + } + if v, found, isDir = resolve(base + ext); found { + return + } + } + + // Not found. + return + } + + // We need to check if this is a regular file imported without an extension. + // There may be ambiguous situations where both foo.js and foo/index.js exists. + // This import order is in line with both how Node and ESBuild's native + // import resolver works. + + // It may be a regular file imported without an extension, e.g. + // foo or foo/index. + v, found, _ = findFirst(impPath) + if found { + return v, found + } + + base := filepath.Base(impPath) + if base == "index" { + // try index.esm.js etc. + v, found, _ = findFirst(impPath + ".esm") + if found { + return v, found + } + } + + // Check the path as is. + var isDir bool + v, found, isDir = resolve(impPath) + if found && isDir { + v, found, _ = findFirst(filepath.Join(impPath, "index")) + if !found { + v, found, _ = findFirst(filepath.Join(impPath, "index.esm")) + } + } + + if !found && strings.HasSuffix(base, ".js") { + v, found, _ = findFirst(strings.TrimSuffix(impPath, ".js")) + } + + return +} + +// ResolveResource resolves a resource using the given resourceGetter. +func ResolveResource(impPath string, resourceGetter resource.ResourceGetter) (r resource.Resource) { + resolve := func(name string) (v resource.Resource, found, isDir bool) { + r := resourceGetter.Get(name) + return r, r != nil, false + } + r, found := ResolveComponent(impPath, resolve) + if !found { + return nil + } + return r +} + +func newFSResolver(fs afero.Fs) *fsResolver { + return &fsResolver{fs: fs, resolved: maps.NewCache[string, *hugofs.FileMeta]()} +} + +type fsResolver struct { + fs afero.Fs + resolved *maps.Cache[string, *hugofs.FileMeta] +} + +func (r *fsResolver) resolveComponent(impPath string) *hugofs.FileMeta { + v, _ := r.resolved.GetOrCreate(impPath, func() (*hugofs.FileMeta, error) { + resolve := func(name string) (*hugofs.FileMeta, bool, bool) { + if fi, err := r.fs.Stat(name); err == nil { + return fi.(hugofs.FileMetaInfo).Meta(), true, fi.IsDir() + } + return nil, false, false + } + v, _ := ResolveComponent(impPath, resolve) + return v, nil + }) + return v +} + +func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsManager identity.Manager, opts Options) ([]api.Plugin, error) { + fs := rs.Assets + + resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) { + impPath := args.Path + shimmed := false + if opts.Shims != nil { + override, found := opts.Shims[impPath] + if found { + impPath = override + shimmed = true + } + } + + if opts.ImportOnResolveFunc != nil { + if s := opts.ImportOnResolveFunc(impPath, args); s != "" { + return api.OnResolveResult{Path: s, Namespace: NsHugoImportResolveFunc}, nil + } + } + + importer := args.Importer + + isStdin := importer == stdinImporter + var relDir string + if !isStdin { + if strings.HasPrefix(importer, PrefixHugoVirtual) { + relDir = filepath.Dir(strings.TrimPrefix(importer, PrefixHugoVirtual)) + } else { + rel, found := fs.MakePathRelative(importer, true) + + if !found { + if shimmed { + relDir = opts.SourceDir + } else { + // Not in any of the /assets folders. + // This is an import from a node_modules, let + // ESBuild resolve this. + return api.OnResolveResult{}, nil + } + } else { + relDir = filepath.Dir(rel) + } + } + } else { + relDir = opts.SourceDir + } + + // Imports not starting with a "." is assumed to live relative to /assets. + // Hugo makes no assumptions about the directory structure below /assets. + if relDir != "" && strings.HasPrefix(impPath, ".") { + impPath = filepath.Join(relDir, impPath) + } + + m := assetsResolver.resolveComponent(impPath) + + if m != nil { + depsManager.AddIdentity(m.PathInfo) + + // Store the source root so we can create a jsconfig.json + // to help IntelliSense when the build is done. + // This should be a small number of elements, and when + // in server mode, we may get stale entries on renames etc., + // but that shouldn't matter too much. + rs.JSConfigBuilder.AddSourceRoot(m.SourceRoot) + return api.OnResolveResult{Path: m.Filename, Namespace: NsHugoImport}, nil + } + + // Fall back to ESBuild's resolve. + return api.OnResolveResult{}, nil + } + + importResolver := api.Plugin{ + Name: "hugo-import-resolver", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `.*`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + return resolveImport(args) + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: NsHugoImport}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + b, err := os.ReadFile(args.Path) + if err != nil { + return api.OnLoadResult{}, fmt.Errorf("failed to read %q: %w", args.Path, err) + } + c := string(b) + + return api.OnLoadResult{ + // See https://github.com/evanw/esbuild/issues/502 + // This allows all modules to resolve dependencies + // in the main project's node_modules. + ResolveDir: opts.ResolveDir, + Contents: &c, + Loader: opts.loaderFromFilename(args.Path), + }, nil + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: NsHugoImportResolveFunc}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + c := opts.ImportOnLoadFunc(args) + if c == "" { + return api.OnLoadResult{}, fmt.Errorf("ImportOnLoadFunc failed to resolve %q", args.Path) + } + + return api.OnLoadResult{ + ResolveDir: opts.ResolveDir, + Contents: &c, + Loader: opts.loaderFromFilename(args.Path), + }, nil + }) + }, + } + + params := opts.Params + if params == nil { + // This way @params will always resolve to something. + params = make(map[string]any) + } + + b, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("failed to marshal params: %w", err) + } + + paramsPlugin := api.Plugin{ + Name: "hugo-params-plugin", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `^@params(/config)?$`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + resolvedPath := args.Importer + + if args.Path == pathHugoConfigParams { + resolvedPath = pathHugoConfigParams + } + + return api.OnResolveResult{ + Path: resolvedPath, + Namespace: nsHugoParams, + }, nil + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsHugoParams}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + bb := b + if args.Path != pathHugoConfigParams && opts.ImportParamsOnLoadFunc != nil { + bb = opts.ImportParamsOnLoadFunc(args) + } + s := string(bb) + + if s == "" { + s = "{}" + } + + return api.OnLoadResult{ + Contents: &s, + Loader: api.LoaderJSON, + }, nil + }) + }, + } + + return []api.Plugin{importResolver, paramsPlugin}, nil +} diff --git a/internal/js/esbuild/resolve_test.go b/internal/js/esbuild/resolve_test.go new file mode 100644 index 000000000..86e3138f2 --- /dev/null +++ b/internal/js/esbuild/resolve_test.go @@ -0,0 +1,86 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "path" + "path/filepath" + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/testconfig" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/hugolib/paths" + "github.com/spf13/afero" +) + +func TestResolveComponentInAssets(t *testing.T) { + c := qt.New(t) + + for _, test := range []struct { + name string + files []string + impPath string + expect string + }{ + {"Basic, extension", []string{"foo.js", "bar.js"}, "foo.js", "foo.js"}, + {"Basic, no extension", []string{"foo.js", "bar.js"}, "foo", "foo.js"}, + {"Basic, no extension, typescript", []string{"foo.ts", "bar.js"}, "foo", "foo.ts"}, + {"Not found", []string{"foo.js", "bar.js"}, "moo.js", ""}, + {"Not found, double js extension", []string{"foo.js.js", "bar.js"}, "foo.js", ""}, + {"Index file, folder only", []string{"foo/index.js", "bar.js"}, "foo", "foo/index.js"}, + {"Index file, folder and index", []string{"foo/index.js", "bar.js"}, "foo/index", "foo/index.js"}, + {"Index file, folder and index and suffix", []string{"foo/index.js", "bar.js"}, "foo/index.js", "foo/index.js"}, + {"Index ESM file, folder only", []string{"foo/index.esm.js", "bar.js"}, "foo", "foo/index.esm.js"}, + {"Index ESM file, folder and index", []string{"foo/index.esm.js", "bar.js"}, "foo/index", "foo/index.esm.js"}, + {"Index ESM file, folder and index and suffix", []string{"foo/index.esm.js", "bar.js"}, "foo/index.esm.js", "foo/index.esm.js"}, + // We added these index.esm.js cases in v0.101.0. The case below is unlikely to happen in the wild, but add a test + // to document Hugo's behavior. We pick the file with the name index.js; anything else would be breaking. + {"Index and Index ESM file, folder only", []string{"foo/index.esm.js", "foo/index.js", "bar.js"}, "foo", "foo/index.js"}, + + // Issue #8949 + {"Check file before directory", []string{"foo.js", "foo/index.js"}, "foo", "foo.js"}, + } { + c.Run(test.name, func(c *qt.C) { + baseDir := "assets" + mfs := afero.NewMemMapFs() + + for _, filename := range test.files { + c.Assert(afero.WriteFile(mfs, filepath.Join(baseDir, filename), []byte("let foo='bar';"), 0o777), qt.IsNil) + } + + conf := testconfig.GetTestConfig(mfs, config.New()) + fs := hugofs.NewFrom(mfs, conf.BaseConfig()) + + p, err := paths.New(fs, conf) + c.Assert(err, qt.IsNil) + bfs, err := filesystems.NewBase(p, nil) + c.Assert(err, qt.IsNil) + resolver := newFSResolver(bfs.Assets.Fs) + + got := resolver.resolveComponent(test.impPath) + + gotPath := "" + expect := test.expect + if got != nil { + gotPath = filepath.ToSlash(got.Filename) + expect = path.Join(baseDir, test.expect) + } + + c.Assert(gotPath, qt.Equals, expect) + }) + } +} diff --git a/internal/js/esbuild/sourcemap.go b/internal/js/esbuild/sourcemap.go new file mode 100644 index 000000000..647f0c081 --- /dev/null +++ b/internal/js/esbuild/sourcemap.go @@ -0,0 +1,80 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/paths" +) + +type sourceMap struct { + Version int `json:"version"` + Sources []string `json:"sources"` + SourcesContent []string `json:"sourcesContent"` + Mappings string `json:"mappings"` + Names []string `json:"names"` +} + +func fixOutputFile(o *api.OutputFile, resolve func(string) string) error { + if strings.HasSuffix(o.Path, ".map") { + b, err := fixSourceMap(o.Contents, resolve) + if err != nil { + return err + } + o.Contents = b + } + return nil +} + +func fixSourceMap(s []byte, resolve func(string) string) ([]byte, error) { + var sm sourceMap + if err := json.Unmarshal([]byte(s), &sm); err != nil { + return nil, err + } + + sm.Sources = fixSourceMapSources(sm.Sources, resolve) + + b, err := json.Marshal(sm) + if err != nil { + return nil, err + } + + return b, nil +} + +func fixSourceMapSources(s []string, resolve func(string) string) []string { + var result []string + for _, src := range s { + if s := resolve(src); s != "" { + // Absolute filenames works fine on U*ix (tested in Chrome on MacOs), but works very poorly on Windows (again Chrome). + // So, convert it to a URL. + if u, err := paths.UrlFromFilename(s); err == nil { + result = append(result, u.String()) + } + } + } + return result +} + +// Used in tests. +func SourcesFromSourceMap(s string) []string { + var sm sourceMap + if err := json.Unmarshal([]byte(s), &sm); err != nil { + return nil + } + return sm.Sources +} diff --git a/lazy/init.go b/lazy/init.go index 7b88a5351..bef3867a9 100644 --- a/lazy/init.go +++ b/lazy/init.go @@ -36,7 +36,7 @@ type Init struct { prev *Init children []*Init - init onceMore + init OnceMore out any err error f func(context.Context) (any, error) diff --git a/lazy/once.go b/lazy/once.go index cea096652..dac689df3 100644 --- a/lazy/once.go +++ b/lazy/once.go @@ -24,13 +24,13 @@ import ( // * it can be reset, so the action can be repeated if needed // * it has methods to check if it's done or in progress -type onceMore struct { +type OnceMore struct { mu sync.Mutex lock uint32 done uint32 } -func (t *onceMore) Do(f func()) { +func (t *OnceMore) Do(f func()) { if atomic.LoadUint32(&t.done) == 1 { return } @@ -53,15 +53,15 @@ func (t *onceMore) Do(f func()) { f() } -func (t *onceMore) InProgress() bool { +func (t *OnceMore) InProgress() bool { return atomic.LoadUint32(&t.lock) == 1 } -func (t *onceMore) Done() bool { +func (t *OnceMore) Done() bool { return atomic.LoadUint32(&t.done) == 1 } -func (t *onceMore) ResetWithLock() *sync.Mutex { +func (t *OnceMore) ResetWithLock() *sync.Mutex { t.mu.Lock() defer atomic.StoreUint32(&t.done, 0) return &t.mu diff --git a/media/mediaType.go b/media/mediaType.go index a7ba1309a..97b10879c 100644 --- a/media/mediaType.go +++ b/media/mediaType.go @@ -273,9 +273,13 @@ func (t Types) GetByType(tp string) (Type, bool) { return Type{}, false } +func (t Types) normalizeSuffix(s string) string { + return strings.ToLower(strings.TrimPrefix(s, ".")) +} + // BySuffix will return all media types matching a suffix. func (t Types) BySuffix(suffix string) []Type { - suffix = strings.ToLower(suffix) + suffix = t.normalizeSuffix(suffix) var types []Type for _, tt := range t { if tt.hasSuffix(suffix) { @@ -287,7 +291,7 @@ func (t Types) BySuffix(suffix string) []Type { // GetFirstBySuffix will return the first type matching the given suffix. func (t Types) GetFirstBySuffix(suffix string) (Type, SuffixInfo, bool) { - suffix = strings.ToLower(suffix) + suffix = t.normalizeSuffix(suffix) for _, tt := range t { if tt.hasSuffix(suffix) { return tt, SuffixInfo{ @@ -304,7 +308,7 @@ func (t Types) GetFirstBySuffix(suffix string) (Type, SuffixInfo, bool) { // is ambiguous. // The lookup is case insensitive. func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) { - suffix = strings.ToLower(suffix) + suffix = t.normalizeSuffix(suffix) for _, tt := range t { if tt.hasSuffix(suffix) { if found { @@ -324,7 +328,7 @@ func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) { } func (t Types) IsTextSuffix(suffix string) bool { - suffix = strings.ToLower(suffix) + suffix = t.normalizeSuffix(suffix) for _, tt := range t { if tt.hasSuffix(suffix) { return tt.IsText() diff --git a/resources/image.go b/resources/image.go index 4595866d4..686f70e27 100644 --- a/resources/image.go +++ b/resources/image.go @@ -51,6 +51,7 @@ var ( _ resource.Source = (*imageResource)(nil) _ resource.Cloner = (*imageResource)(nil) _ resource.NameNormalizedProvider = (*imageResource)(nil) + _ targetPathProvider = (*imageResource)(nil) ) // imageResource represents an image resource. @@ -160,6 +161,10 @@ func (i *imageResource) Colors() ([]images.Color, error) { return i.dominantColors, nil } +func (i *imageResource) targetPath() string { + return i.TargetPath() +} + // Clone is for internal use. func (i *imageResource) Clone() resource.Resource { gr := i.baseResource.Clone().(baseResource) diff --git a/resources/page/page.go b/resources/page/page.go index 032ee320d..40e15dc6f 100644 --- a/resources/page/page.go +++ b/resources/page/page.go @@ -63,8 +63,7 @@ type ChildCareProvider interface { // section. RegularPagesRecursive() Pages - // Resources returns a list of all resources. - Resources() resource.Resources + resource.ResourcesProvider } type MarkupProvider interface { diff --git a/resources/resource.go b/resources/resource.go index cc7008e5a..6025cbf4c 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -47,6 +47,8 @@ var ( _ resource.Cloner = (*genericResource)(nil) _ resource.ResourcesLanguageMerger = (*resource.Resources)(nil) _ resource.Identifier = (*genericResource)(nil) + _ targetPathProvider = (*genericResource)(nil) + _ sourcePathProvider = (*genericResource)(nil) _ identity.IdentityGroupProvider = (*genericResource)(nil) _ identity.DependencyManagerProvider = (*genericResource)(nil) _ identity.Identity = (*genericResource)(nil) @@ -79,6 +81,7 @@ type ResourceSourceDescriptor struct { TargetPath string BasePathRelPermalink string BasePathTargetPath string + SourceFilenameOrPath string // Used for error logging. // The Data to associate with this resource. Data map[string]any @@ -463,6 +466,17 @@ func (l *genericResource) Key() string { return key } +func (l *genericResource) targetPath() string { + return l.paths.TargetPath() +} + +func (l *genericResource) sourcePath() string { + if p := l.sd.SourceFilenameOrPath; p != "" { + return p + } + return "" +} + func (l *genericResource) MediaType() media.Type { return l.sd.MediaType } @@ -660,3 +674,43 @@ func (r *resourceHash) init(l hugio.ReadSeekCloserProvider) error { func hashImage(r io.ReadSeeker) (uint64, int64, error) { return hashing.XXHashFromReader(r) } + +// InternalResourceTargetPath is used internally to get the target path for a Resource. +func InternalResourceTargetPath(r resource.Resource) string { + return r.(targetPathProvider).targetPath() +} + +// InternalResourceSourcePathBestEffort is used internally to get the source path for a Resource. +// It returns an empty string if the source path is not available. +func InternalResourceSourcePath(r resource.Resource) string { + if sp, ok := r.(sourcePathProvider); ok { + if p := sp.sourcePath(); p != "" { + return p + } + } + return "" +} + +// InternalResourceSourcePathBestEffort is used internally to get the source path for a Resource. +// Used for error messages etc. +// It will fall back to the target path if the source path is not available. +func InternalResourceSourcePathBestEffort(r resource.Resource) string { + if s := InternalResourceSourcePath(r); s != "" { + return s + } + return InternalResourceTargetPath(r) +} + +type targetPathProvider interface { + // targetPath is the relative path to this resource. + // In most cases this will be the same as the RelPermalink(), + // but it will not trigger any lazy publishing. + targetPath() string +} + +// Optional interface implemented by resources that can provide the source path. +type sourcePathProvider interface { + // sourcePath is the source path to this resource's source. + // This is used in error messages etc. + sourcePath() string +} diff --git a/resources/resource/resource_helpers.go b/resources/resource/resource_helpers.go index c2bb463c8..8575ae79e 100644 --- a/resources/resource/resource_helpers.go +++ b/resources/resource/resource_helpers.go @@ -14,10 +14,11 @@ package resource import ( - "github.com/gohugoio/hugo/common/maps" "strings" "time" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/helpers" "github.com/pelletier/go-toml/v2" diff --git a/resources/resource/resources.go b/resources/resource/resources.go index 32bcdbb08..480c703b5 100644 --- a/resources/resource/resources.go +++ b/resources/resource/resources.go @@ -16,8 +16,11 @@ package resource import ( "fmt" + "path" "strings" + "github.com/gohugoio/hugo/common/hreflect" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/hugofs/glob" "github.com/spf13/cast" @@ -29,6 +32,51 @@ var _ ResourceFinder = (*Resources)(nil) // I.e. both pages and images etc. type Resources []Resource +// Mount mounts the given resources from base to the given target path. +// Note that leading slashes in target marks an absolute path. +// This method is currently only useful in js.Batch. +func (r Resources) Mount(base, target string) ResourceGetter { + return resourceGetterFunc(func(namev any) Resource { + name1, err := cast.ToStringE(namev) + if err != nil { + panic(err) + } + + isTargetAbs := strings.HasPrefix(target, "/") + + if target != "" { + name1 = strings.TrimPrefix(name1, target) + if !isTargetAbs { + name1 = paths.TrimLeading(name1) + } + } + + if base != "" && isTargetAbs { + name1 = path.Join(base, name1) + } + + for _, res := range r { + name2 := res.Name() + + if base != "" && !isTargetAbs { + name2 = paths.TrimLeading(strings.TrimPrefix(name2, base)) + } + + if strings.EqualFold(name1, name2) { + return res + } + + } + + return nil + }) +} + +type ResourcesProvider interface { + // Resources returns a list of all resources. + Resources() Resources +} + // var _ resource.ResourceFinder = (*Namespace)(nil) // ResourcesConverter converts a given slice of Resource objects to Resources. type ResourcesConverter interface { @@ -63,13 +111,25 @@ func (r Resources) Get(name any) Resource { panic(err) } - namestr = paths.AddLeadingSlash(namestr) + isDotCurrent := strings.HasPrefix(namestr, "./") + if isDotCurrent { + namestr = strings.TrimPrefix(namestr, "./") + } else { + namestr = paths.AddLeadingSlash(namestr) + } + + check := func(name string) bool { + if !isDotCurrent { + name = paths.AddLeadingSlash(name) + } + return strings.EqualFold(namestr, name) + } // First check the Name. // Note that this can be modified by the user in the front matter, // also, it does not contain any language code. for _, resource := range r { - if strings.EqualFold(namestr, paths.AddLeadingSlash(resource.Name())) { + if check(resource.Name()) { return resource } } @@ -77,7 +137,7 @@ func (r Resources) Get(name any) Resource { // Finally, check the normalized name. for _, resource := range r { if nop, ok := resource.(NameNormalizedProvider); ok { - if strings.EqualFold(namestr, paths.AddLeadingSlash(nop.NameNormalized())) { + if check(nop.NameNormalized()) { return resource } } @@ -197,14 +257,35 @@ type Source interface { Publish() error } -// ResourceFinder provides methods to find Resources. -// Note that GetRemote (as found in resources.GetRemote) is -// not covered by this interface, as this is only available as a global template function. -type ResourceFinder interface { +type ResourceGetter interface { // Get locates the Resource with the given name in the current context (e.g. in .Page.Resources). // // It returns nil if no Resource could found, panics if name is invalid. Get(name any) Resource +} + +type IsProbablySameResourceGetter interface { + IsProbablySameResourceGetter(other ResourceGetter) bool +} + +// StaleInfoResourceGetter is a ResourceGetter that also provides information about +// whether the underlying resources are stale. +type StaleInfoResourceGetter interface { + StaleInfo + ResourceGetter +} + +type resourceGetterFunc func(name any) Resource + +func (f resourceGetterFunc) Get(name any) Resource { + return f(name) +} + +// ResourceFinder provides methods to find Resources. +// Note that GetRemote (as found in resources.GetRemote) is +// not covered by this interface, as this is only available as a global template function. +type ResourceFinder interface { + ResourceGetter // GetMatch finds the first Resource matching the given pattern, or nil if none found. // @@ -235,3 +316,92 @@ type ResourceFinder interface { // It returns nil if no Resources could found, panics if typ is invalid. ByType(typ any) Resources } + +// NewCachedResourceGetter creates a new ResourceGetter from the given objects. +// If multiple objects are provided, they are merged into one where +// the first match wins. +func NewCachedResourceGetter(os ...any) *cachedResourceGetter { + var getters multiResourceGetter + for _, o := range os { + if g, ok := unwrapResourceGetter(o); ok { + getters = append(getters, g) + } + } + + return &cachedResourceGetter{ + cache: maps.NewCache[string, Resource](), + delegate: getters, + } +} + +type multiResourceGetter []ResourceGetter + +func (m multiResourceGetter) Get(name any) Resource { + for _, g := range m { + if res := g.Get(name); res != nil { + return res + } + } + return nil +} + +var ( + _ ResourceGetter = (*cachedResourceGetter)(nil) + _ IsProbablySameResourceGetter = (*cachedResourceGetter)(nil) +) + +type cachedResourceGetter struct { + cache *maps.Cache[string, Resource] + delegate ResourceGetter +} + +func (c *cachedResourceGetter) Get(name any) Resource { + namestr, err := cast.ToStringE(name) + if err != nil { + panic(err) + } + v, _ := c.cache.GetOrCreate(namestr, func() (Resource, error) { + v := c.delegate.Get(name) + return v, nil + }) + return v +} + +func (c *cachedResourceGetter) IsProbablySameResourceGetter(other ResourceGetter) bool { + isProbablyEq := true + c.cache.ForEeach(func(k string, v Resource) bool { + if v != other.Get(k) { + isProbablyEq = false + return false + } + return true + }) + + return isProbablyEq +} + +func unwrapResourceGetter(v any) (ResourceGetter, bool) { + if v == nil { + return nil, false + } + switch vv := v.(type) { + case ResourceGetter: + return vv, true + case ResourcesProvider: + return vv.Resources(), true + case func(name any) Resource: + return resourceGetterFunc(vv), true + default: + vvv, ok := hreflect.ToSliceAny(v) + if !ok { + return nil, false + } + var getters multiResourceGetter + for _, vv := range vvv { + if g, ok := unwrapResourceGetter(vv); ok { + getters = append(getters, g) + } + } + return getters, len(getters) > 0 + } +} diff --git a/resources/resource/resources_integration_test.go b/resources/resource/resources_integration_test.go new file mode 100644 index 000000000..920fc7397 --- /dev/null +++ b/resources/resource/resources_integration_test.go @@ -0,0 +1,105 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestResourcesMount(t *testing.T) { + files := ` +-- hugo.toml -- +-- assets/text/txt1.txt -- +Text 1. +-- assets/text/txt2.txt -- +Text 2. +-- assets/text/sub/txt3.txt -- +Text 3. +-- assets/text/sub/txt4.txt -- +Text 4. +-- content/mybundle/index.md -- +--- +title: "My Bundle" +--- +-- content/mybundle/txt1.txt -- +Text 1. +-- content/mybundle/sub/txt2.txt -- +Text 1. +-- layouts/index.html -- +{{ $mybundle := site.GetPage "mybundle" }} +{{ $subResources := resources.Match "/text/sub/*.*" }} +{{ $subResourcesMount := $subResources.Mount "/text/sub" "/newroot" }} +resources:text/txt1.txt:{{ with resources.Get "text/txt1.txt" }}{{ .Name }}{{ end }}| +resources:text/txt2.txt:{{ with resources.Get "text/txt2.txt" }}{{ .Name }}{{ end }}| +resources:text/sub/txt3.txt:{{ with resources.Get "text/sub/txt3.txt" }}{{ .Name }}{{ end }}| +subResources.range:{{ range $subResources }}{{ .Name }}|{{ end }}| +subResources:"text/sub/txt3.txt:{{ with $subResources.Get "text/sub/txt3.txt" }}{{ .Name }}{{ end }}| +subResourcesMount:/newroot/txt3.txt:{{ with $subResourcesMount.Get "/newroot/txt3.txt" }}{{ .Name }}{{ end }}| +page:txt1.txt:{{ with $mybundle.Resources.Get "txt1.txt" }}{{ .Name }}{{ end }}| +page:./txt1.txt:{{ with $mybundle.Resources.Get "./txt1.txt" }}{{ .Name }}{{ end }}| +page:sub/txt2.txt:{{ with $mybundle.Resources.Get "sub/txt2.txt" }}{{ .Name }}{{ end }}| +` + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", ` +resources:text/txt1.txt:/text/txt1.txt| +resources:text/txt2.txt:/text/txt2.txt| +resources:text/sub/txt3.txt:/text/sub/txt3.txt| +subResources:"text/sub/txt3.txt:/text/sub/txt3.txt| +subResourcesMount:/newroot/txt3.txt:/text/sub/txt3.txt| +page:txt1.txt:txt1.txt| +page:./txt1.txt:txt1.txt| +page:sub/txt2.txt:sub/txt2.txt| +`) +} + +func TestResourcesMountOnRename(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "home", "sitemap"] +-- content/mybundle/index.md -- +--- +title: "My Bundle" +resources: +- name: /foo/bars.txt + src: foo/txt1.txt +- name: foo/bars2.txt + src: foo/txt2.txt +--- +-- content/mybundle/foo/txt1.txt -- +Text 1. +-- content/mybundle/foo/txt2.txt -- +Text 2. +-- layouts/_default/single.html -- +Single. +{{ $mybundle := site.GetPage "mybundle" }} +Resources:{{ range $mybundle.Resources }}Name: {{ .Name }}|{{ end }}$ +{{ $subResourcesMount := $mybundle.Resources.Mount "/foo" "/newroot" }} + {{ $subResourcesMount2 := $mybundle.Resources.Mount "foo" "/newroot" }} +{{ $subResourcesMount3 := $mybundle.Resources.Mount "foo" "." }} +subResourcesMount:/newroot/bars.txt:{{ with $subResourcesMount.Get "/newroot/bars.txt" }}{{ .Name }}{{ end }}| +subResourcesMount:/newroot/bars2.txt:{{ with $subResourcesMount.Get "/newroot/bars2.txt" }}{{ .Name }}{{ end }}| +subResourcesMount2:/newroot/bars2.txt:{{ with $subResourcesMount2.Get "/newroot/bars2.txt" }}{{ .Name }}{{ end }}| +subResourcesMount3:bars2.txt:{{ with $subResourcesMount3.Get "bars2.txt" }}{{ .Name }}{{ end }}| +` + b := hugolib.Test(t, files) + b.AssertFileContent("public/mybundle/index.html", + "Resources:Name: foo/bars.txt|Name: foo/bars2.txt|$", + "subResourcesMount:/newroot/bars.txt:|\nsubResourcesMount:/newroot/bars2.txt:|", + "subResourcesMount2:/newroot/bars2.txt:foo/bars2.txt|", + "subResourcesMount3:bars2.txt:foo/bars2.txt|", + ) +} diff --git a/resources/resource/resources_test.go b/resources/resource/resources_test.go new file mode 100644 index 000000000..ebadbb312 --- /dev/null +++ b/resources/resource/resources_test.go @@ -0,0 +1,122 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestResourcesMount(t *testing.T) { + c := qt.New(t) + c.Assert(true, qt.IsTrue) + + var m ResourceGetter + var r Resources + + check := func(in, expect string) { + c.Helper() + r := m.Get(in) + c.Assert(r, qt.Not(qt.IsNil)) + c.Assert(r.Name(), qt.Equals, expect) + } + + checkNil := func(in string) { + c.Helper() + r := m.Get(in) + c.Assert(r, qt.IsNil) + } + + // Misc tests. + r = Resources{ + testResource{name: "/foo/theme.css"}, + } + + m = r.Mount("/foo", ".") + check("./theme.css", "/foo/theme.css") + + // Relative target. + r = Resources{ + testResource{name: "/a/b/c/d.txt"}, + testResource{name: "/a/b/c/e/f.txt"}, + testResource{name: "/a/b/d.txt"}, + testResource{name: "/a/b/e.txt"}, + } + + m = r.Mount("/a/b/c", "z") + check("z/d.txt", "/a/b/c/d.txt") + check("z/e/f.txt", "/a/b/c/e/f.txt") + + m = r.Mount("/a/b", "") + check("d.txt", "/a/b/d.txt") + m = r.Mount("/a/b", ".") + check("d.txt", "/a/b/d.txt") + m = r.Mount("/a/b", "./") + check("d.txt", "/a/b/d.txt") + check("./d.txt", "/a/b/d.txt") + + m = r.Mount("/a/b", ".") + check("./d.txt", "/a/b/d.txt") + + // Absolute target. + m = r.Mount("/a/b/c", "/z") + check("/z/d.txt", "/a/b/c/d.txt") + check("/z/e/f.txt", "/a/b/c/e/f.txt") + checkNil("/z/f.txt") + + m = r.Mount("/a/b", "/z") + check("/z/c/d.txt", "/a/b/c/d.txt") + check("/z/c/e/f.txt", "/a/b/c/e/f.txt") + check("/z/d.txt", "/a/b/d.txt") + checkNil("/z/f.txt") + + m = r.Mount("", "") + check("/a/b/c/d.txt", "/a/b/c/d.txt") + check("/a/b/c/e/f.txt", "/a/b/c/e/f.txt") + check("/a/b/d.txt", "/a/b/d.txt") + checkNil("/a/b/f.txt") + + m = r.Mount("/a/b", "/a/b") + check("/a/b/c/d.txt", "/a/b/c/d.txt") + check("/a/b/c/e/f.txt", "/a/b/c/e/f.txt") + check("/a/b/d.txt", "/a/b/d.txt") + checkNil("/a/b/f.txt") + + // Resources with relative paths. + r = Resources{ + testResource{name: "a/b/c/d.txt"}, + testResource{name: "a/b/c/e/f.txt"}, + testResource{name: "a/b/d.txt"}, + testResource{name: "a/b/e.txt"}, + testResource{name: "n.txt"}, + } + + m = r.Mount("a/b", "z") + check("z/d.txt", "a/b/d.txt") + checkNil("/z/d.txt") +} + +type testResource struct { + Resource + name string +} + +func (r testResource) Name() string { + return r.name +} + +func (r testResource) NameNormalized() string { + return r.name +} diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index 2d868bd15..7dd26f4c0 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -143,16 +143,18 @@ func (c *Client) Get(pathname string) (resource.Resource, error) { return nil, err } - pi := fi.(hugofs.FileMetaInfo).Meta().PathInfo + meta := fi.(hugofs.FileMetaInfo).Meta() + pi := meta.PathInfo return c.rs.NewResource(resources.ResourceSourceDescriptor{ LazyPublish: true, OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { return c.rs.BaseFs.Assets.Fs.Open(filename) }, - Path: pi, - GroupIdentity: pi, - TargetPath: pathname, + Path: pi, + GroupIdentity: pi, + TargetPath: pathname, + SourceFilenameOrPath: meta.Filename, }) }) } @@ -196,10 +198,11 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { return meta.Open() }, - NameNormalized: meta.PathInfo.Path(), - NameOriginal: meta.PathInfo.Unnormalized().Path(), - GroupIdentity: meta.PathInfo, - TargetPath: meta.PathInfo.Unnormalized().Path(), + NameNormalized: meta.PathInfo.Path(), + NameOriginal: meta.PathInfo.Unnormalized().Path(), + GroupIdentity: meta.PathInfo, + TargetPath: meta.PathInfo.Unnormalized().Path(), + SourceFilenameOrPath: meta.Filename, }) if err != nil { return true, err diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go index 7d2459225..8861ded5c 100644 --- a/resources/resource_metadata.go +++ b/resources/resource_metadata.go @@ -15,6 +15,7 @@ package resources import ( "fmt" + "path/filepath" "strconv" "strings" @@ -26,6 +27,7 @@ import ( "github.com/spf13/cast" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" ) var ( @@ -172,6 +174,8 @@ func assignMetadata(metadata []map[string]any, ma *metaResource) error { name, found := meta["name"] if found { name := cast.ToString(name) + // Bundled resources in sub folders are relative paths with forward slashes. Make sure any renames also matches that format: + name = paths.TrimLeading(filepath.ToSlash(name)) if !nameCounterFound { nameCounterFound = strings.Contains(name, counterPlaceHolder) } diff --git a/resources/resource_test.go b/resources/resource_test.go index ccce8eff7..d07770456 100644 --- a/resources/resource_test.go +++ b/resources/resource_test.go @@ -16,8 +16,26 @@ package resources import ( "os" "testing" + + qt "github.com/frankban/quicktest" ) +func TestAtomicStaler(t *testing.T) { + c := qt.New(t) + + type test struct { + AtomicStaler + } + + var v test + + c.Assert(v.StaleVersion(), qt.Equals, uint32(0)) + v.MarkStale() + c.Assert(v.StaleVersion(), qt.Equals, uint32(1)) + v.MarkStale() + c.Assert(v.StaleVersion(), qt.Equals, uint32(2)) +} + func BenchmarkHashImage(b *testing.B) { f, err := os.Open("testdata/sunset.jpg") if err != nil { diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go index cc68d2253..bd943461f 100644 --- a/resources/resource_transformers/js/build.go +++ b/resources/resource_transformers/js/build.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,209 +14,69 @@ package js import ( - "errors" - "fmt" - "io" - "os" "path" - "path/filepath" "regexp" - "strings" - - "github.com/spf13/afero" - - "github.com/gohugoio/hugo/hugofs" - "github.com/gohugoio/hugo/media" - - "github.com/gohugoio/hugo/common/herrors" - "github.com/gohugoio/hugo/common/text" - - "github.com/gohugoio/hugo/hugolib/filesystems" - "github.com/gohugoio/hugo/resources/internal" "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/internal/js/esbuild" + "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" ) // Client context for ESBuild. type Client struct { - rs *resources.Spec - sfs *filesystems.SourceFilesystem + c *esbuild.BuildClient } // New creates a new client context. func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) *Client { return &Client{ - rs: rs, - sfs: fs, + c: esbuild.NewBuildClient(fs, rs), } } -type buildTransformation struct { - optsm map[string]any - c *Client -} - -func (t *buildTransformation) Key() internal.ResourceTransformationKey { - return internal.NewResourceTransformationKey("jsbuild", t.optsm) -} - -func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { - ctx.OutMediaType = media.Builtin.JavascriptType - - opts, err := decodeOptions(t.optsm) - if err != nil { - return err - } - - if opts.TargetPath != "" { - ctx.OutPath = opts.TargetPath - } else { - ctx.ReplaceOutPathExtension(".js") - } - - src, err := io.ReadAll(ctx.From) - if err != nil { - return err - } - - opts.sourceDir = filepath.FromSlash(path.Dir(ctx.SourcePath)) - opts.resolveDir = t.c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved - opts.contents = string(src) - opts.mediaType = ctx.InMediaType - opts.tsConfig = t.c.rs.ResolveJSConfigFile("tsconfig.json") - - buildOptions, err := toBuildOptions(opts) - if err != nil { - return err - } - - buildOptions.Plugins, err = createBuildPlugins(ctx.DependencyManager, t.c, opts) - if err != nil { - return err - } - - if buildOptions.Sourcemap == api.SourceMapExternal && buildOptions.Outdir == "" { - buildOptions.Outdir, err = os.MkdirTemp(os.TempDir(), "compileOutput") - if err != nil { - return err - } - defer os.Remove(buildOptions.Outdir) - } - - if opts.Inject != nil { - // Resolve the absolute filenames. - for i, ext := range opts.Inject { - impPath := filepath.FromSlash(ext) - if filepath.IsAbs(impPath) { - return fmt.Errorf("inject: absolute paths not supported, must be relative to /assets") - } - - m := resolveComponentInAssets(t.c.rs.Assets.Fs, impPath) - - if m == nil { - return fmt.Errorf("inject: file %q not found", ext) - } - - opts.Inject[i] = m.Filename - - } - - buildOptions.Inject = opts.Inject - - } - - result := api.Build(buildOptions) - - if len(result.Errors) > 0 { - - createErr := func(msg api.Message) error { - loc := msg.Location - if loc == nil { - return errors.New(msg.Text) - } - path := loc.File - if path == stdinImporter { - path = ctx.SourcePath - } - - errorMessage := msg.Text - errorMessage = strings.ReplaceAll(errorMessage, nsImportHugo+":", "") - - var ( - f afero.File - err error - ) - - if strings.HasPrefix(path, nsImportHugo) { - path = strings.TrimPrefix(path, nsImportHugo+":") - f, err = hugofs.Os.Open(path) - } else { - var fi os.FileInfo - fi, err = t.c.sfs.Fs.Stat(path) - if err == nil { - m := fi.(hugofs.FileMetaInfo).Meta() - path = m.Filename - f, err = m.Open() - } - - } - - if err == nil { - fe := herrors. - NewFileErrorFromName(errors.New(errorMessage), path). - UpdatePosition(text.Position{Offset: -1, LineNumber: loc.Line, ColumnNumber: loc.Column}). - UpdateContent(f, nil) - - f.Close() - return fe - } - - return fmt.Errorf("%s", errorMessage) - } - - var errors []error - - for _, msg := range result.Errors { - errors = append(errors, createErr(msg)) - } - - // Return 1, log the rest. - for i, err := range errors { - if i > 0 { - t.c.rs.Logger.Errorf("js.Build failed: %s", err) - } - } - - return errors[0] - } - - if buildOptions.Sourcemap == api.SourceMapExternal { - content := string(result.OutputFiles[1].Contents) - symPath := path.Base(ctx.OutPath) + ".map" - re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`) - content = re.ReplaceAllString(content, "//# sourceMappingURL="+symPath+"\n") - - if err = ctx.PublishSourceMap(string(result.OutputFiles[0].Contents)); err != nil { - return err - } - _, err := ctx.To.Write([]byte(content)) - if err != nil { - return err - } - } else { - _, err := ctx.To.Write(result.OutputFiles[0].Contents) - if err != nil { - return err - } - } - return nil -} - -// Process process esbuild transform +// Process processes a resource with the user provided options. func (c *Client) Process(res resources.ResourceTransformer, opts map[string]any) (resource.Resource, error) { return res.Transform( &buildTransformation{c: c, optsm: opts}, ) } + +func (c *Client) transform(opts esbuild.Options, transformCtx *resources.ResourceTransformationCtx) (api.BuildResult, error) { + if transformCtx.DependencyManager != nil { + opts.DependencyManager = transformCtx.DependencyManager + } + + opts.StdinSourcePath = transformCtx.SourcePath + + result, err := c.c.Build(opts) + if err != nil { + return result, err + } + + if opts.ExternalOptions.SourceMap == "linked" || opts.ExternalOptions.SourceMap == "external" { + content := string(result.OutputFiles[1].Contents) + if opts.ExternalOptions.SourceMap == "linked" { + symPath := path.Base(transformCtx.OutPath) + ".map" + re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`) + content = re.ReplaceAllString(content, "//# sourceMappingURL="+symPath+"\n") + } + + if err = transformCtx.PublishSourceMap(string(result.OutputFiles[0].Contents)); err != nil { + return result, err + } + _, err := transformCtx.To.Write([]byte(content)) + if err != nil { + return result, err + } + } else { + _, err := transformCtx.To.Write(result.OutputFiles[0].Contents) + if err != nil { + return result, err + } + + } + return result, nil +} diff --git a/resources/resource_transformers/js/js_integration_test.go b/resources/resource_transformers/js/js_integration_test.go index 304c51d33..c62312ef5 100644 --- a/resources/resource_transformers/js/js_integration_test.go +++ b/resources/resource_transformers/js/js_integration_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,13 +14,16 @@ package js_test import ( + "os" "path/filepath" "strings" "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/internal/js/esbuild" ) func TestBuildVariants(t *testing.T) { @@ -173,7 +176,7 @@ hello: hello: other: "Bonjour" -- layouts/index.html -- -{{ $options := dict "minify" false "externals" (slice "react" "react-dom") }} +{{ $options := dict "minify" false "externals" (slice "react" "react-dom") "sourcemap" "linked" }} {{ $js := resources.Get "js/main.js" | js.Build $options }} JS: {{ template "print" $js }} {{ $jsx := resources.Get "js/myjsx.jsx" | js.Build $options }} @@ -201,14 +204,31 @@ TS2: {{ template "print" $ts2 }} TxtarString: files, }).Build() - b.AssertFileContent("public/js/myts.js", `//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJz`) - b.AssertFileContent("public/js/myts2.js.map", `"version": 3,`) + b.AssertFileContent("public/js/main.js", `//# sourceMappingURL=main.js.map`) + b.AssertFileContent("public/js/main.js.map", `"version":3`, "! ns-hugo") // linked + b.AssertFileContent("public/js/myts.js", `//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJz`) // inline b.AssertFileContent("public/index.html", ` console.log("included"); if (hasSpace.test(string)) var React = __toESM(__require("react")); function greeter(person) { `) + + checkMap := func(p string, expectLen int) { + s := b.FileContent(p) + sources := esbuild.SourcesFromSourceMap(s) + b.Assert(sources, qt.HasLen, expectLen) + + // Check that all source files exist. + for _, src := range sources { + filename, ok := paths.UrlStringToFilename(src) + b.Assert(ok, qt.IsTrue) + _, err := os.Stat(filename) + b.Assert(err, qt.IsNil, qt.Commentf("src: %q", src)) + } + } + + checkMap("public/js/main.js.map", 4) } func TestBuildError(t *testing.T) { diff --git a/resources/resource_transformers/js/options.go b/resources/resource_transformers/js/options.go deleted file mode 100644 index 8c271d032..000000000 --- a/resources/resource_transformers/js/options.go +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package js - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/gohugoio/hugo/common/maps" - "github.com/gohugoio/hugo/common/paths" - "github.com/gohugoio/hugo/identity" - "github.com/spf13/afero" - - "github.com/evanw/esbuild/pkg/api" - - "github.com/gohugoio/hugo/hugofs" - "github.com/gohugoio/hugo/media" - "github.com/mitchellh/mapstructure" -) - -const ( - nsImportHugo = "ns-hugo" - nsParams = "ns-params" - - stdinImporter = "" -) - -// Options esbuild configuration -type Options struct { - // If not set, the source path will be used as the base target path. - // Note that the target path's extension may change if the target MIME type - // is different, e.g. when the source is TypeScript. - TargetPath string - - // Whether to minify to output. - Minify bool - - // Whether to write mapfiles - SourceMap string - - // The language target. - // One of: es2015, es2016, es2017, es2018, es2019, es2020 or esnext. - // Default is esnext. - Target string - - // The output format. - // One of: iife, cjs, esm - // Default is to esm. - Format string - - // External dependencies, e.g. "react". - Externals []string - - // This option allows you to automatically replace a global variable with an import from another file. - // The filenames must be relative to /assets. - // See https://esbuild.github.io/api/#inject - Inject []string - - // User defined symbols. - Defines map[string]any - - // Maps a component import to another. - Shims map[string]string - - // User defined params. Will be marshaled to JSON and available as "@params", e.g. - // import * as params from '@params'; - Params any - - // What to use instead of React.createElement. - JSXFactory string - - // What to use instead of React.Fragment. - JSXFragment string - - // What to do about JSX syntax. - // See https://esbuild.github.io/api/#jsx - JSX string - - // Which library to use to automatically import JSX helper functions from. Only works if JSX is set to automatic. - // See https://esbuild.github.io/api/#jsx-import-source - JSXImportSource string - - // There is/was a bug in WebKit with severe performance issue with the tracking - // of TDZ checks in JavaScriptCore. - // - // Enabling this flag removes the TDZ and `const` assignment checks and - // may improve performance of larger JS codebases until the WebKit fix - // is in widespread use. - // - // See https://bugs.webkit.org/show_bug.cgi?id=199866 - // Deprecated: This no longer have any effect and will be removed. - // TODO(bep) remove. See https://github.com/evanw/esbuild/commit/869e8117b499ca1dbfc5b3021938a53ffe934dba - AvoidTDZ bool - - mediaType media.Type - outDir string - contents string - sourceDir string - resolveDir string - tsConfig string -} - -func decodeOptions(m map[string]any) (Options, error) { - var opts Options - - if err := mapstructure.WeakDecode(m, &opts); err != nil { - return opts, err - } - - if opts.TargetPath != "" { - opts.TargetPath = paths.ToSlashTrimLeading(opts.TargetPath) - } - - opts.Target = strings.ToLower(opts.Target) - opts.Format = strings.ToLower(opts.Format) - - return opts, nil -} - -var extensionToLoaderMap = map[string]api.Loader{ - ".js": api.LoaderJS, - ".mjs": api.LoaderJS, - ".cjs": api.LoaderJS, - ".jsx": api.LoaderJSX, - ".ts": api.LoaderTS, - ".tsx": api.LoaderTSX, - ".css": api.LoaderCSS, - ".json": api.LoaderJSON, - ".txt": api.LoaderText, -} - -func loaderFromFilename(filename string) api.Loader { - l, found := extensionToLoaderMap[filepath.Ext(filename)] - if found { - return l - } - return api.LoaderJS -} - -func resolveComponentInAssets(fs afero.Fs, impPath string) *hugofs.FileMeta { - findFirst := func(base string) *hugofs.FileMeta { - // This is the most common sub-set of ESBuild's default extensions. - // We assume that imports of JSON, CSS etc. will be using their full - // name with extension. - for _, ext := range []string{".js", ".ts", ".tsx", ".jsx"} { - if strings.HasSuffix(impPath, ext) { - // Import of foo.js.js need the full name. - continue - } - if fi, err := fs.Stat(base + ext); err == nil { - return fi.(hugofs.FileMetaInfo).Meta() - } - } - - // Not found. - return nil - } - - var m *hugofs.FileMeta - - // We need to check if this is a regular file imported without an extension. - // There may be ambiguous situations where both foo.js and foo/index.js exists. - // This import order is in line with both how Node and ESBuild's native - // import resolver works. - - // It may be a regular file imported without an extension, e.g. - // foo or foo/index. - m = findFirst(impPath) - if m != nil { - return m - } - - base := filepath.Base(impPath) - if base == "index" { - // try index.esm.js etc. - m = findFirst(impPath + ".esm") - if m != nil { - return m - } - } - - // Check the path as is. - fi, err := fs.Stat(impPath) - - if err == nil { - if fi.IsDir() { - m = findFirst(filepath.Join(impPath, "index")) - if m == nil { - m = findFirst(filepath.Join(impPath, "index.esm")) - } - } else { - m = fi.(hugofs.FileMetaInfo).Meta() - } - } else if strings.HasSuffix(base, ".js") { - m = findFirst(strings.TrimSuffix(impPath, ".js")) - } - - return m -} - -func createBuildPlugins(depsManager identity.Manager, c *Client, opts Options) ([]api.Plugin, error) { - fs := c.rs.Assets - - resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) { - impPath := args.Path - if opts.Shims != nil { - override, found := opts.Shims[impPath] - if found { - impPath = override - } - } - isStdin := args.Importer == stdinImporter - var relDir string - if !isStdin { - rel, found := fs.MakePathRelative(args.Importer, true) - if !found { - // Not in any of the /assets folders. - // This is an import from a node_modules, let - // ESBuild resolve this. - return api.OnResolveResult{}, nil - } - - relDir = filepath.Dir(rel) - } else { - relDir = opts.sourceDir - } - - // Imports not starting with a "." is assumed to live relative to /assets. - // Hugo makes no assumptions about the directory structure below /assets. - if relDir != "" && strings.HasPrefix(impPath, ".") { - impPath = filepath.Join(relDir, impPath) - } - - m := resolveComponentInAssets(fs.Fs, impPath) - - if m != nil { - depsManager.AddIdentity(m.PathInfo) - - // Store the source root so we can create a jsconfig.json - // to help IntelliSense when the build is done. - // This should be a small number of elements, and when - // in server mode, we may get stale entries on renames etc., - // but that shouldn't matter too much. - c.rs.JSConfigBuilder.AddSourceRoot(m.SourceRoot) - return api.OnResolveResult{Path: m.Filename, Namespace: nsImportHugo}, nil - } - - // Fall back to ESBuild's resolve. - return api.OnResolveResult{}, nil - } - - importResolver := api.Plugin{ - Name: "hugo-import-resolver", - Setup: func(build api.PluginBuild) { - build.OnResolve(api.OnResolveOptions{Filter: `.*`}, - func(args api.OnResolveArgs) (api.OnResolveResult, error) { - return resolveImport(args) - }) - build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsImportHugo}, - func(args api.OnLoadArgs) (api.OnLoadResult, error) { - b, err := os.ReadFile(args.Path) - if err != nil { - return api.OnLoadResult{}, fmt.Errorf("failed to read %q: %w", args.Path, err) - } - c := string(b) - return api.OnLoadResult{ - // See https://github.com/evanw/esbuild/issues/502 - // This allows all modules to resolve dependencies - // in the main project's node_modules. - ResolveDir: opts.resolveDir, - Contents: &c, - Loader: loaderFromFilename(args.Path), - }, nil - }) - }, - } - - params := opts.Params - if params == nil { - // This way @params will always resolve to something. - params = make(map[string]any) - } - - b, err := json.Marshal(params) - if err != nil { - return nil, fmt.Errorf("failed to marshal params: %w", err) - } - bs := string(b) - paramsPlugin := api.Plugin{ - Name: "hugo-params-plugin", - Setup: func(build api.PluginBuild) { - build.OnResolve(api.OnResolveOptions{Filter: `^@params$`}, - func(args api.OnResolveArgs) (api.OnResolveResult, error) { - return api.OnResolveResult{ - Path: args.Path, - Namespace: nsParams, - }, nil - }) - build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsParams}, - func(args api.OnLoadArgs) (api.OnLoadResult, error) { - return api.OnLoadResult{ - Contents: &bs, - Loader: api.LoaderJSON, - }, nil - }) - }, - } - - return []api.Plugin{importResolver, paramsPlugin}, nil -} - -func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) { - var target api.Target - switch opts.Target { - case "", "esnext": - target = api.ESNext - case "es5": - target = api.ES5 - case "es6", "es2015": - target = api.ES2015 - case "es2016": - target = api.ES2016 - case "es2017": - target = api.ES2017 - case "es2018": - target = api.ES2018 - case "es2019": - target = api.ES2019 - case "es2020": - target = api.ES2020 - case "es2021": - target = api.ES2021 - case "es2022": - target = api.ES2022 - case "es2023": - target = api.ES2023 - default: - err = fmt.Errorf("invalid target: %q", opts.Target) - return - } - - mediaType := opts.mediaType - if mediaType.IsZero() { - mediaType = media.Builtin.JavascriptType - } - - var loader api.Loader - switch mediaType.SubType { - // TODO(bep) ESBuild support a set of other loaders, but I currently fail - // to see the relevance. That may change as we start using this. - case media.Builtin.JavascriptType.SubType: - loader = api.LoaderJS - case media.Builtin.TypeScriptType.SubType: - loader = api.LoaderTS - case media.Builtin.TSXType.SubType: - loader = api.LoaderTSX - case media.Builtin.JSXType.SubType: - loader = api.LoaderJSX - default: - err = fmt.Errorf("unsupported Media Type: %q", opts.mediaType) - return - } - - var format api.Format - // One of: iife, cjs, esm - switch opts.Format { - case "", "iife": - format = api.FormatIIFE - case "esm": - format = api.FormatESModule - case "cjs": - format = api.FormatCommonJS - default: - err = fmt.Errorf("unsupported script output format: %q", opts.Format) - return - } - - var jsx api.JSX - switch opts.JSX { - case "", "transform": - jsx = api.JSXTransform - case "preserve": - jsx = api.JSXPreserve - case "automatic": - jsx = api.JSXAutomatic - default: - err = fmt.Errorf("unsupported jsx type: %q", opts.JSX) - return - } - - var defines map[string]string - if opts.Defines != nil { - defines = maps.ToStringMapString(opts.Defines) - } - - // By default we only need to specify outDir and no outFile - outDir := opts.outDir - outFile := "" - var sourceMap api.SourceMap - switch opts.SourceMap { - case "inline": - sourceMap = api.SourceMapInline - case "external": - sourceMap = api.SourceMapExternal - case "": - sourceMap = api.SourceMapNone - default: - err = fmt.Errorf("unsupported sourcemap type: %q", opts.SourceMap) - return - } - - buildOptions = api.BuildOptions{ - Outfile: outFile, - Bundle: true, - - Target: target, - Format: format, - Sourcemap: sourceMap, - - MinifyWhitespace: opts.Minify, - MinifyIdentifiers: opts.Minify, - MinifySyntax: opts.Minify, - - Outdir: outDir, - Define: defines, - - External: opts.Externals, - - JSXFactory: opts.JSXFactory, - JSXFragment: opts.JSXFragment, - - JSX: jsx, - JSXImportSource: opts.JSXImportSource, - - Tsconfig: opts.tsConfig, - - // Note: We're not passing Sourcefile to ESBuild. - // This makes ESBuild pass `stdin` as the Importer to the import - // resolver, which is what we need/expect. - Stdin: &api.StdinOptions{ - Contents: opts.contents, - ResolveDir: opts.resolveDir, - Loader: loader, - }, - } - return -} diff --git a/resources/resource_transformers/js/options_test.go b/resources/resource_transformers/js/options_test.go deleted file mode 100644 index 53aa9b6bb..000000000 --- a/resources/resource_transformers/js/options_test.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package js - -import ( - "path" - "path/filepath" - "testing" - - "github.com/gohugoio/hugo/config" - "github.com/gohugoio/hugo/config/testconfig" - "github.com/gohugoio/hugo/hugofs" - "github.com/gohugoio/hugo/hugolib/filesystems" - "github.com/gohugoio/hugo/hugolib/paths" - "github.com/gohugoio/hugo/media" - - "github.com/spf13/afero" - - "github.com/evanw/esbuild/pkg/api" - - qt "github.com/frankban/quicktest" -) - -// This test is added to test/warn against breaking the "stability" of the -// cache key. It's sometimes needed to break this, but should be avoided if possible. -func TestOptionKey(t *testing.T) { - c := qt.New(t) - - opts := map[string]any{ - "TargetPath": "foo", - "Target": "es2018", - } - - key := (&buildTransformation{optsm: opts}).Key() - - c.Assert(key.Value(), qt.Equals, "jsbuild_1533819657654811600") -} - -func TestToBuildOptions(t *testing.T) { - c := qt.New(t) - - opts, err := toBuildOptions(Options{mediaType: media.Builtin.JavascriptType}) - - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ESNext, - Format: api.FormatIIFE, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - }) - - opts, err = toBuildOptions(Options{ - Target: "es2018", - Format: "cjs", - Minify: true, - mediaType: media.Builtin.JavascriptType, - AvoidTDZ: true, - }) - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ES2018, - Format: api.FormatCommonJS, - MinifyIdentifiers: true, - MinifySyntax: true, - MinifyWhitespace: true, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - }) - - opts, err = toBuildOptions(Options{ - Target: "es2018", Format: "cjs", Minify: true, mediaType: media.Builtin.JavascriptType, - SourceMap: "inline", - }) - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ES2018, - Format: api.FormatCommonJS, - MinifyIdentifiers: true, - MinifySyntax: true, - MinifyWhitespace: true, - Sourcemap: api.SourceMapInline, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - }) - - opts, err = toBuildOptions(Options{ - Target: "es2018", Format: "cjs", Minify: true, mediaType: media.Builtin.JavascriptType, - SourceMap: "inline", - }) - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ES2018, - Format: api.FormatCommonJS, - MinifyIdentifiers: true, - MinifySyntax: true, - MinifyWhitespace: true, - Sourcemap: api.SourceMapInline, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - }) - - opts, err = toBuildOptions(Options{ - Target: "es2018", Format: "cjs", Minify: true, mediaType: media.Builtin.JavascriptType, - SourceMap: "external", - }) - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ES2018, - Format: api.FormatCommonJS, - MinifyIdentifiers: true, - MinifySyntax: true, - MinifyWhitespace: true, - Sourcemap: api.SourceMapExternal, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - }) - - opts, err = toBuildOptions(Options{ - mediaType: media.Builtin.JavascriptType, - JSX: "automatic", JSXImportSource: "preact", - }) - c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, api.BuildOptions{ - Bundle: true, - Target: api.ESNext, - Format: api.FormatIIFE, - Stdin: &api.StdinOptions{ - Loader: api.LoaderJS, - }, - JSX: api.JSXAutomatic, - JSXImportSource: "preact", - }) -} - -func TestToBuildOptionsTarget(t *testing.T) { - c := qt.New(t) - - for _, test := range []struct { - target string - expect api.Target - }{ - {"es2015", api.ES2015}, - {"es2016", api.ES2016}, - {"es2017", api.ES2017}, - {"es2018", api.ES2018}, - {"es2019", api.ES2019}, - {"es2020", api.ES2020}, - {"es2021", api.ES2021}, - {"es2022", api.ES2022}, - {"es2023", api.ES2023}, - {"", api.ESNext}, - {"esnext", api.ESNext}, - } { - c.Run(test.target, func(c *qt.C) { - opts, err := toBuildOptions(Options{ - Target: test.target, - mediaType: media.Builtin.JavascriptType, - }) - c.Assert(err, qt.IsNil) - c.Assert(opts.Target, qt.Equals, test.expect) - }) - } -} - -func TestResolveComponentInAssets(t *testing.T) { - c := qt.New(t) - - for _, test := range []struct { - name string - files []string - impPath string - expect string - }{ - {"Basic, extension", []string{"foo.js", "bar.js"}, "foo.js", "foo.js"}, - {"Basic, no extension", []string{"foo.js", "bar.js"}, "foo", "foo.js"}, - {"Basic, no extension, typescript", []string{"foo.ts", "bar.js"}, "foo", "foo.ts"}, - {"Not found", []string{"foo.js", "bar.js"}, "moo.js", ""}, - {"Not found, double js extension", []string{"foo.js.js", "bar.js"}, "foo.js", ""}, - {"Index file, folder only", []string{"foo/index.js", "bar.js"}, "foo", "foo/index.js"}, - {"Index file, folder and index", []string{"foo/index.js", "bar.js"}, "foo/index", "foo/index.js"}, - {"Index file, folder and index and suffix", []string{"foo/index.js", "bar.js"}, "foo/index.js", "foo/index.js"}, - {"Index ESM file, folder only", []string{"foo/index.esm.js", "bar.js"}, "foo", "foo/index.esm.js"}, - {"Index ESM file, folder and index", []string{"foo/index.esm.js", "bar.js"}, "foo/index", "foo/index.esm.js"}, - {"Index ESM file, folder and index and suffix", []string{"foo/index.esm.js", "bar.js"}, "foo/index.esm.js", "foo/index.esm.js"}, - // We added these index.esm.js cases in v0.101.0. The case below is unlikely to happen in the wild, but add a test - // to document Hugo's behavior. We pick the file with the name index.js; anything else would be breaking. - {"Index and Index ESM file, folder only", []string{"foo/index.esm.js", "foo/index.js", "bar.js"}, "foo", "foo/index.js"}, - - // Issue #8949 - {"Check file before directory", []string{"foo.js", "foo/index.js"}, "foo", "foo.js"}, - } { - c.Run(test.name, func(c *qt.C) { - baseDir := "assets" - mfs := afero.NewMemMapFs() - - for _, filename := range test.files { - c.Assert(afero.WriteFile(mfs, filepath.Join(baseDir, filename), []byte("let foo='bar';"), 0o777), qt.IsNil) - } - - conf := testconfig.GetTestConfig(mfs, config.New()) - fs := hugofs.NewFrom(mfs, conf.BaseConfig()) - - p, err := paths.New(fs, conf) - c.Assert(err, qt.IsNil) - bfs, err := filesystems.NewBase(p, nil) - c.Assert(err, qt.IsNil) - - got := resolveComponentInAssets(bfs.Assets.Fs, test.impPath) - - gotPath := "" - expect := test.expect - if got != nil { - gotPath = filepath.ToSlash(got.Filename) - expect = path.Join(baseDir, test.expect) - } - - c.Assert(gotPath, qt.Equals, expect) - }) - } -} diff --git a/resources/resource_transformers/js/transform.go b/resources/resource_transformers/js/transform.go new file mode 100644 index 000000000..13909e54c --- /dev/null +++ b/resources/resource_transformers/js/transform.go @@ -0,0 +1,68 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package js + +import ( + "io" + "path" + "path/filepath" + + "github.com/gohugoio/hugo/internal/js/esbuild" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/internal" +) + +type buildTransformation struct { + optsm map[string]any + c *Client +} + +func (t *buildTransformation) Key() internal.ResourceTransformationKey { + return internal.NewResourceTransformationKey("jsbuild", t.optsm) +} + +func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { + ctx.OutMediaType = media.Builtin.JavascriptType + + var opts esbuild.Options + + if t.optsm != nil { + optsExt, err := esbuild.DecodeExternalOptions(t.optsm) + if err != nil { + return err + } + opts.ExternalOptions = optsExt + } + + if opts.TargetPath != "" { + ctx.OutPath = opts.TargetPath + } else { + ctx.ReplaceOutPathExtension(".js") + } + + src, err := io.ReadAll(ctx.From) + if err != nil { + return err + } + + opts.SourceDir = filepath.FromSlash(path.Dir(ctx.SourcePath)) + opts.Contents = string(src) + opts.MediaType = ctx.InMediaType + opts.Stdin = true + + _, err = t.c.transform(opts, ctx) + + return err +} diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go index 0d7b57062..77bacc115 100644 --- a/resources/resource_transformers/tocss/dartsass/transform.go +++ b/resources/resource_transformers/tocss/dartsass/transform.go @@ -139,7 +139,7 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) { return url, nil } - filePath, isURL := paths.UrlToFilename(url) + filePath, isURL := paths.UrlStringToFilename(url) var prevDir string var pathDir string if isURL { @@ -195,7 +195,7 @@ func (t importResolver) Load(url string) (godartsass.Import, error) { if url == sass.HugoVarsNamespace { return t.varsStylesheet, nil } - filename, _ := paths.UrlToFilename(url) + filename, _ := paths.UrlStringToFilename(url) b, err := afero.ReadFile(hugofs.Os, filename) sourceSyntax := godartsass.SourceSyntaxSCSS diff --git a/resources/transform.go b/resources/transform.go index 336495e6d..4214067bd 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -52,6 +52,8 @@ var ( _ identity.IdentityGroupProvider = (*resourceAdapterInner)(nil) _ resource.Source = (*resourceAdapter)(nil) _ resource.Identifier = (*resourceAdapter)(nil) + _ targetPathProvider = (*resourceAdapter)(nil) + _ sourcePathProvider = (*resourceAdapter)(nil) _ resource.ResourceNameTitleProvider = (*resourceAdapter)(nil) _ resource.WithResourceMetaProvider = (*resourceAdapter)(nil) _ identity.DependencyManagerProvider = (*resourceAdapter)(nil) @@ -277,6 +279,19 @@ func (r *resourceAdapter) Key() string { return r.target.(resource.Identifier).Key() } +func (r *resourceAdapter) targetPath() string { + r.init(false, false) + return r.target.(targetPathProvider).targetPath() +} + +func (r *resourceAdapter) sourcePath() string { + r.init(false, false) + if sp, ok := r.target.(sourcePathProvider); ok { + return sp.sourcePath() + } + return "" +} + func (r *resourceAdapter) MediaType() media.Type { r.init(false, false) return r.target.MediaType() diff --git a/tpl/debug/debug.go b/tpl/debug/debug.go index ae1acf5eb..fd676f0e2 100644 --- a/tpl/debug/debug.go +++ b/tpl/debug/debug.go @@ -41,7 +41,7 @@ func New(d *deps.Deps) *Namespace { l := d.Log.InfoCommand("timer") - d.BuildEndListeners.Add(func() { + d.BuildEndListeners.Add(func(...any) bool { type data struct { Name string Count int @@ -84,6 +84,8 @@ func New(d *deps.Deps) *Namespace { } ns.timers = make(map[string][]*timer) + + return false }) return ns diff --git a/tpl/fmt/fmt.go b/tpl/fmt/fmt.go index e9c18360a..264cb1435 100644 --- a/tpl/fmt/fmt.go +++ b/tpl/fmt/fmt.go @@ -30,8 +30,9 @@ func New(d *deps.Deps) *Namespace { logger: d.Log, } - d.BuildStartListeners.Add(func() { + d.BuildStartListeners.Add(func(...any) bool { ns.logger.Reset() + return false }) return ns diff --git a/tpl/js/init.go b/tpl/js/init.go index 69d7c7275..97e76c33d 100644 --- a/tpl/js/init.go +++ b/tpl/js/init.go @@ -24,7 +24,10 @@ const name = "js" func init() { f := func(d *deps.Deps) *internal.TemplateFuncsNamespace { - ctx := New(d) + ctx, err := New(d) + if err != nil { + panic(err) + } ns := &internal.TemplateFuncsNamespace{ Name: name, diff --git a/tpl/js/js.go b/tpl/js/js.go index c68e0af92..b686b76a7 100644 --- a/tpl/js/js.go +++ b/tpl/js/js.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,29 +17,47 @@ package js import ( "errors" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/internal/js/esbuild" "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/resources/resource_factories/create" "github.com/gohugoio/hugo/resources/resource_transformers/babel" - "github.com/gohugoio/hugo/resources/resource_transformers/js" + jstransform "github.com/gohugoio/hugo/resources/resource_transformers/js" "github.com/gohugoio/hugo/tpl/internal/resourcehelpers" ) // New returns a new instance of the js-namespaced template functions. -func New(deps *deps.Deps) *Namespace { - if deps.ResourceSpec == nil { - return &Namespace{} +func New(d *deps.Deps) (*Namespace, error) { + if d.ResourceSpec == nil { + return &Namespace{}, nil } + + batcherClient, err := esbuild.NewBatcherClient(d) + if err != nil { + return nil, err + } + return &Namespace{ - client: js.New(deps.BaseFs.Assets, deps.ResourceSpec), - babelClient: babel.New(deps.ResourceSpec), - } + d: d, + jsTransformClient: jstransform.New(d.BaseFs.Assets, d.ResourceSpec), + jsBatcherClient: batcherClient, + jsBatcherStore: maps.NewCache[string, esbuild.Batcher](), + createClient: create.New(d.ResourceSpec), + babelClient: babel.New(d.ResourceSpec), + }, nil } // Namespace provides template functions for the "js" namespace. type Namespace struct { - client *js.Client - babelClient *babel.Client + d *deps.Deps + + jsTransformClient *jstransform.Client + createClient *create.Client + babelClient *babel.Client + jsBatcherClient *esbuild.BatcherClient + jsBatcherStore *maps.Cache[string, esbuild.Batcher] } // Build processes the given Resource with ESBuild. @@ -65,7 +83,24 @@ func (ns *Namespace) Build(args ...any) (resource.Resource, error) { m = map[string]any{"targetPath": targetPath} } - return ns.client.Process(r, m) + return ns.jsTransformClient.Process(r, m) +} + +// Batch creates a new Batcher with the given ID. +// Repeated calls with the same ID will return the same Batcher. +// The ID will be used to name the root directory of the batch. +// Forward slashes in the ID is allowed. +func (ns *Namespace) Batch(id string) (esbuild.Batcher, error) { + if err := esbuild.ValidateBatchID(id, true); err != nil { + return nil, err + } + b, err := ns.jsBatcherStore.GetOrCreate(id, func() (esbuild.Batcher, error) { + return ns.jsBatcherClient.New(id) + }) + if err != nil { + return nil, err + } + return b, nil } // Babel processes the given Resource with Babel. diff --git a/tpl/partials/partials.go b/tpl/partials/partials.go index 8de312fd8..4441b5fa5 100644 --- a/tpl/partials/partials.go +++ b/tpl/partials/partials.go @@ -81,8 +81,9 @@ func New(deps *deps.Deps) *Namespace { cache := &partialCache{cache: lru} deps.BuildStartListeners.Add( - func() { + func(...any) bool { cache.clear() + return false }) return &Namespace{ diff --git a/tpl/tplimpl/embedded/templates/_hugo/build/js/batch-esm-runner.gotmpl b/tpl/tplimpl/embedded/templates/_hugo/build/js/batch-esm-runner.gotmpl new file mode 100644 index 000000000..e1b3b9bc3 --- /dev/null +++ b/tpl/tplimpl/embedded/templates/_hugo/build/js/batch-esm-runner.gotmpl @@ -0,0 +1,16 @@ +{{ range $i, $e := .Scripts -}} + {{ printf "import { %s as Script%d } from %q;" .Export $i .Import }} +{{ end -}} +{{ range $i, $e := .Runners }} + {{ printf "import { %s as Run%d } from %q;" .Export $i .Import }} +{{ end }} +{{/* */}} +let scripts = []; +{{ range $i, $e := .Scripts -}} + scripts.push({{ .RunnerJSON $i }}); +{{ end -}} +{{/* */}} +{{ range $i, $e := .Runners }} + {{ $id := printf "Run%d" $i }} + {{ $id }}(scripts); +{{ end }} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index fd07db68e..9e2af046d 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -695,13 +695,13 @@ func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Tem if !base.IsZero() { templ, err = templ.Parse(base.template) if err != nil { - return nil, base.errWithFileContext("parse failed", err) + return nil, base.errWithFileContext("text: base: parse failed", err) } } templ, err = texttemplate.Must(templ.Clone()).Parse(overlay.template) if err != nil { - return nil, overlay.errWithFileContext("parse failed", err) + return nil, overlay.errWithFileContext("text: overlay: parse failed", err) } // The extra lookup is a workaround, see @@ -720,13 +720,13 @@ func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Tem if !base.IsZero() { templ, err = templ.Parse(base.template) if err != nil { - return nil, base.errWithFileContext("parse failed", err) + return nil, base.errWithFileContext("html: base: parse failed", err) } } templ, err = htmltemplate.Must(templ.Clone()).Parse(overlay.template) if err != nil { - return nil, overlay.errWithFileContext("parse failed", err) + return nil, overlay.errWithFileContext("html: overlay: parse failed", err) } // The extra lookup is a workaround, see diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index 3daba74a0..d73d4d336 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -251,6 +251,9 @@ func newTemplateExecuter(d *deps.Deps) (texttemplate.Executer, map[string]reflec } func createFuncMap(d *deps.Deps) map[string]any { + if d.TmplFuncMap != nil { + return d.TmplFuncMap + } funcMap := template.FuncMap{} nsMap := make(map[string]any) @@ -292,5 +295,7 @@ func createFuncMap(d *deps.Deps) map[string]any { } } - return funcMap + d.TmplFuncMap = funcMap + + return d.TmplFuncMap } From 75ad9cdaaba5e2c13582cd95bc77fb1b570e0b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 12 Dec 2024 15:42:17 +0100 Subject: [PATCH 011/374] Add config option disableDefaultLanguageRedirect Fixes #13133 --- config/allconfig/allconfig.go | 3 +++ hugolib/config_test.go | 23 +++++++++++++++++++++++ hugolib/site_render.go | 3 +++ 3 files changed, 29 insertions(+) diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index b158d8a90..be4480f45 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -505,6 +505,9 @@ type RootConfig struct { // Set this to true to put all languages below their language ID. DefaultContentLanguageInSubdir bool + // Disable generation of redirect to the default language when DefaultContentLanguageInSubdir is enabled. + DisableDefaultLanguageRedirect bool + // Disable creation of alias redirect pages. DisableAliases bool diff --git a/hugolib/config_test.go b/hugolib/config_test.go index c9db0d2f0..c0bfde37d 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -1339,6 +1339,29 @@ Home. b.Assert(len(b.H.Sites), qt.Equals, 1) } +func TestDisableDefaultLanguageRedirect(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +defaultContentLanguageInSubdir = true +disableDefaultLanguageRedirect = true +[languages] +[languages.en] +title = "English Title" +[languages.sv] +title = "Swedish Title" +-- layouts/index.html -- +Home. + + +` + b := Test(t, files) + + b.Assert(len(b.H.Sites), qt.Equals, 2) + b.AssertFileExists("public/index.html", false) +} + func TestLoadConfigYamlEnvVar(t *testing.T) { defaultEnv := []string{`HUGO_OUTPUTS=home: ['json']`} diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 83f2fce89..e5b2b62ab 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -334,6 +334,9 @@ func (s *Site) renderAliases() error { // renderMainLanguageRedirect creates a redirect to the main language home, // depending on if it lives in sub folder (e.g. /en) or not. func (s *Site) renderMainLanguageRedirect() error { + if s.conf.DisableDefaultLanguageRedirect { + return nil + } if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultilingual()) { // No need for a redirect return nil From ec1933f79d0da792d5c853cf7bf99fc9f0961162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 12 Dec 2024 22:09:59 +0100 Subject: [PATCH 012/374] js/esbuild: Add platform option Closes #13136 --- internal/js/esbuild/options.go | 19 +++++++++++++++++++ internal/js/esbuild/options_test.go | 27 ++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/internal/js/esbuild/options.go b/internal/js/esbuild/options.go index 16fc0d4bb..21509bc15 100644 --- a/internal/js/esbuild/options.go +++ b/internal/js/esbuild/options.go @@ -121,6 +121,11 @@ type ExternalOptions struct { // Default is to esm. Format string + // One of browser, node, neutral. + // Default is browser. + // See https://esbuild.github.io/api/#platform + Platform string + // External dependencies, e.g. "react". Externals []string @@ -274,6 +279,19 @@ func (opts *Options) compile() (err error) { return } + var platform api.Platform + switch opts.Platform { + case "", "browser": + platform = api.PlatformBrowser + case "node": + platform = api.PlatformNode + case "neutral": + platform = api.PlatformNeutral + default: + err = fmt.Errorf("unsupported platform type: %q", opts.Platform) + return + } + var defines map[string]string if opts.Defines != nil { defines = maps.ToStringMapString(opts.Defines) @@ -310,6 +328,7 @@ func (opts *Options) compile() (err error) { Target: target, Format: format, + Platform: platform, Sourcemap: sourceMap, SourcesContent: sourcesContent, diff --git a/internal/js/esbuild/options_test.go b/internal/js/esbuild/options_test.go index ca19717f7..7312eea27 100644 --- a/internal/js/esbuild/options_test.go +++ b/internal/js/esbuild/options_test.go @@ -38,6 +38,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ESNext, Format: api.FormatIIFE, + Platform: api.PlatformBrowser, SourcesContent: 1, Stdin: &api.StdinOptions{ Loader: api.LoaderJS, @@ -62,6 +63,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ES2018, Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, SourcesContent: 1, MinifyIdentifiers: true, MinifySyntax: true, @@ -87,6 +89,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ES2018, Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, MinifyIdentifiers: true, MinifySyntax: true, MinifyWhitespace: true, @@ -113,6 +116,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ES2018, Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, MinifyIdentifiers: true, MinifySyntax: true, MinifyWhitespace: true, @@ -139,6 +143,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ES2018, Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, MinifyIdentifiers: true, MinifySyntax: true, MinifyWhitespace: true, @@ -164,6 +169,7 @@ func TestToBuildOptions(t *testing.T) { Bundle: true, Target: api.ESNext, Format: api.FormatIIFE, + Platform: api.PlatformBrowser, SourcesContent: 1, Stdin: &api.StdinOptions{ Loader: api.LoaderJS, @@ -210,10 +216,25 @@ func TestToBuildOptionsTarget(t *testing.T) { func TestDecodeExternalOptions(t *testing.T) { c := qt.New(t) - m := map[string]any{} - opts, err := DecodeExternalOptions(m) + m := map[string]any{ + "platform": "node", + } + ext, err := DecodeExternalOptions(m) c.Assert(err, qt.IsNil) - c.Assert(opts, qt.DeepEquals, ExternalOptions{ + c.Assert(ext, qt.DeepEquals, ExternalOptions{ SourcesContent: true, + Platform: "node", + }) + + opts := Options{ + ExternalOptions: ext, + } + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Platform: api.PlatformNode, + SourcesContent: api.SourcesContentInclude, }) } From 9dfa1126177952433f8339a87fb566c2959fc3d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 13 Dec 2024 09:23:09 +0100 Subject: [PATCH 013/374] Write all logging (INFO, WARN, ERROR) to stderr The old setup tried to log >= warning to stderr, the rest to stdout. However, that logic was flawed, so warnings ended up in stdout, which makes `hugo list all` etc. hard to reason about from scripts. This commit fixes this by making all logging (info, warn, error) log to stderr and let stdout be reserved for program output. Fixes #13074 --- commands/commandeer.go | 21 +++++---- commands/list.go | 2 +- common/loggers/handlerterminal.go | 4 +- common/loggers/logger.go | 44 ++++++++++--------- common/loggers/logger_test.go | 16 +++---- deps/deps.go | 8 ++-- hugolib/integrationtest_builder.go | 6 +-- hugolib/site.go | 11 +++-- modules/client.go | 4 +- testscripts/commands/deprecate.txt | 6 +-- testscripts/commands/hugo__configdir.txt | 3 +- testscripts/commands/hugo__path-warnings.txt | 2 +- .../commands/hugo_printpathwarnings.txt | 2 +- .../commands/hugo_printunusedtemplates.txt | 2 +- testscripts/commands/warnf_stderr.txt | 13 ++++++ 15 files changed, 85 insertions(+), 59 deletions(-) create mode 100644 testscripts/commands/warnf_stderr.txt diff --git a/commands/commandeer.go b/commands/commandeer.go index bb82ec654..3f9f02d23 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -103,7 +103,8 @@ type configKey struct { type rootCommand struct { Printf func(format string, v ...interface{}) Println func(a ...interface{}) - Out io.Writer + StdOut io.Writer + StdErr io.Writer logger loggers.Logger @@ -356,7 +357,7 @@ func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotEx } func (r *rootCommand) newDepsConfig(conf *commonConfig) deps.DepsCfg { - return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild} + return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, StdOut: r.logger.StdOut(), StdErr: r.logger.StdErr(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild} } func (r *rootCommand) Name() string { @@ -421,21 +422,23 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args } func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { - r.Out = os.Stdout + r.StdOut = os.Stdout + r.StdErr = os.Stderr if r.quiet { - r.Out = io.Discard + r.StdOut = io.Discard + r.StdErr = io.Discard } // Used by mkcert (server). - log.SetOutput(r.Out) + log.SetOutput(r.StdOut) r.Printf = func(format string, v ...interface{}) { if !r.quiet { - fmt.Fprintf(r.Out, format, v...) + fmt.Fprintf(r.StdOut, format, v...) } } r.Println = func(a ...interface{}) { if !r.quiet { - fmt.Fprintln(r.Out, a...) + fmt.Fprintln(r.StdOut, a...) } } _, running := runner.Command.(*serverCommand) @@ -485,8 +488,8 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { optsLogger := loggers.Options{ DistinctLevel: logg.LevelWarn, Level: level, - Stdout: r.Out, - Stderr: r.Out, + StdOut: r.StdOut, + StdErr: r.StdErr, StoreErrors: running, } diff --git a/commands/list.go b/commands/list.go index f362e22f1..42f3408ba 100644 --- a/commands/list.go +++ b/commands/list.go @@ -57,7 +57,7 @@ func newListCommand() *listCommand { return err } - writer := csv.NewWriter(r.Out) + writer := csv.NewWriter(r.StdOut) defer writer.Flush() writer.Write([]string{ diff --git a/common/loggers/handlerterminal.go b/common/loggers/handlerterminal.go index c5f8fcce8..c6a86d3a2 100644 --- a/common/loggers/handlerterminal.go +++ b/common/loggers/handlerterminal.go @@ -40,8 +40,8 @@ func newNoAnsiEscapeHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, type noAnsiEscapeHandler struct { mu sync.Mutex - outWriter io.Writer // Defaults to os.Stdout. - errWriter io.Writer // Defaults to os.Stderr. + outWriter io.Writer + errWriter io.Writer predicate func(*logg.Entry) bool noLevelPrefix bool } diff --git a/common/loggers/logger.go b/common/loggers/logger.go index 75c8102c7..77a21458a 100644 --- a/common/loggers/logger.go +++ b/common/loggers/logger.go @@ -38,8 +38,8 @@ var ( // Options defines options for the logger. type Options struct { Level logg.Level - Stdout io.Writer - Stderr io.Writer + StdOut io.Writer + StdErr io.Writer DistinctLevel logg.Level StoreErrors bool HandlerPost func(e *logg.Entry) error @@ -48,21 +48,22 @@ type Options struct { // New creates a new logger with the given options. func New(opts Options) Logger { - if opts.Stdout == nil { - opts.Stdout = os.Stdout + if opts.StdOut == nil { + opts.StdOut = os.Stdout } - if opts.Stderr == nil { - opts.Stderr = os.Stdout + if opts.StdErr == nil { + opts.StdErr = os.Stderr } + if opts.Level == 0 { opts.Level = logg.LevelWarn } var logHandler logg.Handler - if terminal.PrintANSIColors(os.Stdout) { - logHandler = newDefaultHandler(opts.Stdout, opts.Stderr) + if terminal.PrintANSIColors(os.Stderr) { + logHandler = newDefaultHandler(opts.StdErr, opts.StdErr) } else { - logHandler = newNoAnsiEscapeHandler(opts.Stdout, opts.Stderr, false, nil) + logHandler = newNoAnsiEscapeHandler(opts.StdErr, opts.StdErr, false, nil) } errorsw := &strings.Builder{} @@ -137,7 +138,8 @@ func New(opts Options) Logger { logCounters: logCounters, errors: errorsw, reset: reset, - out: opts.Stdout, + stdOut: opts.StdOut, + stdErr: opts.StdErr, level: opts.Level, logger: logger, tracel: l.WithLevel(logg.LevelTrace), @@ -153,8 +155,6 @@ func NewDefault() Logger { opts := Options{ DistinctLevel: logg.LevelWarn, Level: logg.LevelWarn, - Stdout: os.Stdout, - Stderr: os.Stdout, } return New(opts) } @@ -163,8 +163,6 @@ func NewTrace() Logger { opts := Options{ DistinctLevel: logg.LevelWarn, Level: logg.LevelTrace, - Stdout: os.Stdout, - Stderr: os.Stdout, } return New(opts) } @@ -189,7 +187,8 @@ type Logger interface { Level() logg.Level LoggCount(logg.Level) int Logger() logg.Logger - Out() io.Writer + StdOut() io.Writer + StdErr() io.Writer Printf(format string, v ...any) Println(v ...any) PrintTimerIfDelayed(start time.Time, name string) @@ -207,7 +206,8 @@ type logAdapter struct { logCounters *logLevelCounter errors *strings.Builder reset func() - out io.Writer + stdOut io.Writer + stdErr io.Writer level logg.Level logger logg.Logger tracel logg.LevelLogger @@ -259,8 +259,12 @@ func (l *logAdapter) Logger() logg.Logger { return l.logger } -func (l *logAdapter) Out() io.Writer { - return l.out +func (l *logAdapter) StdOut() io.Writer { + return l.stdOut +} + +func (l *logAdapter) StdErr() io.Writer { + return l.stdErr } // PrintTimerIfDelayed prints a time statement to the FEEDBACK logger @@ -279,11 +283,11 @@ func (l *logAdapter) Printf(format string, v ...any) { if !strings.HasSuffix(format, "\n") { format += "\n" } - fmt.Fprintf(l.out, format, v...) + fmt.Fprintf(l.stdOut, format, v...) } func (l *logAdapter) Println(v ...any) { - fmt.Fprintln(l.out, v...) + fmt.Fprintln(l.stdOut, v...) } func (l *logAdapter) Reset() { diff --git a/common/loggers/logger_test.go b/common/loggers/logger_test.go index dcf94b123..b03e6d903 100644 --- a/common/loggers/logger_test.go +++ b/common/loggers/logger_test.go @@ -31,8 +31,8 @@ func TestLogDistinct(t *testing.T) { opts := loggers.Options{ DistinctLevel: logg.LevelWarn, StoreErrors: true, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) @@ -54,8 +54,8 @@ func TestHookLast(t *testing.T) { HandlerPost: func(e *logg.Entry) error { panic(e.Message) }, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) @@ -70,8 +70,8 @@ func TestOptionStoreErrors(t *testing.T) { opts := loggers.Options{ StoreErrors: true, - Stderr: &sb, - Stdout: &sb, + StdErr: &sb, + StdOut: &sb, } l := loggers.New(opts) @@ -131,8 +131,8 @@ func TestReset(t *testing.T) { opts := loggers.Options{ StoreErrors: true, DistinctLevel: logg.LevelWarn, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) diff --git a/deps/deps.go b/deps/deps.go index ca0f5ee3b..8e9ec42d8 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -405,9 +405,11 @@ type DepsCfg struct { // The logging level to use. LogLevel logg.Level - // Where to write the logs. - // Currently we typically write everything to stdout. - LogOut io.Writer + // Logging output. + StdErr io.Writer + + // The console output. + StdOut io.Writer // The file systems to use Fs *hugofs.Fs diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index bc83d65b3..b593cd965 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -660,8 +660,8 @@ func (s *IntegrationTestBuilder) initBuilder() error { logger := loggers.New( loggers.Options{ - Stdout: w, - Stderr: w, + StdOut: w, + StdErr: w, Level: s.Cfg.LogLevel, DistinctLevel: logg.LevelWarn, }, @@ -685,7 +685,7 @@ func (s *IntegrationTestBuilder) initBuilder() error { s.Assert(err, qt.IsNil) - depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: logger.Level(), LogOut: logger.Out()} + depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: logger.Level(), StdErr: logger.StdErr()} sites, err := NewHugoSites(depsCfg) if err != nil { initErr = err diff --git a/hugolib/site.go b/hugolib/site.go index 7ec70eb6c..f73bd2517 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -145,8 +145,11 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { if cfg.Configs.Base.PanicOnWarning { logHookLast = loggers.PanicOnWarningHook } - if cfg.LogOut == nil { - cfg.LogOut = os.Stdout + if cfg.StdOut == nil { + cfg.StdOut = os.Stdout + } + if cfg.StdErr == nil { + cfg.StdErr = os.Stderr } if cfg.LogLevel == 0 { cfg.LogLevel = logg.LevelWarn @@ -156,8 +159,8 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { Level: cfg.LogLevel, DistinctLevel: logg.LevelWarn, // This will drop duplicate log warning and errors. HandlerPost: logHookLast, - Stdout: cfg.LogOut, - Stderr: cfg.LogOut, + StdOut: cfg.StdOut, + StdErr: cfg.StdErr, StoreErrors: conf.Watching(), SuppressStatements: conf.IgnoredLogs(), } diff --git a/modules/client.go b/modules/client.go index 404605c8c..d16af2d35 100644 --- a/modules/client.go +++ b/modules/client.go @@ -365,7 +365,7 @@ func (c *Client) Get(args ...string) error { } func (c *Client) get(args ...string) error { - if err := c.runGo(context.Background(), c.logger.Out(), append([]string{"get"}, args...)...); err != nil { + if err := c.runGo(context.Background(), c.logger.StdOut(), append([]string{"get"}, args...)...); err != nil { return fmt.Errorf("failed to get %q: %w", args, err) } return nil @@ -375,7 +375,7 @@ func (c *Client) get(args ...string) error { // If path is empty, Go will try to guess. // If this succeeds, this project will be marked as Go Module. func (c *Client) Init(path string) error { - err := c.runGo(context.Background(), c.logger.Out(), "mod", "init", path) + err := c.runGo(context.Background(), c.logger.StdOut(), "mod", "init", path) if err != nil { return fmt.Errorf("failed to init modules: %w", err) } diff --git a/testscripts/commands/deprecate.txt b/testscripts/commands/deprecate.txt index 3be4976d5..8791c3a78 100644 --- a/testscripts/commands/deprecate.txt +++ b/testscripts/commands/deprecate.txt @@ -1,13 +1,13 @@ # Test deprecation logging. hugo -e info --logLevel info -stdout 'INFO deprecated: item was deprecated in Hugo' +stderr 'INFO deprecated: item was deprecated in Hugo' hugo -e warn --logLevel warn -stdout 'WARN deprecated: item was deprecated in Hugo' +stderr 'WARN deprecated: item was deprecated in Hugo' ! hugo -e error --logLevel warn -stdout 'ERROR deprecated: item was deprecated in Hugo' +stderr 'ERROR deprecated: item was deprecated in Hugo' -- hugo.toml -- baseURL = "https://example.com/" diff --git a/testscripts/commands/hugo__configdir.txt b/testscripts/commands/hugo__configdir.txt index 4da62ade5..148523e9f 100644 --- a/testscripts/commands/hugo__configdir.txt +++ b/testscripts/commands/hugo__configdir.txt @@ -3,4 +3,5 @@ hugo ! stderr . -- config/_default/hugo.toml -- -baseURL = "https://example.com/" \ No newline at end of file +baseURL = "https://example.com/" +disableKinds = ["RSS", "page", "sitemap", "robotsTXT", "404", "taxonomy", "term", "home"] \ No newline at end of file diff --git a/testscripts/commands/hugo__path-warnings.txt b/testscripts/commands/hugo__path-warnings.txt index f7e3acd95..8eccb6567 100644 --- a/testscripts/commands/hugo__path-warnings.txt +++ b/testscripts/commands/hugo__path-warnings.txt @@ -1,6 +1,6 @@ hugo --printPathWarnings -stdout 'Duplicate' +stderr 'Duplicate' -- hugo.toml -- -- assets/css/styles.css -- diff --git a/testscripts/commands/hugo_printpathwarnings.txt b/testscripts/commands/hugo_printpathwarnings.txt index f4c76ebab..51eb46d91 100644 --- a/testscripts/commands/hugo_printpathwarnings.txt +++ b/testscripts/commands/hugo_printpathwarnings.txt @@ -1,6 +1,6 @@ hugo --printPathWarnings -stdout 'Duplicate target paths: .index.html \(2\)' +stderr 'Duplicate target paths: .index.html \(2\)' -- hugo.toml -- disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "404", "section"] diff --git a/testscripts/commands/hugo_printunusedtemplates.txt b/testscripts/commands/hugo_printunusedtemplates.txt index 312e4920d..eb9e068d7 100644 --- a/testscripts/commands/hugo_printunusedtemplates.txt +++ b/testscripts/commands/hugo_printunusedtemplates.txt @@ -1,6 +1,6 @@ hugo --printUnusedTemplates -stdout 'Template _default/list.html is unused' +stderr 'Template _default/list.html is unused' -- hugo.toml -- disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "404", "section", "page"] diff --git a/testscripts/commands/warnf_stderr.txt b/testscripts/commands/warnf_stderr.txt new file mode 100644 index 000000000..f899253c5 --- /dev/null +++ b/testscripts/commands/warnf_stderr.txt @@ -0,0 +1,13 @@ +# Issue #13074 + +hugo +stderr 'warning' +! stdout 'warning' + +-- hugo.toml -- +baseURL = "http://example.org/" +disableKinds = ["RSS", "page", "sitemap", "robotsTXT", "404", "taxonomy", "term"] +-- layouts/index.html -- +Home +{{ warnf "This is a warning" }} + From 4f130f6e4f891d7df1029a29336efe9e1475d17f Mon Sep 17 00:00:00 2001 From: racehd <7813112-racehd@users.noreply.gitlab.com> Date: Sun, 1 Dec 2024 10:30:34 -0500 Subject: [PATCH 014/374] tpl/tplimpl: Add details shortcode - Add new shortcode to render details HTML element. - Implement integration tests to check: default state, custom summary, open state, attribute sanitization, allowed attributes, and localization of default summary text. - Update docs to include details shortcode. Closes # 13090 --- .../en/content-management/shortcodes.md | 34 ++++++ docs/data/embedded_template_urls.toml | 1 + .../templates/shortcodes/details.html | 68 +++++++++++ tpl/tplimpl/tplimpl_integration_test.go | 115 ++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 tpl/tplimpl/embedded/templates/shortcodes/details.html diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md index 8e345f2fb..7a589a340 100644 --- a/docs/content/en/content-management/shortcodes.md +++ b/docs/content/en/content-management/shortcodes.md @@ -94,6 +94,40 @@ Example usage: Although you can call this shortcode using the `{{}}` notation, computationally it is more efficient to call it using the `{{%/* */%}}` notation as shown above. +### details + +{{< new-in 0.140.0 >}} + +{{% note %}} +To override Hugo's embedded `details` shortcode, copy the [source code] to a file with the same name in the layouts/shortcodes directory. + +This may be useful if you are wanting access to more global HTML attributes. + +[source code]: {{% eturl details %}} +{{% /note %}} + +Use the `details` shortcode to generate a collapsible details HTML element. For example: + +```text +{{}} +Showing custom `summary` text. +{{}} +``` + +Additional examples can be found in the source code. The `details` shortcode can use the following named arguments: + +summary +: (`string`) Optional. Specifies the content of the child summary element. Default is "Details" + +open +: (`bool`) Optional. Whether to initially display the contents of the details element. Default is `false`. + +name +: (`string`) Optional. The value of the element's name attribute. + +class +: (`string`) Optional. The value of the element's class attribute. + ### figure {{% note %}} diff --git a/docs/data/embedded_template_urls.toml b/docs/data/embedded_template_urls.toml index 38b437fe1..b7247f272 100644 --- a/docs/data/embedded_template_urls.toml +++ b/docs/data/embedded_template_urls.toml @@ -25,6 +25,7 @@ # Shortcodes 'comment' = 'shortcodes/comment.html' +'details' = 'shortcodes/details.html' 'figure' = 'shortcodes/figure.html' 'gist' = 'shortcodes/gist.html' 'highlight' = 'shortcodes/highlight.html' diff --git a/tpl/tplimpl/embedded/templates/shortcodes/details.html b/tpl/tplimpl/embedded/templates/shortcodes/details.html new file mode 100644 index 000000000..932289517 --- /dev/null +++ b/tpl/tplimpl/embedded/templates/shortcodes/details.html @@ -0,0 +1,68 @@ +{{- /* +Renders an HTML details element. + +@param {string} [summary] The content of the child summary element. +@param {bool} [open=false] Whether to initially display the contents of the details element. +@param {string} [class] The value of the element's class attribute. +@param {string} [name] The value of the element's name attribute. + +@reference https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details + +@examples + + {{< details >}} + A basic collapsible section. + {{< /details >}} + + {{< details summary="Custom Summary Text" >}} + Showing custom `summary` text. + {{< /details >}} + + {{< details summary="Open Details" open=true >}} + Contents displayed initially by using `open`. + {{< /details >}} + + {{< details summary="Styled Content" class="my-custom-class" >}} + Content can be styled with CSS by specifying a `class`. + + Target details element: + + ```css + details.my-custom-class { } + ``` + + Target summary element: + + ```css + details.my-custom-class > summary > * { } + ``` + + Target inner content: + + ```css + details.my-custom-class > :not(summary) { } + ``` + {{< /details >}} + + {{< details summary="Grouped Details" name="my-details" >}} + Specifying a `name` allows elements to be connected, with only one able to be open at a time. + {{< /details >}} + +*/}} + +{{- /* Get arguments. */}} +{{- $summary := or (.Get "summary") (T "shortcodes.details") "Details" }} +{{- $class := or (.Get "class") "" }} +{{- $name := or (.Get "name") "" }} +{{- $open := false }} +{{- if in (slice "false" false 0) (.Get "open") }} + {{- $open = false }} +{{- else if in (slice "true" true 1) (.Get "open")}} + {{- $open = true }} +{{- end }} + +{{- /* Render. */}} + + {{ $summary | .Page.RenderString }} + {{ .Inner | .Page.RenderString (dict "display" "block") -}} + \ No newline at end of file diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index c7e118e82..36355598d 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -600,3 +600,118 @@ a{{< comment >}}b{{< /comment >}}c b := hugolib.Test(t, files) b.AssertFileContent("public/index.html", "

ac

") } + +func TestDetailsShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','section','sitemap','taxonomy','term'] +defaultContentLanguage = "en" +[languages] + [languages.en] + weight = 1 + [languages.es] + weight = 2 +-- i18n/en.toml -- +[shortcodes.details] +other = "Details" +-- i18n/es.toml -- +[shortcodes.details] +other = "Detalles" +-- layouts/_default/single.html -- +{{ .Content }} +-- content/d1.md -- +--- +title: Default State Test +--- +{{< details >}} +Basic example without summary +{{< /details >}} +-- content/d2.md -- +--- +title: Custom Summary Test +--- +{{< details summary="Custom Summary" >}} +Example with custom summary text +{{< /details >}} +-- content/d3.md -- +--- +title: Open State Test +--- +{{< details summary="Test Open State" open="true" >}} +Example with open state +{{< /details >}} +-- content/d4.md -- +--- +title: Attributes Test +--- +{{< details summary="Test Attribute sanitization" style="color: red; font-weight: bold; background-color: #eee" onclick="alert('test')" >}} +Example testing attribute sanitization +{{< /details >}} +-- content/d5.md -- +--- +title: Class Test +--- +{{< details class="custom-class" >}} +Example with allowed class attribute +{{< /details >}} +-- content/d6.md -- +--- +title: Name Test +--- +{{< details name="custom-name" >}} +Example with allowed name attribute +{{< /details >}} +-- content/d7.es.md -- +--- +title: Localization Test +--- +{{< details >}} +Localization example without summary +{{< /details >}} +` + b := hugolib.Test(t, files) + + // Test1: default state (closed by default) + b.AssertFileContentEquals("public/d1/index.html", + "\n
\n Details\n

Basic example without summary

\n
\n", + ) + content1 := b.FileContent("public/d1/index.html") + c := qt.New(t) + c.Assert(content1, qt.Not(qt.Contains), "open") + + // Test2: custom summary + b.AssertFileContentEquals("public/d2/index.html", + "\n
\n Custom Summary\n

Example with custom summary text

\n
\n", + ) + + // Test3: open state + b.AssertFileContentEquals("public/d3/index.html", + "\n
\n Test Open State\n

Example with open state

\n
\n", + ) + + // Test4: Test sanitization + b.AssertFileContentEquals("public/d4/index.html", + "\n
\n Test Attribute sanitization\n

Example testing attribute sanitization

\n
\n", + ) + content4 := b.FileContent("public/d4/index.html") + c.Assert(content4, qt.Not(qt.Contains), "style") + c.Assert(content4, qt.Not(qt.Contains), "onclick") + c.Assert(content4, qt.Not(qt.Contains), "alert") + + // Test5: class attribute + b.AssertFileContentEquals("public/d5/index.html", + "\n
\n Details\n

Example with allowed class attribute

\n
\n", + ) + + // Test6: name attribute + b.AssertFileContentEquals("public/d6/index.html", + "\n
\n Details\n

Example with allowed name attribute

\n
\n", + ) + + // Test7: localization + b.AssertFileContentEquals("public/es/d7/index.html", + "\n
\n Detalles\n

Localization example without summary

\n
\n", + ) +} From a834bb9f7e14298a8df75f7ffb69e31f2a36e655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 13 Dec 2024 11:00:04 +0100 Subject: [PATCH 015/374] js/esbuild: Build groups in order of their ID We already do this for scripts e.g. inside a group. This makes sure that group A's entry points gets added before B's, which can be an important property, see https://github.com/evanw/esbuild/issues/399#issuecomment-1458680887 --- internal/js/esbuild/batch.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go index 43e2da444..1a8a7b09a 100644 --- a/internal/js/esbuild/batch.go +++ b/internal/js/esbuild/batch.go @@ -474,25 +474,25 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { entryPoints = append(entryPoints, pth) } - for k, v := range b.scriptGroups { - keyPath := k + for _, g := range b.scriptGroups.Sorted() { + keyPath := g.id var runners []scriptRunnerTemplateContext - for _, vv := range v.runnersOptions.ByKey() { + for _, vv := range g.runnersOptions.ByKey() { runnerKeyPath := keyPath + "_" + vv.Key().String() runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix) runners = append(runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath}) - addResource(k, runnerImpPath, vv.Compiled().Resource, false) + addResource(g.id, runnerImpPath, vv.Compiled().Resource, false) } t := &batchGroupTemplateContext{ keyPath: keyPath, - ID: v.id, + ID: g.id, Runners: runners, } - instances := v.instancesOptions.ByKey() + instances := g.instancesOptions.ByKey() - for _, vv := range v.scriptsOptions.ByKey() { + for _, vv := range g.scriptsOptions.ByKey() { keyPath := keyPath + "_" + vv.Key().String() opts := vv.Compiled() impPath := path.Join(PrefixHugoVirtual, opts.Dir(), keyPath+opts.Resource.MediaType().FirstSuffix.FullSuffix) @@ -502,7 +502,7 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { name: keyPath, resourceGetter: impCtx, scriptOptions: opts, - dm: v.dependencyManager, + dm: g.dependencyManager, }) bt := scriptBatchTemplateContext{ @@ -528,10 +528,10 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { state.importerImportContext.Set(s, importContext{ name: s, resourceGetter: nil, - dm: v.dependencyManager, + dm: g.dependencyManager, }) - addResource(v.id, s, r, true) + addResource(g.id, s, r, true) } mediaTypes := b.client.d.ResourceSpec.MediaTypes() From 641d2616c71dfff4afa5ab09711c5b45a2a18131 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 11 Dec 2024 13:10:15 -0800 Subject: [PATCH 016/374] tpl/collections: Allow querify to accept a map argument Closes #13131 --- tpl/collections/collections.go | 42 ---------- tpl/collections/collections_test.go | 62 -------------- tpl/collections/querify.go | 125 ++++++++++++++++++++++++++++ tpl/collections/querify_test.go | 121 +++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 104 deletions(-) create mode 100644 tpl/collections/querify.go create mode 100644 tpl/collections/querify_test.go diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index a7e36f689..c1e7286ce 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "math/rand" - "net/url" "reflect" "strings" "time" @@ -383,47 +382,6 @@ func (ns *Namespace) Last(limit any, l any) (any, error) { return seqv.Slice(seqv.Len()-limitv, seqv.Len()).Interface(), nil } -// Querify encodes the given params in URL-encoded form ("bar=baz&foo=quux") sorted by key. -func (ns *Namespace) Querify(params ...any) (string, error) { - qs := url.Values{} - - if len(params) == 1 { - switch v := params[0].(type) { - case []string: - if len(v)%2 != 0 { - return "", errors.New("invalid query") - } - - for i := 0; i < len(v); i += 2 { - qs.Add(v[i], v[i+1]) - } - - return qs.Encode(), nil - - case []any: - params = v - - default: - return "", errors.New("query keys must be strings") - } - } - - if len(params)%2 != 0 { - return "", errors.New("invalid query") - } - - for i := 0; i < len(params); i += 2 { - switch v := params[i].(type) { - case string: - qs.Add(v, fmt.Sprintf("%v", params[i+1])) - default: - return "", errors.New("query keys must be strings") - } - } - - return qs.Encode(), nil -} - // Reverse creates a copy of the list l and reverses it. func (ns *Namespace) Reverse(l any) (any, error) { if l == nil { diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index 0f4bf82f5..2cd6bfc3f 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -512,68 +512,6 @@ func TestLast(t *testing.T) { } } -func TestQuerify(t *testing.T) { - t.Parallel() - c := qt.New(t) - ns := newNs() - - for i, test := range []struct { - params []any - expect any - }{ - {[]any{"a", "b"}, "a=b"}, - {[]any{"a", "b", "c", "d", "f", " &"}, `a=b&c=d&f=+%26`}, - {[]any{[]string{"a", "b"}}, "a=b"}, - {[]any{[]string{"a", "b", "c", "d", "f", " &"}}, `a=b&c=d&f=+%26`}, - {[]any{[]any{"x", "y"}}, `x=y`}, - {[]any{[]any{"x", 5}}, `x=5`}, - // errors - {[]any{5, "b"}, false}, - {[]any{"a", "b", "c"}, false}, - {[]any{[]string{"a", "b", "c"}}, false}, - {[]any{[]string{"a", "b"}, "c"}, false}, - {[]any{[]any{"c", "d", "e"}}, false}, - } { - errMsg := qt.Commentf("[%d] %v", i, test.params) - - result, err := ns.Querify(test.params...) - - if b, ok := test.expect.(bool); ok && !b { - c.Assert(err, qt.Not(qt.IsNil), errMsg) - continue - } - - c.Assert(err, qt.IsNil, errMsg) - c.Assert(result, qt.Equals, test.expect, errMsg) - } -} - -func BenchmarkQuerify(b *testing.B) { - ns := newNs() - params := []any{"a", "b", "c", "d", "f", " &"} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := ns.Querify(params...) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkQuerifySlice(b *testing.B) { - ns := newNs() - params := []string{"a", "b", "c", "d", "f", " &"} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := ns.Querify(params) - if err != nil { - b.Fatal(err) - } - } -} - func TestSeq(t *testing.T) { t.Parallel() c := qt.New(t) diff --git a/tpl/collections/querify.go b/tpl/collections/querify.go new file mode 100644 index 000000000..19e6d8afe --- /dev/null +++ b/tpl/collections/querify.go @@ -0,0 +1,125 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "errors" + "net/url" + + "github.com/gohugoio/hugo/common/maps" + "github.com/spf13/cast" +) + +var ( + errWrongArgStructure = errors.New("expected a map, a slice with an even number of elements, or an even number of scalar values, and each key must be a string") + errKeyIsEmptyString = errors.New("one of the keys is an empty string") +) + +// Querify returns a URL query string composed of the given key-value pairs, +// encoded and sorted by key. +func (ns *Namespace) Querify(params ...any) (string, error) { + if len(params) == 0 { + return "", nil + } + + if len(params) == 1 { + switch v := params[0].(type) { + case map[string]any: // created with collections.Dictionary + return mapToQueryString(v) + case maps.Params: // site configuration or page parameters + return mapToQueryString(v) + case []string: + return stringSliceToQueryString(v) + case []any: + s, err := interfaceSliceToStringSlice(v) + if err != nil { + return "", err + } + return stringSliceToQueryString(s) + default: + return "", errWrongArgStructure + } + } + + if len(params)%2 != 0 { + return "", errWrongArgStructure + } + + s, err := interfaceSliceToStringSlice(params) + if err != nil { + return "", err + } + return stringSliceToQueryString(s) +} + +// mapToQueryString returns a URL query string derived from the given string +// map, encoded and sorted by key. The function returns an error if it cannot +// convert an element value to a string. +func mapToQueryString[T map[string]any | maps.Params](m T) (string, error) { + if len(m) == 0 { + return "", nil + } + + qs := url.Values{} + for k, v := range m { + if len(k) == 0 { + return "", errKeyIsEmptyString + } + vs, err := cast.ToStringE(v) + if err != nil { + return "", err + } + qs.Add(k, vs) + } + return qs.Encode(), nil +} + +// sliceToQueryString returns a URL query string derived from the given slice +// of strings, encoded and sorted by key. The function returns an error if +// there are an odd number of elements. +func stringSliceToQueryString(s []string) (string, error) { + if len(s) == 0 { + return "", nil + } + if len(s)%2 != 0 { + return "", errWrongArgStructure + } + + qs := url.Values{} + for i := 0; i < len(s); i += 2 { + if len(s[i]) == 0 { + return "", errKeyIsEmptyString + } + qs.Add(s[i], s[i+1]) + } + return qs.Encode(), nil +} + +// interfaceSliceToStringSlice converts a slice of interfaces to a slice of +// strings, returning an error if it cannot convert an element to a string. +func interfaceSliceToStringSlice(s []any) ([]string, error) { + if len(s) == 0 { + return []string{}, nil + } + + ss := make([]string, 0, len(s)) + for _, v := range s { + vs, err := cast.ToStringE(v) + if err != nil { + return []string{}, err + } + ss = append(ss, vs) + } + return ss, nil +} diff --git a/tpl/collections/querify_test.go b/tpl/collections/querify_test.go new file mode 100644 index 000000000..17556e4cb --- /dev/null +++ b/tpl/collections/querify_test.go @@ -0,0 +1,121 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collections + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/maps" +) + +func TestQuerify(t *testing.T) { + t.Parallel() + c := qt.New(t) + ns := newNs() + + for _, test := range []struct { + name string + params []any + expect any + }{ + // map + {"01", []any{maps.Params{"a": "foo", "b": "bar"}}, `a=foo&b=bar`}, + {"02", []any{maps.Params{"a": 6, "b": 7}}, `a=6&b=7`}, + {"03", []any{maps.Params{"a": "foo", "b": 7}}, `a=foo&b=7`}, + {"04", []any{map[string]any{"a": "foo", "b": "bar"}}, `a=foo&b=bar`}, + {"05", []any{map[string]any{"a": 6, "b": 7}}, `a=6&b=7`}, + {"06", []any{map[string]any{"a": "foo", "b": 7}}, `a=foo&b=7`}, + // slice + {"07", []any{[]string{"a", "foo", "b", "bar"}}, `a=foo&b=bar`}, + {"08", []any{[]any{"a", 6, "b", 7}}, `a=6&b=7`}, + {"09", []any{[]any{"a", "foo", "b", 7}}, `a=foo&b=7`}, + // sequence of scalar values + {"10", []any{"a", "foo", "b", "bar"}, `a=foo&b=bar`}, + {"11", []any{"a", 6, "b", 7}, `a=6&b=7`}, + {"12", []any{"a", "foo", "b", 7}, `a=foo&b=7`}, + // empty map + {"13", []any{map[string]any{}}, ``}, + // empty slice + {"14", []any{[]string{}}, ``}, + {"15", []any{[]any{}}, ``}, + // no arguments + {"16", []any{}, ``}, + // errors: zero key length + {"17", []any{maps.Params{"": "foo"}}, false}, + {"18", []any{map[string]any{"": "foo"}}, false}, + {"19", []any{[]string{"", "foo"}}, false}, + {"20", []any{[]any{"", 6}}, false}, + {"21", []any{"", "foo"}, false}, + // errors: odd number of values + {"22", []any{[]string{"a", "foo", "b"}}, false}, + {"23", []any{[]any{"a", 6, "b"}}, false}, + {"24", []any{"a", "foo", "b"}, false}, + // errors: value cannot be cast to string + {"25", []any{map[string]any{"a": "foo", "b": tstNoStringer{}}}, false}, + {"26", []any{[]any{"a", "foo", "b", tstNoStringer{}}}, false}, + {"27", []any{"a", "foo", "b", tstNoStringer{}}, false}, + } { + errMsg := qt.Commentf("[%s] %v", test.name, test.params) + + result, err := ns.Querify(test.params...) + + if b, ok := test.expect.(bool); ok && !b { + c.Assert(err, qt.Not(qt.IsNil), errMsg) + continue + } + + c.Assert(err, qt.IsNil, errMsg) + c.Assert(result, qt.Equals, test.expect, errMsg) + } +} + +func BenchmarkQuerify(b *testing.B) { + ns := newNs() + params := []any{"a", "b", "c", "d", "f", " &"} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ns.Querify(params...) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkQuerifySlice(b *testing.B) { + ns := newNs() + params := []string{"a", "b", "c", "d", "f", " &"} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ns.Querify(params) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkQuerifyMap(b *testing.B) { + ns := newNs() + params := map[string]any{"a": "b", "c": "d", "f": " &"} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ns.Querify(params) + if err != nil { + b.Fatal(err) + } + } +} From 1e34e5b26d12e470473064b2825f9bbaa2d66c36 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 13 Dec 2024 03:25:10 -0800 Subject: [PATCH 017/374] tpl/tplimpl: Update details shortcode - Remove localization of default summary value - Add title attribute - Reformat to be consistent with other embedded templates - Simplify and improve integration test - Update documentation --- .../en/content-management/shortcodes.md | 34 +++-- .../templates/shortcodes/details.html | 81 ++++++------ tpl/tplimpl/tplimpl_integration_test.go | 124 ++++-------------- 3 files changed, 95 insertions(+), 144 deletions(-) diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md index 7a589a340..47e4f94ed 100644 --- a/docs/content/en/content-management/shortcodes.md +++ b/docs/content/en/content-management/shortcodes.md @@ -101,32 +101,42 @@ Although you can call this shortcode using the `{{}}` notation, computati {{% note %}} To override Hugo's embedded `details` shortcode, copy the [source code] to a file with the same name in the layouts/shortcodes directory. -This may be useful if you are wanting access to more global HTML attributes. - [source code]: {{% eturl details %}} {{% /note %}} -Use the `details` shortcode to generate a collapsible details HTML element. For example: +Use the `details` shortcode to create a `details` HTML element. For example: ```text -{{}} -Showing custom `summary` text. +{{}} +This is a **bold** word. {{}} ``` -Additional examples can be found in the source code. The `details` shortcode can use the following named arguments: +Hugo renders this to: + +```html +
+ See the details +

This is a bold word.

+
+``` + +The details shortcode accepts these named arguments: summary -: (`string`) Optional. Specifies the content of the child summary element. Default is "Details" +: (`string`) The content of the child `summary` element rendered from Markdown to HTML. Default is `Details`. open -: (`bool`) Optional. Whether to initially display the contents of the details element. Default is `false`. - -name -: (`string`) Optional. The value of the element's name attribute. +: (`bool`) Whether to initially display the content of the `details` element. Default is `false`. class -: (`string`) Optional. The value of the element's class attribute. +: (`string`) The value of the element's `class` attribute. + +name +: (`string`) The value of the element's `name` attribute. + +title +: (`string`) The value of the element's `title` attribute. ### figure diff --git a/tpl/tplimpl/embedded/templates/shortcodes/details.html b/tpl/tplimpl/embedded/templates/shortcodes/details.html index 932289517..c19d78c3e 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/details.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/details.html @@ -1,68 +1,75 @@ {{- /* Renders an HTML details element. -@param {string} [summary] The content of the child summary element. -@param {bool} [open=false] Whether to initially display the contents of the details element. @param {string} [class] The value of the element's class attribute. @param {string} [name] The value of the element's name attribute. +@param {string} [summary] The content of the child summary element. +@param {string} [title] The value of the element's title attribute. +@param {bool} [open=false] Whether to initially display the content of the details element. @reference https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details @examples - {{< details >}} - A basic collapsible section. - {{< /details >}} + {{< details >}} + A basic collapsible section. + {{< /details >}} - {{< details summary="Custom Summary Text" >}} - Showing custom `summary` text. - {{< /details >}} + {{< details summary="Custom Summary Text" >}} + Showing custom `summary` text. + {{< /details >}} - {{< details summary="Open Details" open=true >}} - Contents displayed initially by using `open`. - {{< /details >}} + {{< details summary="Open Details" open=true >}} + Contents displayed initially by using `open`. + {{< /details >}} - {{< details summary="Styled Content" class="my-custom-class" >}} - Content can be styled with CSS by specifying a `class`. + {{< details summary="Styled Content" class="my-custom-class" >}} + Content can be styled with CSS by specifying a `class`. - Target details element: + Target details element: - ```css - details.my-custom-class { } - ``` + ```css + details.my-custom-class { } + ``` - Target summary element: + Target summary element: - ```css - details.my-custom-class > summary > * { } - ``` + ```css + details.my-custom-class > summary > * { } + ``` - Target inner content: + Target inner content: - ```css - details.my-custom-class > :not(summary) { } - ``` - {{< /details >}} + ```css + details.my-custom-class > :not(summary) { } + ``` + {{< /details >}} - {{< details summary="Grouped Details" name="my-details" >}} - Specifying a `name` allows elements to be connected, with only one able to be open at a time. - {{< /details >}} + {{< details summary="Grouped Details" name="my-details" >}} + Specifying a `name` allows elements to be connected, with only one able to be open at a time. + {{< /details >}} */}} {{- /* Get arguments. */}} -{{- $summary := or (.Get "summary") (T "shortcodes.details") "Details" }} {{- $class := or (.Get "class") "" }} {{- $name := or (.Get "name") "" }} +{{- $summary := or (.Get "summary") "Details" }} +{{- $title := or (.Get "title") "" }} {{- $open := false }} {{- if in (slice "false" false 0) (.Get "open") }} - {{- $open = false }} -{{- else if in (slice "true" true 1) (.Get "open")}} - {{- $open = true }} + {{- $open = false }} +{{- else if in (slice "true" true 1) (.Get "open") }} + {{- $open = true }} {{- end }} {{- /* Render. */}} - - {{ $summary | .Page.RenderString }} - {{ .Inner | .Page.RenderString (dict "display" "block") -}} - \ No newline at end of file +
+ {{ $summary | .Page.RenderString }} + {{ .Inner | .Page.RenderString (dict "display" "block") -}} +
diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index 36355598d..445316a0a 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -606,112 +606,46 @@ func TestDetailsShortcode(t *testing.T) { files := ` -- hugo.toml -- -disableKinds = ['rss','section','sitemap','taxonomy','term'] -defaultContentLanguage = "en" -[languages] - [languages.en] - weight = 1 - [languages.es] - weight = 2 --- i18n/en.toml -- -[shortcodes.details] -other = "Details" --- i18n/es.toml -- -[shortcodes.details] -other = "Detalles" --- layouts/_default/single.html -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- {{ .Content }} --- content/d1.md -- +-- content/_index.md -- --- -title: Default State Test +title: home --- {{< details >}} -Basic example without summary +A: An _emphasized_ word. {{< /details >}} --- content/d2.md -- ---- -title: Custom Summary Test ---- -{{< details summary="Custom Summary" >}} -Example with custom summary text + +{{< details + class="my-class" + name="my-name" + open=true + summary="A **bold** word" + title="my-title" +>}} +B: An _emphasized_ word. {{< /details >}} --- content/d3.md -- ---- -title: Open State Test ---- -{{< details summary="Test Open State" open="true" >}} -Example with open state + +{{< details open=false >}} +C: An _emphasized_ word. {{< /details >}} --- content/d4.md -- ---- -title: Attributes Test ---- -{{< details summary="Test Attribute sanitization" style="color: red; font-weight: bold; background-color: #eee" onclick="alert('test')" >}} -Example testing attribute sanitization + +{{< details open="false" >}} +D: An _emphasized_ word. {{< /details >}} --- content/d5.md -- ---- -title: Class Test ---- -{{< details class="custom-class" >}} -Example with allowed class attribute -{{< /details >}} --- content/d6.md -- ---- -title: Name Test ---- -{{< details name="custom-name" >}} -Example with allowed name attribute -{{< /details >}} --- content/d7.es.md -- ---- -title: Localization Test ---- -{{< details >}} -Localization example without summary + +{{< details open=0 >}} +E: An _emphasized_ word. {{< /details >}} ` b := hugolib.Test(t, files) - // Test1: default state (closed by default) - b.AssertFileContentEquals("public/d1/index.html", - "\n
\n Details\n

Basic example without summary

\n
\n", - ) - content1 := b.FileContent("public/d1/index.html") - c := qt.New(t) - c.Assert(content1, qt.Not(qt.Contains), "open") - - // Test2: custom summary - b.AssertFileContentEquals("public/d2/index.html", - "\n
\n Custom Summary\n

Example with custom summary text

\n
\n", - ) - - // Test3: open state - b.AssertFileContentEquals("public/d3/index.html", - "\n
\n Test Open State\n

Example with open state

\n
\n", - ) - - // Test4: Test sanitization - b.AssertFileContentEquals("public/d4/index.html", - "\n
\n Test Attribute sanitization\n

Example testing attribute sanitization

\n
\n", - ) - content4 := b.FileContent("public/d4/index.html") - c.Assert(content4, qt.Not(qt.Contains), "style") - c.Assert(content4, qt.Not(qt.Contains), "onclick") - c.Assert(content4, qt.Not(qt.Contains), "alert") - - // Test5: class attribute - b.AssertFileContentEquals("public/d5/index.html", - "\n
\n Details\n

Example with allowed class attribute

\n
\n", - ) - - // Test6: name attribute - b.AssertFileContentEquals("public/d6/index.html", - "\n
\n Details\n

Example with allowed name attribute

\n
\n", - ) - - // Test7: localization - b.AssertFileContentEquals("public/es/d7/index.html", - "\n
\n Detalles\n

Localization example without summary

\n
\n", + b.AssertFileContent("public/index.html", + "
\n Details\n

A: An emphasized word.

\n
", + "
\n A bold word\n

B: An emphasized word.

\n
", + "
\n Details\n

C: An emphasized word.

\n
", + "
\n Details\n

D: An emphasized word.

\n
", + "
\n Details\n

D: An emphasized word.

\n
", ) } From 852d868549fe8281760cdba6834cf32d445d1d46 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 13 Dec 2024 04:08:25 -0800 Subject: [PATCH 018/374] tpl/tplimpl: Update youtube shortcode Pass a map instead of a slice to the collections.Querify function. --- tpl/tplimpl/embedded/templates/shortcodes/youtube.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html index 441469d0a..b61c6130a 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html @@ -84,12 +84,7 @@ Renders an embedded YouTube video. {{- if $loop }} {{- $params = merge $params (dict "playlist" $id) }} {{- end }} - {{- $s := slice }} - {{- range $k, $v := $params }} - {{- $s = $s | append $k }} - {{- $s = $s | append $v }} - {{- end }} - {{- with querify $s }} + {{- with querify $params }} {{- $src = printf "%s?%s" $src . }} {{- end }} From a32c889a7b1b6cb7b9a80b4fbab342d9b660a988 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 13 Dec 2024 10:50:17 -0800 Subject: [PATCH 019/374] tpl/tplimpl: Fix title attribute in details shortcode --- tpl/tplimpl/embedded/templates/shortcodes/details.html | 2 +- tpl/tplimpl/tplimpl_integration_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/details.html b/tpl/tplimpl/embedded/templates/shortcodes/details.html index c19d78c3e..82c4f68f7 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/details.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/details.html @@ -68,7 +68,7 @@ Renders an HTML details element. {{- with $class }} class="{{ . }}" {{- end }} {{- with $name }} name="{{ . }}" {{- end }} {{- with $open }} open {{- end }} - {{- with $title }} class="{{ . }}" {{- end -}} + {{- with $title }} title="{{ . }}" {{- end -}} > {{ $summary | .Page.RenderString }} {{ .Inner | .Page.RenderString (dict "display" "block") -}} diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index 445316a0a..1e7aa3111 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -643,7 +643,7 @@ E: An _emphasized_ word. b.AssertFileContent("public/index.html", "
\n Details\n

A: An emphasized word.

\n
", - "
\n A bold word\n

B: An emphasized word.

\n
", + "
\n A bold word\n

B: An emphasized word.

\n
", "
\n Details\n

C: An emphasized word.

\n
", "
\n Details\n

D: An emphasized word.

\n
", "
\n Details\n

D: An emphasized word.

\n
", From 4cbd4ef9913d9d167eec7dda029f4fe6f3b22bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 13 Dec 2024 18:04:44 +0100 Subject: [PATCH 020/374] js/esbuild: Batch: Avoid nil Instances slice Ranging over a nil slice in Go works great, but is a hassle onced passed to JS. --- internal/js/esbuild/batch.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go index 1a8a7b09a..688b58d21 100644 --- a/internal/js/esbuild/batch.go +++ b/internal/js/esbuild/batch.go @@ -506,8 +506,9 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { }) bt := scriptBatchTemplateContext{ - opts: vv, - Import: impPath, + opts: vv, + Import: impPath, + Instances: []scriptInstanceBatchTemplateContext{}, } state.importResource.Set(bt.Import, vv.Compiled().Resource) predicate := func(k instanceID) bool { From 7de5317aef7866ed559f0dbcd2f3370944b30ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 14 Dec 2024 11:56:00 +0100 Subject: [PATCH 021/374] js/esbuild: Add runners after scripts --- internal/js/esbuild/batch.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go index 688b58d21..d0b6dba33 100644 --- a/internal/js/esbuild/batch.go +++ b/internal/js/esbuild/batch.go @@ -476,18 +476,10 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { for _, g := range b.scriptGroups.Sorted() { keyPath := g.id - var runners []scriptRunnerTemplateContext - for _, vv := range g.runnersOptions.ByKey() { - runnerKeyPath := keyPath + "_" + vv.Key().String() - runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix) - runners = append(runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath}) - addResource(g.id, runnerImpPath, vv.Compiled().Resource, false) - } t := &batchGroupTemplateContext{ keyPath: keyPath, ID: g.id, - Runners: runners, } instances := g.instancesOptions.ByKey() @@ -521,6 +513,13 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { t.Scripts = append(t.Scripts, bt) } + for _, vv := range g.runnersOptions.ByKey() { + runnerKeyPath := keyPath + "_" + vv.Key().String() + runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix) + t.Runners = append(t.Runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath}) + addResource(g.id, runnerImpPath, vv.Compiled().Resource, false) + } + r, s, err := b.client.buildBatchGroup(ctx, t) if err != nil { return nil, fmt.Errorf("failed to build JS batch: %w", err) From 744b8566ec8ea138a63d3787dc2a0dd81511e492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 15 Dec 2024 10:52:53 +0100 Subject: [PATCH 022/374] Fix a rebuild on resource rename case --- hugolib/hugo_sites_build.go | 18 ++++++++++++++---- internal/js/esbuild/batch_integration_test.go | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 5346e2e6b..02ecd5785 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -738,15 +738,15 @@ type pathChange struct { // The path to the changed file. p *paths.Path - // If true, this is a delete operation (a delete or a rename). - delete bool + // If true, this is a structural change (e.g. a delete or a rename). + structural bool // If true, this is a directory. isDir bool } func (p pathChange) isStructuralChange() bool { - return p.delete || p.isDir + return p.structural || p.isDir } func (h *HugoSites) processPartialRebuildChanges(ctx context.Context, l logg.LevelLogger, config *BuildCfg) error { @@ -912,7 +912,7 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } - addedOrChangedContent = append(addedOrChangedContent, pathChange{p: pathInfo, delete: delete, isDir: isDir}) + addedOrChangedContent = append(addedOrChangedContent, pathChange{p: pathInfo, structural: delete, isDir: isDir}) case files.ComponentFolderLayouts: tmplChanged = true @@ -1033,6 +1033,16 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo handleChange(id, false, true) } + for _, id := range changes { + if id == identity.GenghisKhan { + for i, cp := range addedOrChangedContent { + cp.structural = true + addedOrChangedContent[i] = cp + } + break + } + } + resourceFiles := h.fileEventsContentPaths(addedOrChangedContent) changed := &WhatChanged{ diff --git a/internal/js/esbuild/batch_integration_test.go b/internal/js/esbuild/batch_integration_test.go index 07f99ee4e..3501f820a 100644 --- a/internal/js/esbuild/batch_integration_test.go +++ b/internal/js/esbuild/batch_integration_test.go @@ -184,6 +184,20 @@ func TestBatchEditScriptParam(t *testing.T) { b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main-edited") } +func TestBatchRenameBundledScript(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/p1.js", "P1 Script") + b.RenameFile("content/p1/p1script.js", "content/p1/p1script2.js") + _, err := b.BuildE() + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, "resource not set") + + // Rename it back. + b.RenameFile("content/p1/p1script2.js", "content/p1/p1script.js") + b.Build() +} + func TestBatchErrorScriptResourceNotSet(t *testing.T) { files := strings.Replace(jsBatchFilesTemplate, `(resources.Get "js/main.js")`, `(resources.Get "js/doesnotexist.js")`, 1) b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) From 48dd6a918a0ef070819ef80d59b2553bc99e2964 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Sun, 15 Dec 2024 17:21:05 -0800 Subject: [PATCH 023/374] parser/pageparser: Fix Org Mode summary delimiter assignment Closes #13152 --- parser/pageparser/pagelexer_intro.go | 4 +- parser/pageparser/pagelexer_intro_test.go | 46 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 parser/pageparser/pagelexer_intro_test.go diff --git a/parser/pageparser/pagelexer_intro.go b/parser/pageparser/pagelexer_intro.go index 925c61c9e..334d9a79c 100644 --- a/parser/pageparser/pagelexer_intro.go +++ b/parser/pageparser/pagelexer_intro.go @@ -91,14 +91,14 @@ func lexFrontMatterOrgMode(l *pageLexer) stateFunc { #+DESCRIPTION: Just another golang parser for org content! */ - l.summaryDivider = summaryDividerOrg - l.backup() if !l.hasPrefix(delimOrg) { return lexMainSection } + l.summaryDivider = summaryDividerOrg + // Read lines until we no longer see a #+ prefix LOOP: for { diff --git a/parser/pageparser/pagelexer_intro_test.go b/parser/pageparser/pagelexer_intro_test.go new file mode 100644 index 000000000..c074e6f50 --- /dev/null +++ b/parser/pageparser/pagelexer_intro_test.go @@ -0,0 +1,46 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pageparser + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func Test_lexIntroSection(t *testing.T) { + t.Parallel() + c := qt.New(t) + for i, tt := range []struct { + input string + expectItemType ItemType + expectSummaryDivider []byte + }{ + {"{\"title\": \"JSON\"}\n", TypeFrontMatterJSON, summaryDivider}, + {"#+TITLE: ORG\n", TypeFrontMatterORG, summaryDividerOrg}, + {"+++\ntitle = \"TOML\"\n+++\n", TypeFrontMatterTOML, summaryDivider}, + {"---\ntitle: YAML\n---\n", TypeFrontMatterYAML, summaryDivider}, + // Issue 13152 + {"# ATX Header Level 1\n", tText, summaryDivider}, + } { + errMsg := qt.Commentf("[%d] %v", i, tt.input) + + l := newPageLexer([]byte(tt.input), lexIntroSection, Config{}) + l.run() + + c.Assert(l.items[0].Type, qt.Equals, tt.expectItemType, errMsg) + c.Assert(l.summaryDivider, qt.DeepEquals, tt.expectSummaryDivider, errMsg) + + } +} From 565c30eac9e00b2ebcbdbb8e05b5e8238a15fefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 16 Dec 2024 08:34:17 +0100 Subject: [PATCH 024/374] js: Fix js.Batch for multihost setups Note that this is an unreleased feature. Fixes #13151 --- deps/deps.go | 7 ++ hugolib/paths/paths.go | 2 +- hugolib/site.go | 8 +- internal/js/api.go | 51 +++++++++ internal/js/esbuild/batch.go | 105 ++++++++++-------- internal/js/esbuild/batch_integration_test.go | 63 +++++++++++ resources/resource.go | 7 -- tpl/js/js.go | 18 +-- 8 files changed, 190 insertions(+), 71 deletions(-) create mode 100644 internal/js/api.go diff --git a/deps/deps.go b/deps/deps.go index 8e9ec42d8..56a3d3644 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -24,6 +24,7 @@ import ( "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/internal/js" "github.com/gohugoio/hugo/internal/warpc" "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources/page" @@ -105,6 +106,12 @@ type Deps struct { // TODO(bep) rethink this re. a plugin setup, but this will have to do for now. WasmDispatchers *warpc.Dispatchers + // The JS batcher client. + JSBatcherClient js.BatcherClient + + // The JS batcher client. + // JSBatcherClient *esbuild.BatcherClient + isClosed bool *globalErrHandler diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go index 397dba3f8..60ec873f9 100644 --- a/hugolib/paths/paths.go +++ b/hugolib/paths/paths.go @@ -67,7 +67,7 @@ func New(fs *hugofs.Fs, cfg config.AllProvider) (*Paths, error) { var multihostTargetBasePaths []string if cfg.IsMultihost() && len(cfg.Languages()) > 1 { for _, l := range cfg.Languages() { - multihostTargetBasePaths = append(multihostTargetBasePaths, l.Lang) + multihostTargetBasePaths = append(multihostTargetBasePaths, hpaths.ToSlashPreserveLeading(l.Lang)) } } diff --git a/hugolib/site.go b/hugolib/site.go index f73bd2517..4e2497ee1 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -42,6 +42,7 @@ import ( "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/hugolib/doctree" "github.com/gohugoio/hugo/hugolib/pagesfromdata" + "github.com/gohugoio/hugo/internal/js/esbuild" "github.com/gohugoio/hugo/internal/warpc" "github.com/gohugoio/hugo/langs/i18n" "github.com/gohugoio/hugo/modules" @@ -205,6 +206,12 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { return nil, err } + batcherClient, err := esbuild.NewBatcherClient(firstSiteDeps) + if err != nil { + return nil, err + } + firstSiteDeps.JSBatcherClient = batcherClient + confm := cfg.Configs if err := confm.Validate(logger); err != nil { return nil, err @@ -313,7 +320,6 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { return li.Lang < lj.Lang }) - var err error h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites) if err == nil && h == nil { panic("hugo: newHugoSitesNew returned nil error and nil HugoSites") diff --git a/internal/js/api.go b/internal/js/api.go new file mode 100644 index 000000000..30180dece --- /dev/null +++ b/internal/js/api.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package js + +import ( + "context" + + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/resources/resource" +) + +// BatcherClient is used to do JS batch operations. +type BatcherClient interface { + New(id string) (Batcher, error) + Store() *maps.Cache[string, Batcher] +} + +// BatchPackage holds a group of JavaScript resources. +type BatchPackage interface { + Groups() map[string]resource.Resources +} + +// Batcher is used to build JavaScript packages. +type Batcher interface { + Build(context.Context) (BatchPackage, error) + Config(ctx context.Context) OptionsSetter + Group(ctx context.Context, id string) BatcherGroup +} + +// BatcherGroup is a group of scripts and instances. +type BatcherGroup interface { + Instance(sid, iid string) OptionsSetter + Runner(id string) OptionsSetter + Script(id string) OptionsSetter +} + +// OptionsSetter is used to set options for a batch, script or instance. +type OptionsSetter interface { + SetOptions(map[string]any) string +} diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go index d0b6dba33..c5394ac0a 100644 --- a/internal/js/esbuild/batch.go +++ b/internal/js/esbuild/batch.go @@ -20,6 +20,7 @@ import ( _ "embed" "encoding/json" "fmt" + "io" "path" "path/filepath" "reflect" @@ -34,7 +35,9 @@ import ( "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/internal/js" "github.com/gohugoio/hugo/lazy" "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources" @@ -42,11 +45,10 @@ import ( "github.com/gohugoio/hugo/resources/resource_factories/create" "github.com/gohugoio/hugo/tpl" "github.com/mitchellh/mapstructure" - "github.com/spf13/afero" "github.com/spf13/cast" ) -var _ Batcher = (*batcher)(nil) +var _ js.Batcher = (*batcher)(nil) const ( NsBatch = "_hugo-js-batch" @@ -58,7 +60,7 @@ const ( //go:embed batch-esm-runner.gotmpl var runnerTemplateStr string -var _ BatchPackage = (*Package)(nil) +var _ js.BatchPackage = (*Package)(nil) var _ buildToucher = (*optsHolder[scriptOptions])(nil) @@ -67,16 +69,17 @@ var ( _ isBuiltOrTouchedProvider = (*scriptGroup)(nil) ) -func NewBatcherClient(deps *deps.Deps) (*BatcherClient, error) { +func NewBatcherClient(deps *deps.Deps) (js.BatcherClient, error) { c := &BatcherClient{ d: deps, buildClient: NewBuildClient(deps.BaseFs.Assets, deps.ResourceSpec), createClient: create.New(deps.ResourceSpec), - bundlesCache: maps.NewCache[string, BatchPackage](), + batcherStore: maps.NewCache[string, js.Batcher](), + bundlesStore: maps.NewCache[string, js.BatchPackage](), } deps.BuildEndListeners.Add(func(...any) bool { - c.bundlesCache.Reset() + c.bundlesStore.Reset() return false }) @@ -125,7 +128,7 @@ func (o *opts[K, C]) Reset() { o.h.resetCounter++ } -func (o *opts[K, C]) Get(id uint32) OptionsSetter { +func (o *opts[K, C]) Get(id uint32) js.OptionsSetter { var b *optsHolder[C] o.once.Do(func() { b = o.h @@ -184,18 +187,6 @@ func newOpts[K any, C optionsCompiler[C]](key K, optionsID string, defaults defa } } -// BatchPackage holds a group of JavaScript resources. -type BatchPackage interface { - Groups() map[string]resource.Resources -} - -// Batcher is used to build JavaScript packages. -type Batcher interface { - Build(context.Context) (BatchPackage, error) - Config(ctx context.Context) OptionsSetter - Group(ctx context.Context, id string) BatcherGroup -} - // BatcherClient is a client for building JavaScript packages. type BatcherClient struct { d *deps.Deps @@ -206,12 +197,13 @@ type BatcherClient struct { createClient *create.Client buildClient *BuildClient - bundlesCache *maps.Cache[string, BatchPackage] + batcherStore *maps.Cache[string, js.Batcher] + bundlesStore *maps.Cache[string, js.BatchPackage] } // New creates a new Batcher with the given ID. // This will be typically created once and reused across rebuilds. -func (c *BatcherClient) New(id string) (Batcher, error) { +func (c *BatcherClient) New(id string) (js.Batcher, error) { var initErr error c.once.Do(func() { // We should fix the initialization order here (or use the Go template package directly), but we need to wait @@ -288,6 +280,10 @@ func (c *BatcherClient) New(id string) (Batcher, error) { return b, nil } +func (c *BatcherClient) Store() *maps.Cache[string, js.Batcher] { + return c.batcherStore +} + func (c *BatcherClient) buildBatchGroup(ctx context.Context, t *batchGroupTemplateContext) (resource.Resource, string, error) { var buf bytes.Buffer @@ -304,18 +300,6 @@ func (c *BatcherClient) buildBatchGroup(ctx context.Context, t *batchGroupTempla return r, s, nil } -// BatcherGroup is a group of scripts and instances. -type BatcherGroup interface { - Instance(sid, iid string) OptionsSetter - Runner(id string) OptionsSetter - Script(id string) OptionsSetter -} - -// OptionsSetter is used to set options for a batch, script or instance. -type OptionsSetter interface { - SetOptions(map[string]any) string -} - // Package holds a group of JavaScript resources. type Package struct { id string @@ -353,9 +337,9 @@ type batcher struct { } // Build builds the batch if not already built or if it's stale. -func (b *batcher) Build(ctx context.Context) (BatchPackage, error) { +func (b *batcher) Build(ctx context.Context) (js.BatchPackage, error) { key := dynacache.CleanKey(b.id + ".js") - p, err := b.client.bundlesCache.GetOrCreate(key, func() (BatchPackage, error) { + p, err := b.client.bundlesStore.GetOrCreate(key, func() (js.BatchPackage, error) { return b.build(ctx) }) if err != nil { @@ -364,11 +348,11 @@ func (b *batcher) Build(ctx context.Context) (BatchPackage, error) { return p, nil } -func (b *batcher) Config(ctx context.Context) OptionsSetter { +func (b *batcher) Config(ctx context.Context) js.OptionsSetter { return b.configOptions.Get(b.buildCount) } -func (b *batcher) Group(ctx context.Context, id string) BatcherGroup { +func (b *batcher) Group(ctx context.Context, id string) js.BatcherGroup { if err := ValidateBatchID(id, false); err != nil { panic(err) } @@ -419,7 +403,7 @@ func (b *batcher) isStale() bool { return false } -func (b *batcher) build(ctx context.Context) (BatchPackage, error) { +func (b *batcher) build(ctx context.Context) (js.BatchPackage, error) { b.mu.Lock() defer b.mu.Unlock() defer func() { @@ -463,6 +447,8 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { pathGroup: maps.NewCache[string, string](), } + multihostBasePaths := b.client.d.ResourceSpec.MultihostTargetBasePaths + // Entry points passed to ESBuid. var entryPoints []string addResource := func(group, pth string, r resource.Resource, isResult bool) { @@ -701,15 +687,36 @@ func (b *batcher) doBuild(ctx context.Context) (*Package, error) { if !handled { // Copy to destination. - p := strings.TrimPrefix(o.Path, outDir) - targetFilename := filepath.Join(b.id, p) - fs := b.client.d.BaseFs.PublishFs - if err := fs.MkdirAll(filepath.Dir(targetFilename), 0o777); err != nil { - return nil, fmt.Errorf("failed to create dir %q: %w", targetFilename, err) + // In a multihost setup, we will have multiple targets. + var targetFilenames []string + if len(multihostBasePaths) > 0 { + for _, base := range multihostBasePaths { + p := strings.TrimPrefix(o.Path, outDir) + targetFilename := filepath.Join(base, b.id, p) + targetFilenames = append(targetFilenames, targetFilename) + } + } else { + p := strings.TrimPrefix(o.Path, outDir) + targetFilename := filepath.Join(b.id, p) + targetFilenames = append(targetFilenames, targetFilename) } - if err := afero.WriteFile(fs, targetFilename, o.Contents, 0o666); err != nil { - return nil, fmt.Errorf("failed to write to %q: %w", targetFilename, err) + fs := b.client.d.BaseFs.PublishFs + + if err := func() error { + fw, err := helpers.OpenFilesForWriting(fs, targetFilenames...) + if err != nil { + return err + } + defer fw.Close() + + fr := bytes.NewReader(o.Contents) + + _, err = io.Copy(fw, fr) + + return err + }(); err != nil { + return nil, fmt.Errorf("failed to copy to %q: %w", targetFilenames, err) } } } @@ -845,7 +852,7 @@ type optionsGetSetter[K, C any] interface { Key() K Reset() - Get(uint32) OptionsSetter + Get(uint32) js.OptionsSetter isStale() bool currPrev() (map[string]any, map[string]any) } @@ -975,7 +982,7 @@ func (b *scriptGroup) IdentifierBase() string { return b.id } -func (s *scriptGroup) Instance(sid, id string) OptionsSetter { +func (s *scriptGroup) Instance(sid, id string) js.OptionsSetter { if err := ValidateBatchID(sid, false); err != nil { panic(err) } @@ -1014,7 +1021,7 @@ func (g *scriptGroup) Reset() { } } -func (s *scriptGroup) Runner(id string) OptionsSetter { +func (s *scriptGroup) Runner(id string) js.OptionsSetter { if err := ValidateBatchID(id, false); err != nil { panic(err) } @@ -1043,7 +1050,7 @@ func (s *scriptGroup) Runner(id string) OptionsSetter { return s.runnersOptions[sid].Get(s.b.buildCount) } -func (s *scriptGroup) Script(id string) OptionsSetter { +func (s *scriptGroup) Script(id string) js.OptionsSetter { if err := ValidateBatchID(id, false); err != nil { panic(err) } diff --git a/internal/js/esbuild/batch_integration_test.go b/internal/js/esbuild/batch_integration_test.go index 3501f820a..55528bdf0 100644 --- a/internal/js/esbuild/batch_integration_test.go +++ b/internal/js/esbuild/batch_integration_test.go @@ -184,6 +184,69 @@ func TestBatchEditScriptParam(t *testing.T) { b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main-edited") } +func TestBatchMultiHost(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +[languages] +[languages.en] +weight = 1 +baseURL = "https://example.com/en" +[languages.fr] +weight = 2 +baseURL = "https://example.com/fr" +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import * as foo from 'mylib'; +console.log("Hello, Main!"); +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/index.html -- +Home. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} + + +` + b := hugolib.Test(t, files, hugolib.TestOptWithOSFs()) + b.AssertPublishDir( + "en/mybatch/chunk-TOZKWCDE.js", "en/mybatch/mygroup.js ", + "fr/mybatch/mygroup.js", "fr/mybatch/chunk-TOZKWCDE.js") +} + func TestBatchRenameBundledScript(t *testing.T) { files := jsBatchFilesTemplate b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) diff --git a/resources/resource.go b/resources/resource.go index 6025cbf4c..4b81a478a 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -141,13 +141,6 @@ func (fd *ResourceSourceDescriptor) init(r *Spec) error { } fd.TargetPath = paths.ToSlashPreserveLeading(fd.TargetPath) - for i, base := range fd.TargetBasePaths { - dir := paths.ToSlashPreserveLeading(base) - if dir == "/" { - dir = "" - } - fd.TargetBasePaths[i] = dir - } if fd.NameNormalized == "" { fd.NameNormalized = fd.TargetPath diff --git a/tpl/js/js.go b/tpl/js/js.go index b686b76a7..dfd0a3581 100644 --- a/tpl/js/js.go +++ b/tpl/js/js.go @@ -17,8 +17,8 @@ package js import ( "errors" - "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/internal/js" "github.com/gohugoio/hugo/internal/js/esbuild" "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" @@ -34,16 +34,9 @@ func New(d *deps.Deps) (*Namespace, error) { return &Namespace{}, nil } - batcherClient, err := esbuild.NewBatcherClient(d) - if err != nil { - return nil, err - } - return &Namespace{ d: d, jsTransformClient: jstransform.New(d.BaseFs.Assets, d.ResourceSpec), - jsBatcherClient: batcherClient, - jsBatcherStore: maps.NewCache[string, esbuild.Batcher](), createClient: create.New(d.ResourceSpec), babelClient: babel.New(d.ResourceSpec), }, nil @@ -56,8 +49,6 @@ type Namespace struct { jsTransformClient *jstransform.Client createClient *create.Client babelClient *babel.Client - jsBatcherClient *esbuild.BatcherClient - jsBatcherStore *maps.Cache[string, esbuild.Batcher] } // Build processes the given Resource with ESBuild. @@ -90,12 +81,13 @@ func (ns *Namespace) Build(args ...any) (resource.Resource, error) { // Repeated calls with the same ID will return the same Batcher. // The ID will be used to name the root directory of the batch. // Forward slashes in the ID is allowed. -func (ns *Namespace) Batch(id string) (esbuild.Batcher, error) { +func (ns *Namespace) Batch(id string) (js.Batcher, error) { if err := esbuild.ValidateBatchID(id, true); err != nil { return nil, err } - b, err := ns.jsBatcherStore.GetOrCreate(id, func() (esbuild.Batcher, error) { - return ns.jsBatcherClient.New(id) + + b, err := ns.d.JSBatcherClient.Store().GetOrCreate(id, func() (js.Batcher, error) { + return ns.d.JSBatcherClient.New(id) }) if err != nil { return nil, err From a5e5be234c33016cd44a611ea4b8c6e57e2468e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 13 Dec 2024 13:57:23 +0100 Subject: [PATCH 025/374] Fix panic on server rebuilds when using both base templates and template.Defer Fixes #12963 --- common/types/evictingqueue.go | 3 + hugolib/integrationtest_builder.go | 27 +++++++-- internal/js/esbuild/batch_integration_test.go | 40 ------------- tpl/tplimpl/template.go | 56 ++++++++++++------- tpl/tplimpl/tplimpl_integration_test.go | 49 ++++++++++++++++ 5 files changed, 110 insertions(+), 65 deletions(-) diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go index 88add59d5..5ab715aa8 100644 --- a/common/types/evictingqueue.go +++ b/common/types/evictingqueue.go @@ -65,6 +65,9 @@ func (q *EvictingStringQueue) Len() int { // Contains returns whether the queue contains v. func (q *EvictingStringQueue) Contains(v string) bool { + if q == nil { + return false + } q.mu.Lock() defer q.mu.Unlock() return q.set[v] diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index b593cd965..7a6c040b1 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -25,6 +25,7 @@ import ( "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/config/security" @@ -466,6 +467,28 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder { return s } +func (s *IntegrationTestBuilder) BuildPartial(urls ...string) *IntegrationTestBuilder { + if _, err := s.BuildPartialE(urls...); err != nil { + s.Fatal(err) + } + return s +} + +func (s *IntegrationTestBuilder) BuildPartialE(urls ...string) (*IntegrationTestBuilder, error) { + if s.buildCount == 0 { + panic("BuildPartial can only be used after a full build") + } + if !s.Cfg.Running { + panic("BuildPartial can only be used in server mode") + } + visited := types.NewEvictingStringQueue(len(urls)) + for _, url := range urls { + visited.Add(url) + } + buildCfg := BuildCfg{RecentlyVisited: visited, PartialReRender: true} + return s, s.build(buildCfg) +} + func (s *IntegrationTestBuilder) Close() { s.Helper() s.Assert(s.H.Close(), qt.IsNil) @@ -747,10 +770,6 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error { s.counters = &buildCounters{} cfg.testCounters = s.counters - if s.buildCount > 0 && (len(changeEvents) == 0) { - return nil - } - s.buildCount++ err := s.H.Build(cfg, changeEvents...) diff --git a/internal/js/esbuild/batch_integration_test.go b/internal/js/esbuild/batch_integration_test.go index 55528bdf0..b4a2454ac 100644 --- a/internal/js/esbuild/batch_integration_test.go +++ b/internal/js/esbuild/batch_integration_test.go @@ -721,43 +721,3 @@ console.log("config.params.id", id3); b.EditFileReplaceAll("assets/other/bar.css", ".bar-edit {", ".bar-edit2 {").Build() b.AssertFileContent("public/mybundle/reactbatch.css", ".bar-edit2 {") } - -func TestEditBaseofManyTimes(t *testing.T) { - files := ` --- hugo.toml -- -baseURL = "https://example.com" -disableLiveReload = true -disableKinds = ["taxonomy", "term"] --- layouts/_default/baseof.html -- -Baseof. -{{ block "main" . }}{{ end }} -{{ with (templates.Defer (dict "key" "global")) }} -Now. {{ now }} -{{ end }} --- layouts/_default/single.html -- -{{ define "main" }} -Single. -{{ end }} --- --- layouts/_default/list.html -- -{{ define "main" }} -List. -{{ end }} --- content/mybundle/index.md -- ---- -title: "My Bundle" ---- --- content/_index.md -- ---- -title: "Home" ---- -` - - b := hugolib.TestRunning(t, files) - b.AssertFileContent("public/index.html", "Baseof.") - - for i := 0; i < 100; i++ { - b.EditFileReplaceAll("layouts/_default/baseof.html", "Now", "Now.").Build() - b.AssertFileContent("public/index.html", "Now..") - } -} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 9e2af046d..0a593593b 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -30,6 +30,7 @@ import ( "unicode" "unicode/utf8" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/output/layouts" @@ -191,8 +192,10 @@ func newTemplateHandlers(d *deps.Deps) (*tpl.TemplateHandlers, error) { func newTemplateNamespace(funcs map[string]any) *templateNamespace { return &templateNamespace{ - prototypeHTML: htmltemplate.New("").Funcs(funcs), - prototypeText: texttemplate.New("").Funcs(funcs), + prototypeHTML: htmltemplate.New("").Funcs(funcs), + prototypeText: texttemplate.New("").Funcs(funcs), + prototypeHTMLCloneCache: maps.NewCache[prototypeCloneID, *htmltemplate.Template](), + prototypeTextCloneCache: maps.NewCache[prototypeCloneID, *texttemplate.Template](), templateStateMap: &templateStateMap{ templates: make(map[string]*templateState), }, @@ -688,7 +691,7 @@ func (t *templateHandler) addTemplateTo(info templateInfo, to *templateNamespace func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Template, error) { if overlay.isText { var ( - templ = t.main.prototypeTextClone.New(overlay.name) + templ = t.main.getPrototypeText(prototypeCloneIDBaseof).New(overlay.name) err error ) @@ -713,7 +716,7 @@ func (t *templateHandler) applyBaseTemplate(overlay, base templateInfo) (tpl.Tem } var ( - templ = t.main.prototypeHTMLClone.New(overlay.name) + templ = t.main.getPrototypeHTML(prototypeCloneIDBaseof).New(overlay.name) err error ) @@ -953,27 +956,37 @@ func (t *templateHandler) postTransform() error { return nil } +type prototypeCloneID uint16 + +const ( + prototypeCloneIDBaseof prototypeCloneID = iota + 1 + prototypeCloneIDDefer +) + type templateNamespace struct { - prototypeText *texttemplate.Template - prototypeHTML *htmltemplate.Template - prototypeTextClone *texttemplate.Template - prototypeHTMLClone *htmltemplate.Template + prototypeText *texttemplate.Template + prototypeHTML *htmltemplate.Template + + prototypeHTMLCloneCache *maps.Cache[prototypeCloneID, *htmltemplate.Template] + prototypeTextCloneCache *maps.Cache[prototypeCloneID, *texttemplate.Template] *templateStateMap } -func (t *templateNamespace) getPrototypeText() *texttemplate.Template { - if t.prototypeTextClone != nil { - return t.prototypeTextClone +func (t *templateNamespace) getPrototypeText(id prototypeCloneID) *texttemplate.Template { + v, ok := t.prototypeTextCloneCache.Get(id) + if !ok { + return t.prototypeText } - return t.prototypeText + return v } -func (t *templateNamespace) getPrototypeHTML() *htmltemplate.Template { - if t.prototypeHTMLClone != nil { - return t.prototypeHTMLClone +func (t *templateNamespace) getPrototypeHTML(id prototypeCloneID) *htmltemplate.Template { + v, ok := t.prototypeHTMLCloneCache.Get(id) + if !ok { + return t.prototypeHTML } - return t.prototypeHTML + return v } func (t *templateNamespace) Lookup(name string) (tpl.Template, bool) { @@ -989,9 +1002,10 @@ func (t *templateNamespace) Lookup(name string) (tpl.Template, bool) { } func (t *templateNamespace) createPrototypes() error { - t.prototypeTextClone = texttemplate.Must(t.prototypeText.Clone()) - t.prototypeHTMLClone = htmltemplate.Must(t.prototypeHTML.Clone()) - + for _, id := range []prototypeCloneID{prototypeCloneIDBaseof, prototypeCloneIDDefer} { + t.prototypeHTMLCloneCache.Set(id, htmltemplate.Must(t.prototypeHTML.Clone())) + t.prototypeTextCloneCache.Set(id, texttemplate.Must(t.prototypeText.Clone())) + } return nil } @@ -1021,7 +1035,7 @@ func (t *templateNamespace) addDeferredTemplate(owner *templateState, name strin var templ tpl.Template if owner.isText() { - prototype := t.getPrototypeText() + prototype := t.getPrototypeText(prototypeCloneIDDefer) tt, err := prototype.New(name).Parse("") if err != nil { return fmt.Errorf("failed to parse empty text template %q: %w", name, err) @@ -1029,7 +1043,7 @@ func (t *templateNamespace) addDeferredTemplate(owner *templateState, name strin tt.Tree.Root = n templ = tt } else { - prototype := t.getPrototypeHTML() + prototype := t.getPrototypeHTML(prototypeCloneIDDefer) tt, err := prototype.New(name).Parse("") if err != nil { return fmt.Errorf("failed to parse empty HTML template %q: %w", name, err) diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index 1e7aa3111..dbadece4e 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -649,3 +649,52 @@ E: An _emphasized_ word. "
\n Details\n

D: An emphasized word.

\n
", ) } + +// Issue 12963 +func TestEditBaseofParseAfterExecute(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +disableKinds = ["taxonomy", "term", "rss", "404", "sitemap"] +[internal] +fastRenderMode = true +-- layouts/_default/baseof.html -- +Baseof! +{{ block "main" . }}default{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Now. {{ now }} +{{ end }} +-- layouts/_default/single.html -- +{{ define "main" }} +Single. +{{ end }} +-- layouts/_default/list.html -- +{{ define "main" }} +List. +{{ .Content }} +{{ range .Pages }}{{ .Title }}{{ end }}| +{{ end }} +-- content/mybundle1/index.md -- +--- +title: "My Bundle 1" +--- +-- content/mybundle2/index.md -- +--- +title: "My Bundle 2" +--- +-- content/_index.md -- +--- +title: "Home" +--- +Home! +` + + b := hugolib.TestRunning(t, files) + b.AssertFileContent("public/index.html", "Home!") + b.EditFileReplaceAll("layouts/_default/baseof.html", "Baseof", "Baseof!").Build() + b.BuildPartial("/") + b.AssertFileContent("public/index.html", "Baseof!!") + b.BuildPartial("/mybundle1/") + b.AssertFileContent("public/mybundle1/index.html", "Baseof!!") +} From 5c80cb0d208134437c8446698e3a18c8381cfe72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 17 Dec 2024 14:35:13 +0100 Subject: [PATCH 026/374] js/esbuild: Add missing es2024 target --- internal/js/esbuild/options.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/js/esbuild/options.go b/internal/js/esbuild/options.go index 21509bc15..5ea2bcea9 100644 --- a/internal/js/esbuild/options.go +++ b/internal/js/esbuild/options.go @@ -45,6 +45,7 @@ var ( "es2021": api.ES2021, "es2022": api.ES2022, "es2023": api.ES2023, + "es2024": api.ES2024, } // source names: https://github.com/evanw/esbuild/blob/9eca46464ed5615cb36a3beb3f7a7b9a8ffbe7cf/internal/config/config.go#L208 From 3f35721fb2c75a1f7cc5a7a14400b66e73d4b06e Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Tue, 17 Dec 2024 14:20:55 +0000 Subject: [PATCH 027/374] releaser: Bump versions for release of 0.140.0 [ci skip] --- common/hugo/version_current.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 3b814c2ed..f065171b9 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 139, - PatchLevel: 4, + Minor: 140, + PatchLevel: 0, Suffix: "", } From 55ecd3a90e37b8d14651789a582d3b60c7dc4bd1 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Tue, 17 Dec 2024 14:37:51 +0000 Subject: [PATCH 028/374] releaser: Prepare repository for 0.141.0-DEV [ci skip] --- common/hugo/version_current.go | 4 ++-- hugoreleaser.env | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index f065171b9..c3ef7c5f8 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 140, + Minor: 141, PatchLevel: 0, - Suffix: "", + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index 4c8ed54a6..c7adad805 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.139.3 -HUGORELEASER_COMMITISH=2f6864387cd31b975914e8373d4bf38bddbd47bc +HUGORELEASER_TAG=v0.140.0 +HUGORELEASER_COMMITISH=3f35721fb2c75a1f7cc5a7a14400b66e73d4b06e + From b3f32949cb089ae686af8548161fafc50869214b Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 18 Dec 2024 11:41:47 -0800 Subject: [PATCH 029/374] hugolib: Fix fallbacks for menu entry Name and Title Closes #13161 --- hugolib/menu_test.go | 94 +++++++++++++++++++++++++++++++++++++++----- hugolib/site.go | 2 + 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go index 6ee62771b..3be999c31 100644 --- a/hugolib/menu_test.go +++ b/hugolib/menu_test.go @@ -252,9 +252,9 @@ menu: `) - b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|Children: + b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|Children: {{- $children := sort .Children ".Page.Date" "desc" }}{{ range $children }}{{ .Title }}|{{ end }}{{ end }} - + `) b.Build(BuildCfg{}) @@ -272,11 +272,11 @@ func TestMenuParamsEmptyYaml(t *testing.T) { b.WithContent("p1.md", `--- menus: - main: + main: identity: journal weight: 2 params: ---- +--- `) b.Build(BuildCfg{}) } @@ -289,9 +289,9 @@ title = "Contact Us" url = "mailto:noreply@example.com" weight = 300 [menus.main.params] -foo = "foo_config" -key2 = "key2_config" -camelCase = "camelCase_config" +foo = "foo_config" +key2 = "key2_config" +camelCase = "camelCase_config" `) b.WithTemplatesAdded("index.html", ` @@ -343,7 +343,7 @@ weight = 1 pageRef = "/blog/post3" title = "My Post 3" url = "/blog/post3" - + `) commonTempl := ` @@ -519,7 +519,7 @@ Menu Item: {{ $i }}: {{ .Pre }}{{ .Name }}{{ .Post }}|{{ .URL }}| b := Test(t, files) b.AssertFileContent("public/index.html", ` -Menu Item: 0: Home|/| +Menu Item: 0: Home|/| `) } @@ -622,3 +622,79 @@ title: p3 b.AssertFileExists("public/index.html", true) b.AssertFileContent("public/index.html", `p2s1`) } + +// Issue 13161 +func TestMenuNameAndTitleFallback(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','sitemap','taxonomy','term'] +[[menus.main]] +name = 'P1_ME_Name' +title = 'P1_ME_Title' +pageRef = '/p1' +weight = 10 +[[menus.main]] +pageRef = '/p2' +weight = 20 +[[menus.main]] +pageRef = '/p3' +weight = 30 +[[menus.main]] +name = 'S1_ME_Name' +title = 'S1_ME_Title' +pageRef = '/s1' +weight = 40 +[[menus.main]] +pageRef = '/s2' +weight = 50 +[[menus.main]] +pageRef = '/s3' +weight = 60 +-- content/p1.md -- +--- +title: P1_Title +--- +-- content/p2.md -- +--- +title: P2_Title +--- +-- content/p3.md -- +--- +title: P3_Title +linkTitle: P3_LinkTitle +--- +-- content/s1/_index.md -- +--- +title: S1_Title +--- +-- content/s2/_index.md -- +--- +title: S2_Title +--- +-- content/s3/_index.md -- +--- +title: S3_Title +linkTitle: S3_LinkTitle +--- +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/list.html -- +{{ .Content }} +-- layouts/_default/home.html -- +{{- range site.Menus.main }} +URL: {{ .URL }}| Name: {{ .Name }}| Title: {{ .Title }}| PageRef: {{ .PageRef }}| Page.Title: {{ .Page.Title }}| Page.LinkTitle: {{ .Page.LinkTitle }}| +{{- end }} +` + + b := Test(t, files) + b.AssertFileContent("public/index.html", + `URL: /p1/| Name: P1_ME_Name| Title: P1_ME_Title| PageRef: /p1| Page.Title: P1_Title| Page.LinkTitle: P1_Title|`, + `URL: /p2/| Name: P2_Title| Title: P2_Title| PageRef: /p2| Page.Title: P2_Title| Page.LinkTitle: P2_Title|`, + `URL: /p3/| Name: P3_LinkTitle| Title: P3_Title| PageRef: /p3| Page.Title: P3_Title| Page.LinkTitle: P3_LinkTitle|`, + `URL: /s1/| Name: S1_ME_Name| Title: S1_ME_Title| PageRef: /s1| Page.Title: S1_Title| Page.LinkTitle: S1_Title|`, + `URL: /s2/| Name: S2_Title| Title: S2_Title| PageRef: /s2| Page.Title: S2_Title| Page.LinkTitle: S2_Title|`, + `URL: /s3/| Name: S3_LinkTitle| Title: S3_Title| PageRef: /s3| Page.Title: S3_Title| Page.LinkTitle: S3_LinkTitle|`, + ) +} diff --git a/hugolib/site.go b/hugolib/site.go index 4e2497ee1..ebe4a771b 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -1224,6 +1224,8 @@ func (s *Site) assembleMenus() error { // If page is still nill, we must make sure that we have a URL that considers baseURL etc. if types.IsNil(me.Page) { me.ConfiguredURL = s.createNodeMenuEntryURL(me.MenuConfig.URL) + } else { + navigation.SetPageValues(me, me.Page) } flat[twoD{name, me.KeyName()}] = me From 5d64b492f41ca9f805948f33b47672780f9cea7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:33:53 +0000 Subject: [PATCH 030/374] build(deps): bump github.com/spf13/cast from 1.7.0 to 1.7.1 Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/spf13/cast/releases) - [Commits](https://github.com/spf13/cast/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: github.com/spf13/cast dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c2582e28a..335d836a4 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/rogpeppe/go-internal v1.13.1 github.com/sanity-io/litter v1.5.5 github.com/spf13/afero v1.11.0 - github.com/spf13/cast v1.7.0 + github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.8.1 github.com/spf13/fsync v0.10.1 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index d393396e2..4f01f67b0 100644 --- a/go.sum +++ b/go.sum @@ -424,8 +424,8 @@ github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2h github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE= From 6c583e322759228d65c4a7576515e19b43f67bc2 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Thu, 19 Dec 2024 08:44:11 -0800 Subject: [PATCH 031/374] common/loggers: Write PrintTimerIfDelayed output to stdErr Closes #13171 --- common/loggers/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/loggers/logger.go b/common/loggers/logger.go index 77a21458a..a013049f7 100644 --- a/common/loggers/logger.go +++ b/common/loggers/logger.go @@ -275,7 +275,7 @@ func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) { if milli < 500 { return } - l.Printf("%s in %v ms", name, milli) + fmt.Fprintf(l.stdErr, "%s in %v ms", name, milli) } func (l *logAdapter) Printf(format string, v ...any) { From 48a7aee961de83ce5ee1b9ada06567878665a795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 22 Dec 2024 16:55:52 +0100 Subject: [PATCH 032/374] release: Add withdeploy deb extended archives Also refactor and get the config up to date for new version of Hugoreleaser. Closes #13166 --- .circleci/config.yml | 4 +- hugoreleaser.toml | 307 ------------------------------------------- hugoreleaser.yaml | 272 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 309 deletions(-) delete mode 100644 hugoreleaser.toml create mode 100644 hugoreleaser.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 849d08ad4..f7a47a64b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ parameters: defaults: &defaults resource_class: large docker: - - image: bepsays/ci-hugoreleaser:1.22300.20400 + - image: bepsays/ci-hugoreleaser:1.22301.20401 environment: &buildenv GOMODCACHE: /root/project/gomodcache version: 2 @@ -58,7 +58,7 @@ jobs: environment: <<: [*buildenv] docker: - - image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20400 + - image: bepsays/ci-hugoreleaser-linux-arm64:1.22301.20401 steps: - *restore-cache - &attach-workspace diff --git a/hugoreleaser.toml b/hugoreleaser.toml deleted file mode 100644 index c5b7910bd..000000000 --- a/hugoreleaser.toml +++ /dev/null @@ -1,307 +0,0 @@ -project = "hugo" - -# In Hugo v0.103.0 we removed the archive name replacements (e.g. amd64 => 64bit). -# Using standard GOOS/GOARCH values makes it easier for scripts out there, -# but to prevent breakage in Netlify etc. that has adopted to the old names, -# we create aliases for the most common variants. -# According to download numbers from v0.101.0, these are by a good margin the two most popular: -# hugo_extended_0.101.0_Linux-64bit.tar.gz Downloaded 129,016 times -# hugo_0.101.0_Linux-64bit.tar.gz Downloaded 87,846 times -# This replacement will create 2 extra alias archives. -archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" } - -[go_settings] - go_proxy = "https://proxy.golang.org" - go_exe = "go" - -[build_settings] - binary = "hugo" - flags = ["-buildmode", "exe"] - env = ["CGO_ENABLED=0"] - ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio" - -[archive_settings] - name_template = "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - extra_files = [ - { source_path = "README.md", target_path = "README.md" }, - { source_path = "LICENSE", target_path = "LICENSE" }, - ] - [archive_settings.type] - format = "tar.gz" - extension = ".tar.gz" - -[release_settings] - name = "${HUGORELEASER_TAG}" - type = "github" - repository = "hugo" - repository_owner = "gohugoio" - draft = true - prerelease = false - - [release_settings.release_notes_settings] - # Use Hugoreleaser's autogenerated release notes. - generate = true - - # Collapse releases with < 10 changes below one title. - short_threshold = 10 - short_title = "What's Changed" - - groups = [ - # Group the changes in the release notes by title. - # You need at least one. - # The groups will be tested in order until a match is found. - # The titles will so be listed in the given order in the release note. - # Any match with ignore=true title will be dropped. - { regexp = "Merge commit|Squashed|releaser:", ignore = true }, - { title = "Note", regexp = "(note|deprecated)", ordinal = 10 }, - { title = "Bug fixes", regexp = "fix", ordinal = 15 }, - { title = "Dependency Updates", regexp = "deps", ordinal = 30 }, - { title = "Build Setup", regexp = "(snap|release|update to)", ordinal = 40 }, - { title = "Documentation", regexp = "(doc|readme)", ordinal = 40 }, - { title = "Improvements", regexp = ".*", ordinal = 20 }, - ] - -[[builds]] - path = "container1/unix/regular" - - [[builds.os]] - goos = "darwin" - [[builds.os.archs]] - goarch = "universal" - [[builds.os]] - goos = "linux" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os.archs]] - goarch = "arm64" - [[builds.os.archs]] - goarch = "arm" - [builds.os.archs.build_settings] - env = ["CGO_ENABLED=0", "GOARM=7"] - - # Unix BSD variants - [[builds.os]] - goos = "dragonfly" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "freebsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "netbsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "openbsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "solaris" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container1/unix/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - env = ["CGO_ENABLED=1"] - - [[builds.os]] - goos = "darwin" - [builds.os.build_settings] - env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"] - [[builds.os.archs]] - goarch = "universal" - [[builds.os]] - goos = "linux" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container1/unix/extended-withdeploy" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended,withdeploy"] - env = ["CGO_ENABLED=1"] - - [[builds.os]] - goos = "darwin" - [builds.os.build_settings] - env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"] - [[builds.os.archs]] - goarch = "universal" - [[builds.os]] - goos = "linux" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container2/linux/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - - [[builds.os]] - goos = "linux" - [builds.os.build_settings] - env = [ - "CGO_ENABLED=1", - "CC=aarch64-linux-gnu-gcc", - "CXX=aarch64-linux-gnu-g++", - ] - [[builds.os.archs]] - goarch = "arm64" - -[[builds]] - path = "container2/linux/extended-withdeploy" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended,withdeploy"] - - [[builds.os]] - goos = "linux" - [builds.os.build_settings] - env = [ - "CGO_ENABLED=1", - "CC=aarch64-linux-gnu-gcc", - "CXX=aarch64-linux-gnu-g++", - ] - [[builds.os.archs]] - goarch = "arm64" - -[[builds]] - path = "container1/windows/regular" - - [[builds.os]] - goos = "windows" - [builds.os.build_settings] - binary = "hugo.exe" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os.archs]] - goarch = "arm64" - -[[builds]] - path = "container1/windows/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - env = [ - "CGO_ENABLED=1", - "CC=x86_64-w64-mingw32-gcc", - "CXX=x86_64-w64-mingw32-g++", - ] - ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'" - - [[builds.os]] - goos = "windows" - [builds.os.build_settings] - binary = "hugo.exe" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container1/windows/extended-withdeploy" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended,withdeploy"] - env = [ - "CGO_ENABLED=1", - "CC=x86_64-w64-mingw32-gcc", - "CXX=x86_64-w64-mingw32-g++", - ] - ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'" - - [[builds.os]] - goos = "windows" - [builds.os.build_settings] - binary = "hugo.exe" - [[builds.os.archs]] - goarch = "amd64" - -[[archives]] - paths = ["builds/container1/unix/regular/**"] -[[archives]] - paths = ["builds/container1/unix/extended/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - paths = ["builds/container1/unix/extended-withdeploy/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - paths = ["builds/container2/*/extended/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - paths = ["builds/container2/*/extended-withdeploy/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - paths = ["builds/**/windows/regular/**"] - [archives.archive_settings.type] - format = "zip" - extension = ".zip" -[[archives]] - paths = ["builds/**/windows/extended/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - [archives.archive_settings.type] - format = "zip" - extension = ".zip" -[[archives]] - paths = ["builds/**/windows/extended-withdeploy/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - [archives.archive_settings.type] - format = "zip" - extension = ".zip" -[[archives]] - paths = ["builds/**/regular/linux/{arm64,amd64}"] - [archives.archive_settings] - binary_dir = "/usr/local/bin" - extra_files = [] - [archives.archive_settings.type] - format = "_plugin" - extension = ".deb" - [archives.archive_settings.plugin] - id = "deb" - type = "gorun" - command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@v0.6.1" - [archives.archive_settings.custom_settings] - vendor = "gohugo.io" - homepage = "https://github.com/gohugoio/hugo" - maintainer = "Bjørn Erik Pedersen " - description = "A fast and flexible Static Site Generator written in Go." - license = "Apache-2.0" -[[archives]] - paths = ["builds/**/extended/linux/{arm64,amd64}"] - [archives.archive_settings] - binary_dir = "/usr/local/bin" - extra_files = [] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - [archives.archive_settings.type] - format = "_plugin" - extension = ".deb" - [archives.archive_settings.plugin] - id = "deb" - type = "gorun" - command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest" - [archives.archive_settings.custom_settings] - vendor = "gohugo.io" - homepage = "https://github.com/gohugoio/hugo" - maintainer = "Bjørn Erik Pedersen " - description = "A fast and flexible Static Site Generator written in Go." - license = "Apache-2.0" - -[[releases]] - paths = ["archives/**"] - path = "r1" - - # The above should allow the following build commands: - # hugoreleaser build -paths "builds/container1/**" - # hugoreleaser build -paths "builds/container2/**" - # hugoreleaser archive - # hugoreleaser release diff --git a/hugoreleaser.yaml b/hugoreleaser.yaml new file mode 100644 index 000000000..368bc898f --- /dev/null +++ b/hugoreleaser.yaml @@ -0,0 +1,272 @@ +project: hugo + +# Common definitions. +definitions: + archive_type_zip: &archive_type_zip + type: + format: zip + extension: .zip + env_extended_linux: &env_extended_linux + - CGO_ENABLED=1 + - CC=aarch64-linux-gnu-gcc + - CXX=aarch64-linux-gnu-g++ + env_extended_windows: &env_extended_windows + - CGO_ENABLED=1 + - CC=x86_64-w64-mingw32-gcc + - CXX=x86_64-w64-mingw32-g++ + env_extended_darwin: &env_extended_darwin + - CGO_ENABLED=1 + - CC=o64-clang + - CXX=o64-clang++ + name_template_extended_withdeploy: &name_template_extended_withdeploy "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + name_template_extended: &name_template_extended "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + archive_deb: &archive_deb + binary_dir: /usr/local/bin + extra_files: [] + type: + format: _plugin + extension: .deb + plugin: + id: deb + type: gorun + command: github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest + custom_settings: + vendor: gohugo.io + homepage: https://github.com/gohugoio/hugo + maintainer: Bjørn Erik Pedersen + description: A fast and flexible Static Site Generator written in Go. + license: Apache-2.0 +archive_alias_replacements: + linux-amd64.tar.gz: Linux-64bit.tar.gz +go_settings: + go_proxy: https://proxy.golang.org + go_exe: go +build_settings: + binary: hugo + flags: + - -buildmode + - exe + env: + - CGO_ENABLED=0 + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio +archive_settings: + name_template: "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + extra_files: + - source_path: README.md + target_path: README.md + - source_path: LICENSE + target_path: LICENSE + type: + format: tar.gz + extension: .tar.gz +release_settings: + name: ${HUGORELEASER_TAG} + type: github + repository: hugo + repository_owner: gohugoio + draft: true + prerelease: false + release_notes_settings: + generate: true + short_threshold: 10 + short_title: What's Changed + groups: + - regexp: "Merge commit|Squashed|releaser:" + ignore: true + - title: Note + regexp: (note|deprecated) + ordinal: 10 + - title: Bug fixes + regexp: fix + ordinal: 15 + - title: Dependency Updates + regexp: deps + ordinal: 30 + - title: Build Setup + regexp: (snap|release|update to) + ordinal: 40 + - title: Documentation + regexp: (doc|readme) + ordinal: 40 + - title: Improvements + regexp: .* + ordinal: 20 +builds: + - path: container1/unix/regular + os: + - goos: darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - goarch: arm64 + - goarch: arm + build_settings: + env: + - CGO_ENABLED=0 + - GOARM=7 + - goos: dragonfly + archs: + - goarch: amd64 + - goos: freebsd + archs: + - goarch: amd64 + - goos: netbsd + archs: + - goarch: amd64 + - goos: openbsd + archs: + - goarch: amd64 + - goos: solaris + archs: + - goarch: amd64 + - path: container1/unix/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + env: + - CGO_ENABLED=1 + os: + - goos: darwin + build_settings: + env: *env_extended_darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - path: container1/unix/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + env: + - CGO_ENABLED=1 + os: + - goos: darwin + build_settings: + env: *env_extended_darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - path: container2/linux/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + os: + - goos: linux + build_settings: + env: *env_extended_linux + archs: + - goarch: arm64 + - path: container2/linux/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + os: + - goos: linux + build_settings: + env: *env_extended_linux + archs: + - goarch: arm64 + - path: container1/windows/regular + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 + - goarch: arm64 + - path: container1/windows/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + env: *env_extended_windows + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static' + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 + - path: container1/windows/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + env: *env_extended_windows + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static' + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 +archives: + - paths: + - builds/container1/unix/regular/** + - paths: + - builds/container1/unix/extended/** + archive_settings: + name_template: *name_template_extended + - paths: + - builds/container1/unix/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + - paths: + - builds/container2/*/extended/** + archive_settings: + name_template: *name_template_extended + - paths: + - builds/container2/*/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + - paths: + - builds/**/windows/regular/** + archive_settings: *archive_type_zip + - paths: + - builds/**/windows/extended/** + archive_settings: + name_template: *name_template_extended + <<: *archive_type_zip + - paths: + - builds/**/windows/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + <<: *archive_type_zip + - paths: + - builds/**/regular/linux/{arm64,amd64} + archive_settings: *archive_deb + - paths: + - builds/**/extended/linux/{arm64,amd64} + archive_settings: + name_template: *name_template_extended + <<: *archive_deb + - paths: + - builds/**/extended-withdeploy/linux/{arm64,amd64} + archive_settings: + name_template: *name_template_extended_withdeploy + <<: *archive_deb +releases: + - paths: + - archives/** + path: r1 From 4a5e94087ba2fe1405ad2edbcbeeccac2a6147a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 22 Dec 2024 17:59:03 +0100 Subject: [PATCH 033/374] Fix union, complement, symdiff, and intersect for transient resources Fixes #13181 --- resources/resource.go | 32 ++++++++++++------- resources/resource/resourcetypes.go | 10 +++++- resources/resource_spec.go | 1 + resources/transform.go | 6 ++++ .../collections_integration_test.go | 29 +++++++++++++++++ tpl/collections/reflect_helpers.go | 12 +++++-- 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/resources/resource.go b/resources/resource.go index 4b81a478a..7ab10b0ae 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -47,6 +47,7 @@ var ( _ resource.Cloner = (*genericResource)(nil) _ resource.ResourcesLanguageMerger = (*resource.Resources)(nil) _ resource.Identifier = (*genericResource)(nil) + _ resource.TransientIdentifier = (*genericResource)(nil) _ targetPathProvider = (*genericResource)(nil) _ sourcePathProvider = (*genericResource)(nil) _ identity.IdentityGroupProvider = (*genericResource)(nil) @@ -359,6 +360,9 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo { type genericResource struct { publishInit *sync.Once + key string + keyInit *sync.Once + sd ResourceSourceDescriptor paths internal.ResourcePaths @@ -444,19 +448,24 @@ func (l *genericResource) Data() any { } func (l *genericResource) Key() string { - basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash - var key string - if basePath == "" { - key = l.RelPermalink() - } else { - key = strings.TrimPrefix(l.RelPermalink(), basePath) - } + l.keyInit.Do(func() { + basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash + if basePath == "" { + l.key = l.RelPermalink() + } else { + l.key = strings.TrimPrefix(l.RelPermalink(), basePath) + } - if l.spec.Cfg.IsMultihost() { - key = l.spec.Lang() + key - } + if l.spec.Cfg.IsMultihost() { + l.key = l.spec.Lang() + l.key + } + }) - return key + return l.key +} + +func (l *genericResource) TransientKey() string { + return l.Key() } func (l *genericResource) targetPath() string { @@ -623,6 +632,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour func (l genericResource) clone() *genericResource { l.publishInit = &sync.Once{} + l.keyInit = &sync.Once{} return &l } diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go index 0fb87f371..b33750e80 100644 --- a/resources/resource/resourcetypes.go +++ b/resources/resource/resourcetypes.go @@ -170,11 +170,19 @@ type ResourcesLanguageMerger interface { // Identifier identifies a resource. type Identifier interface { - // Key is is mostly for internal use and should be considered opaque. + // Key is mostly for internal use and should be considered opaque. // This value may change between Hugo versions. Key() string } +// TransientIdentifier identifies a transient resource. +type TransientIdentifier interface { + // TransientKey is mostly for internal use and should be considered opaque. + // This value is implemented by transient resources where pointers may be short lived and + // not suitable for use as a map keys. + TransientKey() string +} + // WeightProvider provides a weight. type WeightProvider interface { Weight() int diff --git a/resources/resource_spec.go b/resources/resource_spec.go index d50edeb73..912a0d786 100644 --- a/resources/resource_spec.go +++ b/resources/resource_spec.go @@ -187,6 +187,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro Staler: &AtomicStaler{}, h: &resourceHash{}, publishInit: &sync.Once{}, + keyInit: &sync.Once{}, paths: rp, spec: r, sd: rd, diff --git a/resources/transform.go b/resources/transform.go index 4214067bd..c5d240669 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -52,8 +52,10 @@ var ( _ identity.IdentityGroupProvider = (*resourceAdapterInner)(nil) _ resource.Source = (*resourceAdapter)(nil) _ resource.Identifier = (*resourceAdapter)(nil) + _ resource.TransientIdentifier = (*resourceAdapter)(nil) _ targetPathProvider = (*resourceAdapter)(nil) _ sourcePathProvider = (*resourceAdapter)(nil) + _ resource.Identifier = (*resourceAdapter)(nil) _ resource.ResourceNameTitleProvider = (*resourceAdapter)(nil) _ resource.WithResourceMetaProvider = (*resourceAdapter)(nil) _ identity.DependencyManagerProvider = (*resourceAdapter)(nil) @@ -279,6 +281,10 @@ func (r *resourceAdapter) Key() string { return r.target.(resource.Identifier).Key() } +func (r *resourceAdapter) TransientKey() string { + return r.Key() +} + func (r *resourceAdapter) targetPath() string { r.init(false, false) return r.target.(targetPathProvider).targetPath() diff --git a/tpl/collections/collections_integration_test.go b/tpl/collections/collections_integration_test.go index e39493b52..2aabee03e 100644 --- a/tpl/collections/collections_integration_test.go +++ b/tpl/collections/collections_integration_test.go @@ -249,3 +249,32 @@ tags: ['tag-b'] "2: Intersect: 1|\n2: Union: 3|\n2: SymDiff: 2|\n2: Uniq: 3|", ) } + +// Issue #13181 +func TestUnionResourcesMatch(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +disableKinds = ['rss','sitemap', 'taxonomy', 'term', 'page'] +-- layouts/index.html -- +{{ $a := resources.Match "*a*" }} +{{ $b := resources.Match "*b*" }} +{{ $union := $a | union $b }} +{{ range $i, $e := $union }} +{{ $i }}: {{ .Name }} +{{ end }}$ +-- assets/a1.html -- +
file1
+-- assets/a2.html -- +
file2
+-- assets/a3_b1.html -- +
file3
+-- assets/b2.html -- +
file4
+` + + b := hugolib.Test(t, files) + + b.AssertFileContentExact("public/index.html", "0: /a3_b1.html\n\n1: /b2.html\n\n2: /a1.html\n\n3: /a2.html\n$") +} diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go index 4b222be15..6b986cbc4 100644 --- a/tpl/collections/reflect_helpers.go +++ b/tpl/collections/reflect_helpers.go @@ -20,11 +20,12 @@ import ( "github.com/gohugoio/hugo/common/hashing" "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/resources/resource" ) var ( zero reflect.Value - errorType = reflect.TypeOf((*error)(nil)).Elem() + errorType = reflect.TypeFor[error]() ) func numberToFloat(v reflect.Value) (float64, error) { @@ -56,7 +57,13 @@ func normalize(v reflect.Value) any { return f } } - return types.Unwrapv(v.Interface()) + + vv := types.Unwrapv(v.Interface()) + if ip, ok := vv.(resource.TransientIdentifier); ok { + return ip.TransientKey() + } + + return vv } // collects identities from the slices in seqs into a set. Numeric values are normalized, @@ -151,7 +158,6 @@ func convertNumber(v reflect.Value, to reflect.Kind) (reflect.Value, error) { case reflect.Uint64: n = reflect.ValueOf(uint64(i)) } - } if !n.IsValid() { From 020253904f335643ead1c390f9fa52f24b185a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 22 Dec 2024 20:46:19 +0100 Subject: [PATCH 034/374] js/esbuild: Don't try to resolve packages in /assets marked as external Fixes #13183 --- internal/js/esbuild/resolve.go | 11 +++++++ .../js/js_integration_test.go | 29 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/internal/js/esbuild/resolve.go b/internal/js/esbuild/resolve.go index ac0010da9..8ceec97ef 100644 --- a/internal/js/esbuild/resolve.go +++ b/internal/js/esbuild/resolve.go @@ -167,6 +167,17 @@ func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsMana } } + for _, ext := range opts.Externals { + // ESBuild will do a more thorough check for packages resolved in node_modules, + // but we need to make sure that we don't try to resolve these in the /assets folder. + if ext == impPath { + return api.OnResolveResult{ + Path: impPath, + External: true, + }, nil + } + } + if opts.ImportOnResolveFunc != nil { if s := opts.ImportOnResolveFunc(impPath, args); s != "" { return api.OnResolveResult{Path: s, Namespace: NsHugoImportResolveFunc}, nil diff --git a/resources/resource_transformers/js/js_integration_test.go b/resources/resource_transformers/js/js_integration_test.go index c62312ef5..9cee19a86 100644 --- a/resources/resource_transformers/js/js_integration_test.go +++ b/resources/resource_transformers/js/js_integration_test.go @@ -391,3 +391,32 @@ class A {} }).Build() b.AssertFileContent("public/js/main.js", "__decorateClass") } + +// Issue 13183. +func TestExternalsInAssets(t *testing.T) { + files := ` +-- assets/js/util1.js -- +export function hello1() { + return 'abcd'; +} +-- assets/js/util2.js -- +export function hello2() { + return 'efgh'; +} +-- assets/js/main.js -- +import { hello1 } from './util1.js'; +import { hello2 } from './util2.js'; + +hello1(); +hello2(); +-- layouts/index.html -- +Home. +{{ $js := resources.Get "js/main.js" | js.Build (dict "externals" (slice "./util1.js")) }} +{{ $js.Publish }} +` + + b := hugolib.Test(t, files, hugolib.TestOptOsFs()) + + b.AssertFileContent("public/js/main.js", "efgh") + b.AssertFileContent("public/js/main.js", "! abcd") +} From e229f4b38728fcde2172857c54fcdfdff15bf471 Mon Sep 17 00:00:00 2001 From: Chris Moultrie <821688+tebriel@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:12:29 -0500 Subject: [PATCH 035/374] Update gocloud and docs for S3-Compatible Endpoints --- docs/content/en/hosting-and-deployment/hugo-deploy.md | 4 +++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/content/en/hosting-and-deployment/hugo-deploy.md b/docs/content/en/hosting-and-deployment/hugo-deploy.md index a7925a666..ea42da39f 100644 --- a/docs/content/en/hosting-and-deployment/hugo-deploy.md +++ b/docs/content/en/hosting-and-deployment/hugo-deploy.md @@ -55,9 +55,11 @@ URL = "" #URL = "gs://" # Amazon Web Services S3; see https://gocloud.dev/howto/blob/#s3 -# For S3-compatible endpoints, see https://gocloud.dev/howto/blob/#s3-compatible #URL = "s3://?region=" +# For S3-compatible endpoints, see https://gocloud.dev/howto/blob/#s3-compatible +#URL = "s3://?endpoint=https://my.minio.instance&awssdk=v2&use_path_style=true&disable_https=false + # Microsoft Azure Blob Storage; see https://gocloud.dev/howto/blob/#azure #URL = "azblob://$web" diff --git a/go.mod b/go.mod index 335d836a4..d5a4cd6d2 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark-emoji v1.0.4 go.uber.org/automaxprocs v1.5.3 - gocloud.dev v0.39.0 + gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 golang.org/x/image v0.22.0 golang.org/x/mod v0.22.0 diff --git a/go.sum b/go.sum index 4f01f67b0..3f39a7a46 100644 --- a/go.sum +++ b/go.sum @@ -489,8 +489,8 @@ go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt3 go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= -gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= +gocloud.dev v0.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng= +gocloud.dev v0.40.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= From a9b0b95ef402a1cc70bc5386d649d947e2bcc027 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Mon, 23 Dec 2024 16:26:35 +0000 Subject: [PATCH 036/374] releaser: Bump versions for release of 0.140.1 [ci skip] --- common/hugo/version_current.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index c3ef7c5f8..205a8bca5 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 141, - PatchLevel: 0, - Suffix: "-DEV", + Minor: 140, + PatchLevel: 1, + Suffix: "", } From 43385d6aebc51336cd88015574b0161824214f01 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Mon, 23 Dec 2024 16:40:37 +0000 Subject: [PATCH 037/374] releaser: Prepare repository for 0.141.0-DEV [ci skip] --- common/hugo/version_current.go | 6 +++--- hugoreleaser.env | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 205a8bca5..c3ef7c5f8 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 140, - PatchLevel: 1, - Suffix: "", + Minor: 141, + PatchLevel: 0, + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index c7adad805..1166fa627 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.140.0 -HUGORELEASER_COMMITISH=3f35721fb2c75a1f7cc5a7a14400b66e73d4b06e +HUGORELEASER_TAG=v0.140.1 +HUGORELEASER_COMMITISH=a9b0b95ef402a1cc70bc5386d649d947e2bcc027 + From 845b8885def02b1e52f5e03fb5d9503248d5022b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 23 Dec 2024 18:57:19 +0100 Subject: [PATCH 038/374] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7dfbca7c1..10ce6e200 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,6 @@ See the [features] section of the documentation for a comprehensive summary of H

Linode     - Route Planning & Route Optimization Software -     The complete IDE crafted for professional Go developers.

From ec0caaec7ce41ca6236da9d661e92dc433e1951b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 25 Dec 2024 18:20:16 +0100 Subject: [PATCH 039/374] markup/highlight: Add wrapperClass option The need comes from Tailwind's typography plugin. There's currently no way to turn that off outside of the markup, see https://github.com/tailwindlabs/tailwindcss-typography/pull/363 --- markup/highlight/config.go | 5 ++++ markup/highlight/highlight.go | 7 ++--- .../highlight/highlight_integration_test.go | 26 +++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/markup/highlight/config.go b/markup/highlight/config.go index 62db6b237..338ea77e5 100644 --- a/markup/highlight/config.go +++ b/markup/highlight/config.go @@ -45,13 +45,18 @@ var DefaultConfig = Config{ NoClasses: true, LineNumbersInTable: true, TabWidth: 4, + WrapperClass: "highlight", } type Config struct { Style string + // Enable syntax highlighting of fenced code blocks. CodeFences bool + // The class or classes to use for the outermost element of the highlighted code. + WrapperClass string + // Use inline CSS styles. NoClasses bool diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go index a284b5981..30f225c0b 100644 --- a/markup/highlight/highlight.go +++ b/markup/highlight/highlight.go @@ -202,7 +202,7 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes. } if !cfg.Hl_inline { - writeDivStart(w, attributes) + writeDivStart(w, attributes, cfg.WrapperClass) } options := cfg.toHTMLOptions() @@ -303,8 +303,9 @@ func (s startEnd) End(code bool) string { return s.end(code) } -func writeDivStart(w hugio.FlexiWriter, attrs []attributes.Attribute) { - w.WriteString(`
xəx `) } + +func TestHighlightClass(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.highlight] +noClasses = false +wrapperClass = "highlight no-prose" +-- content/_index.md -- +--- +title: home +--- +§§§go +xəx := 0 +§§§ +-- layouts/index.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", ` +
Date: Thu, 26 Dec 2024 13:47:10 +0100
Subject: [PATCH 040/374] Fix same resource file published more than once

This may still happen, though, in low memory situations or very big sites, but I'm not sure it's worth spending time on fixing that. Writing the same file more than once isn't harmful, the negative effect is the false path warning.
 We may find a way to detect that situation if this becomes a real problem.

Fixes #13164
---
 hugofs/rootmapping_fs.go                      |  3 +-
 resources/resource_cache.go                   | 12 +++++
 resources/resource_factories/create/create.go | 45 ++++++++-----------
 .../hugo__path-warnings_issue13164.txt        | 15 +++++++
 4 files changed, 48 insertions(+), 27 deletions(-)
 create mode 100644 testscripts/commands/hugo__path-warnings_issue13164.txt

diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go
index 02e541a05..388493174 100644
--- a/hugofs/rootmapping_fs.go
+++ b/hugofs/rootmapping_fs.go
@@ -311,12 +311,13 @@ func (fs *RootMappingFs) Open(name string) (afero.File, error) {
 
 // Stat returns the os.FileInfo structure describing a given file.  If there is
 // an error, it will be of type *os.PathError.
+// If multiple roots are found, the last one will be used.
 func (fs *RootMappingFs) Stat(name string) (os.FileInfo, error) {
 	fis, err := fs.doStat(name)
 	if err != nil {
 		return nil, err
 	}
-	return fis[0], nil
+	return fis[len(fis)-1], nil
 }
 
 type ComponentPath struct {
diff --git a/resources/resource_cache.go b/resources/resource_cache.go
index a3ba9aa26..898cd4c31 100644
--- a/resources/resource_cache.go
+++ b/resources/resource_cache.go
@@ -36,6 +36,11 @@ func newResourceCache(rs *Spec, memCache *dynacache.Cache) *ResourceCache {
 			"/res1",
 			dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
 		),
+		cacheResourceFile: dynacache.GetOrCreatePartition[string, resource.Resource](
+			memCache,
+			"/res2",
+			dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
+		),
 		CacheResourceRemote: dynacache.GetOrCreatePartition[string, resource.Resource](
 			memCache,
 			"/resr",
@@ -58,6 +63,7 @@ type ResourceCache struct {
 	sync.RWMutex
 
 	cacheResource               *dynacache.Partition[string, resource.Resource]
+	cacheResourceFile           *dynacache.Partition[string, resource.Resource]
 	CacheResourceRemote         *dynacache.Partition[string, resource.Resource]
 	cacheResources              *dynacache.Partition[string, resource.Resources]
 	cacheResourceTransformation *dynacache.Partition[string, *resourceAdapterInner]
@@ -79,6 +85,12 @@ func (c *ResourceCache) GetOrCreate(key string, f func() (resource.Resource, err
 	})
 }
 
+func (c *ResourceCache) GetOrCreateFile(key string, f func() (resource.Resource, error)) (resource.Resource, error) {
+	return c.cacheResourceFile.GetOrCreate(key, func(key string) (resource.Resource, error) {
+		return f()
+	})
+}
+
 func (c *ResourceCache) GetOrCreateResources(key string, f func() (resource.Resources, error)) (resource.Resources, error) {
 	return c.cacheResources.GetOrCreate(key, func(key string) (resource.Resources, error) {
 		return f()
diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go
index 7dd26f4c0..581c0a854 100644
--- a/resources/resource_factories/create/create.go
+++ b/resources/resource_factories/create/create.go
@@ -143,19 +143,7 @@ func (c *Client) Get(pathname string) (resource.Resource, error) {
 			return nil, err
 		}
 
-		meta := fi.(hugofs.FileMetaInfo).Meta()
-		pi := meta.PathInfo
-
-		return c.rs.NewResource(resources.ResourceSourceDescriptor{
-			LazyPublish: true,
-			OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
-				return c.rs.BaseFs.Assets.Fs.Open(filename)
-			},
-			Path:                 pi,
-			GroupIdentity:        pi,
-			TargetPath:           pathname,
-			SourceFilenameOrPath: meta.Filename,
-		})
+		return c.getOrCreateFileResource(fi.(hugofs.FileMetaInfo))
 	})
 }
 
@@ -181,6 +169,23 @@ func (c *Client) GetMatch(pattern string) (resource.Resource, error) {
 	return res[0], err
 }
 
+func (c *Client) getOrCreateFileResource(info hugofs.FileMetaInfo) (resource.Resource, error) {
+	meta := info.Meta()
+	return c.rs.ResourceCache.GetOrCreateFile(filepath.ToSlash(meta.Filename), func() (resource.Resource, error) {
+		return c.rs.NewResource(resources.ResourceSourceDescriptor{
+			LazyPublish: true,
+			OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
+				return meta.Open()
+			},
+			NameNormalized:       meta.PathInfo.Path(),
+			NameOriginal:         meta.PathInfo.Unnormalized().Path(),
+			GroupIdentity:        meta.PathInfo,
+			TargetPath:           meta.PathInfo.Unnormalized().Path(),
+			SourceFilenameOrPath: meta.Filename,
+		})
+	})
+}
+
 func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) bool, firstOnly bool) (resource.Resources, error) {
 	pattern = glob.NormalizePath(pattern)
 	partitions := glob.FilterGlobParts(strings.Split(pattern, "/"))
@@ -191,19 +196,7 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
 		var res resource.Resources
 
 		handle := func(info hugofs.FileMetaInfo) (bool, error) {
-			meta := info.Meta()
-
-			r, err := c.rs.NewResource(resources.ResourceSourceDescriptor{
-				LazyPublish: true,
-				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
-					return meta.Open()
-				},
-				NameNormalized:       meta.PathInfo.Path(),
-				NameOriginal:         meta.PathInfo.Unnormalized().Path(),
-				GroupIdentity:        meta.PathInfo,
-				TargetPath:           meta.PathInfo.Unnormalized().Path(),
-				SourceFilenameOrPath: meta.Filename,
-			})
+			r, err := c.getOrCreateFileResource(info)
 			if err != nil {
 				return true, err
 			}
diff --git a/testscripts/commands/hugo__path-warnings_issue13164.txt b/testscripts/commands/hugo__path-warnings_issue13164.txt
new file mode 100644
index 000000000..1342c287a
--- /dev/null
+++ b/testscripts/commands/hugo__path-warnings_issue13164.txt
@@ -0,0 +1,15 @@
+hugo --printPathWarnings
+
+! stderr 'Duplicate target paths'
+
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- assets/foo.txt --
+foo
+-- layouts/index.html --
+A: {{ (resources.Get "foo.txt").RelPermalink }}
+B: {{ (resources.GetMatch "foo.txt").RelPermalink }}
+C: {{ (index (resources.Match "foo.txt") 0).RelPermalink }}
+D: {{ (index (resources.ByType "text") 0).RelPermalink }}
+-- layouts/unused/single.html --
+{{ .Title }}

From eb1dbe070923f75332d22b56b44a921734e19f0d Mon Sep 17 00:00:00 2001
From: Joe Mooring 
Date: Sun, 29 Dec 2024 14:30:58 -0500
Subject: [PATCH 041/374] config/allconfig: Throw error when output format is
 not defined

Fixes #13199
---
 config/allconfig/allconfig.go                  |  2 +-
 config/allconfig/allconfig_integration_test.go | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index be4480f45..5fe6edcd1 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -734,7 +734,7 @@ func (c *Configs) Validate(logger loggers.Logger) error {
 
 // transientErr returns the last transient error found during config compilation.
 func (c *Configs) transientErr() error {
-	for _, l := range c.LanguageConfigSlice {
+	for _, l := range c.LanguageConfigMap {
 		if l.C.transientErr != nil {
 			return l.C.transientErr
 		}
diff --git a/config/allconfig/allconfig_integration_test.go b/config/allconfig/allconfig_integration_test.go
index 38e763b70..43d5261d0 100644
--- a/config/allconfig/allconfig_integration_test.go
+++ b/config/allconfig/allconfig_integration_test.go
@@ -175,3 +175,21 @@ func TestMapUglyURLs(t *testing.T) {
 	b.Assert(c.C.IsUglyURLSection("posts"), qt.IsTrue)
 	b.Assert(c.C.IsUglyURLSection("blog"), qt.IsFalse)
 }
+
+// Issue 13199
+func TestInvalidOutputFormat(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[outputs]
+home = ['html','foo']
+-- layouts/index.html --
+x
+`
+
+	b, err := hugolib.TestE(t, files)
+	b.Assert(err, qt.IsNotNil)
+	b.Assert(err.Error(), qt.Contains, `failed to create config: unknown output format "foo" for kind "home"`)
+}

From 7888ac585c7dedce6423104c071c9e086c6d49c4 Mon Sep 17 00:00:00 2001
From: Joe Mooring 
Date: Sun, 29 Dec 2024 16:44:13 -0500
Subject: [PATCH 042/374] config/allconfig: Fix slice of language configs

Fixes #13201
---
 config/allconfig/allconfig.go                 |  1 -
 .../allconfig/allconfig_integration_test.go   | 22 +++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index 5fe6edcd1..4d65d5f2b 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -751,7 +751,6 @@ func (c *Configs) Init() error {
 	var languages langs.Languages
 	defaultContentLanguage := c.Base.DefaultContentLanguage
 	for k, v := range c.LanguageConfigMap {
-		c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
 		languageConf := v.Languages[k]
 		language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
 		if err != nil {
diff --git a/config/allconfig/allconfig_integration_test.go b/config/allconfig/allconfig_integration_test.go
index 43d5261d0..162a36cdb 100644
--- a/config/allconfig/allconfig_integration_test.go
+++ b/config/allconfig/allconfig_integration_test.go
@@ -193,3 +193,25 @@ x
 	b.Assert(err, qt.IsNotNil)
 	b.Assert(err.Error(), qt.Contains, `failed to create config: unknown output format "foo" for kind "home"`)
 }
+
+// Issue 13201
+func TestLanguageConfigSlice(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[languages.en]
+title = 'TITLE_EN'
+weight = 2
+[languages.de]
+title = 'TITLE_DE'
+weight = 1
+[languages.fr]
+title = 'TITLE_FR'
+weight = 3
+`
+
+	b := hugolib.Test(t, files)
+	b.Assert(b.H.Configs.LanguageConfigSlice[0].Title, qt.Equals, `TITLE_DE`)
+}

From 4e52be8b90dca178ef8a0545e8bb3d0fe9424437 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 19 Dec 2024 08:33:49 +0000
Subject: [PATCH 043/374] build(deps): bump golang.org/x/net from 0.32.0 to
 0.33.0

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.0)

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

Signed-off-by: dependabot[bot] 
---
 go.mod | 2 +-
 go.sum | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index d5a4cd6d2..1f420e2fa 100644
--- a/go.mod
+++ b/go.mod
@@ -77,7 +77,7 @@ require (
 	golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
 	golang.org/x/image v0.22.0
 	golang.org/x/mod v0.22.0
-	golang.org/x/net v0.32.0
+	golang.org/x/net v0.33.0
 	golang.org/x/sync v0.10.0
 	golang.org/x/text v0.21.0
 	golang.org/x/tools v0.28.0
diff --git a/go.sum b/go.sum
index 3f39a7a46..58d002d5a 100644
--- a/go.sum
+++ b/go.sum
@@ -588,8 +588,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
-golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

From 83cec785cffd19a00eee83b7550b99e42fea0377 Mon Sep 17 00:00:00 2001
From: Diwas Rimal 
Date: Sat, 28 Dec 2024 22:54:04 +0545
Subject: [PATCH 044/374] Print cli usage of `hugo gen chromastyles` alongside
 css

---
 commands/gen.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/commands/gen.go b/commands/gen.go
index 83b4d637c..67aa7a896 100644
--- a/commands/gen.go
+++ b/commands/gen.go
@@ -75,7 +75,9 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
 					return err
 				}
 				formatter := html.New(html.WithAllClasses(true))
-				formatter.WriteCSS(os.Stdout, style)
+				w := os.Stdout
+				fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
+				formatter.WriteCSS(w, style)
 				return nil
 			},
 			withc: func(cmd *cobra.Command, r *rootCommand) {

From aae02ca612a02e085c08366a9c9279f4abb39d94 Mon Sep 17 00:00:00 2001
From: hugoreleaser 
Date: Mon, 30 Dec 2024 15:01:53 +0000
Subject: [PATCH 045/374] releaser: Bump versions for release of 0.140.2

[ci skip]
---
 common/hugo/version_current.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go
index c3ef7c5f8..035b85a65 100644
--- a/common/hugo/version_current.go
+++ b/common/hugo/version_current.go
@@ -17,7 +17,7 @@ package hugo
 // This should be the only one.
 var CurrentVersion = Version{
 	Major:      0,
-	Minor:      141,
-	PatchLevel: 0,
-	Suffix:     "-DEV",
+	Minor:      140,
+	PatchLevel: 2,
+	Suffix:     "",
 }

From 46ce1f191bbc4c263e3c4fafad8ad8a16a81450c Mon Sep 17 00:00:00 2001
From: hugoreleaser 
Date: Mon, 30 Dec 2024 15:16:00 +0000
Subject: [PATCH 046/374] releaser: Prepare repository for 0.141.0-DEV

[ci skip]
---
 common/hugo/version_current.go | 6 +++---
 hugoreleaser.env               | 5 +++--
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go
index 035b85a65..c3ef7c5f8 100644
--- a/common/hugo/version_current.go
+++ b/common/hugo/version_current.go
@@ -17,7 +17,7 @@ package hugo
 // This should be the only one.
 var CurrentVersion = Version{
 	Major:      0,
-	Minor:      140,
-	PatchLevel: 2,
-	Suffix:     "",
+	Minor:      141,
+	PatchLevel: 0,
+	Suffix:     "-DEV",
 }
diff --git a/hugoreleaser.env b/hugoreleaser.env
index 1166fa627..0c40dd43e 100644
--- a/hugoreleaser.env
+++ b/hugoreleaser.env
@@ -1,7 +1,8 @@
 # Release env. 
 # These will be replaced by script before release.
-HUGORELEASER_TAG=v0.140.1
-HUGORELEASER_COMMITISH=a9b0b95ef402a1cc70bc5386d649d947e2bcc027
+HUGORELEASER_TAG=v0.140.2
+HUGORELEASER_COMMITISH=aae02ca612a02e085c08366a9c9279f4abb39d94
+
 
 
 

From 2db43f841c7d2db0edcb3a3478c0833c38ae5997 Mon Sep 17 00:00:00 2001
From: Joe Mooring 
Date: Tue, 31 Dec 2024 08:35:22 -0500
Subject: [PATCH 047/374] markup/highlight: Remove noHl option

Closes #9885
---
 markup/highlight/config.go               | 6 ------
 markup/highlight/highlight.go            | 2 +-
 markup/internal/attributes/attributes.go | 1 -
 3 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/markup/highlight/config.go b/markup/highlight/config.go
index 338ea77e5..b19e9ec1b 100644
--- a/markup/highlight/config.go
+++ b/markup/highlight/config.go
@@ -33,7 +33,6 @@ const (
 	lineNosKey     = "linenos"
 	hlLinesKey     = "hl_lines"
 	linosStartKey  = "linenostart"
-	noHlKey        = "nohl"
 )
 
 var DefaultConfig = Config{
@@ -60,9 +59,6 @@ type Config struct {
 	// Use inline CSS styles.
 	NoClasses bool
 
-	// No highlighting.
-	NoHl bool
-
 	// When set, line numbers will be printed.
 	LineNos            bool
 	LineNumbersInTable bool
@@ -234,8 +230,6 @@ func normalizeHighlightOptions(m map[string]any) {
 
 	for k, v := range m {
 		switch k {
-		case noHlKey:
-			m[noHlKey] = cast.ToBool(v)
 		case lineNosKey:
 			if v == "table" || v == "inline" {
 				m["lineNumbersInTable"] = v == "table"
diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go
index 30f225c0b..ee07fb9ad 100644
--- a/markup/highlight/highlight.go
+++ b/markup/highlight/highlight.go
@@ -168,7 +168,7 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
 		lexer = chromalexers.Get(lang)
 	}
 
-	if lexer == nil && (cfg.GuessSyntax && !cfg.NoHl) {
+	if lexer == nil && cfg.GuessSyntax {
 		lexer = lexers.Analyse(code)
 		if lexer == nil {
 			lexer = lexers.Fallback
diff --git a/markup/internal/attributes/attributes.go b/markup/internal/attributes/attributes.go
index 9c147f766..b2d4a5ed6 100644
--- a/markup/internal/attributes/attributes.go
+++ b/markup/internal/attributes/attributes.go
@@ -36,7 +36,6 @@ var chromaHighlightProcessingAttributes = map[string]bool{
 	"lineNoStart":        true,
 	"lineNumbersInTable": true,
 	"noClasses":          true,
-	"nohl":               true,
 	"style":              true,
 	"tabWidth":           true,
 }

From d913f46a8bf0cf3a612ac57516ed9c33bb16e04e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?=
 
Date: Wed, 1 Jan 2025 13:48:18 +0100
Subject: [PATCH 048/374] Fix server refresh on 404 template changes

Fixes #13209
---
 hugolib/404_test.go         | 31 +++++++++++++++++++++++++++++++
 hugolib/content_map_page.go |  3 ++-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/hugolib/404_test.go b/hugolib/404_test.go
index fef020905..78cf0e70c 100644
--- a/hugolib/404_test.go
+++ b/hugolib/404_test.go
@@ -14,6 +14,7 @@
 package hugolib
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -82,3 +83,33 @@ Page not found
 Base:
 Page not found`)
 }
+
+func Test404EditTemplate(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+baseURL = "http://example.com/"
+disableLiveReload = true
+[internal]
+fastRenderMode = true
+-- layouts/_default/baseof.html --
+Base: {{ block "main" . }}{{ end }}
+-- layouts/404.html --
+{{ define "main" }}
+Not found.
+{{ end }}
+	
+	`
+
+	b := TestRunning(t, files)
+
+	b.AssertFileContent("public/404.html", `Not found.`)
+
+	b.EditFiles("layouts/404.html", `Not found. Updated.`).Build()
+
+	fmt.Println("Rebuilding")
+	b.BuildPartial("/does-not-exist")
+
+	b.AssertFileContent("public/404.html", `Not found. Updated.`)
+}
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index eda6f769e..b369dfe13 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -1274,7 +1274,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 
 		po.renderState = 0
 		po.p.resourcesPublishInit = &sync.Once{}
-		if r == identity.FinderFoundOneOfMany {
+		if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatusHTMLFormat.Name {
 			// Will force a re-render even in fast render mode.
 			po.renderOnce = false
 		}
@@ -1310,6 +1310,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 				if !po.isRendered() {
 					continue
 				}
+
 				for _, id := range changes {
 					checkedCounter.Add(1)
 					if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition {

From 723e3f4342dbf7b64350eb1db2d96dd53b919a0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?=
 
Date: Fri, 3 Jan 2025 18:36:44 +0100
Subject: [PATCH 049/374] resources: Add FromOpts for more effective resource
 creation

E.g. when the targetPath already contains a hash or if the resource content is expensive to create.
---
 common/hashing/hashing.go                     | 13 ++++
 resources/resource_factories/create/create.go | 75 +++++++++++++++++--
 2 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/common/hashing/hashing.go b/common/hashing/hashing.go
index 70aa74ecd..0b470cafe 100644
--- a/common/hashing/hashing.go
+++ b/common/hashing/hashing.go
@@ -38,6 +38,19 @@ func XXHashFromReader(r io.Reader) (uint64, int64, error) {
 	return h.Sum64(), size, nil
 }
 
+// XxHashFromReaderHexEncoded calculates the xxHash for the given reader
+// and returns the hash as a hex encoded string.
+func XxHashFromReaderHexEncoded(r io.Reader) (string, error) {
+	h := getXxHashReadFrom()
+	defer putXxHashReadFrom(h)
+	_, err := io.Copy(h, r)
+	if err != nil {
+		return "", err
+	}
+	hash := h.Sum(nil)
+	return hex.EncodeToString(hash), nil
+}
+
 // XXHashFromString calculates the xxHash for the given string.
 func XXHashFromString(s string) (uint64, error) {
 	h := xxhash.New()
diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go
index 581c0a854..1d78a2923 100644
--- a/resources/resource_factories/create/create.go
+++ b/resources/resource_factories/create/create.go
@@ -218,22 +218,83 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
 	})
 }
 
-// FromString creates a new Resource from a string with the given relative target path.
-// TODO(bep) see #10912; we currently emit a warning for this config scenario.
-func (c *Client) FromString(targetPath, content string) (resource.Resource, error) {
-	targetPath = path.Clean(targetPath)
-	key := dynacache.CleanKey(targetPath) + hashing.MD5FromStringHexEncoded(content)
+type Options struct {
+	// The target path relative to the publish directory.
+	// Unix style path, i.e. "images/logo.png".
+	TargetPath string
+
+	// Whether the TargetPath has a hash in it which will change if the resource changes.
+	// If not, we will calculate a hash from the content.
+	TargetPathHasHash bool
+
+	// The content to create the Resource from.
+	CreateContent func() (func() (hugio.ReadSeekCloser, error), error)
+}
+
+// FromOpts creates a new Resource from the given Options.
+// Make sure to set optis.TargetPathHasHash if the TargetPath already contains a hash,
+// as this avoids the need to calculate it.
+// To create a new ReadSeekCloser from a string, use hugio.NewReadSeekerNoOpCloserFromString,
+// or hugio.NewReadSeekerNoOpCloserFromBytes for a byte slice.
+// See FromString.
+func (c *Client) FromOpts(opts Options) (resource.Resource, error) {
+	opts.TargetPath = path.Clean(opts.TargetPath)
+	var hash string
+	var newReadSeeker func() (hugio.ReadSeekCloser, error) = nil
+	if !opts.TargetPathHasHash {
+		var err error
+		newReadSeeker, err = opts.CreateContent()
+		if err != nil {
+			return nil, err
+		}
+		if err := func() error {
+			r, err := newReadSeeker()
+			if err != nil {
+				return err
+			}
+			defer r.Close()
+
+			hash, err = hashing.XxHashFromReaderHexEncoded(r)
+			if err != nil {
+				return err
+			}
+			return nil
+		}(); err != nil {
+			return nil, err
+		}
+	}
+
+	key := dynacache.CleanKey(opts.TargetPath) + hash
 	r, err := c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
+		if newReadSeeker == nil {
+			var err error
+			newReadSeeker, err = opts.CreateContent()
+			if err != nil {
+				return nil, err
+			}
+		}
 		return c.rs.NewResource(
 			resources.ResourceSourceDescriptor{
 				LazyPublish:   true,
 				GroupIdentity: identity.Anonymous, // All usage of this resource are tracked via its string content.
 				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
-					return hugio.NewReadSeekerNoOpCloserFromString(content), nil
+					return newReadSeeker()
 				},
-				TargetPath: targetPath,
+				TargetPath: opts.TargetPath,
 			})
 	})
 
 	return r, err
 }
+
+// FromString creates a new Resource from a string with the given relative target path.
+func (c *Client) FromString(targetPath, content string) (resource.Resource, error) {
+	return c.FromOpts(Options{
+		TargetPath: targetPath,
+		CreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {
+			return func() (hugio.ReadSeekCloser, error) {
+				return hugio.NewReadSeekerNoOpCloserFromString(content), nil
+			}, nil
+		},
+	})
+}

From 5d2cbee9890d159ca2c776e85eaabf032c2df7c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?=
 
Date: Tue, 19 Apr 2022 16:56:49 +0200
Subject: [PATCH 050/374] Add try

Updates #9737
---
 .../texttemplate/hugo_template.go             | 30 +++++++++++++++-
 tpl/partials/init.go                          |  2 +-
 tpl/safe/init.go                              |  7 ++++
 tpl/templates/templates_integration_test.go   | 34 +++++++++++++++++++
 4 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/tpl/internal/go_templates/texttemplate/hugo_template.go b/tpl/internal/go_templates/texttemplate/hugo_template.go
index 12dbe0412..0dbee02f7 100644
--- a/tpl/internal/go_templates/texttemplate/hugo_template.go
+++ b/tpl/internal/go_templates/texttemplate/hugo_template.go
@@ -15,6 +15,7 @@ package template
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"reflect"
 
@@ -255,10 +256,32 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
 	panic("not reached")
 }
 
+// TryValue is what gets returned when using the "try" keyword.
+type TryValue struct {
+	// Value is the value returned by the function or method wrapped with "try".
+	// This will always be nil if Err is set.
+	Value any
+	// Err is the error returned by the function or method wrapped with "try".
+	// This will always be nil if Value is set.
+	Err error
+}
+
 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
 // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
 // as the function itself.
-func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value {
+func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) (val reflect.Value) {
+	// Added for Hugo.
+	if name == "try" {
+		defer func() {
+			if r := recover(); r != nil {
+				if err, ok := r.(error); ok {
+					val = reflect.ValueOf(TryValue{nil, err})
+				} else {
+					val = reflect.ValueOf(TryValue{nil, fmt.Errorf("%v", r)})
+				}
+			}
+		}()
+	}
 	if args != nil {
 		args = args[1:] // Zeroth arg is function name/node; not passed to function.
 	}
@@ -371,6 +394,11 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
 		s.helper.OnCalled(s.ctx, s.prep, name, argv, vv)
 	}
 
+	// Added for Hugo.
+	if name == "try" {
+		return reflect.ValueOf(TryValue{vv.Interface(), nil})
+	}
+
 	return vv
 }
 
diff --git a/tpl/partials/init.go b/tpl/partials/init.go
index e9d901bbf..6f7c0ffc1 100644
--- a/tpl/partials/init.go
+++ b/tpl/partials/init.go
@@ -38,7 +38,7 @@ func init() {
 			},
 		)
 
-		// TODO(bep) we need the return to be a valid identifier, but
+		// TODO(bep) we need the return to be a valid identifiers, but
 		// should consider another way of adding it.
 		ns.AddMethodMapping(func() string { return "" },
 			[]string{"return"},
diff --git a/tpl/safe/init.go b/tpl/safe/init.go
index 3b498e6df..ea1469e80 100644
--- a/tpl/safe/init.go
+++ b/tpl/safe/init.go
@@ -70,6 +70,13 @@ func init() {
 			},
 		)
 
+		ns.AddMethodMapping(func(v any) (any, error) {
+			return v, nil
+		},
+			[]string{"try"},
+			[][2]string{},
+		)
+
 		return ns
 	}
 
diff --git a/tpl/templates/templates_integration_test.go b/tpl/templates/templates_integration_test.go
index 301f783a5..aa5540862 100644
--- a/tpl/templates/templates_integration_test.go
+++ b/tpl/templates/templates_integration_test.go
@@ -93,3 +93,37 @@ Home: true
 
 `)
 }
+
+func TestTry(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+-- layouts/index.html --
+Home.
+{{ $g :=  try ("hello = \"Hello Hugo\"" | transform.Unmarshal)   }}
+{{ with $g.Err }}
+Err1: {{ . }}
+{{ else }}
+Value1: {{ $g.Value.hello | safeHTML }}|
+{{ end }}
+{{ $g :=  try ("hello != \"Hello Hugo\"" | transform.Unmarshal)   }}
+{{ with $g.Err }}
+Err2: {{ . | safeHTML }}
+{{ else }}
+Value2: {{ $g.Value.hello | safeHTML }}|
+{{ end }}
+Try upper: {{ (try ("hello" | upper)).Value }}
+Try printf: {{ (try (printf "hello %s" "world")).Value }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html",
+		"Value1: Hello Hugo|",
+		"Err2: template: index.html:",
+		"Try upper: HELLO",
+		"Try printf: hello world",
+	)
+}

From 4ea94c451df9aaf85c03fb302662310e71f98677 Mon Sep 17 00:00:00 2001
From: Joe Mooring 
Date: Tue, 31 Dec 2024 17:43:35 -0500
Subject: [PATCH 051/374] tpl/images: Add images.QR function

Closes #13205
---
 README.md                                     |  31 ++---
 .../en/content-management/shortcodes.md       | 102 +++++++++++++++-
 docs/content/en/functions/images/QR.md        | 115 ++++++++++++++++++
 docs/data/embedded_template_urls.toml         |   9 +-
 go.mod                                        |   1 +
 go.sum                                        |   2 +
 tpl/images/images.go                          | 100 +++++++++++++--
 tpl/images/images_integration_test.go         |  51 ++++++++
 .../embedded/templates/shortcodes/qr.html     |  76 ++++++++++++
 tpl/tplimpl/tplimpl_integration_test.go       |  36 ++++++
 10 files changed, 495 insertions(+), 28 deletions(-)
 create mode 100644 docs/content/en/functions/images/QR.md
 create mode 100644 tpl/tplimpl/embedded/templates/shortcodes/qr.html

diff --git a/README.md b/README.md
index 10ce6e200..913130b5d 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,7 @@ Build the extended edition:
 ```text
 CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
 ```
+
 ## Star History
 
 [![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline)
@@ -154,7 +155,7 @@ github.com/bep/clocks="v0.5.0"
 github.com/bep/debounce="v1.2.0"
 github.com/bep/gitmap="v1.6.0"
 github.com/bep/goat="v0.5.0"
-github.com/bep/godartsass/v2="v2.3.0"
+github.com/bep/godartsass/v2="v2.3.2"
 github.com/bep/golibsass="v1.2.0"
 github.com/bep/gowebp="v0.3.0"
 github.com/bep/imagemeta="v0.8.3"
@@ -189,7 +190,7 @@ github.com/gohugoio/locales="v0.14.0"
 github.com/gohugoio/localescompressed="v1.0.1"
 github.com/google/go-cmp="v0.6.0"
 github.com/gorilla/websocket="v1.5.3"
-github.com/hairyhenderson/go-codeowners="v0.6.1"
+github.com/hairyhenderson/go-codeowners="v0.7.0"
 github.com/hashicorp/golang-lru/v2="v2.0.7"
 github.com/invopop/yaml="v0.2.0"
 github.com/jdkato/prose="v1.2.1"
@@ -218,30 +219,32 @@ github.com/russross/blackfriday/v2="v2.1.0"
 github.com/sass/dart-sass/compiler="1.81.0"
 github.com/sass/dart-sass/implementation="1.81.0"
 github.com/sass/dart-sass/protocol="3.1.0"
+github.com/sass/libsass="3.6.6"
 github.com/spf13/afero="v1.11.0"
-github.com/spf13/cast="v1.7.0"
+github.com/spf13/cast="v1.7.1"
 github.com/spf13/cobra="v1.8.1"
 github.com/spf13/fsync="v0.10.1"
 github.com/spf13/pflag="v1.0.5"
-github.com/tdewolff/minify/v2="v2.21.1"
-github.com/tdewolff/parse/v2="v2.7.18"
-github.com/tetratelabs/wazero="v1.8.1"
+github.com/tdewolff/minify/v2="v2.20.37"
+github.com/tdewolff/parse/v2="v2.7.15"
+github.com/tetratelabs/wazero="v1.8.2"
+github.com/webmproject/libwebp="v1.3.2"
 github.com/yuin/goldmark-emoji="v1.0.4"
 github.com/yuin/goldmark="v1.7.8"
 go.uber.org/automaxprocs="v1.5.3"
-golang.org/x/crypto="v0.29.0"
+golang.org/x/crypto="v0.31.0"
 golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326"
 golang.org/x/image="v0.22.0"
 golang.org/x/mod="v0.22.0"
-golang.org/x/net="v0.31.0"
-golang.org/x/sync="v0.9.0"
-golang.org/x/sys="v0.27.0"
-golang.org/x/text="v0.20.0"
-golang.org/x/tools="v0.27.0"
-google.golang.org/protobuf="v1.35.1"
+golang.org/x/net="v0.33.0"
+golang.org/x/sync="v0.10.0"
+golang.org/x/sys="v0.28.0"
+golang.org/x/text="v0.21.0"
+golang.org/x/tools="v0.28.0"
+google.golang.org/protobuf="v1.35.2"
 gopkg.in/yaml.v2="v2.4.0"
 gopkg.in/yaml.v3="v3.0.1"
-howett.net/plist="v1.0.0"
+rsc.io/qr="v0.2.0"
 software.sslmate.com/src/go-pkcs12="v0.2.0"
 ```
 
diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md
index 47e4f94ed..afe84e79b 100644
--- a/docs/content/en/content-management/shortcodes.md
+++ b/docs/content/en/content-management/shortcodes.md
@@ -121,7 +121,7 @@ Hugo renders this to:
 
 ```
 
-The details shortcode accepts these named arguments:
+The `details` shortcode accepts these named arguments:
 
 summary
 : (`string`) The content of the child `summary` element rendered from Markdown to HTML. Default is `Details`.
@@ -333,6 +333,106 @@ Access nested values by [chaining] the [identifiers]:
 {{}}
 ```
 
+### qr
+
+{{% note %}}
+To override Hugo's embedded `qr` shortcode, copy the [source code] to a file with the same name in the layouts/shortcodes directory.
+
+[source code]: {{% eturl qr %}}
+{{% /note %}}
+
+The `qr` shortcode encodes the given text into a [QR code] using the specified options and renders the resulting image.
+
+[QR code]: https://en.wikipedia.org/wiki/QR_code
+
+Use the self-closing syntax to pass the text as an argument:
+
+```text
+{{}}
+```
+
+Or insert the text between the opening and closing tags:
+
+```text
+{{}}
+https://gohugo.io
+{{}}
+```
+
+Both of the above produce this image:
+
+{{< qr text="https://gohugo.io" class="qrcode" />}}
+
+To create a QR code for a phone number:
+
+```text
+{{}}
+```
+
+{{< qr text="tel:+12065550101" class="qrcode" />}}
+
+To create a QR code containing contact information in the [vCard] format:
+
+[vCard]: https://en.wikipedia.org/wiki/VCard
+
+```text
+{{}}
+BEGIN:VCARD
+VERSION:2.1
+N;CHARSET=UTF-8:Smith;John;R.;Dr.;PhD
+FN;CHARSET=UTF-8:Dr. John R. Smith, PhD.
+ORG;CHARSET=UTF-8:ABC Widgets
+TITLE;CHARSET=UTF-8:Vice President Engineering
+TEL;TYPE=WORK:+12065550101
+EMAIL;TYPE=WORK:jsmith@example.org
+END:VCARD
+{{}}
+```
+
+{{< qr level="low" scale=2 alt="QR code of vCard for John Smith" class="qrcode" >}}
+BEGIN:VCARD
+VERSION:2.1
+N;CHARSET=UTF-8:Smith;John;R.;Dr.;PhD
+FN;CHARSET=UTF-8:Dr. John R. Smith, PhD.
+ORG;CHARSET=UTF-8:ABC Widgets
+TITLE;CHARSET=UTF-8:Vice President Engineering
+TEL;TYPE=WORK:+12065550101
+EMAIL;TYPE=WORK:jsmith@example.org
+END:VCARD
+{{< /qr >}}
+
+Internally this shortcode calls the `images.QR` function. Please read the [related documentation] for implementation details and guidance.
+
+[related documentation]: /functions/images/qr/
+
+The `qr` shortcode accepts these named arguments:
+
+text
+: (`string`) The text to encode, falling back to the text between the opening and closing shortcode tags.
+
+level
+: (`string`) The error correction level to use when encoding the text, one of `low`, `medium`, `quartile`, or `high`. Default is `medium`.
+
+scale
+: (`int`) The number of image pixels per QR code module. Must be greater than or equal to 2. Default is `4`.
+
+targetDir
+: (`string`) The subdirectory within the [`publishDir`] where Hugo will place the generated image.
+
+[`publishDir`]: /getting-started/configuration/#publishdir
+
+alt
+: (`string`) The `alt` attribute of the `img` element.
+
+class
+: (`string`) The `class` attribute of the `img` element.
+
+id
+: (`string`) The `id` attribute of the `img` element.
+
+title
+: (`string`) The `title` attribute of the `img` element.
+
 ### ref
 
 {{% note %}}
diff --git a/docs/content/en/functions/images/QR.md b/docs/content/en/functions/images/QR.md
new file mode 100644
index 000000000..c1b8fb465
--- /dev/null
+++ b/docs/content/en/functions/images/QR.md
@@ -0,0 +1,115 @@
+---
+title: images.QR
+description: Encodes the given text into a QR code using the specified options, returning an image resource.
+keywords: []
+action:
+  aliases: []
+  related: []
+  returnType: images.ImageResource
+  signatures: ['images.QR OPTIONS']
+toc: true
+math: true
+---
+
+{{< new-in 0.141.0 >}}
+
+The `images.QR` function encodes the given text into a [QR code] using the specified options, returning an image resource. The size of the generated image depends on three factors:
+
+- Data length: Longer text necessitates a larger image to accommodate the increased information density.
+- Error correction level: Higher error correction levels enhance the QR code's resistance to damage, but this typically results in a slightly larger image size to maintain readability.
+- Pixels per module: The number of image pixels assigned to each individual module (the smallest unit of the QR code) directly impacts the overall image size. A higher pixel count per module leads to a larger, higher-resolution image.
+
+Although the default option values are sufficient for most applications, you should test the rendered QR code both on-screen and in print.
+
+[QR code]: https://en.wikipedia.org/wiki/QR_code
+
+## Options
+
+text
+: (`string`) The text to encode.
+
+level
+: (`string`) The error correction level to use when encoding the text, one of `low`, `medium`, `quartile`, or `high`. Default is `medium`.
+
+Error correction level|Redundancy
+:--|:--|:--
+low|20%
+medium|38%
+quartile|55%
+high|65%
+
+scale
+: (`int`) The number of image pixels per QR code module. Must be greater than or equal to `2`. Default is `4`.
+
+targetDir
+: (`string`) The subdirectory within the [`publishDir`] where Hugo will place the generated image. Use Unix-style slashes (`/`) to separarate path segments. If empty or not provided, the image is placed directly in the `publishDir` root. Hugo automatically creates the necessary subdirectories if they don't exist.
+
+[`publishDir`]: /getting-started/configuration/#publishdir
+
+## Examples
+
+To create a QR code using the default values for `level` and `scale`:
+
+```go-html-template
+{{ $opts := dict "text" "https://gohugo.io" }}
+{{ with images.QR $opts }}
+  
+{{ end }}
+```
+
+{{< qr text="https://gohugo.io" class="qrcode" />}}
+
+Specify `level`, `scale`, and `targetDir` as needed to achieve the desired result:
+
+```go-html-template
+{{ $opts := dict 
+  "text" "https://gohugo.io"
+  "level" "high" 
+  "scale" 3
+  "targetDir" "codes"
+}}
+{{ with images.QR $opts }}
+  
+{{ end }}
+```
+
+{{< qr text="https://gohugo.io" level="high" scale=3 targetDir="codes" class="qrcode" />}}
+
+## Scale
+
+As you decrease the size of a QR code, the maximum distance at which it can be reliably scanned by a device also decreases.
+
+In the example above, we set the `scale` to `2`, resulting in a QR code where each module consists of 2x2 pixels. While this might be sufficient for on-screen display, it's likely to be problematic when printed at 600 dpi.
+
+\[ \frac{2\:px}{module} \times \frac{1\:inch}{600\:px} \times \frac{25.4\:mm}{1\:inch} = \frac{0.085\:mm}{module} \]
+
+This module size is half of the commonly recommended minimum of 0.170 mm.\
+If the QR code will be printed, use the default `scale` value of `4` pixels per module.
+
+Avoid using Hugo's image processing methods to resize QR codes. Resizing can introduce blurring due to anti-aliasing when a QR code module occupies a fractional number of pixels.
+
+{{% note %}}
+Always test the rendered QR code both on-screen and in print.
+{{% /note %}}
+
+## Shortcode
+
+Call the `qr` shortcode to insert a QR code into your content.
+
+Use the self-closing syntax to pass the text as an argument:
+
+```text
+{{}}
+```
+
+Or insert the text between the opening and closing tags:
+
+```text
+{{}}
+https://gohugo.io
+{{}}
+```
+
+The `qr` shortcode accepts several arguments including `level` and `scale`. See the [related documentation] for details.
+
+[related documentation]: /content-management/shortcodes/#qr
diff --git a/docs/data/embedded_template_urls.toml b/docs/data/embedded_template_urls.toml
index b7247f272..359105e60 100644
--- a/docs/data/embedded_template_urls.toml
+++ b/docs/data/embedded_template_urls.toml
@@ -10,18 +10,18 @@
 'google_analytics' = 'google_analytics.html'
 'opengraph' = 'opengraph.html'
 'pagination' = 'pagination.html'
-'schema' = 'schema.html'
-'twitter_cards' = 'twitter_cards.html'
-
 'robots' = '_default/robots.txt'
 'rss' = '_default/rss.xml'
+'schema' = 'schema.html'
 'sitemap' = '_default/sitemap.xml'
 'sitemapindex' = '_default/sitemapindex.xml'
+'twitter_cards' = 'twitter_cards.html'
 
 # Render hooks
+'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html'
 'render-image' = '_default/_markup/render-image.html'
 'render-link' = '_default/_markup/render-link.html'
-'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html'
+'render-table' = '_default/_markup/render-table.html'
 
 # Shortcodes
 'comment' = 'shortcodes/comment.html'
@@ -31,6 +31,7 @@
 'highlight' = 'shortcodes/highlight.html'
 'instagram' = 'shortcodes/instagram.html'
 'param' = 'shortcodes/param.html'
+'qr' = 'shortcodes/qr.html'
 'ref' = 'shortcodes/ref.html'
 'relref' = 'shortcodes/relref.html'
 'twitter' = 'shortcodes/twitter.html'
diff --git a/go.mod b/go.mod
index 1f420e2fa..4ee08313e 100644
--- a/go.mod
+++ b/go.mod
@@ -165,6 +165,7 @@ require (
 	google.golang.org/protobuf v1.35.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	howett.net/plist v1.0.0 // indirect
+	rsc.io/qr v0.2.0 // indirect
 	software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
 )
 
diff --git a/go.sum b/go.sum
index 58d002d5a..0db43c535 100644
--- a/go.sum
+++ b/go.sum
@@ -878,6 +878,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
 howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
+rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
diff --git a/tpl/images/images.go b/tpl/images/images.go
index 02ffb333f..08ab3cc95 100644
--- a/tpl/images/images.go
+++ b/tpl/images/images.go
@@ -16,11 +16,18 @@ package images
 
 import (
 	"errors"
+	"fmt"
 	"image"
+	"path"
 	"sync"
 
 	"github.com/bep/overlayfs"
+	"github.com/gohugoio/hugo/common/hashing"
+	"github.com/gohugoio/hugo/common/hugio"
 	"github.com/gohugoio/hugo/resources/images"
+	"github.com/gohugoio/hugo/resources/resource_factories/create"
+	"github.com/mitchellh/mapstructure"
+	"rsc.io/qr"
 
 	// Importing image codecs for image.DecodeConfig
 	_ "image/gif"
@@ -50,21 +57,22 @@ func New(d *deps.Deps) *Namespace {
 	}
 
 	return &Namespace{
-		readFileFs: readFileFs,
-		Filters:    &images.Filters{},
-		cache:      map[string]image.Config{},
-		deps:       d,
+		readFileFs:   readFileFs,
+		Filters:      &images.Filters{},
+		cache:        map[string]image.Config{},
+		deps:         d,
+		createClient: create.New(d.ResourceSpec),
 	}
 }
 
 // Namespace provides template functions for the "images" namespace.
 type Namespace struct {
 	*images.Filters
-	readFileFs afero.Fs
-	cacheMu    sync.RWMutex
-	cache      map[string]image.Config
-
-	deps *deps.Deps
+	readFileFs   afero.Fs
+	cacheMu      sync.RWMutex
+	cache        map[string]image.Config
+	deps         *deps.Deps
+	createClient *create.Client
 }
 
 // Config returns the image.Config for the specified path relative to the
@@ -117,3 +125,77 @@ func (ns *Namespace) Filter(args ...any) (images.ImageResource, error) {
 
 	return img.Filter(filtersv...)
 }
+
+var qrErrorCorrectionLevels = map[string]qr.Level{
+	"low":      qr.L,
+	"medium":   qr.M,
+	"quartile": qr.Q,
+	"high":     qr.H,
+}
+
+// QR encodes the given text into a QR code using the specified options,
+// returning an image resource.
+func (ns *Namespace) QR(options any) (images.ImageResource, error) {
+	const (
+		qrDefaultErrorCorrectionLevel = "medium"
+		qrDefaultScale                = 4
+	)
+
+	opts := struct {
+		Text      string // text to encode
+		Level     string // error correction level; one of low, medium, quartile, or high
+		Scale     int    // number of image pixels per QR code module
+		TargetDir string // target directory relative to publishDir
+	}{
+		Level: qrDefaultErrorCorrectionLevel,
+		Scale: qrDefaultScale,
+	}
+
+	err := mapstructure.WeakDecode(options, &opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if opts.Text == "" {
+		return nil, errors.New("cannot encode an empty string")
+	}
+
+	level, ok := qrErrorCorrectionLevels[opts.Level]
+	if !ok {
+		return nil, errors.New("error correction level must be one of low, medium, quartile, or high")
+	}
+
+	if opts.Scale < 2 {
+		return nil, errors.New("scale must be an integer greater than or equal to 2")
+	}
+
+	targetPath := path.Join(opts.TargetDir, fmt.Sprintf("qr_%s.png", hashing.HashString(opts)))
+
+	r, err := ns.createClient.FromOpts(
+		create.Options{
+			TargetPath:        targetPath,
+			TargetPathHasHash: true,
+			CreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {
+				code, err := qr.Encode(opts.Text, level)
+				if err != nil {
+					return nil, err
+				}
+				code.Scale = opts.Scale
+				png := code.PNG()
+				return func() (hugio.ReadSeekCloser, error) {
+					return hugio.NewReadSeekerNoOpCloserFromBytes(png), nil
+				}, nil
+			},
+		},
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	ir, ok := r.(images.ImageResource)
+	if !ok {
+		panic("bug: resource is not an image resource")
+	}
+
+	return ir, nil
+}
diff --git a/tpl/images/images_integration_test.go b/tpl/images/images_integration_test.go
index 003422aed..e9a31c9af 100644
--- a/tpl/images/images_integration_test.go
+++ b/tpl/images/images_integration_test.go
@@ -14,8 +14,10 @@
 package images_test
 
 import (
+	"strings"
 	"testing"
 
+	qt "github.com/frankban/quicktest"
 	"github.com/gohugoio/hugo/hugolib"
 )
 
@@ -49,3 +51,52 @@ fileExists2 OK: true|
 imageConfig2 OK: 1|
 `)
 }
+
+func TestQR(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- layouts/index.html --
+{{- $text := "https://gohugo.io" }}
+{{- $optionMaps := slice
+    (dict "text" $text)
+    (dict "text" $text "level" "medium")
+    (dict "text" $text "level" "medium" "scale" 4)
+    (dict "text" $text "level" "low" "scale" 2)
+    (dict "text" $text "level" "medium" "scale" 3)
+    (dict "text" $text "level" "quartile" "scale" 5)
+    (dict "text" $text "level" "high" "scale" 6)
+    (dict "text" $text "level" "high" "scale" 6 "targetDir" "foo/bar")
+}}
+{{- range $k, $opts := $optionMaps }}
+    {{- with images.QR $opts }}
+
+    {{- end }}
+{{- end }}
+`
+
+	b := hugolib.Test(t, files)
+	b.AssertFileContent("public/index.html",
+		``,
+		``,
+		``,
+		``,
+		``,
+		``,
+		``,
+		``,
+	)
+
+	files = strings.ReplaceAll(files, "low", "foo")
+
+	b, err := hugolib.TestE(t, files)
+	b.Assert(err.Error(), qt.Contains, "error correction level must be one of low, medium, quartile, or high")
+
+	files = strings.ReplaceAll(files, "foo", "low")
+	files = strings.ReplaceAll(files, "https://gohugo.io", "")
+
+	b, err = hugolib.TestE(t, files)
+	b.Assert(err.Error(), qt.Contains, "cannot encode an empty string")
+}
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/qr.html b/tpl/tplimpl/embedded/templates/shortcodes/qr.html
new file mode 100644
index 000000000..cae7e5670
--- /dev/null
+++ b/tpl/tplimpl/embedded/templates/shortcodes/qr.html
@@ -0,0 +1,76 @@
+{{- /*
+Encodes the given text into a QR code using the specified options and renders the resulting image.
+
+@param {string} text The text to encode, falling back to the text between the opening and closing shortcode tags.
+@param {string} [level=medium] The error correction level to use when encoding the text, one of low, medium, quartile, or high.
+@param {int} [scale=4] The number of image pixels per QR code module. Must be greater than or equal to 2.
+@param {string} [targetDir] The subdirectory within the publishDir where Hugo will place the generated image.
+@param {string} [alt] The alt attribute of the img element.
+@param {string} [class] The class attribute of the img element.
+@param {string} [id] The id attribute of the img element.
+@param {string} [title] The title attribute of the img element.
+
+@returns {template.HTML}
+
+@examples
+
+  {{< qr text="https://gohugo.io" />}}
+
+  {{< qr >}}
+  https://gohugo.io"
+  {{< /qr >}}
+
+  {{< qr
+    text="https://gohugo.io"
+    level="high"
+    scale=4
+    targetDir="codes"
+    alt="QR code linking to https://gohugo.io"
+    class="my-class"
+    id="my-id"
+    title="My Title"
+  />}}
+
+*/}}
+
+{{- /* Constants. */}}
+{{- $validLevels := slice "low" "medium" "quartile" "high" }}
+{{- $minimumScale := 2 }}
+
+{{- /* Get arguments. */}}
+{{- $text := or (.Get "text") (strings.TrimSpace .Inner) "" }}
+{{- $level := or (.Get "level") "medium" }}
+{{- $scale := or (.Get "scale") 4 }}
+{{- $targetDir := or (.Get "targetDir") "" }}
+{{- $alt := or (.Get "alt") "" }}
+{{- $class := or (.Get "class") "" }}
+{{- $id := or (.Get "id") "" }}
+{{- $title := or (.Get "title") "" }}
+
+{{- /* Validate arguments. */}}
+{{- $errors := false}}
+{{- if not $text }}
+  {{- errorf "The %q shortcode requires a %q argument. See %s" .Name "text" .Position }}
+  {{- $errors = true }}
+{{- end }}
+{{- if not (in $validLevels $level) }}
+  {{- errorf "The %q argument passed to the %q shortcode must be one of %s. See %s" "level" .Name (delimit $validLevels ", " ", or ") .Position }}
+  {{- $errors = true }}
+{{- end }}
+{{- if or (lt $scale $minimumScale) (ne $scale (int $scale)) }}
+  {{- errorf "The %q argument passed to the %q shortcode must be an integer greater than or equal to %d. See %s" "scale" .Name $minimumScale .Position }}
+  {{- $errors = true }}
+{{- end }}
+
+{{- /* Render image. */}}
+{{- if not $errors }}
+  {{- $opts := dict "text" $text "level" $level "scale" $scale "targetDir" $targetDir }}
+  {{- with images.QR $opts -}}
+    {{ $alt }}
+  {{- end }}
+{{- end -}}
diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go
index dbadece4e..636e6aa68 100644
--- a/tpl/tplimpl/tplimpl_integration_test.go
+++ b/tpl/tplimpl/tplimpl_integration_test.go
@@ -698,3 +698,39 @@ Home!
 	b.BuildPartial("/mybundle1/")
 	b.AssertFileContent("public/mybundle1/index.html", "Baseof!!")
 }
+
+func TestQRShortcode(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- layouts/index.html --
+{{ .Content }}
+-- content/_index.md --
+---
+title: home
+---
+{{< qr
+	text="https://gohugo.io"
+	level="high"
+	scale=4
+	targetDir="codes"
+	alt="QR code linking to https://gohugo.io"
+	class="my-class"
+	id="my-id"
+	title="My Title"
+/>}}
+
+{{< qr >}}
+https://gohugo.io"
+{{< /qr >}}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html",
+		`QR code linking to https://gohugo.io`,
+		``,
+	)
+}

From 0918e087ec9dfe4b32011846313913e5c48f9475 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?=
 
Date: Sun, 5 Jan 2025 15:43:18 +0100
Subject: [PATCH 052/374] resources: Replace error handling in GetRemote with
 try (note)

Closes #13216
---
 common/herrors/file_error.go                  |  23 ++-
 hugolib/hugo_sites_build.go                   |  14 +-
 hugolib/page.go                               |   4 -
 hugolib/resource_chain_test.go                |  14 +-
 resources/errorResource.go                    | 145 ------------------
 resources/page/page_nop.go                    |   4 -
 resources/page/testhelpers_test.go            |   4 -
 resources/resource.go                         |   7 -
 resources/resource/resourcetypes.go           |  11 +-
 .../create/create_integration_test.go         |  20 +--
 resources/transform.go                        |   4 -
 .../texttemplate/hugo_template.go             |  30 +++-
 tpl/resources/resources.go                    |  16 +-
 .../templates/shortcodes/twitter.html         |   8 +-
 .../templates/shortcodes/twitter_simple.html  |  19 ++-
 .../templates/shortcodes/vimeo_simple.html    |   8 +-
 16 files changed, 108 insertions(+), 223 deletions(-)
 delete mode 100644 resources/errorResource.go

diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go
index c8a48823e..38b198656 100644
--- a/common/herrors/file_error.go
+++ b/common/herrors/file_error.go
@@ -258,8 +258,27 @@ func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
 	return f, realFilename, nil
 }
 
-// Cause returns the underlying error or itself if it does not implement Unwrap.
+// Cause returns the underlying error, that is,
+// it unwraps errors until it finds one that does not implement
+// the Unwrap method.
+// For a shallow variant, see Unwrap.
 func Cause(err error) error {
+	type unwrapper interface {
+		Unwrap() error
+	}
+
+	for err != nil {
+		cause, ok := err.(unwrapper)
+		if !ok {
+			break
+		}
+		err = cause.Unwrap()
+	}
+	return err
+}
+
+// Unwrap returns the underlying error or itself if it does not implement Unwrap.
+func Unwrap(err error) error {
 	if u := errors.Unwrap(err); u != nil {
 		return u
 	}
@@ -267,7 +286,7 @@ func Cause(err error) error {
 }
 
 func extractFileTypePos(err error) (string, text.Position) {
-	err = Cause(err)
+	err = Unwrap(err)
 
 	var fileType string
 
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index 02ecd5785..60fb8ecc0 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -343,6 +343,18 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
 
 	siteRenderContext := &siteRenderContext{cfg: config, multihost: h.Configs.IsMultihost}
 
+	renderErr := func(err error) error {
+		if err == nil {
+			return nil
+		}
+		if strings.Contains(err.Error(), "can't evaluate field Err in type resource.Resource") {
+			// In Hugo 0.141.0 we replaced the special error handling for resources.GetRemote
+			// with the more general try.
+			return fmt.Errorf("%s: Resource.Err was removed in Hugo v0.141.0 and replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/", err)
+		}
+		return err
+	}
+
 	i := 0
 	for _, s := range h.Sites {
 		segmentFilter := s.conf.C.SegmentFilter
@@ -390,7 +402,7 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
 							}
 						} else {
 							if err := s.render(siteRenderContext); err != nil {
-								return err
+								return renderErr(err)
 							}
 						}
 						loggers.TimeTrackf(ll, start, nil, "")
diff --git a/hugolib/page.go b/hugolib/page.go
index 83f0c6e25..365376737 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -185,10 +185,6 @@ func (p *pageState) isContentNodeBranch() bool {
 	return p.IsNode()
 }
 
-func (p *pageState) Err() resource.ResourceError {
-	return nil
-}
-
 // Eq returns whether the current page equals the given page.
 // This is what's invoked when doing `{{ if eq $page $otherPage }}`
 func (p *pageState) Eq(other any) bool {
diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go
index 0b17a8db0..669114c8a 100644
--- a/hugolib/resource_chain_test.go
+++ b/hugolib/resource_chain_test.go
@@ -67,11 +67,11 @@ FIT: {{ $fit.Name }}|{{ $fit.RelPermalink }}|{{ $fit.Width }}
 CSS integrity Data first: {{ $cssFingerprinted1.Data.Integrity }} {{ $cssFingerprinted1.RelPermalink }}
 CSS integrity Data last:  {{ $cssFingerprinted2.RelPermalink }} {{ $cssFingerprinted2.Data.Integrity }}
 
-{{ $failedImg := resources.GetRemote "%[1]s/fail.jpg" }}
+{{ $failedImg := try (resources.GetRemote "%[1]s/fail.jpg") }}
 {{ $rimg := resources.GetRemote "%[1]s/sunset.jpg" }}
 {{ $remotenotfound := resources.GetRemote "%[1]s/notfound.jpg" }}
 {{ $localnotfound := resources.Get "images/notfound.jpg" }}
-{{ $gopherprotocol := resources.GetRemote "gopher://example.org" }}
+{{ $gopherprotocol := try (resources.GetRemote "gopher://example.org") }}
 {{ $rfit := $rimg.Fit "200x200" }}
 {{ $rfit2 := $rfit.Fit "100x200" }}
 {{ $rimg = $rimg | fingerprint }}
@@ -79,10 +79,10 @@ SUNSET REMOTE: {{ $rimg.Name }}|{{ $rimg.RelPermalink }}|{{ $rimg.Width }}|{{ le
 FIT REMOTE: {{ $rfit.Name }}|{{ $rfit.RelPermalink }}|{{ $rfit.Width }}
 REMOTE NOT FOUND: {{ if $remotenotfound }}FAILED{{ else}}OK{{ end }}
 LOCAL NOT FOUND: {{ if $localnotfound }}FAILED{{ else}}OK{{ end }}
-PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ . | safeHTML }}{{ end }}
+PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ .Value | safeHTML }}{{ end }}
 PRINT PROTOCOL ERROR2: {{ with $gopherprotocol }}{{ .Err | safeHTML }}{{ end }}
-PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}Err: {{ .Err | safeHTML }}{{ with .Err }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}
-FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}|
+PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}{{ with .Err }}Err: {{ . | safeHTML }}{{ with .Cause }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}{{ end }}
+FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with .Cause }}{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}{{ end }}{{ end }}|
 `, ts.URL))
 
 	fs := b.Fs.Source
@@ -114,8 +114,8 @@ SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d
 FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200
 REMOTE NOT FOUND: OK
 LOCAL NOT FOUND: OK
-PRINT PROTOCOL ERROR DETAILS: Err: error calling resources.GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"||
-FAILED REMOTE ERROR DETAILS CONTENT: |failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed }
+PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at : error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"|
+FAILED REMOTE ERROR DETAILS CONTENT: failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed }
 |StatusCode: 501|ContentLength: 16|ContentType: text/plain; charset=utf-8|
 
 
diff --git a/resources/errorResource.go b/resources/errorResource.go
deleted file mode 100644
index 582c54f6d..000000000
--- a/resources/errorResource.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2021 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package resources
-
-import (
-	"context"
-	"image"
-
-	"github.com/gohugoio/hugo/common/hugio"
-	"github.com/gohugoio/hugo/common/maps"
-	"github.com/gohugoio/hugo/media"
-	"github.com/gohugoio/hugo/resources/images"
-	"github.com/gohugoio/hugo/resources/images/exif"
-	"github.com/gohugoio/hugo/resources/resource"
-)
-
-var (
-	_ error = (*errorResource)(nil)
-	// Image covers all current Resource implementations.
-	_ images.ImageResource = (*errorResource)(nil)
-	// The list of user facing and exported interfaces in resource.go
-	// Note that if we're missing some interface here, the user will still
-	// get an error, but not as pretty.
-	_ resource.ContentResource         = (*errorResource)(nil)
-	_ resource.ReadSeekCloserResource  = (*errorResource)(nil)
-	_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
-	// Make sure it also fails when passed to a pipe function.
-	_ ResourceTransformer = (*errorResource)(nil)
-)
-
-// NewErrorResource wraps err in a Resource where all but the Err method will panic.
-func NewErrorResource(err resource.ResourceError) resource.Resource {
-	return &errorResource{ResourceError: err}
-}
-
-type errorResource struct {
-	resource.ResourceError
-}
-
-func (e *errorResource) Err() resource.ResourceError {
-	return e.ResourceError
-}
-
-func (e *errorResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Content(context.Context) (any, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) ResourceType() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) MediaType() media.Type {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Permalink() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) RelPermalink() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Name() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Title() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Params() maps.Params {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Data() any {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Height() int {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Width() int {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Process(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Crop(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Fill(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Fit(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Resize(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Filter(filters ...any) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Exif() *exif.ExifInfo {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Colors() ([]images.Color, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) DecodeImage() (image.Image, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Transform(...ResourceTransformation) (ResourceTransformer, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) {
-	panic(e.ResourceError)
-}
diff --git a/resources/page/page_nop.go b/resources/page/page_nop.go
index 5a03b1994..af9f2682d 100644
--- a/resources/page/page_nop.go
+++ b/resources/page/page_nop.go
@@ -61,10 +61,6 @@ type nopPage int
 
 var noOpPathInfo = media.DefaultPathParser.Parse(files.ComponentFolderContent, "no-op.md")
 
-func (p *nopPage) Err() resource.ResourceError {
-	return nil
-}
-
 func (p *nopPage) Aliases() []string {
 	return nil
 }
diff --git a/resources/page/testhelpers_test.go b/resources/page/testhelpers_test.go
index 8a2d28e31..8e6dfb79a 100644
--- a/resources/page/testhelpers_test.go
+++ b/resources/page/testhelpers_test.go
@@ -111,10 +111,6 @@ type testPage struct {
 	sectionEntries []string
 }
 
-func (p *testPage) Err() resource.ResourceError {
-	return nil
-}
-
 func (p *testPage) Aliases() []string {
 	panic("testpage: not implemented")
 }
diff --git a/resources/resource.go b/resources/resource.go
index 7ab10b0ae..29b9e5ddd 100644
--- a/resources/resource.go
+++ b/resources/resource.go
@@ -224,9 +224,6 @@ type resourceCopier interface {
 
 // Copy copies r to the targetPath given.
 func Copy(r resource.Resource, targetPath string) resource.Resource {
-	if r.Err() != nil {
-		panic(fmt.Sprintf("Resource has an .Err: %s", r.Err()))
-	}
 	return r.(resourceCopier).cloneTo(targetPath)
 }
 
@@ -439,10 +436,6 @@ func (l *genericResource) Content(context.Context) (any, error) {
 	return hugio.ReadString(r)
 }
 
-func (r *genericResource) Err() resource.ResourceError {
-	return nil
-}
-
 func (l *genericResource) Data() any {
 	return l.sd.Data
 }
diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go
index b33750e80..585dfd150 100644
--- a/resources/resource/resourcetypes.go
+++ b/resources/resource/resourcetypes.go
@@ -43,6 +43,9 @@ type OriginProvider interface {
 
 // NewResourceError creates a new ResourceError.
 func NewResourceError(err error, data any) ResourceError {
+	if data == nil {
+		data = map[string]any{}
+	}
 	return &resourceError{
 		error: err,
 		data:  data,
@@ -65,13 +68,6 @@ type ResourceError interface {
 	ResourceDataProvider
 }
 
-// ErrProvider provides an Err.
-type ErrProvider interface {
-	// Err returns an error if this resource is in an error state.
-	// This will currently only be set for resources obtained from resources.GetRemote.
-	Err() ResourceError
-}
-
 // Resource represents a linkable resource, i.e. a content page, image etc.
 type Resource interface {
 	ResourceWithoutMeta
@@ -83,7 +79,6 @@ type ResourceWithoutMeta interface {
 	MediaTypeProvider
 	ResourceLinksProvider
 	ResourceDataProvider
-	ErrProvider
 }
 
 type ResourceWrapper interface {
diff --git a/resources/resource_factories/create/create_integration_test.go b/resources/resource_factories/create/create_integration_test.go
index 17084574d..7b9c96e34 100644
--- a/resources/resource_factories/create/create_integration_test.go
+++ b/resources/resource_factories/create/create_integration_test.go
@@ -31,18 +31,17 @@ func TestGetRemoteHead(t *testing.T) {
   [security.http]
     methods = ['(?i)GET|POST|HEAD']
     urls = ['.*gohugo\.io.*']
-
 -- layouts/index.html --
 {{ $url := "https://gohugo.io/img/hugo.png" }}
 {{ $opts := dict "method" "head" }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
   {{ with .Err }}
     {{ errorf "Unable to get remote resource: %s" . }}
-  {{ else }}
+  {{ else with .Value }}
     Head Content: {{ .Content }}. Head Data: {{ .Data }}
-  {{ end }}
-{{ else }}
+  {{ else }}
   {{ errorf "Unable to get remote resource: %s" $url }}
+  {{ end }}
 {{ end }}
 `
 
@@ -90,14 +89,15 @@ mediaTypes = ['text/plain']
 -- layouts/_default/single.html --
 {{ $url := printf "%s%s" "URL" .RelPermalink}}
 {{ $opts := dict }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
   {{ with .Err }}
-    {{ errorf "Got Err: %s. Data: %v" . .Data }}
-  {{ else }}
+     {{ errorf "Got Err: %s" . }}
+	 {{ with .Cause }}{{ errorf "Data: %s" .Data }}{{ end }}
+  {{ else with .Value }}
     Content: {{ .Content }}
+  {{ else }}
+    {{ errorf "Unable to get remote resource: %s" $url }}
   {{ end }}
-{{ else }}
-  {{ errorf "Unable to get remote resource: %s" $url }}
 {{ end }}
 `
 
diff --git a/resources/transform.go b/resources/transform.go
index c5d240669..73f3b85d2 100644
--- a/resources/transform.go
+++ b/resources/transform.go
@@ -192,10 +192,6 @@ func (r *resourceAdapter) Content(ctx context.Context) (any, error) {
 	return r.target.Content(ctx)
 }
 
-func (r *resourceAdapter) Err() resource.ResourceError {
-	return nil
-}
-
 func (r *resourceAdapter) GetIdentity() identity.Identity {
 	return identity.FirstIdentity(r.target)
 }
diff --git a/tpl/internal/go_templates/texttemplate/hugo_template.go b/tpl/internal/go_templates/texttemplate/hugo_template.go
index 0dbee02f7..36962c444 100644
--- a/tpl/internal/go_templates/texttemplate/hugo_template.go
+++ b/tpl/internal/go_templates/texttemplate/hugo_template.go
@@ -19,6 +19,7 @@ import (
 	"io"
 	"reflect"
 
+	"github.com/gohugoio/hugo/common/herrors"
 	"github.com/gohugoio/hugo/common/hreflect"
 
 	"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
@@ -256,14 +257,34 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
 	panic("not reached")
 }
 
+// newErrorWithCause creates a new error with the given cause.
+func newErrorWithCause(err error) *TryError {
+	return &TryError{Err: err, Cause: herrors.Cause(err)}
+}
+
+// TryError wraps an error with a cause.
+type TryError struct {
+	Err   error
+	Cause error
+}
+
+func (e *TryError) Error() string {
+	return e.Err.Error()
+}
+
+func (e *TryError) Unwrap() error {
+	return e.Err
+}
+
 // TryValue is what gets returned when using the "try" keyword.
 type TryValue struct {
 	// Value is the value returned by the function or method wrapped with "try".
 	// This will always be nil if Err is set.
 	Value any
+
 	// Err is the error returned by the function or method wrapped with "try".
 	// This will always be nil if Value is set.
-	Err error
+	Err *TryError
 }
 
 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
@@ -274,10 +295,11 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
 	if name == "try" {
 		defer func() {
 			if r := recover(); r != nil {
+				// Cause: herrors.Cause(err)
 				if err, ok := r.(error); ok {
-					val = reflect.ValueOf(TryValue{nil, err})
+					val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(err)})
 				} else {
-					val = reflect.ValueOf(TryValue{nil, fmt.Errorf("%v", r)})
+					val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(fmt.Errorf("%v", r))})
 				}
 			}
 		}()
@@ -396,7 +418,7 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
 
 	// Added for Hugo.
 	if name == "try" {
-		return reflect.ValueOf(TryValue{vv.Interface(), nil})
+		return reflect.ValueOf(TryValue{Value: vv.Interface()})
 	}
 
 	return vv
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
index beace14e6..f28cc36fe 100644
--- a/tpl/resources/resources.go
+++ b/tpl/resources/resources.go
@@ -115,14 +115,10 @@ func (ns *Namespace) Get(filename any) resource.Resource {
 //
 // Note: This method does not return any error as a second return value,
 // for any error situations the error can be checked in .Err.
-func (ns *Namespace) GetRemote(args ...any) resource.Resource {
+func (ns *Namespace) GetRemote(args ...any) (resource.Resource, error) {
 	get := func(args ...any) (resource.Resource, error) {
-		if len(args) < 1 {
-			return nil, errors.New("must provide an URL")
-		}
-
-		if len(args) > 2 {
-			return nil, errors.New("must not provide more arguments than URL and options")
+		if len(args) < 1 || len(args) > 2 {
+			return nil, errors.New("must provide an URL and optionally an options map")
 		}
 
 		urlstr, err := cast.ToStringE(args[0])
@@ -146,12 +142,12 @@ func (ns *Namespace) GetRemote(args ...any) resource.Resource {
 	if err != nil {
 		switch v := err.(type) {
 		case *create.HTTPError:
-			return resources.NewErrorResource(resource.NewResourceError(v, v.Data))
+			return nil, resource.NewResourceError(v, v.Data)
 		default:
-			return resources.NewErrorResource(resource.NewResourceError(fmt.Errorf("error calling resources.GetRemote: %w", err), make(map[string]any)))
+			return nil, resource.NewResourceError(err, nil)
 		}
 	}
-	return r
+	return r, nil
 }
 
 // GetMatch finds the first Resource matching the given pattern, or nil if none found.
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
index ba5a851ee..b88cf7ce0 100644
--- a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
+++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
@@ -17,13 +17,13 @@
   {{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
   {{- $query := querify "url" $url "dnt" .dnt -}}
   {{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
-  {{- with resources.GetRemote $request -}}
+  {{- with try (resources.GetRemote $request) -}}
     {{- with .Err -}}
       {{- errorf "%s" . -}}
-    {{- else -}}
+    {{- else with .Value -}}
       {{- (. | transform.Unmarshal).html | safeHTML -}}
-    {{- end -}}
-  {{- else -}}
+    {{- else -}}
     {{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
+    {{- end -}}
   {{- end -}}
 {{- end -}}
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
index 1f3b3c523..0fc8613b9 100644
--- a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
+++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
@@ -14,14 +14,14 @@
   {{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
   {{- $query := querify "url" $url "dnt" .dnt "omit_script" true -}}
   {{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
-  {{- with resources.GetRemote $request -}}
+  {{- with try (resources.GetRemote $request) -}}
     {{- with .Err -}}
       {{- errorf "%s" . -}}
-    {{- else -}}
+    {{- else with .Value -}}
       {{- (. | transform.Unmarshal).html | safeHTML -}}
+    {{- else -}}
+      {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
     {{- end -}}
-  {{- else -}}
-    {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
   {{- end -}}
 {{- end -}}
 
@@ -31,7 +31,16 @@
     {{- .Page.Scratch.Set "__h_simple_twitter_css" true -}}
     
+  {{- end -}}
+{{- end -}}
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 0a593593b..0ea7117a3 100644
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -66,6 +66,8 @@ const (
 // We need this to identify position in templates with base templates applied.
 var identifiersRe = regexp.MustCompile(`at \<(.*?)(\.{3})?\>:`)
 
+// The tweet and twitter shortcodes were deprecated in favor of the x shortcode
+// in v0.141.0. We can remove these aliases in v0.155.0 or later.
 var embeddedTemplatesAliases = map[string][]string{
 	"shortcodes/twitter.html": {"shortcodes/tweet.html"},
 }
diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go
index 8088a0551..6ccf27f80 100644
--- a/tpl/tplimpl/tplimpl_integration_test.go
+++ b/tpl/tplimpl/tplimpl_integration_test.go
@@ -734,3 +734,154 @@ https://gohugo.io"
 		``,
 	)
 }
+
+// Issue 13214
+// We deprecated the twitter, tweet (alias of twitter), and twitter_simple
+// shortcodes in v0.141.0, replacing them with x and x_simple.
+func TestXShortcodes(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['home','rss','section','sitemap','taxonomy','term']
+#CONFIG
+-- content/p1.md --
+---
+title: p1
+---
+{{< x user="SanDiegoZoo" id="1453110110599868418" >}}
+-- content/p2.md --
+---
+title: p2
+---
+{{< twitter user="SanDiegoZoo" id="1453110110599868418" >}}
+-- content/p3.md --
+---
+title: p3
+---
+{{< tweet user="SanDiegoZoo" id="1453110110599868418" >}}
+-- content/p4.md --
+---
+title: p4
+---
+{{< x_simple user="SanDiegoZoo" id="1453110110599868418" >}}
+-- content/p5.md --
+---
+title: p5
+---
+{{< twitter_simple user="SanDiegoZoo" id="1453110110599868418" >}}
+-- layouts/_default/single.html --
+{{ .Content | strings.TrimSpace | safeHTML }}
+--
+`
+
+	b := hugolib.Test(t, files)
+
+	// Test x, twitter, and tweet shortcodes
+	want := `
+	`
+	b.AssertFileContent("public/p1/index.html", want)
+
+	htmlFiles := []string{
+		b.FileContent("public/p1/index.html"),
+		b.FileContent("public/p2/index.html"),
+		b.FileContent("public/p3/index.html"),
+	}
+	if !allElementsEqual(htmlFiles) {
+		t.Error("A: expected all files to be equal")
+	}
+
+	// Test x_simple and twitter_simple shortcodes
+	wantSimple := "

Owl bet you'll lose this staring contest 🦉 pic.twitter.com/eJh4f2zncC

— San Diego Zoo Wildlife Alliance (@sandiegozoo) October 26, 2021
\n--" + b.AssertFileContent("public/p4/index.html", wantSimple) + + htmlFiles = []string{ + b.FileContent("public/p4/index.html"), + b.FileContent("public/p5/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("B: expected all files to be equal") + } + + filesOriginal := files + + // Test privacy.twitter.simple + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.simple=true") + b = hugolib.Test(t, files) + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + b.FileContent("public/p5/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("C: expected all files to be equal") + } + + // Test privacy.x.simple + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.simple=true") + b = hugolib.Test(t, files) + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p4/index.html"), + b.FileContent("public/p4/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("D: expected all files to be equal") + } + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("E: expected all files to be equal") + } + + // Test privacy.twitter.disable + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.disable = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", "") + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + b.FileContent("public/p4/index.html"), + b.FileContent("public/p4/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("F: expected all files to be equal") + } + + // Test privacy.x.disable + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.disable = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", "") + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p4/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("G: expected all files to be equal") + } + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + } + if !allElementsEqual(htmlFiles) { + t.Error("F: expected all files to be equal") + } +} + +// allElementsEqual reports whether all elements in the given string slice are +// equal. +func allElementsEqual(slice []string) bool { + if len(slice) == 0 { + return true + } + first := slice[0] + for _, v := range slice[1:] { + if v != first { + return false + } + } + return true +} From 88ecc3b753ed8adbd3854082652d1016df2de1ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 16 Jan 2025 10:34:52 +0100 Subject: [PATCH 085/374] docs: Regen CLI docs --- docs/content/en/commands/hugo_config.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/en/commands/hugo_config.md b/docs/content/en/commands/hugo_config.md index 4cf08e6ad..2b4eaafa1 100644 --- a/docs/content/en/commands/hugo_config.md +++ b/docs/content/en/commands/hugo_config.md @@ -24,6 +24,7 @@ hugo config [command] [flags] --format string preferred file format (toml, yaml or json) (default "toml") -h, --help help for config --lang string the language to display config for. Defaults to the first language defined. + --printZero include config options with zero values (e.g. false, 0, "") in the output --renderSegments strings named segments to render (configured in the segments config) -t, --theme strings themes to use (located in /themes/THEMENAME/) ``` From 81a7b6390036138356773c87a886679c81c524e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 16 Jan 2025 10:35:07 +0100 Subject: [PATCH 086/374] Squashed 'docs/' changes from f0f4bcb24..4429eeeea 4429eeeea Update image render hooks examples to conform with Commonmark 5391dddea Add class attribute to heading render hook examples git-subtree-dir: docs git-subtree-split: 4429eeeea84bfa4b9e636deaab5c9620ccf776a6 --- content/en/render-hooks/headings.md | 4 ++-- content/en/render-hooks/images.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/content/en/render-hooks/headings.md b/content/en/render-hooks/headings.md index dc64296ea..2635802e8 100755 --- a/content/en/render-hooks/headings.md +++ b/content/en/render-hooks/headings.md @@ -64,7 +64,7 @@ In its default configuration, Hugo renders Markdown headings according to the [C [CommonMark specification]: https://spec.commonmark.org/current/ {{< code file=layouts/_default/_markup/render-heading.html copy=true >}} - + {{- .Text -}} {{< /code >}} @@ -72,7 +72,7 @@ In its default configuration, Hugo renders Markdown headings according to the [C To add an anchor link to the right of each heading: {{< code file=layouts/_default/_markup/render-heading.html copy=true >}} - + {{ .Text }} # diff --git a/content/en/render-hooks/images.md b/content/en/render-hooks/images.md index eca346c10..d3067c5a4 100755 --- a/content/en/render-hooks/images.md +++ b/content/en/render-hooks/images.md @@ -91,7 +91,7 @@ In its default configuration, Hugo renders Markdown images according to the [Com {{< code file=layouts/_default/_markup/render-image.html copy=true >}} {{ . }} {{- /* chomp trailing newline */ -}} @@ -103,13 +103,13 @@ To render standalone images within `figure` elements: {{- if .IsBlock -}}
{{ . }} {{- with .Title }}
{{ . }}
{{ end -}}
{{- else -}} {{ . }} {{- end -}} From 8b526269158de5ea092506a40202900c0786f41f Mon Sep 17 00:00:00 2001 From: Jack Baldry Date: Thu, 16 Jan 2025 09:58:18 +0000 Subject: [PATCH 087/374] common/paths: Fix docstring --- common/paths/url.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/paths/url.go b/common/paths/url.go index 867f68baf..1d1408b51 100644 --- a/common/paths/url.go +++ b/common/paths/url.go @@ -228,7 +228,7 @@ func UrlFromFilename(filename string) (*url.URL, error) { }, nil } -// UrlToFilename converts the URL s to a filename. +// UrlStringToFilename converts the URL s to a filename. // If ParseRequestURI fails, the input is just converted to OS specific slashes and returned. func UrlStringToFilename(s string) (string, bool) { u, err := url.ParseRequestURI(s) From 1fad3832a920dc435b43ce0e6e812984b6b77175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 16 Jan 2025 10:53:53 +0100 Subject: [PATCH 088/374] tpl/tplimpl: Simplify some test assertions --- htesting/hqt/checkers.go | 26 +++++++++++++ tpl/tplimpl/tplimpl_integration_test.go | 51 ++++++------------------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/htesting/hqt/checkers.go b/htesting/hqt/checkers.go index bf2cb9428..a05206535 100644 --- a/htesting/hqt/checkers.go +++ b/htesting/hqt/checkers.go @@ -96,6 +96,32 @@ func normalizeString(s string) string { return strings.Join(lines, "\n") } +// IsAllElementsEqual asserts that all elements in the slice are equal. +var IsAllElementsEqual qt.Checker = &sliceAllElementsEqualChecker{ + argNames: []string{"got"}, +} + +type sliceAllElementsEqualChecker struct { + argNames +} + +func (c *sliceAllElementsEqualChecker) Check(got any, args []any, note func(key string, value any)) (err error) { + gotSlice := reflect.ValueOf(got) + numElements := gotSlice.Len() + if numElements < 2 { + return nil + } + first := gotSlice.Index(0).Interface() + // Check that the others are equal to the first. + for i := 1; i < numElements; i++ { + if diff := cmp.Diff(first, gotSlice.Index(i).Interface()); diff != "" { + return fmt.Errorf("element %d is not equal to the first element:\n%s", i, diff) + } + } + + return nil +} + // DeepAllowUnexported creates an option to allow compare of unexported types // in the given list of types. // see https://github.com/google/go-cmp/issues/40#issuecomment-328615283 diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index 6ccf27f80..d1e214ce2 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -6,6 +6,7 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/htesting/hqt" "github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/tpl" ) @@ -787,9 +788,8 @@ title: p5 b.FileContent("public/p2/index.html"), b.FileContent("public/p3/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("A: expected all files to be equal") - } + + b.Assert(htmlFiles, hqt.IsAllElementsEqual) // Test x_simple and twitter_simple shortcodes wantSimple := "

Owl bet you'll lose this staring contest 🦉 pic.twitter.com/eJh4f2zncC

— San Diego Zoo Wildlife Alliance (@sandiegozoo) October 26, 2021
\n--" @@ -799,9 +799,7 @@ title: p5 b.FileContent("public/p4/index.html"), b.FileContent("public/p5/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("B: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) filesOriginal := files @@ -813,9 +811,7 @@ title: p5 b.FileContent("public/p3/index.html"), b.FileContent("public/p5/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("C: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) // Test privacy.x.simple files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.simple=true") @@ -825,16 +821,13 @@ title: p5 b.FileContent("public/p4/index.html"), b.FileContent("public/p4/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("D: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + htmlFiles = []string{ b.FileContent("public/p2/index.html"), b.FileContent("public/p3/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("E: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) // Test privacy.twitter.disable files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.disable = true") @@ -847,9 +840,7 @@ title: p5 b.FileContent("public/p4/index.html"), b.FileContent("public/p4/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("F: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) // Test privacy.x.disable files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.disable = true") @@ -859,29 +850,11 @@ title: p5 b.FileContent("public/p1/index.html"), b.FileContent("public/p4/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("G: expected all files to be equal") - } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + htmlFiles = []string{ b.FileContent("public/p2/index.html"), b.FileContent("public/p3/index.html"), } - if !allElementsEqual(htmlFiles) { - t.Error("F: expected all files to be equal") - } -} - -// allElementsEqual reports whether all elements in the given string slice are -// equal. -func allElementsEqual(slice []string) bool { - if len(slice) == 0 { - return true - } - first := slice[0] - for _, v := range slice[1:] { - if v != first { - return false - } - } - return true + b.Assert(htmlFiles, hqt.IsAllElementsEqual) } From e7bd51698e5c3778a86003018702b1a7dcb9559a Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Thu, 16 Jan 2025 13:11:18 +0000 Subject: [PATCH 089/374] releaser: Bump versions for release of 0.141.0 [ci skip] --- common/hugo/version_current.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index c3ef7c5f8..fa8049885 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -19,5 +19,5 @@ var CurrentVersion = Version{ Major: 0, Minor: 141, PatchLevel: 0, - Suffix: "-DEV", + Suffix: "", } From e91d3cff98b58e1f7ede2ccf086962eb18bc808d Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Thu, 16 Jan 2025 13:26:52 +0000 Subject: [PATCH 090/374] releaser: Prepare repository for 0.142.0-DEV [ci skip] --- common/hugo/version_current.go | 4 ++-- hugoreleaser.env | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index fa8049885..29944bdb9 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 141, + Minor: 142, PatchLevel: 0, - Suffix: "", + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index 0c40dd43e..d41200e30 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.140.2 -HUGORELEASER_COMMITISH=aae02ca612a02e085c08366a9c9279f4abb39d94 +HUGORELEASER_TAG=v0.141.0 +HUGORELEASER_COMMITISH=e7bd51698e5c3778a86003018702b1a7dcb9559a + From 8897113666c9e7e62fed945602910ebcd0ec6421 Mon Sep 17 00:00:00 2001 From: Nicolas Friedli Date: Fri, 17 Jan 2025 19:06:54 +0100 Subject: [PATCH 091/374] tpl: Add loading attribute to qr shortcode --- tpl/tplimpl/embedded/templates/shortcodes/qr.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/qr.html b/tpl/tplimpl/embedded/templates/shortcodes/qr.html index 252ebeae7..818f6656d 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/qr.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/qr.html @@ -9,6 +9,7 @@ Encodes the given text into a QR code using the specified options and renders th @param {string} [class] The class attribute of the img element. @param {string} [id] The id attribute of the img element. @param {string} [title] The title attribute of the img element. +@param {string} [loading] The loading attribute of the img element, one of lazy, or eager. @returns {template.HTML} @@ -46,6 +47,8 @@ Encodes the given text into a QR code using the specified options and renders th {{- $class := or (.Get "class") "" }} {{- $id := or (.Get "id") "" }} {{- $title := or (.Get "title") "" }} +{{- $loading := or (.Get "loading") "" }} + {{- /* Validate arguments. */}} {{- $errors := false}} @@ -71,6 +74,7 @@ Encodes the given text into a QR code using the specified options and renders th {{- with $class }} class="{{ $class }}" {{- end }} {{- with $id }} id="{{ $id }}" {{- end }} {{- with $title }} title="{{ $title }}" {{- end -}} + {{- with $loading }} loading="{{ $loading }}" {{- end -}} > {{- end }} {{- end -}} From 1f5a15aba0fc7cc4f4cf4ce1afc8d6db81c63e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 16 Jan 2025 19:15:30 +0100 Subject: [PATCH 092/374] resources: Fix 2 image file cache key issues * Always include the content hash in the cache key for unprocessed images. * Always include the image config hash in the cache key. This is also a major cleanup/simplification of the implementation in this area. Note that this, unfortunately, forces new hashes/filenames for generated images. Fixes #13273 Fixes #13272 --- config/namespace.go | 2 +- config/namespace_test.go | 2 +- hugolib/hugo_sites_multihost_test.go | 4 +- hugolib/image_test.go | 16 +- .../pagesfromgotmpl_integration_test.go | 2 +- hugolib/resource_chain_test.go | 4 +- resources/image.go | 96 ++++-------- resources/image_cache.go | 2 +- resources/image_extended_test.go | 2 +- resources/image_test.go | 26 ++-- resources/images/config.go | 127 +++++++--------- resources/images/config_test.go | 28 ++-- resources/images/filters.go | 5 +- resources/images/image.go | 8 +- .../images/images_golden_integration_test.go | 142 ++++++++++++++++++ resources/images/imagetesting/testing.go | 25 ++- resources/images/smartcrop.go | 6 +- .../images_golden/filters/mask2/green.jpg | Bin 0 -> 8811 bytes .../images_golden/filters/mask2/pink.jpg | Bin 0 -> 6147 bytes .../methods/crop-sunsetjpg-200x200.jpg | Bin 0 -> 2524 bytes .../crop-sunsetjpg-350x400-center-q20.jpg | Bin 0 -> 4026 bytes .../crop-sunsetjpg-350x400-center-r90.jpg | Bin 0 -> 8499 bytes .../methods/crop-sunsetjpg-350x400-center.jpg | Bin 0 -> 8619 bytes .../methods/crop-sunsetjpg-350x400-smart.jpg | Bin 0 -> 8628 bytes .../methods/fill-sunsetjpg-90x120-left.jpg | Bin 0 -> 1668 bytes .../methods/fill-sunsetjpg-90x120-right.jpg | Bin 0 -> 1631 bytes .../methods/fit-sunsetjpg-200x200.jpg | Bin 0 -> 2877 bytes .../resize-gopherpng-100x-03fc56-jpg.jpg | Bin 0 -> 3181 bytes .../methods/resize-gopherpng-100x-fc03ec.png | Bin 0 -> 3209 bytes .../methods/resize-gopherpng-100x.png | Bin 0 -> 3528 bytes .../methods/resize-sunsetjpg-300x.jpg | Bin 0 -> 4950 bytes .../methods/resize-sunsetjpg-x200.jpg | Bin 0 -> 5434 bytes resources/resource.go | 5 + resources/resource_spec.go | 44 +++--- resources/resources_integration_test.go | 12 +- resources/testdata/mask2.png | Bin 0 -> 36823 bytes resources/transform_test.go | 15 +- tpl/resources/resources_integration_test.go | 2 +- 38 files changed, 342 insertions(+), 233 deletions(-) create mode 100644 resources/images/testdata/images_golden/filters/mask2/green.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask2/pink.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-r90.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-smart.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-left.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg create mode 100644 resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-fc03ec.png create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-x200.jpg create mode 100644 resources/testdata/mask2.png diff --git a/config/namespace.go b/config/namespace.go index 46b5014c3..e41b56e2d 100644 --- a/config/namespace.go +++ b/config/namespace.go @@ -22,7 +22,7 @@ import ( func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) { // Calculate the hash of the input (not including any defaults applied later). // This allows us to introduce new config options without breaking the hash. - h := hashing.HashString(configSource) + h := hashing.HashStringHex(configSource) // Build the config c, ext, err := buildConfig(configSource) diff --git a/config/namespace_test.go b/config/namespace_test.go index 5eacdeac7..df27ae05c 100644 --- a/config/namespace_test.go +++ b/config/namespace_test.go @@ -43,7 +43,7 @@ func TestNamespace(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(ns, qt.Not(qt.IsNil)) c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"}) - c.Assert(ns.SourceHash, qt.Equals, "1450430416588600409") + c.Assert(ns.SourceHash, qt.Equals, "1420f6c7782f7459") c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"}) c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil)) } diff --git a/hugolib/hugo_sites_multihost_test.go b/hugolib/hugo_sites_multihost_test.go index 39504202b..37f7ab927 100644 --- a/hugolib/hugo_sites_multihost_test.go +++ b/hugolib/hugo_sites_multihost_test.go @@ -205,9 +205,9 @@ title: mybundle-en b.AssertFileExists("public/de/mybundle/pixel.png", true) b.AssertFileExists("public/en/mybundle/pixel.png", true) - b.AssertFileExists("public/de/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/de/mybundle/pixel_hu_58204cbc58507d74.png", true) // failing test below - b.AssertFileExists("public/en/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/en/mybundle/pixel_hu_58204cbc58507d74.png", true) } func TestMultihostResourceOneBaseURLWithSuPath(t *testing.T) { diff --git a/hugolib/image_test.go b/hugolib/image_test.go index 7dcd9fc26..09a5b841e 100644 --- a/hugolib/image_test.go +++ b/hugolib/image_test.go @@ -72,20 +72,20 @@ SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Ex b.Build(BuildCfg{}) - b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") - b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") - b.AssertImage(200, 200, "public/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "public/bundle/sunset_hu_77061c65c31d2244.jpg") // Check the file cache - b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu_77061c65c31d2244.jpg") - b.AssertFileContent("resources/_gen/images/bundle/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/bundle/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") - b.AssertFileContent("resources/_gen/images/images/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/images/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") b.AssertNoDuplicateWrites() diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go index b033aad2b..64ee70397 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go @@ -119,7 +119,7 @@ docs/p1/sub/mymixcasetext2.txt "RelPermalink: /docs/p1/sub/mymixcasetext2.txt|Name: sub/mymixcasetext2.txt|", "RelPermalink: /mydata.yaml|Name: sub/data1.yaml|Title: Sub data|Params: map[]|", "Featured Image: /a/pixel.png|featured.png|", - "Resized Featured Image: /a/pixel_hu16809842526914527184.png|10|", + "Resized Featured Image: /a/pixel_hu_a32b3e361d55df1.png|10|", // Resource from string "RelPermalink: /docs/p1/mytext.txt|Name: textresource|Title: My Text Resource|Params: map[param1:param1v]|", // Dates diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 669114c8a..942873ae4 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -106,12 +106,12 @@ FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with b.AssertFileContent("public/index.html", fmt.Sprintf(` SUNSET: /images/sunset.jpg|/images/sunset.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT: /images/sunset.jpg|/images/sunset_hu15210517121918042184.jpg|200 +FIT: /images/sunset.jpg|/images/sunset_hu_f2aae87288f3c13b.jpg|200 CSS integrity Data first: sha256-od9YaHw8nMOL8mUy97Sy8sKwMV3N4hI3aVmZXATxH+8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03+ZmKY/1t2GCOjEEOXj2x2qow94vCc7o= SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200 +FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu_f2aae87288f3c13b.jpg|200 REMOTE NOT FOUND: OK LOCAL NOT FOUND: OK PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at : error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"| diff --git a/resources/image.go b/resources/image.go index 686f70e27..c1f107b59 100644 --- a/resources/image.go +++ b/resources/image.go @@ -30,7 +30,6 @@ import ( "github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/common/hashing" - "github.com/gohugoio/hugo/common/hstrings" "github.com/gohugoio/hugo/common/paths" "github.com/disintegration/gift" @@ -205,15 +204,12 @@ func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource, }, nil } -var imageActions = []string{images.ActionResize, images.ActionCrop, images.ActionFit, images.ActionFill} - // Process processes the image with the given spec. // The spec can contain an optional action, one of "resize", "crop", "fit" or "fill". // This makes this method a more flexible version that covers all of Resize, Crop, Fit and Fill, // but it also supports e.g. format conversions without any resize action. func (i *imageResource) Process(spec string) (images.ImageResource, error) { - action, options := i.resolveActionOptions(spec) - return i.processActionOptions(action, options) + return i.processActionSpec("", spec) } // Resize resizes the image to the specified width and height using the specified resampling @@ -243,7 +239,7 @@ func (i *imageResource) Fill(spec string) (images.ImageResource, error) { } func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { - var conf images.ImageConfig + var confMain images.ImageConfig var gfilters []gift.Filter @@ -251,47 +247,30 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { gfilters = append(gfilters, images.ToFilters(f)...) } - var ( - targetFormat images.Format - configSet bool - ) + var options []string + for _, f := range gfilters { f = images.UnwrapFilter(f) if specProvider, ok := f.(images.ImageProcessSpecProvider); ok { - action, options := i.resolveActionOptions(specProvider.ImageProcessSpec()) - var err error - conf, err = images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) - if err != nil { - return nil, err - } - configSet = true - if conf.TargetFormat != 0 { - targetFormat = conf.TargetFormat - // We only support one target format, but prefer the last one, - // so we keep going. - } + options = append(options, strings.Fields(specProvider.ImageProcessSpec())...) } } - if !configSet { - conf = images.GetDefaultImageConfig("filter", i.Proc.Cfg) + confMain, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) + if err != nil { + return nil, err } - conf.Action = "filter" - conf.Key = hashing.HashString(gfilters) - conf.TargetFormat = targetFormat - if conf.TargetFormat == 0 { - conf.TargetFormat = i.Format - } + confMain.Action = "filter" + confMain.Key = hashing.HashString(gfilters) - return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { + return i.doWithImageConfig(confMain, func(src image.Image) (image.Image, error) { var filters []gift.Filter for _, f := range gfilters { f = images.UnwrapFilter(f) if specProvider, ok := f.(images.ImageProcessSpecProvider); ok { - processSpec := specProvider.ImageProcessSpec() - action, options := i.resolveActionOptions(processSpec) - conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) + options := strings.Fields(specProvider.ImageProcessSpec()) + conf, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) if err != nil { return nil, err } @@ -313,25 +292,13 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { }) } -func (i *imageResource) resolveActionOptions(spec string) (string, []string) { - var action string - options := strings.Fields(spec) - for i, p := range options { - if hstrings.InSlicEqualFold(imageActions, p) { - action = p - options = append(options[:i], options[i+1:]...) - break - } - } - return action, options -} - func (i *imageResource) processActionSpec(action, spec string) (images.ImageResource, error) { - return i.processActionOptions(action, strings.Fields(spec)) + options := append([]string{action}, strings.Fields(strings.ToLower(spec))...) + return i.processOptions(options) } -func (i *imageResource) processActionOptions(action string, options []string) (images.ImageResource, error) { - conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) +func (i *imageResource) processOptions(options []string) (images.ImageResource, error) { + conf, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) if err != nil { return nil, err } @@ -343,13 +310,12 @@ func (i *imageResource) processActionOptions(action string, options []string) (i return nil, err } - if action == images.ActionFill { - if conf.Anchor == 0 && img.Width() == 0 || img.Height() == 0 { + if conf.Action == images.ActionFill { + if conf.Anchor == images.SmartCropAnchor && img.Width() == 0 || img.Height() == 0 { // See https://github.com/gohugoio/hugo/issues/7955 // Smartcrop fails silently in some rare cases. // Fall back to a center fill. - conf.Anchor = gift.CenterAnchor - conf.AnchorStr = "center" + conf = conf.Reanchor(gift.CenterAnchor) return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { return i.Proc.ApplyFiltersFromConfig(src, conf) }) @@ -417,7 +383,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im } ci := i.clone(converted) - targetPath := i.relTargetPathFromConfig(conf) + targetPath := i.relTargetPathFromConfig(conf, i.getSpec().imaging.Cfg.SourceHash) ci.setTargetPath(targetPath) ci.Format = conf.TargetFormat ci.setMediaType(conf.TargetFormat.MediaType()) @@ -485,26 +451,30 @@ func (i *imageResource) getImageMetaCacheTargetPath() string { df := i.getResourcePaths() p1, _ := paths.FileAndExt(df.File) h := i.hash() - idStr := hashing.HashString(h, i.size(), imageMetaVersionNumber, cfgHash) + idStr := hashing.HashStringHex(h, i.size(), imageMetaVersionNumber, cfgHash) df.File = fmt.Sprintf("%s_%s.json", p1, idStr) return df.TargetPath() } -func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) internal.ResourcePaths { +func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig, imagingConfigSourceHash string) internal.ResourcePaths { p1, p2 := paths.FileAndExt(i.getResourcePaths().File) if conf.TargetFormat != i.Format { p2 = conf.TargetFormat.DefaultExtension() } - const prefix = "_hu" - huIdx := strings.LastIndex(p1, prefix) - incomingID := "i" + + // Do not change. + const imageHashPrefix = "_hu_" + + huIdx := strings.LastIndex(p1, imageHashPrefix) + incomingID := "" if huIdx > -1 { - incomingID = p1[huIdx+len(prefix):] + incomingID = p1[huIdx+len(imageHashPrefix):] p1 = p1[:huIdx] } - hash := hashing.HashUint64(incomingID, i.hash(), conf.GetKey(i.Format)) + + hash := hashing.HashStringHex(incomingID, i.hash(), conf.Key, imagingConfigSourceHash) rp := i.getResourcePaths() - rp.File = fmt.Sprintf("%s%s%d%s", p1, prefix, hash, p2) + rp.File = fmt.Sprintf("%s%s%s%s", p1, imageHashPrefix, hash, p2) return rp } diff --git a/resources/image_cache.go b/resources/image_cache.go index d824c5d1a..1fc722609 100644 --- a/resources/image_cache.go +++ b/resources/image_cache.go @@ -37,7 +37,7 @@ func (c *ImageCache) getOrCreate( parent *imageResource, conf images.ImageConfig, createImage func() (*imageResource, image.Image, error), ) (*resourceAdapter, error) { - relTarget := parent.relTargetPathFromConfig(conf) + relTarget := parent.relTargetPathFromConfig(conf, parent.getSpec().imaging.Cfg.SourceHash) relTargetPath := relTarget.TargetPath() memKey := relTargetPath diff --git a/resources/image_extended_test.go b/resources/image_extended_test.go index e570c4a3d..f481be9bb 100644 --- a/resources/image_extended_test.go +++ b/resources/image_extended_test.go @@ -42,6 +42,6 @@ func TestImageResizeWebP(t *testing.T) { resized, err := image.Resize("123x") c.Assert(err, qt.IsNil) c.Assert(image.MediaType(), qt.Equals, media.Builtin.WEBPType) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunrise_hu544374262273649331.webp") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunrise_hu_a1deb893888915d9.webp") c.Assert(resized.Width(), qt.Equals, 123) } diff --git a/resources/image_test.go b/resources/image_test.go index 5639d457e..1ba5a149a 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -113,28 +113,28 @@ func TestImageTransformBasic(t *testing.T) { assertWidthHeight(resizedAndRotated, 125, 200) assertWidthHeight(resized, 300, 200) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu2082030801149749592.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu_d2115125d9324a79.jpg") fitted, err := resized.Fit("50x50") c.Assert(err, qt.IsNil) - c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu16263619592447877226.jpg") + c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu_c2c98e06123b048e.jpg") assertWidthHeight(fitted, 50, 33) // Check the MD5 key threshold fittedAgain, _ := fitted.Fit("10x20") fittedAgain, err = fittedAgain.Fit("10x20") c.Assert(err, qt.IsNil) - c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu847809310637164306.jpg") + c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu_dc9e89c10109de72.jpg") assertWidthHeight(fittedAgain, 10, 7) filled, err := image.Fill("200x100 bottomLeft") c.Assert(err, qt.IsNil) - c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu18289448341423092707.jpg") + c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu_b9f6d350738928fe.jpg") assertWidthHeight(filled, 200, 100) smart, err := image.Fill("200x100 smart") c.Assert(err, qt.IsNil) - c.Assert(smart.RelPermalink(), qt.Equals, "/a/sunset_hu11649371610839769766.jpg") + c.Assert(smart.RelPermalink(), qt.Equals, "/a/sunset_hu_6fd390e7b0d26f0b.jpg") assertWidthHeight(smart, 200, 100) // Check cache @@ -144,12 +144,12 @@ func TestImageTransformBasic(t *testing.T) { cropped, err := image.Crop("300x300 topRight") c.Assert(err, qt.IsNil) - c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu2242042514052853140.jpg") + c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu_3df036e11f4ddd43.jpg") assertWidthHeight(cropped, 300, 300) smartcropped, err := image.Crop("200x200 smart") c.Assert(err, qt.IsNil) - c.Assert(smartcropped.RelPermalink(), qt.Equals, "/a/sunset_hu12983255101170993571.jpg") + c.Assert(smartcropped.RelPermalink(), qt.Equals, "/a/sunset_hu_12e2d26de89b464b.jpg") assertWidthHeight(smartcropped, 200, 200) // Check cache @@ -216,7 +216,7 @@ func TestImageTransformFormat(t *testing.T) { imagePng, err := image.Resize("450x png") c.Assert(err, qt.IsNil) - c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu11737890885216583918.png") + c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu_e8b9444dcf2e75ef.png") c.Assert(imagePng.ResourceType(), qt.Equals, "image") assertExtWidthHeight(imagePng, ".png", 450, 281) c.Assert(imagePng.Name(), qt.Equals, "sunset.jpg") @@ -224,7 +224,7 @@ func TestImageTransformFormat(t *testing.T) { imageGif, err := image.Resize("225x gif") c.Assert(err, qt.IsNil) - c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu1431827106749674475.gif") + c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu_f80842d4c3789345.gif") c.Assert(imageGif.ResourceType(), qt.Equals, "image") assertExtWidthHeight(imageGif, ".gif", 225, 141) c.Assert(imageGif.Name(), qt.Equals, "sunset.jpg") @@ -247,7 +247,7 @@ func TestImagePermalinkPublishOrder(t *testing.T) { }() check1 := func(img images.ImageResource) { - resizedLink := "/a/sunset_hu7919355342577096259.jpg" + resizedLink := "/a/sunset_hu_3910bca82e28c9d6.jpg" c.Assert(img.RelPermalink(), qt.Equals, resizedLink) assertImageFile(c, spec.PublishFs, resizedLink, 100, 50) } @@ -288,12 +288,12 @@ func TestImageBugs(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(resized, qt.Not(qt.IsNil)) c.Assert(resized.Width(), qt.Equals, 200) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu9514381480012510326.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu_951d3980b18c52a9.jpg") resized, err = resized.Resize("100x") c.Assert(err, qt.IsNil) c.Assert(resized, qt.Not(qt.IsNil)) c.Assert(resized.Width(), qt.Equals, 100) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu1776700126481066216.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu_1daa203572ecd6ec.jpg") }) // Issue #6137 @@ -391,7 +391,7 @@ func TestImageResize8BitPNG(t *testing.T) { resized, err := image.Resize("800x") c.Assert(err, qt.IsNil) c.Assert(resized.MediaType().Type, qt.Equals, "image/png") - c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu8582372628235034388.png") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu_fe2b762e9cac406c.png") c.Assert(resized.Width(), qt.Equals, 800) } diff --git a/resources/images/config.go b/resources/images/config.go index 9655e9a51..6fcd2e334 100644 --- a/resources/images/config.go +++ b/resources/images/config.go @@ -20,6 +20,7 @@ import ( "strconv" "strings" + "github.com/gohugoio/hugo/common/hashing" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/media" @@ -37,6 +38,13 @@ const ( ActionFill = "fill" ) +var Actions = map[string]bool{ + ActionResize: true, + ActionCrop: true, + ActionFit: true, + ActionFill: true, +} + var ( imageFormats = map[string]Format{ ".jpg": JPEG, @@ -64,9 +72,9 @@ var ( // Add or increment if changes to an image format's processing requires // re-generation. imageFormatsVersions = map[Format]int{ - PNG: 3, // Fix transparency issue with 32 bit images. - WEBP: 2, // Fix transparency issue with 32 bit images. - GIF: 1, // Fix resize issue with animated GIFs when target != GIF. + PNG: 0, + WEBP: 0, + GIF: 0, } // Increment to mark all processed images as stale. Only use when absolutely needed. @@ -84,6 +92,7 @@ var anchorPositions = map[string]gift.Anchor{ strings.ToLower("BottomLeft"): gift.BottomLeftAnchor, strings.ToLower("Bottom"): gift.BottomAnchor, strings.ToLower("BottomRight"): gift.BottomRightAnchor, + smartCropIdentifier: SmartCropAnchor, } // These encoding hints are currently only relevant for Webp. @@ -176,7 +185,7 @@ func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, Ima return i, nil, err } - if i.Imaging.Anchor != "" && i.Imaging.Anchor != smartCropIdentifier { + if i.Imaging.Anchor != "" { anchor, found := anchorPositions[i.Imaging.Anchor] if !found { return i, nil, fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor) @@ -201,36 +210,34 @@ func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, Ima return ns, nil } -func DecodeImageConfig(action string, options []string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], sourceFormat Format) (ImageConfig, error) { +func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], sourceFormat Format) (ImageConfig, error) { var ( - c ImageConfig = GetDefaultImageConfig(action, defaults) + c ImageConfig = GetDefaultImageConfig(defaults) err error ) - action = strings.ToLower(action) - - c.Action = action - - if options == nil { - return c, errors.New("image options cannot be empty") + // Make to lower case, trim space and remove any empty strings. + n := 0 + for _, s := range options { + s = strings.TrimSpace(s) + if s != "" { + options[n] = strings.ToLower(s) + n++ + } } + options = options[:n] for _, part := range options { - part = strings.ToLower(part) - - if part == smartCropIdentifier { - c.AnchorStr = smartCropIdentifier + if _, ok := Actions[part]; ok { + c.Action = part } else if pos, ok := anchorPositions[part]; ok { c.Anchor = pos - c.AnchorStr = part } else if filter, ok := imageFilters[part]; ok { c.Filter = filter - c.FilterStr = part } else if hint, ok := hints[part]; ok { c.Hint = hint } else if part[0] == '#' { - c.BgColorStr = part[1:] - c.BgColor, err = hexStringToColorGo(c.BgColorStr) + c.BgColor, err = hexStringToColorGo(part[1:]) if err != nil { return c, err } @@ -291,8 +298,7 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN } } - if action != "" && c.FilterStr == "" { - c.FilterStr = defaults.Config.Imaging.ResampleFilter + if c.Action != "" && c.Filter == nil { c.Filter = defaults.Config.ResampleFilter } @@ -300,8 +306,7 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN c.Hint = webpoptions.EncodingPresetPhoto } - if action != "" && c.AnchorStr == "" { - c.AnchorStr = defaults.Config.Imaging.Anchor + if c.Action != "" && c.Anchor == -1 { c.Anchor = defaults.Config.Anchor } @@ -318,10 +323,23 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN if c.BgColor == nil && c.TargetFormat != sourceFormat { if sourceFormat.SupportsTransparency() && !c.TargetFormat.SupportsTransparency() { c.BgColor = defaults.Config.BgColor - c.BgColorStr = defaults.Config.Imaging.BgColor } } + if mainImageVersionNumber > 0 { + options = append(options, strconv.Itoa(mainImageVersionNumber)) + } + + if v, ok := imageFormatsVersions[sourceFormat]; ok && v > 0 { + options = append(options, strconv.Itoa(v)) + } + + if smartCropVersionNumber > 0 && c.Anchor == SmartCropAnchor { + options = append(options, strconv.Itoa(smartCropVersionNumber)) + } + + c.Key = hashing.HashStringHex(options) + return c, nil } @@ -350,8 +368,7 @@ type ImageConfig struct { // not support transparency. // When set per image operation, it's used even for formats that does support // transparency. - BgColor color.Color - BgColorStr string + BgColor color.Color // Hint about what type of picture this is. Used to optimize encoding // when target is set to webp. @@ -360,57 +377,15 @@ type ImageConfig struct { Width int Height int - Filter gift.Resampling - FilterStr string + Filter gift.Resampling - Anchor gift.Anchor - AnchorStr string + Anchor gift.Anchor } -func (i ImageConfig) GetKey(format Format) string { - if i.Key != "" { - return i.Action + "_" + i.Key - } - - k := strconv.Itoa(i.Width) + "x" + strconv.Itoa(i.Height) - if i.Action != "" { - k += "_" + i.Action - } - // This slightly odd construct is here to preserve the old image keys. - if i.qualitySetForImage || i.TargetFormat.RequiresDefaultQuality() { - k += "_q" + strconv.Itoa(i.Quality) - } - if i.Rotate != 0 { - k += "_r" + strconv.Itoa(i.Rotate) - } - if i.BgColorStr != "" { - k += "_bg" + i.BgColorStr - } - - if i.TargetFormat == WEBP { - k += "_h" + strconv.Itoa(int(i.Hint)) - } - - anchor := i.AnchorStr - if anchor == smartCropIdentifier { - anchor = anchor + strconv.Itoa(smartCropVersionNumber) - } - - k += "_" + i.FilterStr - - if i.Action == ActionFill || i.Action == ActionCrop { - k += "_" + anchor - } - - if v, ok := imageFormatsVersions[format]; ok { - k += "_" + strconv.Itoa(v) - } - - if mainImageVersionNumber > 0 { - k += "_" + strconv.Itoa(mainImageVersionNumber) - } - - return k +func (cfg ImageConfig) Reanchor(a gift.Anchor) ImageConfig { + cfg.Anchor = a + cfg.Key = hashing.HashStringHex(cfg.Key, "reanchor", a) + return cfg } type ImagingConfigInternal struct { @@ -429,7 +404,7 @@ func (i *ImagingConfigInternal) Compile(externalCfg *ImagingConfig) error { return err } - if externalCfg.Anchor != "" && externalCfg.Anchor != smartCropIdentifier { + if externalCfg.Anchor != "" { anchor, found := anchorPositions[externalCfg.Anchor] if !found { return fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor) diff --git a/resources/images/config_test.go b/resources/images/config_test.go index 6dd545f2c..d3c9827bd 100644 --- a/resources/images/config_test.go +++ b/resources/images/config_test.go @@ -19,6 +19,7 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/hashing" ) func TestDecodeConfig(t *testing.T) { @@ -106,7 +107,8 @@ func TestDecodeImageConfig(t *testing.T) { if err != nil { t.Fatal(err) } - result, err := DecodeImageConfig(this.action, strings.Fields(this.in), cfg, PNG) + options := append([]string{this.action}, strings.Fields(this.in)...) + result, err := DecodeImageConfig(options, cfg, PNG) if b, ok := this.expect.(bool); ok && !b { if err == nil { t.Errorf("[%d] parseImageConfig didn't return an expected error", i) @@ -115,15 +117,19 @@ func TestDecodeImageConfig(t *testing.T) { if err != nil { t.Fatalf("[%d] err: %s", i, err) } - if fmt.Sprint(result) != fmt.Sprint(this.expect) { - t.Fatalf("[%d] got\n%v\n but expected\n%v", i, result, this.expect) + expect := this.expect.(ImageConfig) + expect.Key = hashing.HashStringHex(options) + + if fmt.Sprint(result) != fmt.Sprint(expect) { + t.Fatalf("[%d] got\n%v\n but expected\n%v", i, result, expect) } } } } func newImageConfig(action string, width, height, quality, rotate int, filter, anchor, bgColor string) ImageConfig { - var c ImageConfig = GetDefaultImageConfig(action, nil) + var c ImageConfig = GetDefaultImageConfig(nil) + c.Action = action c.TargetFormat = PNG c.Hint = 2 c.Width = width @@ -131,26 +137,20 @@ func newImageConfig(action string, width, height, quality, rotate int, filter, a c.Quality = quality c.qualitySetForImage = quality != 75 c.Rotate = rotate - c.BgColorStr = bgColor c.BgColor, _ = hexStringToColorGo(bgColor) + c.Anchor = SmartCropAnchor if filter != "" { filter = strings.ToLower(filter) if v, ok := imageFilters[filter]; ok { c.Filter = v - c.FilterStr = filter } } if anchor != "" { - if anchor == smartCropIdentifier { - c.AnchorStr = anchor - } else { - anchor = strings.ToLower(anchor) - if v, ok := anchorPositions[anchor]; ok { - c.Anchor = v - c.AnchorStr = anchor - } + anchor = strings.ToLower(anchor) + if v, ok := anchorPositions[anchor]; ok { + c.Anchor = v } } diff --git a/resources/images/filters.go b/resources/images/filters.go index 64776affe..9c2b9b46f 100644 --- a/resources/images/filters.go +++ b/resources/images/filters.go @@ -36,10 +36,11 @@ type Filters struct{} // Process creates a filter that processes an image using the given specification. func (*Filters) Process(spec any) gift.Filter { + specs := strings.ToLower(cast.ToString(spec)) return filter{ - Options: newFilterOpts(spec), + Options: newFilterOpts(specs), Filter: processFilter{ - spec: cast.ToString(spec), + spec: specs, }, } } diff --git a/resources/images/image.go b/resources/images/image.go index 4d5285acd..c891b0168 100644 --- a/resources/images/image.go +++ b/resources/images/image.go @@ -217,7 +217,7 @@ func (p *ImageProcessor) FiltersFromConfig(src image.Image, conf ImageConfig) ([ case "resize": filters = append(filters, gift.Resize(conf.Width, conf.Height, conf.Filter)) case "crop": - if conf.AnchorStr == smartCropIdentifier { + if conf.Anchor == SmartCropAnchor { bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) if err != nil { return nil, err @@ -232,7 +232,7 @@ func (p *ImageProcessor) FiltersFromConfig(src image.Image, conf ImageConfig) ([ filters = append(filters, gift.CropToSize(conf.Width, conf.Height, conf.Anchor)) } case "fill": - if conf.AnchorStr == smartCropIdentifier { + if conf.Anchor == SmartCropAnchor { bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) if err != nil { return nil, err @@ -329,12 +329,12 @@ func (p *ImageProcessor) doFilter(src image.Image, targetFormat Format, filters return dst, nil } -func GetDefaultImageConfig(action string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) ImageConfig { +func GetDefaultImageConfig(defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) ImageConfig { if defaults == nil { defaults = defaultImageConfig } return ImageConfig{ - Action: action, + Anchor: -1, // The real values start at 0. Hint: defaults.Config.Hint, Quality: defaults.Config.Imaging.Quality, } diff --git a/resources/images/images_golden_integration_test.go b/resources/images/images_golden_integration_test.go index 526e3ba53..c49de7bd1 100644 --- a/resources/images/images_golden_integration_test.go +++ b/resources/images/images_golden_integration_test.go @@ -15,6 +15,7 @@ package images_test import ( _ "image/jpeg" + "strings" "testing" "github.com/gohugoio/hugo/resources/images/imagetesting" @@ -158,6 +159,76 @@ the last entry will win. imagetesting.RunGolden(opts) } +// Issue 13272, 13273. +func TestImagesGoldenFiltersMaskCacheIssues(t *testing.T) { + if imagetesting.SkipGoldenTests { + t.Skip("Skip golden test on this architecture") + } + + // Will be used as the base folder for generated images. + name := "filters/mask2" + + files := ` +-- hugo.toml -- +[caches] + [caches.images] + dir = ':cacheDir/golden_images' + maxAge = "30s" +[imaging] + bgColor = '#33ff44' + hint = 'photo' + quality = 75 + resampleFilter = 'Lanczos' +-- assets/sunset.jpg -- +sourcefilename: ../testdata/sunset.jpg +-- assets/mask.png -- +sourcefilename: ../testdata/mask.png + +-- layouts/index.html -- +Home. +{{ $sunset := resources.Get "sunset.jpg" }} +{{ $mask := resources.Get "mask.png" }} + + +{{ template "mask" (dict "name" "green.jpg" "base" $sunset "mask" $mask) }} + +{{ define "mask"}} +{{ $ext := path.Ext .name }} +{{ if lt (len (path.Ext .name)) 4 }} + {{ errorf "No extension in %q" .name }} +{{ end }} +{{ $format := strings.TrimPrefix "." $ext }} +{{ $spec := .spec | default (printf "resize x300 %s" $format) }} +{{ $filters := slice (images.Process $spec) (images.Mask .mask) }} +{{ $name := printf "images/%s" .name }} +{{ $img := .base.Filter $filters }} +{{ with $img | resources.Copy $name }} +{{ .Publish }} +{{ end }} +{{ end }} +` + + tempDir := t.TempDir() + + opts := imagetesting.DefaultGoldenOpts + opts.WorkingDir = tempDir + opts.T = t + opts.Name = name + opts.Files = files + opts.SkipAssertions = true + + imagetesting.RunGolden(opts) + + files = strings.Replace(files, "#33ff44", "#a83269", -1) + files = strings.Replace(files, "green", "pink", -1) + files = strings.Replace(files, "mask.png", "mask2.png", -1) + opts.Files = files + opts.SkipAssertions = false + opts.Rebuild = true + + imagetesting.RunGolden(opts) +} + func TestImagesGoldenFiltersText(t *testing.T) { t.Parallel() @@ -263,3 +334,74 @@ Home. imagetesting.RunGolden(opts) } + +func TestImagesGoldenMethods(t *testing.T) { + t.Parallel() + + if imagetesting.SkipGoldenTests { + t.Skip("Skip golden test on this architecture") + } + + // Will be used as the base folder for generated images. + name := "methods" + + files := ` +-- hugo.toml -- +[imaging] + bgColor = '#ebcc34' + hint = 'photo' + quality = 75 + resampleFilter = 'MitchellNetravali' +-- assets/sunset.jpg -- +sourcefilename: ../testdata/sunset.jpg +-- assets/gopher.png -- +sourcefilename: ../testdata/gopher-hero8.png + +-- layouts/index.html -- +Home. +{{ $sunset := resources.Get "sunset.jpg" }} +{{ $gopher := resources.Get "gopher.png" }} + + +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "resize" "spec" "300x" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "resize" "spec" "x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fill" "spec" "90x120 left" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fill" "spec" "90x120 right" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fit" "spec" "200x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "200x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center" ) }} + {{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 smart" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center r90" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center q20" ) }} +{{ template "invoke" (dict "copyFormat" "png" "base" $gopher "method" "resize" "spec" "100x" ) }} +{{ template "invoke" (dict "copyFormat" "png" "base" $gopher "method" "resize" "spec" "100x #fc03ec" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $gopher "method" "resize" "spec" "100x #03fc56 jpg" ) }} + +{{ define "invoke"}} +{{ $spec := .spec }} +{{ $name := printf "images/%s-%s-%s.%s" .method ((trim .base.Name "/") | lower | anchorize) ($spec | anchorize) .copyFormat }} +{{ $img := ""}} +{{ if eq .method "resize" }} + {{ $img = .base.Resize $spec }} +{{ else if eq .method "fill" }} + {{ $img = .base.Fill $spec }} +{{ else if eq .method "fit" }} + {{ $img = .base.Fit $spec }} +{{ else if eq .method "crop" }} + {{ $img = .base.Crop $spec }} +{{ else }} + {{ errorf "Unknown method %q" .method }} +{{ end }} +{{ with $img | resources.Copy $name }} +{{ .Publish }} +{{ end }} +{{ end }} +` + + opts := imagetesting.DefaultGoldenOpts + opts.T = t + opts.Name = name + opts.Files = files + + imagetesting.RunGolden(opts) +} diff --git a/resources/images/imagetesting/testing.go b/resources/images/imagetesting/testing.go index 8e00751e0..22a2317a1 100644 --- a/resources/images/imagetesting/testing.go +++ b/resources/images/imagetesting/testing.go @@ -63,8 +63,18 @@ type GoldenImageTestOpts struct { // Set to true to write golden files to disk. WriteFiles bool + // If not set, a temporary directory will be created. + WorkingDir string + // Set to true to skip any assertions. Useful when adding new golden variants to a test. DevMode bool + + // Set to skip any assertions. + SkipAssertions bool + + // Whether this represents a rebuild of the same site. + // Setting this to true will keep the previous golden image set. + Rebuild bool } // To rebuild all Golden image tests, toggle WriteFiles=true and run: @@ -78,7 +88,10 @@ var DefaultGoldenOpts = GoldenImageTestOpts{ func RunGolden(opts GoldenImageTestOpts) *hugolib.IntegrationTestBuilder { opts.T.Helper() - c := hugolib.Test(opts.T, opts.Files, hugolib.TestOptWithOSFs()) // hugolib.TestOptWithPrintAndKeepTempDir(true)) + c := hugolib.Test(opts.T, opts.Files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) { + conf.NeedsOsFS = true + conf.WorkingDir = opts.WorkingDir + })) c.AssertFileContent("public/index.html", "Home.") outputDir := filepath.Join(c.H.Conf.WorkingDir(), "public", "images") @@ -86,12 +99,18 @@ func RunGolden(opts GoldenImageTestOpts) *hugolib.IntegrationTestBuilder { goldenDir := filepath.Join(goldenBaseDir, filepath.FromSlash(opts.Name)) if opts.WriteFiles { c.Assert(htesting.IsRealCI(), qt.IsFalse) - c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil) - c.Assert(os.RemoveAll(goldenDir), qt.IsNil) + if !opts.Rebuild { + c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil) + c.Assert(os.RemoveAll(goldenDir), qt.IsNil) + } c.Assert(hugio.CopyDir(hugofs.Os, outputDir, goldenDir, nil), qt.IsNil) return c } + if opts.SkipAssertions { + return c + } + if opts.DevMode { c.Assert(htesting.IsRealCI(), qt.IsFalse) return c diff --git a/resources/images/smartcrop.go b/resources/images/smartcrop.go index 864c6de0a..af45c241c 100644 --- a/resources/images/smartcrop.go +++ b/resources/images/smartcrop.go @@ -25,10 +25,10 @@ import ( const ( // Do not change. smartCropIdentifier = "smart" - - // This is just a increment, starting on 1. If Smart Crop improves its cropping, we + SmartCropAnchor = 1000 + // This is just a increment, starting on 0. If Smart Crop improves its cropping, we // need a way to trigger a re-generation of the crops in the wild, so increment this. - smartCropVersionNumber = 1 + smartCropVersionNumber = 0 ) func (p *ImageProcessor) newSmartCropAnalyzer(filter gift.Resampling) smartcrop.Analyzer { diff --git a/resources/images/testdata/images_golden/filters/mask2/green.jpg b/resources/images/testdata/images_golden/filters/mask2/green.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48a9dd0830f7437a20f7acc07c45e8ed2fc8c7c9 GIT binary patch literal 8811 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 R3@w50UuVew>-ERX{{nqf%KZQU literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/filters/mask2/pink.jpg b/resources/images/testdata/images_golden/filters/mask2/pink.jpg new file mode 100644 index 0000000000000000000000000000000000000000..640e41ab156598b15259c5fd999e745376b69f9f GIT binary patch literal 6147 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10569ab2c55facee47a0efb5f33fa88717694953 GIT binary patch literal 2524 zcmbV~dsq`^7RDzDArPR73|FHFh$Iz)A<=;4qE&(z1v7vbjIzX)C3FJ>3L=7vh_yjP zB^WPQAR%&5*IFaRwu%B-Aq#2{ya0k+1i{rHVz?Eu-vsKj``_-)^UQoR=bZ1n@9%u0 zd#-DTjzUI;h6F>r5rIH3HZ~%f!e*u>CZ@IvEat(B?CcjWvLlg}ETy_EaddSek;qJn z>k1l!!LWDn^6~WWrqUT67-M5&Qxj7gGcy|x2aTRZ1vE@aA=jFqdHo?ffht@mdIYzW*K5*oI3 z+x8vdd;YvP>Mzm!n8a_BlJ}+TPt819glA+PZss`(F1Cycrz&F!FKqlXh%;ViE)4W^urC z7U5)UV8W5UyQV$g+mf)j5G6#U>S=_|Sb6SIjUqT*JmMR_pqu&sy|tfZ2$-ki!3<-%zF+Ky@* zh*q*I^8^^4k3vvR3lu@QFt0>uQ{=i$No`A}tdYmt;XR4yi!cYMKbI@NI^2Qu6yR-2 zu)Gr?hviC$4u;T!ii@}*H`qXv#ug=0g#P!UD8eA6jr0{0=3NqTIq^R>azqCyG&Dg& zczH0_5Ony!B3D`72*Qeq+|Z8X`NS=hHKiE;ni`%2u99Q3vySyh;C~`L`8Bm`yE4fCSYE6)={NRDN6lg2S+bhQ!8Yl4y{d0K?Yb7o|s*MGJ;Y`yLsNg0vS|T3iT#A z30IkLk>0kI1zdz=1c96x00n6(i0}&VRC3k!*jbV}Y1G5HIS<3NlFiPQ)Q#?xga2p%c$}4bpqd2_@JyH+ z`a@DC4FH!YNxgjwMYJHP#B+i;E?2r)=6a+j6 ztj(x*gT+x21sd*9UkECiAaFJ{*_LZFU)x{U^d!;WJ(^Sk-&#DhtCd?fk}Gct_(hUL zjU35sa-%0vhrq;>&=>#U6%zsdC!Xx%R3ZJ##RsFCK(IRmjCVrDXd1mZm^e?I$B2@3 zv4*(!Bhgq3h-H1xr(AsVl3n2HD~cMT>(^L*%>Ftz+ajcSz`U@!QVBKD_ z)!3A}y0cC%Lysk~JevrpZs?#!vV5-0yi;c~Ms zwZvBRY>ZFcoGmU)^0YhbjT_i4SV3}hTCIu zYu`^){L*iXrw7x(HkGy0(*ml`5Z7cwi^bHF-zHVoi$}YKnk}!QPnQoIP!Ak<8eP~~ z;4)So@8)mk7#^|eW?koKxBDF`XUandUEJtg;TRsD*D0?HI{z&@ctM!Cvv(lUvo0eg z5V_K7dFFCJxzIywB5GZ2cR6S4o|RM=Z#ADXT0a!5DwbXP!Cl)xh|)oKH`@y&>T0LW zu?pr(OKsuSjDhszA{|t@k#?%q$2WwnZ8-3q{jkNl>G7BV%{Y>>ecszvLz5Tj3uh)6 z(vgVw)zc$5R3{_Dw2o7I@9Chk35U82#k(KrAn!i3ddI<* zeP2p$^7} zvWKf5xJ(bnteyFFxVZWny9RNK3X}ZFPF#Bx*)v^>n6bFHz9t=%W*0eC`gSW}@>W^O zYF0M=THnefrUtH^fnxkych2nBm+%M2LqG*MyS)2|`xNW?-mydXUH?`QFw90f5u5*|`^^Dj7CU(40K_!q(EbASK< literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b316ff17c5fb6e86117cf4fad7b6172aa309adb GIT binary patch literal 4026 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

#CDXQY8cz}vHMGb2KIekcctl*-V z?)l>~eI*56{uAaJ2I}mBN&UR6EQU^loBd|`o@^R@tm673#<_1x$ByqXp8G38+SBTn zS-%9!WogeZzvsq}zm9!A$WyM*-2II-_}xM;vpYcsmMQ#s^2DB+;J|>UM79GfFJQn0 zcIxv%>9bh!At$UjSuVgk?rP1A>zta_b>e;Ppz^te7Bihfi^wQT)c*JZd5V2x?t|;K z$BuaHxV+a0Vb+h$ZC9OIv-j6II%4=)5te6Y9Jj^ro>N9f!Gz)NHaV5_0839GSsiLE zkF(`C7+?b_*q?G&Alm{&4sT;E+W^M4L!->vxsHPYjzS7btEHc#ULX%Rqrep?65i1n z*Hy)jnI5V5w{kbL(rlsD+~KW66z5|yV>dO$qqQZ+2{z~{MpVkdjfoMq)cTSS(6jbWcjP-;TRIKR zcDVE`N$sP;u6B=U6gd@tWt>am3J~EB*t5Yg$p$b;;95xSDUkZ(W`NT|K4GAyMG^2& zy|4x1or8<~8X1{G>7C&OrIq7SCNi{*t-~P`;2E<7F!eTb$XJXTH?_W@85=2U16KPRZ}0cULy8;>Bu{^yT*)Q;0<|2!qkL592vq!oR4%TSjBxuTKVYHH!Qi4%eRySC1Z< zn1}I(j8dFb+u{D&eRiSYt8OQ@J>h*+HLS7XH_7cUPsXJ#Ye1AqyJf6}Uh`rmR!N|F zOvp9`FWOF_eLC3~0>T4bDlk(( zF1z0PbZ{xL=mNkWiZ-pRcBh{f5;yc#v)_O~p0vni8z^wt-JIicqU<;iZFZcGNn-JP z;?-!z*N#d{r*_vnd7Brw$q)SYS}x1|fUPJl*4&mjM#*sT>00s06N%o5h!?I%yzVd& zOO?o#}Q7e#cU^Pl)(h41Nf(K6J!f&z(sLbjLay@F=fs8MdisrD^54K(l0`7p>m8k%cRv{(P-^VDC_fOlNV~YT zZZg#EEiY8NUE`X!Xq=yX&fxHvXiY%S{$Hy`83(^)S|&H@&S)u|cPWE~9~ozTwPhcG zNB4XQVD(C`x(%EfF*12Z<6yQo$aNZH(QB^gHEUTUi=j?NT07%rY7Q5H*MhNw8XM-FNNpKunsrWf>Oma*$Wyv#ps4WRTRh{04RZM8HUfa1+%@t|KoxGX6e6e>R@oN+F zzTUjrcY}@%^sW6TJ0JGR8a}cS-k7h~+{Wknb@@)l3|Y_$l*)xnmm(_D18mKgN_ zP;D!KQU09p<~ceR7^&h!FQj0eg5gFT)eOr=w~xE{0FI42hNI$!0M{~>D4zF@j6l%2 zGd4PGlP(4Yzdk{{aCXb>*uOsk(I`onNTndD2Wg!0Sz^zj`tqLgokz=OUr(M5kT@`? znV`wkl7KW~qNWK#Vf;>Dw#G43&In{GAYb1V*cKNDw=v z|L|$#nA-X)%+Z3bH;Z29%zCEs+8^4pE-ULUF~Vmgw9!{NL0$@^xnZnwz*YUV@&p2W zQ4b6p-T_#oAb=rZ1t6dyfWSjyok7wS0&G#7=ob(TS)gdArNVF^0%l$m@(k~pkSj+y z*{1+w1i4>75Da4ug~u~01Of(-4%HaPgK=yZWZWW3hEe#;4c1LnTsXVr2CLnH&2;W_ z9N*6SgW{V{-ph5^UR`Zu`>G)<7EfZ7XEfqEf=+`x5kZZDNh zcgM&R{$lvrr(JbLcY~3{TQk%cHl70{kUX-eX>F`9$SKfkK0`0DP`s-L5??SC{-O@) z1!$$&FIo^^#g`>!Aww-%p|2BKB+O|idY8$$GMB6i{;fxs98~*{2XW!!v2~9BcH@{so1LF;&0gJnu#~#P>uP{JdGPoIafR~yug2e8 z+05oaOC$G!NJ^@Ewqf(&Kl!_y zo1x!J* z-4(pfQcNlvaBS74Cvtfcy+E++6Km0FMNkAYFBmV=Q&4;uLH&i?VY_*O=n$@FKHna^ z>f5PZNN@}SE&W~cgrCV)v)?-bvBmb30l}8Z$ZYWZW@b8MiG3U}cu;6)2((Z6 zsaPS8>>nio8N@V*1Z(V7rD`J_oEUJ7RyI21dUSVO!H4tQWQ^ibFgN8XR*g&q#jxdZrl5+0dGtW{gwGU6V>AFv?I3miq*(!_ z9|U=ndZAPYq-Z$EBE8Z*F#6Rq<>nOld^t)zUy$H{N2W~hYe0bt*6<7miH+qJtZDCB zS5e2Nvmz=&#s!Ui+W(`uh6S07umR1JXsejiFsRSpwO1k z9I^gU@pNSLUA&dLI;WP$$Y98CrRh z(uJyjT7SmjpsplS+VzQW?9ES?$uW7B48MfeK4!g}gvG$YX-`a*w38r6fJFwv9ZH;V zH34Th!YQC0Vl&0gcmcKa2^yOybKi4ssxdWW!G;?5izCyj9(s5`~pUS zOe9Kjz?B`cWd*cYgMU*A@Wes{peD-nAZS3Qo^IOV63y7CbHp9Ib3FG5?J(v|eq`@5 z-9+(m7bjQds@W9Za`iRw`d&NVoxd-QobY-*TKBjb8*pgP{2mKKtJFbE-;nR9_=06b z>odQoOsjA4UY{q`HH_uN%q;a@)VI!3|0(5sy{uF2z4urL-KZo-Zg~Q;!`ARL?R?Oa z`~Mu^&Xj&j+Nkr}^8#71X>#3C7q03Zw46R6T4ju6sY~_e+qj1fBmqK!q|k2}5eMq< z6fdyF!V}R?g0g1;6d`~v!3w~D^1%cO0s_-c74HCbZNN0B0C2!A5h~cco7r%X?iCBD zMr`PVta)kGMPSC!ps!=D7=V=}9s_!SV8htvivAA}`?!Ln z)8IV2Vzx}D;QB*Hly;=xH0?v&eE0qyw-gbwNK21-aSvdA@L-nXKa;{KhPb~^}aaO)BL2R-mktqxueR4W=3hgT>~DBN2rR9 zKW%(f3GeO>^%?gyzB_%-egRQ-^BbO1Bg9R;v3qe_lJ}(}xYpf6DJ!RlN}UWCwMg&C zR+CafEIvDTXw-<@kNe;*UL|b@g#`=Rygge`{$M1WpzuK40pWrG00YUO7sP#mnv)gO zej+B!E{80F6u9m#dKn~$>KX7pZl*R@0Jtgb*iUZpntX@aNgcYtJp_wcMX$n`w=DFX zMj?2Bs9Wq|i?v_=R(u!s7j|HM+pd>F0fxrDbA5aI4TWnL zth4C@{yE>*KO^x+8*KO9C{D^rNKT%sxLSV*LDV_3-38#%R-a)&Fe)v6Wo|yR*OG(fiz=m!9 z=gvbAdB8GL4GS%qNV@PDsG6ZufWm@rEwvB<`4s|6*1eD`gQNrPf%|HUEGYdT{DH3( z8C_>!0lrpJT`fJE;oUqJmQAa`T_8&p1MctZ2#qMSH(y~9&%HkWspjm8>4&rRPus(q zmBQaB)+S4ZoH!M%)XtA7uCPp-m9B+A78n&Q_<~QO8ML? zu2rc*b40#qVyx?@ld*n53)`Ko$DYi2QFclUjQF^quq&4rMq=xgCzT9_!=)t1oYee= z>y*2Hp^s*_^Pc|_TxRjT>IckucIEr7Wtn~QBX=KMBm1AV@Sa{KyDd356ZgsAlI0e} zmHeUn9dpw6#=by)&2cFPUvi9i?oPDhd!@`pZ-qy57Kx_c)tRd~Gn2Ph;y~t!m-Q7Z zZTS%AHH>dJ6AtaF2_PwFy!1}|JaI@y3O2k+c(`BlHjJSlYC`UNcU$uPMjE3i3Ua%^*dMs8?CmZV`x-e=)dBU3CWoZ!Az`YI_ z31lq67fJ=5pjIUT|LGA3pnc$LM}RI0j1yfdYXDgVxB~#Z(}ck0u)U#ijwb4d6;UAn zY<@w4){-S0`XeZ!K?@^hpJS^sE?ndn#(*Q&&H+s`CfK>z&({*S@sB;j1nZQIUT*yQ z1{E^Gd+Ja=(A2~@xpmP-gqvt6^n1ymnes z<^DIlCl_v8e2Ox%$;T?E^T>n?Z`?@a(g5rPHhWun#&q%r{mrn!Tl&b9HGYc5y0U7A z#X(K^GjEpKl9y~&tyS|t#AO!H<^`7c(0y%j?FaQHrZQMuX*ZR!L|2euAU+u`qD|ZUG<^p z9c8f9pLaImG=An*;Fgdq6vzr>feBt_7*Yo5)<6sZ*ARlm1f(T^grF>vpbZ!$gER<| znLH@rpd1O7h1_6va08Wx2dxuqTmdF-wsjoJ2()x%o-m{|DJWLQIcO;~`!>oniZzt- zq!OE7u<>rbZJR9$xgn!TPaSHYn@cONzG1(KM2;r$Ri<}^nJ$SDUu3=>xHY)zbkyIS zdCm1AH(Q=F(}6`c@PyrJo%7qyOnL4YoA$rUlvo@|VkI27q!mJX9+uKrD4C4+4(O&- z)*etF6XLR1xA>a;=5N&}r@M}Nmc}fX1y)-;L0q_YW>b4(lPD)%OAT4HR?R|LP;D#p z{*TZ=GP^$oKmsBbO18z)dJ!lIXs5SIEJhv+6wWv+bJaq)rb!rDMjk^mbQ--)dj2@lLwt0YpJIb&gA zsKWIJERp~9&2q)%boc*A*#sVXex?rdw_`|SAsqcCP~q3=YiA<+^G!d~?q84YJFC0E z$Vpvt3x0H`ROZQKtxEnLzv6@&YIEh(rOPZ2_peV)y>j9kLVAwnU2Q1b4R(hk1d_@6 znfR|cLCX0ISkB-GsS(z%5+2tNJ9z|3pP*BNdxyYiJV8MCucMex^j1lT2)0*HfIxzE z7U4VzT4zx*)W9MUTF;}j34(i#3@}VknM9BuX+crM(k)~ln^s5hG;pqKW3hE8 TGG147_`JCc<$f6A$BX{~pt#FN literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa89eedf28d53e0b8318fe131f28d1b0e78945f9 GIT binary patch literal 8619 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 R-by(aGM3`xXYu>(e*tIwFC=!wcXrfG{zE zo4^ooI2?&YpiC{yOwnjlYn(a8!p_FQ-pcCNF#>s-PDCtF+M61N2&o>VH;!Iidr zDS4SYg-Qk?kw{at>1;Ez*$91VjM9SP%pYGBkn&004r4z6SU^ zATR_9Gl3(JDD*4<41z$wPzVeLg&JRG8Seln7KU^5^fNIJi-!}~7UZ0ga|kE@x`%j1 ztJayq+?|U=Sz67uwwdSRN^~Pp7cE}0l;*W^)sF#z^q}DIb?YM{qc%h*Y)Ra@ZF>@H zPfF_EwDf%$hYlaf%P;tqbNoap_heal#VNu0N?}#?1(Epjm8Qwji_VvGct9dstIQn61{G)DS^3xOufPCW^&u^fAc(6tf7z%|z;a@x; zaGJ3oSSZZV(*)-i29IZ(6UaFT3;&XHbq|qF6owYh+}(<@bf)&q8~sB1D*AVz-2Wx| z4*Kpfr~y+5$oOFpEZ_@_0#{#i=SHUoD{?(bN9}@f!l-v(kR6>M>hR(wRKeo$bb^FH zO6Qbw1f=AA^~C~FFA1n}6n@AOYYWw7Ejqp_z$FOjB8D{8$th|uu~0H)PJKUV)4+eRVRyxVBJT6*ah}xG)FRG|X7ANg1E;WtqF>eCZ}7p=mh6IB?^2 zf*$6DJYK(2nRm8dAC_F#5Wr2s74+&P?1rrQ_9>f*iejmA%4ClZH__3j^sd0c%_cCs z@_>@$pG9g|kww%M&=8istcnDk1P!lWbIBoiQ!A!~dy+E1kRDJb#_2?s<^!wM{Qkt$ z;uZd*!r9fP)N+LLRnXgm&fQQr|( z+p6XB$;Cf>gy5sRnl?UpxLOuY0X~2;$+))y{hQ&fzz_(;yow-|VOC`bZ_&NbW`*0LqipHME(+;#qtvrL zaM#5!wLF{5?kHa6vp3~YqdyK6yXKXqTZ*yr@DF$!-%1~fEv-=94xtYPzjBMUlV&Jx zd1MN>Nf#^HS@y)AC5w-2$ooio#P3hQhX*JL?d~1veQFe*`NP3y!Hr!@#Na(4-etM+ z+Gtj(MJ(fw5NX%VTG7kuB0?O!bW|l01r{E0 zQ$lyy{TN9XZw{OkvbLUnFPZiOKf^`}(JY(6lQh3jnm^W@DBUqpHcS5D5>01v#XiVx zg66F6XzMS+hx$h6_P&fUyKdH&O@ZX7d2h`gL36sRGUG7T17XWx8KGA8wupNxixJinb2Pt7KtwPm}+Azjsi{kCJz zZ{XeZ_q8_p?L%w)cS7UN$!jv&*FcH@@1oNR&Gbgfr1>Zx(e3gnji`A?!eU)>9#0HJ lZqql1){bUv2g!g)n&w{8nb4WZ&*EKLJW?+oTH#>m{0n9);E4bL literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg b/resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4afcb6decdb5ecf1040dc037000d7e09ab4db81d GIT binary patch literal 1631 zcmbW!dpOg390%~&+GXrpD6o*{Qw$; z0#Qgb2!a?4S{+NkVKp?cL~R{S!XlC(c@fFLz{t$j!pOwN)WCq^XlY|drO{}H7IdbQ zy|b+Y%^rrqV6YlkJseKY-q^s{{y#@~6TqVZ0T2v_nE+~d7#t5%w!lgN0EUEp4e)nh zYH$P+1)?$P8d`uF3=UUAz>!D)Rrc#tXDt*X{a_WRrcxe^OK#fBT@HEwaxT6roT5vi)?HMr#0 zsRmxX{0ugrL0%~wlGV%lWyP_nd{hLDEbXw6iDjDn?0fRFsA&nrsV4XA2onXU7xJEz zTuAn7A@`(&iI6wM29gM;5QX3M4k&o#knFUolD@Z<-5@V7Naa8NW@5K5pb7_){Bdzm z$k8oU3mXVmG9qv_pQ>1Oy=@YRok*4@Adhmh%7UtH-R4v;YlCtd>LN9Cy)Gtm(=*HP zY$zvrIT!EOe4bx#Qug{?RzTw-y{fEEHfKxy&m|hZOBcjxckpzhM7 zs+Y702#vadP|UQYhpceqlA!mfi)bwP=2;_uYYP8@sf{%~8i(oHzso4h>gq_vDevgI z=thmFr3$x8qZvo)7a8lKhPmU-ZcoppG_hW4{vpm{xC``OY&C7|1g+(J=1Irj_YN`! zmyY#JVoc_L$!E{Z_*fCP_{~3ll(KT)lwc^dx~VYlIwKU~)jJ1YQZBv=$f2 z2i{~E2xs%$!gZG1F)3sO#980Ie&F!3mR%q5fA)T6beHm)CI)7U-ikY8e#UPn24SL( zocA>}Y!7T7;ppK5e(g8X1Br9BNmp#iv8z}R$QrT!40e)=T5x7{)d{ZD8S{h_WLt3~ zXLc|oT?y3YkKs&z%uDZhbPeYz(QbMo>n^4i8{6i*KwVsy|UW~8sbquJQI@|_uJg$Zbx?^*1Jj`ZiQKPBdje&kzmm@yQ5 zWjJwavV0Ap!*r`U=<+r`ReFDp(4u($mUiDNrGThsvqBgVe1zMhsF+GUy?St6?@5BN xFMU-+R1J|KtQ=&K+>PclP{%b3ChpLiGGi3d1Blufjp)3*G__3+%?W5_-(PM2$5a3S literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg b/resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75c6a30b02467bf9cd1ac0ba39ad4ba3464c269c GIT binary patch literal 2877 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg b/resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2e78ce461c194c7a1528b7a434bb4509d51a079a GIT binary patch literal 3181 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png b/resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png new file mode 100644 index 0000000000000000000000000000000000000000..9bca47b147f8ce0d40528fd99a1c3ce5e7db5a90 GIT binary patch literal 3528 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg b/resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..319385e9a5ac217e6241409636979b77fd2de69a GIT binary patch literal 4950 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 JsWJA#{SQ Date: Sun, 19 Jan 2025 07:00:49 -0800 Subject: [PATCH 093/374] tpl/tplimpl: Fix context in shortcode error messages Fixes #13279 --- .../embedded/templates/shortcodes/qr.html | 3 +- .../templates/shortcodes/twitter.html | 6 +- .../templates/shortcodes/twitter_simple.html | 2 +- .../embedded/templates/shortcodes/vimeo.html | 4 +- .../templates/shortcodes/vimeo_simple.html | 12 +- .../embedded/templates/shortcodes/x.html | 6 +- .../templates/shortcodes/x_simple.html | 2 +- tpl/tplimpl/shortcodes_integration_test.go | 330 ++++++++++++++++++ tpl/tplimpl/tplimpl_integration_test.go | 225 ------------ 9 files changed, 347 insertions(+), 243 deletions(-) create mode 100644 tpl/tplimpl/shortcodes_integration_test.go diff --git a/tpl/tplimpl/embedded/templates/shortcodes/qr.html b/tpl/tplimpl/embedded/templates/shortcodes/qr.html index 818f6656d..05e8dbde9 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/qr.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/qr.html @@ -9,7 +9,7 @@ Encodes the given text into a QR code using the specified options and renders th @param {string} [class] The class attribute of the img element. @param {string} [id] The id attribute of the img element. @param {string} [title] The title attribute of the img element. -@param {string} [loading] The loading attribute of the img element, one of lazy, or eager. +@param {string} [loading] The loading attribute of the img element, one of lazy or eager. @returns {template.HTML} @@ -49,7 +49,6 @@ Encodes the given text into a QR code using the specified options and renders th {{- $title := or (.Get "title") "" }} {{- $loading := or (.Get "loading") "" }} - {{- /* Validate arguments. */}} {{- $errors := false}} {{- if not $text }} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html index 7a4adea5d..ce356559d 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html @@ -1,5 +1,5 @@ {{- warnf "The \"twitter\", \"tweet\", and \"twitter_simple\" shortcodes were deprecated in v0.142.0 and will be removed in a future release. Please use the \"x\" shortcode instead." }} -{{- $pc := .Page.Site.Config.Privacy.Twitter -}} +{{- $pc := site.Config.Privacy.Twitter -}} {{- if not $pc.Disable -}} {{- if $pc.Simple -}} {{- template "_internal/shortcodes/twitter_simple.html" . -}} @@ -7,7 +7,7 @@ {{- $id := or (.Get "id") "" -}} {{- $user := or (.Get "user") "" -}} {{- if and $id $user -}} - {{- template "render-tweet" (dict "id" $id "user" $user "dnt" $pc.EnableDNT "name" .Name "position" .Position) -}} + {{- template "render-tweet" (dict "id" $id "user" $user "dnt" $pc.EnableDNT "ctx" .) -}} {{- else -}} {{- errorf "The %q shortcode requires two named parameters: user and id. See %s" .Name .Position -}} {{- end -}} @@ -24,7 +24,7 @@ {{- else with .Value -}} {{- (. | transform.Unmarshal).html | safeHTML -}} {{- else -}} - {{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}} + {{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" $.ctx.Name $.ctx.Position -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html index 7251f64e3..e9dcc76ba 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html @@ -23,7 +23,7 @@ {{- end }} {{- (. | transform.Unmarshal).html | safeHTML -}} {{- else -}} - {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .ctx.Name .ctx.Position -}} + {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" $.ctx.Name $.ctx.Position -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html index 8ddad9b43..b3fc781a3 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html @@ -1,4 +1,4 @@ -{{- $pc := .Page.Site.Config.Privacy.Vimeo -}} +{{- $pc := site.Config.Privacy.Vimeo -}} {{- if not $pc.Disable -}} {{- if $pc.Simple -}} {{ template "_internal/shortcodes/vimeo_simple.html" . }} @@ -11,4 +11,4 @@

{{ end }} {{- end -}} -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html index 7f7940b80..11f19b1f6 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html @@ -1,6 +1,6 @@ -{{- $pc := .Page.Site.Config.Privacy.Vimeo -}} +{{- $pc := site.Config.Privacy.Vimeo -}} {{- if not $pc.Disable -}} - {{- $ctx := dict "page" .Page "pc" $pc "name" .Name "position" .Position }} + {{- $ctx := dict "ctx" . }} {{- if .IsNamedParams -}} {{- with .Get "id" -}} {{- $ctx = merge $ctx (dict "id" . "class" ($.Get "class")) -}} @@ -19,7 +19,7 @@ {{- end -}} {{- define "render-vimeo" -}} - {{- $dnt := cond .pc.EnableDNT 1 0 -}} + {{- $dnt := cond site.Config.Privacy.Vimeo.EnableDNT 1 0 -}} {{- $url := urls.JoinPath "https://vimeo.com" .id -}} {{- $query := querify "url" $url "dnt" $dnt -}} {{- $request := printf "https://vimeo.com/api/oembed.json?%s" $query -}} @@ -32,7 +32,7 @@ {{- with $.class -}} {{- $class = printf "%s %s" "s_video_simple" . -}} {{- else -}} - {{ template "__h_simple_css" $.page }} + {{ template "__h_simple_css" $.ctx.Page }} {{- end -}} {{- $thumbnail := .thumbnail_url -}} {{- $original := $thumbnail | replaceRE "(_.*\\.)" "." -}} @@ -40,13 +40,13 @@ {{ .title }}
- {{ template "__h_simple_icon_play" $.page }} + {{ template "__h_simple_icon_play" $.ctx.Page }}
{{- end -}} {{- else -}} - {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}} + {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" $.ctx.Name $.ctx.Position -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/x.html b/tpl/tplimpl/embedded/templates/shortcodes/x.html index 78c0ac08b..28a5e331b 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/x.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/x.html @@ -1,4 +1,4 @@ -{{- $pc := .Page.Site.Config.Privacy.X -}} +{{- $pc := site.Config.Privacy.X -}} {{- if not $pc.Disable -}} {{- if $pc.Simple -}} {{- template "_internal/shortcodes/x_simple.html" . -}} @@ -6,7 +6,7 @@ {{- $id := or (.Get "id") "" -}} {{- $user := or (.Get "user") "" -}} {{- if and $id $user -}} - {{- template "render-x" (dict "id" $id "user" $user "dnt" $pc.EnableDNT "name" .Name "position" .Position) -}} + {{- template "render-x" (dict "id" $id "user" $user "dnt" $pc.EnableDNT "ctx" .) -}} {{- else -}} {{- errorf "The %q shortcode requires two named parameters: user and id. See %s" .Name .Position -}} {{- end -}} @@ -23,7 +23,7 @@ {{- else with .Value -}} {{- (. | transform.Unmarshal).html | safeHTML -}} {{- else -}} - {{- warnidf "shortcode-x-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}} + {{- warnidf "shortcode-x-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" $.ctx.Name $.ctx.Position -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/embedded/templates/shortcodes/x_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/x_simple.html index 661ed7756..be7830668 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/x_simple.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/x_simple.html @@ -22,7 +22,7 @@ {{- end }} {{- (. | transform.Unmarshal).html | safeHTML -}} {{- else -}} - {{- warnidf "shortcode-x-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .ctx.Name .ctx.Position -}} + {{- warnidf "shortcode-x-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" $.ctx.Name $.ctx.Position -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go new file mode 100644 index 000000000..d9c540cd9 --- /dev/null +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -0,0 +1,330 @@ +// Copyright 2025 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tplimpl_test + +import ( + "strings" + "testing" + + "github.com/gohugoio/hugo/htesting/hqt" + "github.com/gohugoio/hugo/hugolib" +) + +func TestCommentShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- content/_index.md -- +a{{< comment >}}b{{< /comment >}}c +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "

ac

") +} + +func TestDetailsShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- content/_index.md -- +--- +title: home +--- +{{< details >}} +A: An _emphasized_ word. +{{< /details >}} + +{{< details + class="my-class" + name="my-name" + open=true + summary="A **bold** word" + title="my-title" +>}} +B: An _emphasized_ word. +{{< /details >}} + +{{< details open=false >}} +C: An _emphasized_ word. +{{< /details >}} + +{{< details open="false" >}} +D: An _emphasized_ word. +{{< /details >}} + +{{< details open=0 >}} +E: An _emphasized_ word. +{{< /details >}} +` + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", + "
\n Details\n

A: An emphasized word.

\n
", + "
\n A bold word\n

B: An emphasized word.

\n
", + "
\n Details\n

C: An emphasized word.

\n
", + "
\n Details\n

D: An emphasized word.

\n
", + "
\n Details\n

D: An emphasized word.

\n
", + ) +} + +func TestInstagramShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +privacy.instagram.simple = false +-- content/_index.md -- +--- +title: home +--- +{{< instagram CxOWiQNP2MO >}} +-- layouts/index.html -- +Hash: {{ .Content | hash.XxHash }} +Content: {{ .Content }} +` + + // Regular mode + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "a7937c49665872d3") + + // Simple mode + files = strings.ReplaceAll(files, "privacy.instagram.simple = false", "privacy.instagram.simple = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "2c1dce3881be0513") +} + +func TestQRShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- content/_index.md -- +--- +title: home +--- +{{< qr + text="https://gohugo.io" + level="high" + scale=4 + targetDir="codes" + alt="QR code linking to https://gohugo.io" + class="my-class" + id="my-id" + title="My Title" +/>}} + +{{< qr >}} +https://gohugo.io" +{{< /qr >}} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", + `QR code linking to https://gohugo.io`, + ``, + ) +} + +func TestVimeoShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +privacy.vimeo.simple = false +-- content/_index.md -- +--- +title: home +--- +{{< vimeo 55073825 >}} +-- layouts/index.html -- +Hash: {{ .Content | hash.XxHash }} +Content: {{ .Content }} +` + + // Regular mode + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "d5b2a079cc37d0ed") + + // Simple mode + files = strings.ReplaceAll(files, "privacy.vimeo.simple = false", "privacy.vimeo.simple = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "73b8767ce8bdf694") + + // Simple mode with non-existent id + files = strings.ReplaceAll(files, "{{< vimeo 55073825 >}}", "{{< vimeo __id_does_not_exist__ >}}") + b = hugolib.Test(t, files, hugolib.TestOptWarn()) + b.AssertLogContains(`WARN The "vimeo" shortcode was unable to retrieve the remote data.`) +} + +// Issue 13214 +// We deprecated the twitter, tweet (alias of twitter), and twitter_simple +// shortcodes in v0.141.0, replacing them with x and x_simple. +func TestXShortcodes(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +#CONFIG +-- content/p1.md -- +--- +title: p1 +--- +{{< x user="SanDiegoZoo" id="1453110110599868418" >}} +-- content/p2.md -- +--- +title: p2 +--- +{{< twitter user="SanDiegoZoo" id="1453110110599868418" >}} +-- content/p3.md -- +--- +title: p3 +--- +{{< tweet user="SanDiegoZoo" id="1453110110599868418" >}} +-- content/p4.md -- +--- +title: p4 +--- +{{< x_simple user="SanDiegoZoo" id="1453110110599868418" >}} +-- content/p5.md -- +--- +title: p5 +--- +{{< twitter_simple user="SanDiegoZoo" id="1453110110599868418" >}} +-- layouts/_default/single.html -- +{{ .Content | strings.TrimSpace | safeHTML }} +-- +` + + b := hugolib.Test(t, files) + + // Test x, twitter, and tweet shortcodes + want := ` + ` + b.AssertFileContent("public/p1/index.html", want) + + htmlFiles := []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + } + + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + // Test x_simple and twitter_simple shortcodes + wantSimple := "

Owl bet you'll lose this staring contest 🦉 pic.twitter.com/eJh4f2zncC

— San Diego Zoo Wildlife Alliance (@sandiegozoo) October 26, 2021
\n--" + b.AssertFileContent("public/p4/index.html", wantSimple) + + htmlFiles = []string{ + b.FileContent("public/p4/index.html"), + b.FileContent("public/p5/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + filesOriginal := files + + // Test privacy.twitter.simple + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.simple=true") + b = hugolib.Test(t, files) + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + b.FileContent("public/p5/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + // Test privacy.x.simple + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.simple=true") + b = hugolib.Test(t, files) + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p4/index.html"), + b.FileContent("public/p4/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + // Test privacy.twitter.disable + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.disable = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", "") + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + b.FileContent("public/p4/index.html"), + b.FileContent("public/p4/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + // Test privacy.x.disable + files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.disable = true") + b = hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", "") + htmlFiles = []string{ + b.FileContent("public/p1/index.html"), + b.FileContent("public/p4/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + htmlFiles = []string{ + b.FileContent("public/p2/index.html"), + b.FileContent("public/p3/index.html"), + } + b.Assert(htmlFiles, hqt.IsAllElementsEqual) + + // Test warnings + files = ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- content/_index.md -- +--- +title: home +--- +{{< x user="__user_does_not_exist__" id="__id_does_not_exist__" >}} +{{< x_simple user="__user_does_not_exist__" id="__id_does_not_exist__" >}} +{{< twitter user="__user_does_not_exist__" id="__id_does_not_exist__" >}} +{{< twitter_simple user="__user_does_not_exist__" id="__id_does_not_exist__" >}} +-- layouts/index.html -- +{{ .Content }} +` + + b = hugolib.Test(t, files, hugolib.TestOptWarn()) + b.AssertLogContains( + `WARN The "x" shortcode was unable to retrieve the remote data.`, + `WARN The "x_simple" shortcode was unable to retrieve the remote data.`, + `WARN The "twitter", "tweet", and "twitter_simple" shortcodes were deprecated in v0.142.0 and will be removed in a future release.`, + `WARN The "twitter" shortcode was unable to retrieve the remote data.`, + `WARN The "twitter_simple" shortcode was unable to retrieve the remote data.`, + ) +} diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go index d1e214ce2..9b30466e8 100644 --- a/tpl/tplimpl/tplimpl_integration_test.go +++ b/tpl/tplimpl/tplimpl_integration_test.go @@ -6,7 +6,6 @@ import ( "testing" qt "github.com/frankban/quicktest" - "github.com/gohugoio/hugo/htesting/hqt" "github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/tpl" ) @@ -586,71 +585,6 @@ title: p5 ) } -func TestCommentShortcode(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ['page','rss','section','sitemap','taxonomy','term'] --- layouts/index.html -- -{{ .Content }} --- content/_index.md -- -a{{< comment >}}b{{< /comment >}}c -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", "

ac

") -} - -func TestDetailsShortcode(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ['page','rss','section','sitemap','taxonomy','term'] --- layouts/index.html -- -{{ .Content }} --- content/_index.md -- ---- -title: home ---- -{{< details >}} -A: An _emphasized_ word. -{{< /details >}} - -{{< details - class="my-class" - name="my-name" - open=true - summary="A **bold** word" - title="my-title" ->}} -B: An _emphasized_ word. -{{< /details >}} - -{{< details open=false >}} -C: An _emphasized_ word. -{{< /details >}} - -{{< details open="false" >}} -D: An _emphasized_ word. -{{< /details >}} - -{{< details open=0 >}} -E: An _emphasized_ word. -{{< /details >}} -` - b := hugolib.Test(t, files) - - b.AssertFileContent("public/index.html", - "
\n Details\n

A: An emphasized word.

\n
", - "
\n A bold word\n

B: An emphasized word.

\n
", - "
\n Details\n

C: An emphasized word.

\n
", - "
\n Details\n

D: An emphasized word.

\n
", - "
\n Details\n

D: An emphasized word.

\n
", - ) -} - // Issue 12963 func TestEditBaseofParseAfterExecute(t *testing.T) { files := ` @@ -699,162 +633,3 @@ Home! b.BuildPartial("/mybundle1/") b.AssertFileContent("public/mybundle1/index.html", "Baseof!!") } - -func TestQRShortcode(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ['page','rss','section','sitemap','taxonomy','term'] --- layouts/index.html -- -{{ .Content }} --- content/_index.md -- ---- -title: home ---- -{{< qr - text="https://gohugo.io" - level="high" - scale=4 - targetDir="codes" - alt="QR code linking to https://gohugo.io" - class="my-class" - id="my-id" - title="My Title" -/>}} - -{{< qr >}} -https://gohugo.io" -{{< /qr >}} -` - - b := hugolib.Test(t, files) - - b.AssertFileContent("public/index.html", - `QR code linking to https://gohugo.io`, - ``, - ) -} - -// Issue 13214 -// We deprecated the twitter, tweet (alias of twitter), and twitter_simple -// shortcodes in v0.141.0, replacing them with x and x_simple. -func TestXShortcodes(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ['home','rss','section','sitemap','taxonomy','term'] -#CONFIG --- content/p1.md -- ---- -title: p1 ---- -{{< x user="SanDiegoZoo" id="1453110110599868418" >}} --- content/p2.md -- ---- -title: p2 ---- -{{< twitter user="SanDiegoZoo" id="1453110110599868418" >}} --- content/p3.md -- ---- -title: p3 ---- -{{< tweet user="SanDiegoZoo" id="1453110110599868418" >}} --- content/p4.md -- ---- -title: p4 ---- -{{< x_simple user="SanDiegoZoo" id="1453110110599868418" >}} --- content/p5.md -- ---- -title: p5 ---- -{{< twitter_simple user="SanDiegoZoo" id="1453110110599868418" >}} --- layouts/_default/single.html -- -{{ .Content | strings.TrimSpace | safeHTML }} --- -` - - b := hugolib.Test(t, files) - - // Test x, twitter, and tweet shortcodes - want := ` - ` - b.AssertFileContent("public/p1/index.html", want) - - htmlFiles := []string{ - b.FileContent("public/p1/index.html"), - b.FileContent("public/p2/index.html"), - b.FileContent("public/p3/index.html"), - } - - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - // Test x_simple and twitter_simple shortcodes - wantSimple := "

Owl bet you'll lose this staring contest 🦉 pic.twitter.com/eJh4f2zncC

— San Diego Zoo Wildlife Alliance (@sandiegozoo) October 26, 2021
\n--" - b.AssertFileContent("public/p4/index.html", wantSimple) - - htmlFiles = []string{ - b.FileContent("public/p4/index.html"), - b.FileContent("public/p5/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - filesOriginal := files - - // Test privacy.twitter.simple - files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.simple=true") - b = hugolib.Test(t, files) - htmlFiles = []string{ - b.FileContent("public/p2/index.html"), - b.FileContent("public/p3/index.html"), - b.FileContent("public/p5/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - // Test privacy.x.simple - files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.simple=true") - b = hugolib.Test(t, files) - htmlFiles = []string{ - b.FileContent("public/p1/index.html"), - b.FileContent("public/p4/index.html"), - b.FileContent("public/p4/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - htmlFiles = []string{ - b.FileContent("public/p2/index.html"), - b.FileContent("public/p3/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - // Test privacy.twitter.disable - files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.twitter.disable = true") - b = hugolib.Test(t, files) - b.AssertFileContent("public/p1/index.html", "") - htmlFiles = []string{ - b.FileContent("public/p1/index.html"), - b.FileContent("public/p2/index.html"), - b.FileContent("public/p3/index.html"), - b.FileContent("public/p4/index.html"), - b.FileContent("public/p4/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - // Test privacy.x.disable - files = strings.ReplaceAll(filesOriginal, "#CONFIG", "privacy.x.disable = true") - b = hugolib.Test(t, files) - b.AssertFileContent("public/p1/index.html", "") - htmlFiles = []string{ - b.FileContent("public/p1/index.html"), - b.FileContent("public/p4/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) - - htmlFiles = []string{ - b.FileContent("public/p2/index.html"), - b.FileContent("public/p3/index.html"), - } - b.Assert(htmlFiles, hqt.IsAllElementsEqual) -} From 4b0c194fb318bc8fa38ed021d161901b7f6f7f95 Mon Sep 17 00:00:00 2001 From: "W. Michael Petullo" Date: Mon, 20 Jan 2025 19:34:39 -0600 Subject: [PATCH 094/374] Fix build with Go 1.24 Go 1.24 provides stricter checking that forbids passing a variable as a format string to Printf-family functions with no other arguments. Remove instances of this. See also: https://tip.golang.org/doc/go1.24#vet Signed-off-by: W. Michael Petullo --- common/hugio/hasBytesWriter_test.go | 8 ++++---- common/hugo/hugo.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/hugio/hasBytesWriter_test.go b/common/hugio/hasBytesWriter_test.go index 49487ab0b..f0d6c3a7b 100644 --- a/common/hugio/hasBytesWriter_test.go +++ b/common/hugio/hasBytesWriter_test.go @@ -48,16 +48,16 @@ func TestHasBytesWriter(t *testing.T) { for i := 0; i < 22; i++ { h, w := neww() - fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr()) + fmt.Fprint(w, rndStr()+"abc __foobar"+rndStr()) c.Assert(h.Patterns[0].Match, qt.Equals, true) h, w = neww() - fmt.Fprintf(w, rndStr()+"abc __f") - fmt.Fprintf(w, "oo bar"+rndStr()) + fmt.Fprint(w, rndStr()+"abc __f") + fmt.Fprint(w, "oo bar"+rndStr()) c.Assert(h.Patterns[0].Match, qt.Equals, true) h, w = neww() - fmt.Fprintf(w, rndStr()+"abc __moo bar") + fmt.Fprint(w, rndStr()+"abc __moo bar") c.Assert(h.Patterns[0].Match, qt.Equals, false) } diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index e480baa94..eecf4bc2f 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -423,7 +423,7 @@ func DeprecateLevel(item, alternative, version string, level logg.Level) { msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative) } - loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf(msg) + loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf("%s", msg) } // We usually do about one minor version a month. From f1de5d2a043ea2271419c0ff145e7f76044be7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 21 Jan 2025 10:33:27 +0100 Subject: [PATCH 095/374] Do not warn on potentially unsafe HTML comments when unsafe=false We will still not render these comments, so from a safety perspective this is the same, but HTML comments are very common also inside Markdown and too useful to throw away. Updates #13278 --- markup/goldmark/goldmark_integration_test.go | 51 ++++++++++++++++++++ markup/goldmark/hugocontext/hugocontext.go | 17 +++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index 591226dc2..23e22b5ca 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -851,3 +851,54 @@ title: "p1" b.AssertFileContent("public/p1/index.html", "! ") b.AssertLogContains("! WARN") } + +// See https://github.com/gohugoio/hugo/issues/13278#issuecomment-2603280548 +func TestGoldmarkRawHTMLCommentNoWarning(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +markup.goldmark.renderer.unsafe = false +-- content/p1.md -- +--- +title: "p1" +--- +# HTML comments + +## Simple + + + + + **Hello**_world_. +## With HTML + + + +## With HTML and JS + + + +## With Block + + + +XSS + + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/p1/index.html", "! ") + b.AssertLogContains("! Raw HTML omitted") + + b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) + b.AssertFileContent("public/p1/index.html", "") + b.AssertLogContains("! WARN") +} diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go index 601014b37..e610bbbeb 100644 --- a/markup/goldmark/hugocontext/hugocontext.go +++ b/markup/goldmark/hugocontext/hugocontext.go @@ -174,6 +174,9 @@ func (r *hugoContextRenderer) renderHTMLBlock( w util.BufWriter, source []byte, node ast.Node, entering bool, ) (ast.WalkStatus, error) { n := node.(*ast.HTMLBlock) + isHTMLComment := func(b []byte) bool { + return len(b) > 4 && b[0] == '<' && b[1] == '!' && b[2] == '-' && b[3] == '-' + } if entering { if r.Unsafe { l := n.Lines().Len() @@ -188,8 +191,12 @@ func (r *hugoContextRenderer) renderHTMLBlock( r.Writer.SecureWrite(w, linev) } } else { - r.logRawHTMLEmittedWarn(w) - _, _ = w.WriteString("\n") + l := n.Lines().At(0) + v := l.Value(source) + if !isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("\n") + } } } else { if n.HasClosure() { @@ -197,7 +204,11 @@ func (r *hugoContextRenderer) renderHTMLBlock( closure := n.ClosureLine r.Writer.SecureWrite(w, closure.Value(source)) } else { - _, _ = w.WriteString("\n") + l := n.Lines().At(0) + v := l.Value(source) + if !isHTMLComment(v) { + _, _ = w.WriteString("\n") + } } } } From 637995ba8f14d3100d2ef185489836d3dd19165c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 21 Jan 2025 18:52:58 +0100 Subject: [PATCH 096/374] Also handle inline HTML comments --- markup/goldmark/goldmark_integration_test.go | 19 +++++++++++++++- markup/goldmark/hugocontext/hugocontext.go | 24 ++++++++++++-------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index 23e22b5ca..356e60153 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -885,10 +885,27 @@ title: "p1" Trulli --> -XSS +## XSS + +## More + +This is a word. + +This is a word. + +This is a word. + +This is a word. + +This is a word. + + -- layouts/_default/single.html -- {{ .Content }} ` diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go index e610bbbeb..e68acb8c3 100644 --- a/markup/goldmark/hugocontext/hugocontext.go +++ b/markup/goldmark/hugocontext/hugocontext.go @@ -169,14 +169,16 @@ func (r *hugoContextRenderer) getPage(w util.BufWriter) any { return p } +func (r *hugoContextRenderer) isHTMLComment(b []byte) bool { + return len(b) > 4 && b[0] == '<' && b[1] == '!' && b[2] == '-' && b[3] == '-' +} + // HTML rendering based on Goldmark implementation. func (r *hugoContextRenderer) renderHTMLBlock( w util.BufWriter, source []byte, node ast.Node, entering bool, ) (ast.WalkStatus, error) { n := node.(*ast.HTMLBlock) - isHTMLComment := func(b []byte) bool { - return len(b) > 4 && b[0] == '<' && b[1] == '!' && b[2] == '-' && b[3] == '-' - } + if entering { if r.Unsafe { l := n.Lines().Len() @@ -193,7 +195,7 @@ func (r *hugoContextRenderer) renderHTMLBlock( } else { l := n.Lines().At(0) v := l.Value(source) - if !isHTMLComment(v) { + if !r.isHTMLComment(v) { r.logRawHTMLEmittedWarn(w) _, _ = w.WriteString("\n") } @@ -206,7 +208,7 @@ func (r *hugoContextRenderer) renderHTMLBlock( } else { l := n.Lines().At(0) v := l.Value(source) - if !isHTMLComment(v) { + if !r.isHTMLComment(v) { _, _ = w.WriteString("\n") } } @@ -221,17 +223,21 @@ func (r *hugoContextRenderer) renderRawHTML( if !entering { return ast.WalkSkipChildren, nil } + n := node.(*ast.RawHTML) + l := n.Segments.Len() if r.Unsafe { - n := node.(*ast.RawHTML) - l := n.Segments.Len() for i := 0; i < l; i++ { segment := n.Segments.At(i) _, _ = w.Write(segment.Value(source)) } return ast.WalkSkipChildren, nil } - r.logRawHTMLEmittedWarn(w) - _, _ = w.WriteString("") + segment := n.Segments.At(0) + v := segment.Value(source) + if !r.isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("") + } return ast.WalkSkipChildren, nil } From 91101d24f23255a45f0bb0e5eb8aeeba819e20a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 21 Jan 2025 19:19:20 +0100 Subject: [PATCH 097/374] Improve assertions --- markup/goldmark/goldmark_integration_test.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index 356e60153..faccf15ac 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -912,10 +912,19 @@ hidden b := hugolib.Test(t, files, hugolib.TestOptWarn()) - b.AssertFileContent("public/p1/index.html", "! ") + b.AssertFileContent("public/p1/index.html", + "! ", + "! ", + "! ", + "! script", + ) b.AssertLogContains("! Raw HTML omitted") b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) - b.AssertFileContent("public/p1/index.html", "") + b.AssertFileContent("public/p1/index.html", + "! ", + "", + "", + ) b.AssertLogContains("! WARN") } From 9885e7020d75bb69f5ce67a766571bd0c4b119f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 21 Jan 2025 19:24:51 +0100 Subject: [PATCH 098/374] Improve assert --- markup/goldmark/goldmark_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index faccf15ac..67348d894 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -918,7 +918,7 @@ hidden "! ", "! script", ) - b.AssertLogContains("! Raw HTML omitted") + b.AssertLogContains("! WARN") b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) b.AssertFileContent("public/p1/index.html", From 6aa72acaf95723bef83051c4fceb1f326a9a5fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 21 Jan 2025 10:57:06 +0100 Subject: [PATCH 099/374] Fix render hook's PlainText with typographer extension enabled Fixes #13286 Fixes #13292 --- markup/goldmark/goldmark_integration_test.go | 33 ++++++++++++++++++++ markup/goldmark/internal/render/context.go | 7 ++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go index 67348d894..184d0d7fc 100644 --- a/markup/goldmark/goldmark_integration_test.go +++ b/markup/goldmark/goldmark_integration_test.go @@ -527,6 +527,39 @@ a c ) } +// Issue 13286 +func TestImageAltApostrophesWithTypographer(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[markup.goldmark.extensions.typographer] +disable = false + [markup.goldmark.renderHooks.image] + enableDefault = true +-- content/p1.md -- +--- +title: "p1" +--- + +## Image + +![A's is > than B's](some-image.png) + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContentExact("public/p1/index.html", + // Note that this markup is slightly different than the one produced by the default Goldmark renderer, + // see issue 13292. + "alt=\"A’s is > than B’s\">", + ) +} + // Issue #7332 // Issue #11587 func TestGoldmarkEmojiExtension(t *testing.T) { diff --git a/markup/goldmark/internal/render/context.go b/markup/goldmark/internal/render/context.go index cd64fc944..32304fafa 100644 --- a/markup/goldmark/internal/render/context.go +++ b/markup/goldmark/internal/render/context.go @@ -27,6 +27,7 @@ import ( "github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/converter/hooks" "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/util" ) type BufWriter struct { @@ -264,6 +265,8 @@ func (c *hookBase) PositionerSourceTarget() []byte { } // TextPlain returns a plain text representation of the given node. +// This will resolve any lefover HTML entities. This will typically be +// entities inserted by e.g. the typographer extension. // Goldmark's Node.Text was deprecated in 1.7.8. func TextPlain(n ast.Node, source []byte) string { buf := bp.GetBuffer() @@ -272,7 +275,7 @@ func TextPlain(n ast.Node, source []byte) string { for c := n.FirstChild(); c != nil; c = c.NextSibling() { textPlainTo(c, source, buf) } - return buf.String() + return string(util.ResolveEntityNames(buf.Bytes())) } func textPlainTo(c ast.Node, source []byte, buf *bytes.Buffer) { @@ -283,6 +286,8 @@ func textPlainTo(c ast.Node, source []byte, buf *bytes.Buffer) { case *ast.RawHTML: s := strings.TrimSpace(tpl.StripHTML(string(c.Segments.Value(source)))) buf.WriteString(s) + case *ast.String: + buf.Write(c.Value) case *ast.Text: buf.Write(c.Segment.Value(source)) default: From 1f746a872442e66b6afd47c8c04ac42dc92cdb6f Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Wed, 22 Jan 2025 12:20:52 +0000 Subject: [PATCH 100/374] releaser: Bump versions for release of 0.142.0 [ci skip] --- common/hugo/version_current.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 29944bdb9..ab3282571 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -19,5 +19,5 @@ var CurrentVersion = Version{ Major: 0, Minor: 142, PatchLevel: 0, - Suffix: "-DEV", + Suffix: "", } From 9d765e1b99c120e14991939e89b5656010e4792c Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Wed, 22 Jan 2025 12:35:08 +0000 Subject: [PATCH 101/374] releaser: Prepare repository for 0.143.0-DEV [ci skip] --- common/hugo/version_current.go | 4 ++-- hugoreleaser.env | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index ab3282571..6bb143da1 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 142, + Minor: 143, PatchLevel: 0, - Suffix: "", + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index d41200e30..5bd6f37dc 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.141.0 -HUGORELEASER_COMMITISH=e7bd51698e5c3778a86003018702b1a7dcb9559a +HUGORELEASER_TAG=v0.142.0 +HUGORELEASER_COMMITISH=1f746a872442e66b6afd47c8c04ac42dc92cdb6f + From f704d75699e41a3df7012bcab91650149bcd3504 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Wed, 22 Jan 2025 08:57:24 -0500 Subject: [PATCH 102/374] markup/goldmark: Fix typo in func comment --- markup/goldmark/internal/render/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup/goldmark/internal/render/context.go b/markup/goldmark/internal/render/context.go index 32304fafa..469ea72e3 100644 --- a/markup/goldmark/internal/render/context.go +++ b/markup/goldmark/internal/render/context.go @@ -265,7 +265,7 @@ func (c *hookBase) PositionerSourceTarget() []byte { } // TextPlain returns a plain text representation of the given node. -// This will resolve any lefover HTML entities. This will typically be +// This will resolve any leftover HTML entities. This will typically be // entities inserted by e.g. the typographer extension. // Goldmark's Node.Text was deprecated in 1.7.8. func TextPlain(n ast.Node, source []byte) string { From 77a8e347bc2128b7fc7c82358512268e863d61f9 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 22 Jan 2025 08:59:16 -0800 Subject: [PATCH 103/374] tpl/tplimpl: Deprecate comment shortcode --- tpl/tplimpl/embedded/templates/shortcodes/comment.html | 1 + tpl/tplimpl/shortcodes_integration_test.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/comment.html b/tpl/tplimpl/embedded/templates/shortcodes/comment.html index cb3293401..c35b5cad6 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/comment.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/comment.html @@ -1 +1,2 @@ +{{- warnf "The %q shortcode was deprecated in v0.143.0 and will be removed in a future release. Please use HTML comments instead. See %s" .Name .Position -}} {{- $noop := .Inner -}} diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index d9c540cd9..7f560f53a 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -33,8 +33,9 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term'] a{{< comment >}}b{{< /comment >}}c ` - b := hugolib.Test(t, files) + b := hugolib.Test(t, files, hugolib.TestOptWarn()) b.AssertFileContent("public/index.html", "

ac

") + b.AssertLogContains(`WARN The "comment" shortcode was deprecated in v0.143.0 and will be removed in a future release. Please use HTML comments instead.`) } func TestDetailsShortcode(t *testing.T) { From 7f0f50b133e768bd939bfc2be7e09ecebba270ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 22 Jan 2025 17:47:54 +0100 Subject: [PATCH 104/374] Make cascade front matter order deterministic Fixes #12594 --- common/hashing/hashing.go | 16 ++- common/maps/ordered.go | 133 ++++++++++++++++++++ common/maps/ordered_test.go | 99 +++++++++++++++ config/allconfig/allconfig.go | 7 +- hugolib/cascade_test.go | 35 ++++++ hugolib/content_map_page.go | 12 +- hugolib/page__meta.go | 32 ++--- resources/page/page_matcher.go | 14 +-- resources/page/page_matcher_test.go | 12 +- resources/page/pagemeta/page_frontmatter.go | 6 +- 10 files changed, 318 insertions(+), 48 deletions(-) create mode 100644 common/maps/ordered.go create mode 100644 common/maps/ordered_test.go diff --git a/common/hashing/hashing.go b/common/hashing/hashing.go index 18ec82623..e45356758 100644 --- a/common/hashing/hashing.go +++ b/common/hashing/hashing.go @@ -123,16 +123,24 @@ func HashUint64(vs ...any) uint64 { o = elements } - hashOpts := getHashOpts() - defer putHashOpts(hashOpts) - - hash, err := hashstructure.Hash(o, hashOpts) + hash, err := Hash(o) if err != nil { panic(err) } return hash } +// Hash returns a hash from vs. +func Hash(vs ...any) (uint64, error) { + hashOpts := getHashOpts() + defer putHashOpts(hashOpts) + var v any = vs + if len(vs) == 1 { + v = vs[0] + } + return hashstructure.Hash(v, hashOpts) +} + type keyer interface { Key() string } diff --git a/common/maps/ordered.go b/common/maps/ordered.go new file mode 100644 index 000000000..eaa4d73c6 --- /dev/null +++ b/common/maps/ordered.go @@ -0,0 +1,133 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package maps + +import ( + "github.com/gohugoio/hugo/common/hashing" +) + +// Ordered is a map that can be iterated in the order of insertion. +// Note that insertion order is not affected if a key is re-inserted into the map. +// In a nil map, all operations are no-ops. +// This is not thread safe. +type Ordered[K comparable, T any] struct { + // The keys in the order they were added. + keys []K + // The values. + values map[K]T +} + +// NewOrdered creates a new Ordered map. +func NewOrdered[K comparable, T any]() *Ordered[K, T] { + return &Ordered[K, T]{values: make(map[K]T)} +} + +// Set sets the value for the given key. +// Note that insertion order is not affected if a key is re-inserted into the map. +func (m *Ordered[K, T]) Set(key K, value T) { + if m == nil { + return + } + // Check if key already exists. + if _, found := m.values[key]; !found { + m.keys = append(m.keys, key) + } + m.values[key] = value +} + +// Get gets the value for the given key. +func (m *Ordered[K, T]) Get(key K) (T, bool) { + if m == nil { + var v T + return v, false + } + value, found := m.values[key] + return value, found +} + +// Delete deletes the value for the given key. +func (m *Ordered[K, T]) Delete(key K) { + if m == nil { + return + } + delete(m.values, key) + for i, k := range m.keys { + if k == key { + m.keys = append(m.keys[:i], m.keys[i+1:]...) + break + } + } +} + +// Clone creates a shallow copy of the map. +func (m *Ordered[K, T]) Clone() *Ordered[K, T] { + if m == nil { + return nil + } + clone := NewOrdered[K, T]() + for _, k := range m.keys { + clone.Set(k, m.values[k]) + } + return clone +} + +// Keys returns the keys in the order they were added. +func (m *Ordered[K, T]) Keys() []K { + if m == nil { + return nil + } + return m.keys +} + +// Values returns the values in the order they were added. +func (m *Ordered[K, T]) Values() []T { + if m == nil { + return nil + } + var values []T + for _, k := range m.keys { + values = append(values, m.values[k]) + } + return values +} + +// Len returns the number of items in the map. +func (m *Ordered[K, T]) Len() int { + if m == nil { + return 0 + } + return len(m.keys) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// TODO(bep) replace with iter.Seq2 when we bump go Go 1.24. +func (m *Ordered[K, T]) Range(f func(key K, value T) bool) { + if m == nil { + return + } + for _, k := range m.keys { + if !f(k, m.values[k]) { + return + } + } +} + +// Hash calculates a hash from the values. +func (m *Ordered[K, T]) Hash() (uint64, error) { + if m == nil { + return 0, nil + } + return hashing.Hash(m.values) +} diff --git a/common/maps/ordered_test.go b/common/maps/ordered_test.go new file mode 100644 index 000000000..65a827810 --- /dev/null +++ b/common/maps/ordered_test.go @@ -0,0 +1,99 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package maps + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestOrdered(t *testing.T) { + c := qt.New(t) + + m := NewOrdered[string, int]() + m.Set("a", 1) + m.Set("b", 2) + m.Set("c", 3) + + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 2, 3}) + + v, found := m.Get("b") + c.Assert(found, qt.Equals, true) + c.Assert(v, qt.Equals, 2) + + m.Set("b", 22) + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 22, 3}) + + m.Delete("b") + + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 3}) +} + +func TestOrderedHash(t *testing.T) { + c := qt.New(t) + + m := NewOrdered[string, int]() + m.Set("a", 1) + m.Set("b", 2) + m.Set("c", 3) + + h1, err := m.Hash() + c.Assert(err, qt.IsNil) + + m.Set("d", 4) + + h2, err := m.Hash() + c.Assert(err, qt.IsNil) + + c.Assert(h1, qt.Not(qt.Equals), h2) + + m = NewOrdered[string, int]() + m.Set("b", 2) + m.Set("a", 1) + m.Set("c", 3) + + h3, err := m.Hash() + c.Assert(err, qt.IsNil) + // Order does not matter. + c.Assert(h1, qt.Equals, h3) +} + +func TestOrderedNil(t *testing.T) { + c := qt.New(t) + + var m *Ordered[string, int] + + m.Set("a", 1) + c.Assert(m.Keys(), qt.IsNil) + c.Assert(m.Values(), qt.IsNil) + v, found := m.Get("a") + c.Assert(found, qt.Equals, false) + c.Assert(v, qt.Equals, 0) + m.Delete("a") + var b bool + m.Range(func(k string, v int) bool { + b = true + return true + }) + c.Assert(b, qt.Equals, false) + c.Assert(m.Len(), qt.Equals, 0) + c.Assert(m.Clone(), qt.IsNil) + h, err := m.Hash() + c.Assert(err, qt.IsNil) + c.Assert(h, qt.Equals, uint64(0)) +} diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 96d10b3bd..3c2eddcab 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -143,7 +143,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, map[page.PageMatcher]maps.Params] `mapstructure:"-"` + Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"` // The segments defines segments for the site. Used for partial/segmented builds. Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"` @@ -766,9 +766,10 @@ type Configs struct { } func (c *Configs) Validate(logger loggers.Logger) error { - for p := range c.Base.Cascade.Config { + c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool { page.CheckCascadePattern(logger, p) - } + return true + }) return nil } diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index cbceaaed5..47e53d927 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -841,3 +841,38 @@ title: p1 b.AssertFileExists("public/s1/index.html", false) b.AssertFileExists("public/s1/p1/index.html", false) } + +// Issue 12594. +func TestCascadeOrder(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','sitemap','taxonomy','term', 'home'] +-- content/_index.md -- +--- +title: Home +cascade: +- _target: + path: "**" + params: + background: yosemite.jpg +- _target: + params: + background: goldenbridge.jpg +--- +-- content/p1.md -- +--- +title: p1 +--- +-- layouts/_default/single.html -- +Background: {{ .Params.background }}| +-- layouts/_default/list.html -- +{{ .Title }}| + ` + + for i := 0; i < 10; i++ { + b := Test(t, files) + b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg") + } +} diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index b48504203..6927562f1 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1387,7 +1387,7 @@ func (sa *sitePagesAssembler) applyAggregates() error { } // Handle cascades first to get any default dates set. - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, maps.Params] if keyPage == "" { // Home page gets it's cascade from the site config. cascade = sa.conf.Cascade.Config @@ -1399,7 +1399,7 @@ func (sa *sitePagesAssembler) applyAggregates() error { } else { _, data := pw.WalkContext.Data().LongestPrefix(keyPage) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params]) } } @@ -1481,11 +1481,11 @@ func (sa *sitePagesAssembler) applyAggregates() error { pageResource := rs.r.(*pageState) relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo) pageResource.m.resourcePath = relPath - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, maps.Params] // Apply cascade (if set) to the page. _, data := pw.WalkContext.Data().LongestPrefix(resourceKey) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params]) } if err := pageResource.setMetaPost(cascade); err != nil { return false, err @@ -1549,10 +1549,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error { const eventName = "dates" if p.Kind() == kinds.KindTerm { - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, maps.Params] _, data := pw.WalkContext.Data().LongestPrefix(s) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params]) } if err := p.setMetaPost(cascade); err != nil { return false, err diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 07d9d1c0e..38fe6db56 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -87,8 +87,8 @@ type pageMetaParams struct { // These are only set in watch mode. datesOriginal pagemeta.Dates - paramsOriginal map[string]any // contains the original params as defined in the front matter. - cascadeOriginal map[page.PageMatcher]maps.Params // contains the original cascade as defined in the front matter. + paramsOriginal map[string]any // contains the original params as defined in the front matter. + cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter. } // From page front matter. @@ -96,10 +96,10 @@ type pageMetaFrontMatter struct { configuredOutputFormats output.Formats // outputs defined in front matter. } -func (m *pageMetaParams) init(preserveOringal bool) { - if preserveOringal { +func (m *pageMetaParams) init(preserveOriginal bool) { + if preserveOriginal { m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params) - m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.pageConfig.CascadeCompiled) + m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone() } } @@ -306,22 +306,22 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf return nil } -func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error { +func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error { ps.m.setMetaPostCount++ var cascadeHashPre uint64 if ps.m.setMetaPostCount > 1 { cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled) - ps.m.pageConfig.CascadeCompiled = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal) + ps.m.pageConfig.CascadeCompiled = ps.m.cascadeOriginal.Clone() } // Apply cascades first so they can be overridden later. if cascade != nil { if ps.m.pageConfig.CascadeCompiled != nil { - for k, v := range cascade { - vv, found := ps.m.pageConfig.CascadeCompiled[k] + cascade.Range(func(k page.PageMatcher, v maps.Params) bool { + vv, found := ps.m.pageConfig.CascadeCompiled.Get(k) if !found { - ps.m.pageConfig.CascadeCompiled[k] = v + ps.m.pageConfig.CascadeCompiled.Set(k, v) } else { // Merge for ck, cv := range v { @@ -330,7 +330,8 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error } } } - } + return true + }) cascade = ps.m.pageConfig.CascadeCompiled } else { ps.m.pageConfig.CascadeCompiled = cascade @@ -354,16 +355,17 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error } // Cascade is also applied to itself. - for m, v := range cascade { - if !m.Matches(ps) { - continue + cascade.Range(func(k page.PageMatcher, v maps.Params) bool { + if !k.Matches(ps) { + return true } for kk, vv := range v { if _, found := ps.m.pageConfig.Params[kk]; !found { ps.m.pageConfig.Params[kk] = vv } } - } + return true + }) if err := ps.setMetaPostParams(); err != nil { return err diff --git a/resources/page/page_matcher.go b/resources/page/page_matcher.go index f2075273a..8155be99d 100644 --- a/resources/page/page_matcher.go +++ b/resources/page/page_matcher.go @@ -104,9 +104,9 @@ func CheckCascadePattern(logger loggers.Logger, m PageMatcher) { } } -func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, map[PageMatcher]maps.Params], error) { - buildConfig := func(in any) (map[PageMatcher]maps.Params, any, error) { - cascade := make(map[PageMatcher]maps.Params) +func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]], error) { + buildConfig := func(in any) (*maps.Ordered[PageMatcher, maps.Params], any, error) { + cascade := maps.NewOrdered[PageMatcher, maps.Params]() if in == nil { return cascade, []map[string]any{}, nil } @@ -134,7 +134,7 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace for _, cfg := range cfgs { m := cfg.Target CheckCascadePattern(logger, m) - c, found := cascade[m] + c, found := cascade.Get(m) if found { // Merge for k, v := range cfg.Params { @@ -143,18 +143,18 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace } } } else { - cascade[m] = cfg.Params + cascade.Set(m, cfg.Params) } } return cascade, cfgs, nil } - return config.DecodeNamespace[[]PageMatcherParamsConfig](in, buildConfig) + return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]](in, buildConfig) } // DecodeCascade decodes in which could be either a map or a slice of maps. -func DecodeCascade(logger loggers.Logger, in any) (map[PageMatcher]maps.Params, error) { +func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, maps.Params], error) { conf, err := DecodeCascadeConfig(logger, in) if err != nil { return nil, err diff --git a/resources/page/page_matcher_test.go b/resources/page/page_matcher_test.go index dfb479d5e..bc072ce15 100644 --- a/resources/page/page_matcher_test.go +++ b/resources/page/page_matcher_test.go @@ -133,16 +133,8 @@ func TestDecodeCascadeConfig(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(got, qt.IsNotNil) - c.Assert(got.Config, qt.DeepEquals, - map[PageMatcher]maps.Params{ - {Path: "", Kind: "page", Lang: "", Environment: ""}: { - "b": "bv", - }, - {Path: "", Kind: "page", Lang: "", Environment: "production"}: { - "a": "av", - }, - }, - ) + c.Assert(got.Config.Keys(), qt.DeepEquals, []PageMatcher{{Kind: "page", Environment: "production"}, {Kind: "page"}}) + c.Assert(got.Config.Values(), qt.DeepEquals, []maps.Params{{"a": string("av")}, {"b": string("bv")}}) c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{ { Params: maps.Params{"a": string("av")}, diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go index d5d612609..21789909e 100644 --- a/resources/page/pagemeta/page_frontmatter.go +++ b/resources/page/pagemeta/page_frontmatter.go @@ -114,9 +114,9 @@ type PageConfig struct { Content Source // Compiled values. - CascadeCompiled map[page.PageMatcher]maps.Params - ContentMediaType media.Type `mapstructure:"-" json:"-"` - IsFromContentAdapter bool `mapstructure:"-" json:"-"` + CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"` + ContentMediaType media.Type `mapstructure:"-" json:"-"` + IsFromContentAdapter bool `mapstructure:"-" json:"-"` } var DefaultPageConfig = PageConfig{ From 346b60358dd8ec2ca228e6635bff9d7914b398b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 23 Jan 2025 09:46:02 +0100 Subject: [PATCH 105/374] Squashed 'docs/' changes from 4429eeeea..73a01565c 73a01565c Remove comment shortcode documentation 0ca7ccd30 Replace usage of comment shortcode with HTML comments fe10d9899 Remove expired new-in labels 11e89dfcb [editorial] Link to proper render-hook page in relref.md 11a581c2f netlify: Hugo 0.142.0 1a4fcf7f7 Miscellaneous edits 2c7a3165f Markdown linting and cleanup 69d7a781b Replace links to glossary terms with custom render hook syntax 441752d2d Refactor glossary lookup portion of link render hook 80109a14f Fix glossary term linking for plural form cd95f0f34 Update link render hook to support glossary links 53eadb430 Remove the glossary template 1d40a7f3b Improve transform.ToMath examples 586970df2 Misc edits 6a06a8de7 Add glossary link shortcode 4171c0eb7 Improve description of masking with non-transparent images 41c8feb64 Fix example of image.Mask filter 704a81656 Add alignx option to images.Text usage example 7c03eb0cc Clarify context in example of using the try statement 56d9c9b71 Refactor glossary of terms 042a6846b Add expiry dates to deprecated methods pages 365ab345f Remove services key from instagram shortcode page b7fe31e07 Reorder options list for images.Text filter 8051ff818 Format directory names, file names, and file paths as code d09a14623 Update version refs for Hugo and Dart Sass 3bb006974 Add NODE_VERSION to Netlify config examples 3a0f2531e Fix typo 7e3198eaf Fix typo cf56452a3 Fix typo a9f51d13e Fix typo 82bfdd8c3 Fix typo a5c41a053 Fix typo abcfed7a5 Fix typo 8c1debf3a Remove outdated new-in badges 809ddf9ef Update theme 63867d56f Use warnf instead of errorf in try-catch example dee3e5f09 Update theme 9791f7057 Remove TODO from comment shortcode examples a346ca1fd Elevate embedded shortcode documentation to its own section 8fa19b50f hugoreleaser.toml => hugoreleaser.yaml 575d60345 Update docs for v0.141.0 a0a442d62 netlify: Hugo 0.141.0 6667cbcd8 Merge commit '81a7b6390036138356773c87a886679c81c524e1' f36fc013e docs: Regen CLI docs 365a47ded tpl/images: Change signature of images.QR to images.QR TEXT OPTIONS ae8f8af0a images.Text: Add "alignx" option for horizontal alignment 8f45ccca6 docs: Regen CLI docs f0e6304f4 Merge commit 'e9fbadacc3f09191e2e19f112a49777eeb8df06c' cb9bec2b2 tpl/images: Add images.QR function git-subtree-dir: docs git-subtree-split: 73a01565c5ba0774d65aa6f2384c44804fefa37d --- .markdownlint.yaml | 1 + .../gohugoioTheme/assets/css/_code.css | 4 +- .../gohugoioTheme/assets/output/css/app.css | 4 +- .../components/author-github-data-card.html | 8 +- .../components/author-github-data.html | 8 +- .../partials/utilities/get-remote-data.html | 8 +- .../gohugoioTheme/layouts/shortcodes/hl.html | 11 + .../gohugoioTheme/layouts/shortcodes/img.html | 26 +- _vendor/modules.txt | 2 +- archetypes/glossary.md | 19 + archetypes/showcase/index.md | 2 +- assets/images/examples/mask.png | Bin 0 -> 137283 bytes config/_default/menus/menus.en.toml | 20 +- content/en/about/features.md | 6 +- content/en/about/privacy.md | 18 +- content/en/commands/hugo.md | 1 - content/en/commands/hugo_completion.md | 1 + content/en/commands/hugo_completion_bash.md | 1 + content/en/commands/hugo_completion_fish.md | 1 + .../en/commands/hugo_completion_powershell.md | 1 + content/en/commands/hugo_completion_zsh.md | 1 + content/en/commands/hugo_config.md | 2 + content/en/commands/hugo_config_mounts.md | 1 + content/en/commands/hugo_convert.md | 1 + content/en/commands/hugo_convert_toJSON.md | 1 + content/en/commands/hugo_convert_toTOML.md | 1 + content/en/commands/hugo_convert_toYAML.md | 1 + content/en/commands/hugo_deploy.md | 1 + content/en/commands/hugo_env.md | 1 + content/en/commands/hugo_gen.md | 1 + content/en/commands/hugo_gen_chromastyles.md | 1 + content/en/commands/hugo_gen_doc.md | 1 + content/en/commands/hugo_gen_man.md | 1 + content/en/commands/hugo_import.md | 1 + content/en/commands/hugo_import_jekyll.md | 1 + content/en/commands/hugo_list.md | 1 + content/en/commands/hugo_list_all.md | 1 + content/en/commands/hugo_list_drafts.md | 1 + content/en/commands/hugo_list_expired.md | 1 + content/en/commands/hugo_list_future.md | 1 + content/en/commands/hugo_list_published.md | 1 + content/en/commands/hugo_mod.md | 1 + content/en/commands/hugo_mod_clean.md | 1 + content/en/commands/hugo_mod_get.md | 1 + content/en/commands/hugo_mod_graph.md | 1 + content/en/commands/hugo_mod_init.md | 1 + content/en/commands/hugo_mod_npm.md | 1 + content/en/commands/hugo_mod_npm_pack.md | 1 + content/en/commands/hugo_mod_tidy.md | 1 + content/en/commands/hugo_mod_vendor.md | 1 + content/en/commands/hugo_mod_verify.md | 1 + content/en/commands/hugo_new.md | 1 + content/en/commands/hugo_new_content.md | 1 + content/en/commands/hugo_new_site.md | 1 + content/en/commands/hugo_new_theme.md | 1 + content/en/commands/hugo_server.md | 2 +- content/en/commands/hugo_server_trust.md | 1 + content/en/commands/hugo_version.md | 1 + content/en/content-management/archetypes.md | 34 +- .../en/content-management/build-options.md | 5 +- .../en/content-management/content-adapters.md | 29 +- .../en/content-management/cross-references.md | 2 +- content/en/content-management/data-sources.md | 16 +- content/en/content-management/formats.md | 56 +- content/en/content-management/front-matter.md | 44 +- .../image-processing/index.md | 19 +- .../content-management/markdown-attributes.md | 1 - content/en/content-management/mathematics.md | 52 +- content/en/content-management/menus.md | 7 +- content/en/content-management/multilingual.md | 64 +- content/en/content-management/page-bundles.md | 38 +- content/en/content-management/related.md | 32 +- content/en/content-management/sections.md | 16 +- content/en/content-management/shortcodes.md | 452 +--------- content/en/content-management/summaries.md | 4 +- .../content-management/syntax-highlighting.md | 2 +- content/en/content-management/urls.md | 3 +- content/en/contribute/development.md | 4 +- content/en/contribute/documentation.md | 144 ++- content/en/contribute/themes.md | 2 +- .../functions/_common/highlighting-options.md | 59 ++ content/en/functions/collections/After.md | 2 +- content/en/functions/collections/Append.md | 2 +- .../en/functions/collections/Dictionary.md | 1 - content/en/functions/collections/In.md | 6 +- .../en/functions/collections/NewScratch.md | 3 +- content/en/functions/collections/Where.md | 34 +- content/en/functions/css/Sass.md | 11 +- content/en/functions/css/TailwindCSS.md | 2 +- content/en/functions/data/GetCSV.md | 17 +- content/en/functions/data/GetJSON.md | 15 +- content/en/functions/debug/Timer.md | 2 +- content/en/functions/encoding/Base64Decode.md | 10 +- content/en/functions/fmt/Warnf.md | 1 - content/en/functions/global/page.md | 8 +- content/en/functions/go-template/range.md | 3 +- content/en/functions/go-template/return.md | 13 +- content/en/functions/go-template/try.md | 113 +++ content/en/functions/go-template/with.md | 4 +- content/en/functions/hugo/Environment.md | 4 +- content/en/functions/hugo/Generator.md | 2 +- content/en/functions/hugo/Store.md | 7 +- content/en/functions/hugo/Version.md | 2 +- content/en/functions/hugo/WorkingDir.md | 2 - content/en/functions/images/Config.md | 5 +- content/en/functions/images/Dither.md | 9 +- content/en/functions/images/Mask.md | 80 ++ content/en/functions/images/Overlay.md | 6 +- content/en/functions/images/Process.md | 1 - content/en/functions/images/QR.md | 112 +++ content/en/functions/images/Text.md | 72 +- content/en/functions/js/Batch.md | 22 +- content/en/functions/js/Build.md | 8 +- content/en/functions/js/_common/options.md | 5 +- content/en/functions/lang/Translate.md | 2 +- content/en/functions/math/Abs.md | 2 - content/en/functions/math/Add.md | 4 +- content/en/functions/math/Div.md | 4 +- content/en/functions/math/Mul.md | 4 +- content/en/functions/math/Rand.md | 7 +- content/en/functions/math/Sub.md | 4 +- content/en/functions/openapi3/Unmarshal.md | 14 +- content/en/functions/path/Join.md | 1 - content/en/functions/resources/ByType.md | 2 +- content/en/functions/resources/Concat.md | 2 +- .../functions/resources/ExecuteAsTemplate.md | 4 +- content/en/functions/resources/Fingerprint.md | 2 +- content/en/functions/resources/FromString.md | 14 +- content/en/functions/resources/Get.md | 2 +- content/en/functions/resources/GetMatch.md | 2 +- content/en/functions/resources/GetRemote.md | 49 +- content/en/functions/resources/Match.md | 2 +- content/en/functions/resources/PostProcess.md | 6 +- content/en/functions/resources/ToCSS.md | 11 +- content/en/functions/safe/CSS.md | 6 +- .../en/functions/strings/ContainsNonSpace.md | 2 - content/en/functions/strings/SliceString.md | 3 +- content/en/functions/templates/Defer.md | 2 - content/en/functions/time/AsTime.md | 9 +- content/en/functions/time/Duration.md | 1 - content/en/functions/time/Format.md | 6 +- content/en/functions/time/Now.md | 4 +- content/en/functions/time/ParseDuration.md | 1 - content/en/functions/transform/Emojify.md | 1 - content/en/functions/transform/Highlight.md | 83 +- content/en/functions/transform/ToMath.md | 211 +++-- content/en/functions/transform/Unmarshal.md | 24 +- content/en/functions/urls/JoinPath.md | 2 - content/en/functions/urls/Ref.md | 2 +- content/en/functions/urls/RelRef.md | 2 +- content/en/functions/urls/URLize.md | 3 +- .../en/getting-started/configuration-build.md | 15 +- .../getting-started/configuration-markup.md | 8 +- content/en/getting-started/configuration.md | 35 +- .../en/getting-started/directory-structure.md | 12 +- .../external-learning-resources/index.md | 7 +- content/en/getting-started/glossary.md | 472 ---------- content/en/getting-started/glossary/_index.md | 21 + content/en/getting-started/glossary/action.md | 5 + .../en/getting-started/glossary/archetype.md | 5 + .../en/getting-started/glossary/argument.md | 5 + content/en/getting-started/glossary/array.md | 5 + content/en/getting-started/glossary/bool.md | 5 + .../en/getting-started/glossary/boolean.md | 5 + .../getting-started/glossary/branch-bundle.md | 5 + content/en/getting-started/glossary/build.md | 5 + content/en/getting-started/glossary/bundle.md | 5 + content/en/getting-started/glossary/cache.md | 5 + content/en/getting-started/glossary/chain.md | 5 + content/en/getting-started/glossary/cjk.md | 5 + content/en/getting-started/glossary/cli.md | 5 + .../en/getting-started/glossary/collection.md | 5 + .../glossary/content-adapter.md | 5 + .../glossary/content-format.md | 5 + .../getting-started/glossary/content-type.md | 5 + .../getting-started/glossary/content-view.md | 5 + .../en/getting-started/glossary/context.md | 5 + .../glossary/default-sort-order.md | 5 + .../en/getting-started/glossary/element.md | 5 + .../getting-started/glossary/environment.md | 11 + content/en/getting-started/glossary/field.md | 5 + content/en/getting-started/glossary/flag.md | 5 + content/en/getting-started/glossary/float.md | 6 + .../glossary/floating-point.md | 5 + .../en/getting-started/glossary/fragment.md | 5 + .../getting-started/glossary/front-matter.md | 5 + .../en/getting-started/glossary/function.md | 5 + .../glossary/global-resource.md | 10 + .../glossary/headless-bundle.md | 5 + .../en/getting-started/glossary/identifier.md | 5 + content/en/getting-started/glossary/int.md | 5 + .../en/getting-started/glossary/integer.md | 5 + .../glossary/internationalization.md | 5 + .../glossary/interpreted-string-literal.md | 5 + .../en/getting-started/glossary/interval.md | 11 + content/en/getting-started/glossary/kind.md | 5 + content/en/getting-started/glossary/layout.md | 5 + .../getting-started/glossary/leaf-bundle.md | 5 + content/en/getting-started/glossary/lexer.md | 5 + .../en/getting-started/glossary/list-page.md | 5 + .../getting-started/glossary/list-template.md | 5 + .../getting-started/glossary/localization.md | 5 + .../getting-started/glossary/logical-path.md | 7 + content/en/getting-started/glossary/map.md | 5 + .../glossary/markdown-attribute.md | 5 + .../en/getting-started/glossary/marshal.md | 5 + content/en/getting-started/glossary/method.md | 5 + content/en/getting-started/glossary/module.md | 5 + content/en/getting-started/glossary/node.md | 5 + content/en/getting-started/glossary/noop.md | 5 + content/en/getting-started/glossary/object.md | 5 + .../glossary/ordered-taxonomy.md | 8 + .../getting-started/glossary/output-format.md | 7 + .../getting-started/glossary/page-bundle.md | 5 + .../glossary/page-collection.md | 5 + .../en/getting-started/glossary/page-kind.md | 7 + .../getting-started/glossary/page-resource.md | 7 + content/en/getting-started/glossary/pager.md | 5 + .../en/getting-started/glossary/paginate.md | 5 + .../en/getting-started/glossary/pagination.md | 5 + .../en/getting-started/glossary/paginator.md | 5 + .../en/getting-started/glossary/parameter.md | 5 + .../en/getting-started/glossary/partial.md | 5 + .../en/getting-started/glossary/permalink.md | 5 + content/en/getting-started/glossary/pipe.md | 5 + .../en/getting-started/glossary/pipeline.md | 7 + .../en/getting-started/glossary/publish.md | 5 + .../glossary/raw-string-literal.md | 5 + .../getting-started/glossary/regular-page.md | 5 + .../glossary/relative-permalink.md | 5 + .../glossary/remote-resource.md | 5 + .../getting-started/glossary/render-hook.md | 5 + .../getting-started/glossary/resource-type.md | 8 + .../en/getting-started/glossary/resource.md | 7 + content/en/getting-started/glossary/scalar.md | 5 + .../getting-started/glossary/scratch-pad.md | 8 + .../getting-started/glossary/section-page.md | 5 + .../en/getting-started/glossary/section.md | 5 + .../en/getting-started/glossary/shortcode.md | 5 + content/en/getting-started/glossary/slice.md | 5 + content/en/getting-started/glossary/string.md | 5 + .../glossary/taxonomic-weight.md | 5 + .../glossary/taxonomy-object.md | 5 + .../getting-started/glossary/taxonomy-page.md | 5 + .../en/getting-started/glossary/taxonomy.md | 5 + .../glossary/template-action.md | 5 + .../en/getting-started/glossary/template.md | 5 + .../en/getting-started/glossary/term-page.md | 5 + content/en/getting-started/glossary/term.md | 5 + content/en/getting-started/glossary/theme.md | 5 + content/en/getting-started/glossary/token.md | 5 + content/en/getting-started/glossary/type.md | 5 + .../en/getting-started/glossary/unmarshal.md | 5 + .../en/getting-started/glossary/variable.md | 5 + content/en/getting-started/glossary/walk.md | 5 + content/en/getting-started/glossary/weight.md | 5 + .../getting-started/glossary/weighted-page.md | 5 + .../en/getting-started/glossary/zero-time.md | 5 + content/en/getting-started/quick-start.md | 12 +- content/en/getting-started/usage.md | 11 +- .../deployment-with-rsync.md | 2 +- .../hosting-on-21yunbox.md | 2 +- .../hosting-on-aws-amplify/index.md | 8 +- .../hosting-on-github/index.md | 6 +- .../hosting-on-netlify/index.md | 16 +- .../en/hosting-and-deployment/hugo-deploy.md | 9 +- content/en/hugo-modules/_index.md | 2 +- content/en/hugo-modules/configuration.md | 6 +- content/en/hugo-modules/theme-components.md | 2 +- content/en/hugo-modules/use-modules.md | 6 +- content/en/hugo-pipes/introduction.md | 4 +- content/en/hugo-pipes/js.md | 1 - content/en/hugo-pipes/postprocess.md | 6 +- .../en/hugo-pipes/transpile-sass-to-css.md | 11 +- content/en/installation/linux.md | 5 +- content/en/methods/menu-entry/PageRef.md | 2 - content/en/methods/menu-entry/Params.md | 1 - content/en/methods/menu-entry/URL.md | 1 - content/en/methods/menu/ByWeight.md | 4 +- .../methods/page/AlternativeOutputFormats.md | 2 +- content/en/methods/page/BundleType.md | 6 +- content/en/methods/page/CurrentSection.md | 6 +- content/en/methods/page/Data.md | 11 +- content/en/methods/page/File.md | 8 +- content/en/methods/page/Fragments.md | 15 +- content/en/methods/page/GetPage.md | 2 +- content/en/methods/page/InSection.md | 3 +- content/en/methods/page/IsAncestor.md | 3 +- content/en/methods/page/IsDescendant.md | 3 +- content/en/methods/page/IsHome.md | 4 +- content/en/methods/page/IsNode.md | 3 +- content/en/methods/page/IsPage.md | 4 +- content/en/methods/page/IsSection.md | 4 +- content/en/methods/page/IsTranslated.md | 4 +- content/en/methods/page/Kind.md | 4 +- content/en/methods/page/OutputFormats.md | 2 +- content/en/methods/page/Page.md | 8 +- content/en/methods/page/Pages.md | 9 +- content/en/methods/page/Paginate.md | 8 +- content/en/methods/page/Paginator.md | 3 +- content/en/methods/page/Params.md | 4 +- content/en/methods/page/Path.md | 17 +- content/en/methods/page/Plain.md | 3 +- content/en/methods/page/RawContent.md | 3 +- content/en/methods/page/Ref.md | 2 +- content/en/methods/page/RegularPages.md | 9 +- .../en/methods/page/RegularPagesRecursive.md | 6 +- content/en/methods/page/RelRef.md | 2 +- content/en/methods/page/Render.md | 3 +- content/en/methods/page/RenderShortcodes.md | 1 - content/en/methods/page/Resources.md | 4 +- content/en/methods/page/Scratch.md | 8 +- content/en/methods/page/Section.md | 1 - content/en/methods/page/Store.md | 7 +- content/en/methods/page/Summary.md | 7 +- content/en/methods/page/Type.md | 6 +- content/en/methods/page/Weight.md | 4 +- .../page/_common/definition-of-section.md | 2 +- .../en/methods/page/_common/next-and-prev.md | 2 +- .../nextinsection-and-previnsection.md | 2 +- .../page/_common/output-format-definition.md | 10 - content/en/methods/pages/ByWeight.md | 4 +- content/en/methods/pages/GroupByDate.md | 3 +- content/en/methods/pages/GroupByExpiryDate.md | 3 +- content/en/methods/pages/GroupByLastmod.md | 3 +- content/en/methods/pages/GroupByParamDate.md | 3 +- .../en/methods/pages/GroupByPublishDate.md | 3 +- content/en/methods/pages/Related.md | 4 +- content/en/methods/resource/Colors.md | 1 - content/en/methods/resource/Data.md | 9 +- content/en/methods/resource/Err.md | 7 + content/en/methods/resource/Key.md | 1 - content/en/methods/resource/Name.md | 11 +- content/en/methods/resource/Params.md | 6 +- content/en/methods/resource/Permalink.md | 4 +- content/en/methods/resource/RelPermalink.md | 4 +- content/en/methods/resource/Title.md | 10 +- .../_common/global-page-remote-resources.md | 8 +- content/en/methods/shortcode/Inner.md | 1 - content/en/methods/shortcode/Parent.md | 4 +- content/en/methods/shortcode/Ref.md | 2 +- content/en/methods/shortcode/RelRef.md | 2 +- content/en/methods/shortcode/Scratch.md | 5 +- content/en/methods/shortcode/Store.md | 4 +- content/en/methods/site/AllPages.md | 3 +- content/en/methods/site/Data.md | 14 +- content/en/methods/site/GetPage.md | 6 +- content/en/methods/site/Pages.md | 3 +- content/en/methods/site/Param.md | 1 - content/en/methods/site/Params.md | 4 +- content/en/methods/site/RegularPages.md | 4 +- content/en/methods/site/Store.md | 7 +- content/en/methods/site/Taxonomies.md | 5 - content/en/methods/taxonomy/Alphabetical.md | 11 +- content/en/methods/taxonomy/ByCount.md | 11 +- content/en/methods/taxonomy/Count.md | 5 +- content/en/methods/taxonomy/Get.md | 8 +- .../taxonomy/_common/get-a-taxonomy-object.md | 1 - .../ordered-taxonomy-element-methods.md | 5 +- content/en/methods/time/Format.md | 3 +- content/en/methods/time/Round.md | 4 +- content/en/methods/time/Truncate.md | 4 +- content/en/myshowcase/index.md | 7 +- content/en/quick-reference/emojis.md | 4 +- .../en/quick-reference/page-collections.md | 13 +- content/en/render-hooks/_common/pageinner.md | 3 +- content/en/render-hooks/blockquotes.md | 4 +- content/en/render-hooks/code-blocks.md | 4 +- content/en/render-hooks/headings.md | 4 +- content/en/render-hooks/images.md | 11 +- content/en/render-hooks/introduction.md | 6 +- content/en/render-hooks/links.md | 13 +- content/en/render-hooks/passthrough.md | 40 +- content/en/render-hooks/tables.md | 4 +- content/en/shortcodes/_index.md | 16 + content/en/shortcodes/comment.md | 39 + content/en/shortcodes/details.md | 80 ++ content/en/shortcodes/figure.md | 115 +++ content/en/shortcodes/gist.md | 41 + content/en/shortcodes/highlight.md | 117 +++ content/en/shortcodes/instagram.md | 48 + content/en/shortcodes/param.md | 43 + content/en/shortcodes/qr.md | 113 +++ content/en/shortcodes/ref.md | 48 + content/en/shortcodes/relref.md | 48 + content/en/shortcodes/vimeo.md | 72 ++ content/en/shortcodes/x.md | 61 ++ content/en/shortcodes/youtube.md | 97 +++ .../en/showcase/1password-support/index.md | 2 +- content/en/showcase/_index.md | 2 +- .../en/showcase/hartwell-insurance/index.md | 2 +- content/en/showcase/letsencrypt/index.md | 2 +- content/en/showcase/overmindstudios/bio.md | 1 - content/en/showcase/template/index.md | 6 +- content/en/templates/404.md | 2 +- content/en/templates/_index.md | 2 +- content/en/templates/content-view.md | 18 +- content/en/templates/embedded.md | 10 +- content/en/templates/home.md | 3 +- content/en/templates/introduction.md | 45 +- content/en/templates/lookup-order.md | 6 +- content/en/templates/pagination.md | 19 +- content/en/templates/partial.md | 3 +- content/en/templates/robots.md | 6 +- content/en/templates/rss.md | 4 +- content/en/templates/shortcode.md | 28 +- content/en/templates/sitemap.md | 10 +- content/en/templates/taxonomy.md | 17 +- content/en/templates/term.md | 9 +- content/en/templates/types/index.md | 14 +- content/en/tools/migrations.md | 2 +- content/en/tools/search.md | 3 +- content/en/troubleshooting/deprecation.md | 8 +- content/en/troubleshooting/faq.md | 31 +- content/en/troubleshooting/performance.md | 4 +- data/docs.yaml | 52 +- data/embedded_template_urls.toml | 14 +- go.mod | 2 +- go.sum | 4 +- hugo_stats.json | 820 ------------------ hugoreleaser.toml | 29 - hugoreleaser.yaml | 29 + layouts/_default/_markup/render-link.html | 317 +++++++ layouts/shortcodes/glossary-term.html | 20 + layouts/shortcodes/glossary.html | 43 + netlify.toml | 2 +- 426 files changed, 3464 insertions(+), 3171 deletions(-) create mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html create mode 100644 archetypes/glossary.md create mode 100644 assets/images/examples/mask.png create mode 100644 content/en/functions/_common/highlighting-options.md create mode 100644 content/en/functions/go-template/try.md create mode 100644 content/en/functions/images/Mask.md create mode 100644 content/en/functions/images/QR.md delete mode 100644 content/en/getting-started/glossary.md create mode 100644 content/en/getting-started/glossary/_index.md create mode 100644 content/en/getting-started/glossary/action.md create mode 100644 content/en/getting-started/glossary/archetype.md create mode 100644 content/en/getting-started/glossary/argument.md create mode 100644 content/en/getting-started/glossary/array.md create mode 100644 content/en/getting-started/glossary/bool.md create mode 100644 content/en/getting-started/glossary/boolean.md create mode 100644 content/en/getting-started/glossary/branch-bundle.md create mode 100644 content/en/getting-started/glossary/build.md create mode 100644 content/en/getting-started/glossary/bundle.md create mode 100644 content/en/getting-started/glossary/cache.md create mode 100644 content/en/getting-started/glossary/chain.md create mode 100644 content/en/getting-started/glossary/cjk.md create mode 100644 content/en/getting-started/glossary/cli.md create mode 100644 content/en/getting-started/glossary/collection.md create mode 100644 content/en/getting-started/glossary/content-adapter.md create mode 100644 content/en/getting-started/glossary/content-format.md create mode 100644 content/en/getting-started/glossary/content-type.md create mode 100644 content/en/getting-started/glossary/content-view.md create mode 100644 content/en/getting-started/glossary/context.md create mode 100644 content/en/getting-started/glossary/default-sort-order.md create mode 100644 content/en/getting-started/glossary/element.md create mode 100644 content/en/getting-started/glossary/environment.md create mode 100644 content/en/getting-started/glossary/field.md create mode 100644 content/en/getting-started/glossary/flag.md create mode 100644 content/en/getting-started/glossary/float.md create mode 100644 content/en/getting-started/glossary/floating-point.md create mode 100644 content/en/getting-started/glossary/fragment.md create mode 100644 content/en/getting-started/glossary/front-matter.md create mode 100644 content/en/getting-started/glossary/function.md create mode 100644 content/en/getting-started/glossary/global-resource.md create mode 100644 content/en/getting-started/glossary/headless-bundle.md create mode 100644 content/en/getting-started/glossary/identifier.md create mode 100644 content/en/getting-started/glossary/int.md create mode 100644 content/en/getting-started/glossary/integer.md create mode 100644 content/en/getting-started/glossary/internationalization.md create mode 100644 content/en/getting-started/glossary/interpreted-string-literal.md create mode 100644 content/en/getting-started/glossary/interval.md create mode 100644 content/en/getting-started/glossary/kind.md create mode 100644 content/en/getting-started/glossary/layout.md create mode 100644 content/en/getting-started/glossary/leaf-bundle.md create mode 100644 content/en/getting-started/glossary/lexer.md create mode 100644 content/en/getting-started/glossary/list-page.md create mode 100644 content/en/getting-started/glossary/list-template.md create mode 100644 content/en/getting-started/glossary/localization.md create mode 100644 content/en/getting-started/glossary/logical-path.md create mode 100644 content/en/getting-started/glossary/map.md create mode 100644 content/en/getting-started/glossary/markdown-attribute.md create mode 100644 content/en/getting-started/glossary/marshal.md create mode 100644 content/en/getting-started/glossary/method.md create mode 100644 content/en/getting-started/glossary/module.md create mode 100644 content/en/getting-started/glossary/node.md create mode 100644 content/en/getting-started/glossary/noop.md create mode 100644 content/en/getting-started/glossary/object.md create mode 100644 content/en/getting-started/glossary/ordered-taxonomy.md create mode 100644 content/en/getting-started/glossary/output-format.md create mode 100644 content/en/getting-started/glossary/page-bundle.md create mode 100644 content/en/getting-started/glossary/page-collection.md create mode 100644 content/en/getting-started/glossary/page-kind.md create mode 100644 content/en/getting-started/glossary/page-resource.md create mode 100644 content/en/getting-started/glossary/pager.md create mode 100644 content/en/getting-started/glossary/paginate.md create mode 100644 content/en/getting-started/glossary/pagination.md create mode 100644 content/en/getting-started/glossary/paginator.md create mode 100644 content/en/getting-started/glossary/parameter.md create mode 100644 content/en/getting-started/glossary/partial.md create mode 100644 content/en/getting-started/glossary/permalink.md create mode 100644 content/en/getting-started/glossary/pipe.md create mode 100644 content/en/getting-started/glossary/pipeline.md create mode 100644 content/en/getting-started/glossary/publish.md create mode 100644 content/en/getting-started/glossary/raw-string-literal.md create mode 100644 content/en/getting-started/glossary/regular-page.md create mode 100644 content/en/getting-started/glossary/relative-permalink.md create mode 100644 content/en/getting-started/glossary/remote-resource.md create mode 100644 content/en/getting-started/glossary/render-hook.md create mode 100644 content/en/getting-started/glossary/resource-type.md create mode 100644 content/en/getting-started/glossary/resource.md create mode 100644 content/en/getting-started/glossary/scalar.md create mode 100644 content/en/getting-started/glossary/scratch-pad.md create mode 100644 content/en/getting-started/glossary/section-page.md create mode 100644 content/en/getting-started/glossary/section.md create mode 100644 content/en/getting-started/glossary/shortcode.md create mode 100644 content/en/getting-started/glossary/slice.md create mode 100644 content/en/getting-started/glossary/string.md create mode 100644 content/en/getting-started/glossary/taxonomic-weight.md create mode 100644 content/en/getting-started/glossary/taxonomy-object.md create mode 100644 content/en/getting-started/glossary/taxonomy-page.md create mode 100644 content/en/getting-started/glossary/taxonomy.md create mode 100644 content/en/getting-started/glossary/template-action.md create mode 100644 content/en/getting-started/glossary/template.md create mode 100644 content/en/getting-started/glossary/term-page.md create mode 100644 content/en/getting-started/glossary/term.md create mode 100644 content/en/getting-started/glossary/theme.md create mode 100644 content/en/getting-started/glossary/token.md create mode 100644 content/en/getting-started/glossary/type.md create mode 100644 content/en/getting-started/glossary/unmarshal.md create mode 100644 content/en/getting-started/glossary/variable.md create mode 100644 content/en/getting-started/glossary/walk.md create mode 100644 content/en/getting-started/glossary/weight.md create mode 100644 content/en/getting-started/glossary/weighted-page.md create mode 100644 content/en/getting-started/glossary/zero-time.md delete mode 100644 content/en/methods/page/_common/output-format-definition.md create mode 100644 content/en/shortcodes/_index.md create mode 100755 content/en/shortcodes/comment.md create mode 100755 content/en/shortcodes/details.md create mode 100755 content/en/shortcodes/figure.md create mode 100755 content/en/shortcodes/gist.md create mode 100755 content/en/shortcodes/highlight.md create mode 100755 content/en/shortcodes/instagram.md create mode 100755 content/en/shortcodes/param.md create mode 100755 content/en/shortcodes/qr.md create mode 100755 content/en/shortcodes/ref.md create mode 100755 content/en/shortcodes/relref.md create mode 100755 content/en/shortcodes/vimeo.md create mode 100755 content/en/shortcodes/x.md create mode 100755 content/en/shortcodes/youtube.md delete mode 100644 hugo_stats.json delete mode 100644 hugoreleaser.toml create mode 100644 hugoreleaser.yaml create mode 100644 layouts/_default/_markup/render-link.html create mode 100644 layouts/shortcodes/glossary-term.html create mode 100644 layouts/shortcodes/glossary.html diff --git a/.markdownlint.yaml b/.markdownlint.yaml index d9c2c5a67..ee0a28909 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -23,3 +23,4 @@ MD046: false MD049: false MD050: false MD053: false +MD055: false diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css index c82e77ee7..9906a13d0 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css +++ b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css @@ -10,9 +10,9 @@ } code { - padding: 0.2em; + padding: 2px 3px; margin: 0; - font-size: 85%; + font-size: 93.75%; background-color: rgba(27,31,35,0.05); border-radius: 3px; } diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css index b6343c7be..4fd374a1f 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css +++ b/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css @@ -4769,9 +4769,9 @@ h6:hover .header-link { margin: 0; } code { - padding: 0.2em; + padding: 2px 3px; margin: 0; - font-size: 85%; + font-size: 93.75%; background-color: rgba(27, 31, 35, .05); border-radius: 3px; } diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data-card.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data-card.html index 4caa21f3e..d3edadaac 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data-card.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data-card.html @@ -4,14 +4,14 @@ {{ $data := "" }} {{ $url := urls.JoinPath "https://api.github.com/users" $author }} - {{ with resources.GetRemote $url }} + {{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} - {{ else }} + {{ else with .Value }} {{ $data = . | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} {{ end }} - {{ else }} - {{ errorf "Unable to get remote resource %q" $url }} {{ end }}
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data.html index fbad3a0a7..c281e411f 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data.html @@ -4,14 +4,14 @@ {{ $data := "" }} {{ $url := urls.JoinPath "https://api.github.com/users" $author }} - {{ with resources.GetRemote $url }} + {{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} - {{ else }} + {{ else with .Value }} {{ $data = . | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} {{ end }} - {{ else }} - {{ errorf "Unable to get remote resource %q" $url }} {{ end }} {{ with $data }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html index 69ac41da4..78b2ba06e 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html @@ -11,13 +11,13 @@ Supports CSV, JSON, TOML, YAML, and XML. {{ $url := . }} {{ $data := dict }} -{{ with resources.GetRemote $url }} +{{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} - {{ else }} + {{ else with .Value }} {{ $data = .Content | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} {{ end }} -{{ else }} - {{ errorf "Unable to get remote resource %q" $url }} {{ end }} {{ return $data }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html new file mode 100644 index 000000000..1a5b4e1ec --- /dev/null +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html @@ -0,0 +1,11 @@ +{{- /* +Returns syntax-highlighted code from the given text. + +This is useful as a terse way to highlight inline code snippets. Calling the +highlight shortcode for inline snippets is verbose. +*/}} + +{{- $code := .Inner | strings.TrimSpace }} +{{- $lang := or (.Get 0) "go" }} +{{- $opts := dict "hl_inline" true "noClasses" true }} +{{- transform.Highlight $code $lang $opts }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html index dd8c60e18..7c2d805d2 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html +++ b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html @@ -120,8 +120,8 @@ Renders the given image using the given filter, if any. {{- $validFilters := slice "autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither" - "gamma" "gaussianblur" "grayscale" "hue" "invert" "none" "opacity" "overlay" - "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" + "gamma" "gaussianblur" "grayscale" "hue" "invert" "mask" "none" "opacity" + "overlay" "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" "unsharpmask" }} @@ -229,6 +229,12 @@ Renders the given image using the given filter, if any. {{- $ctx = merge $ctx (dict "argsRequired" 0) }} {{- template "validate-arg-count" $ctx }} {{- $f = images.Invert }} +{{- else if eq $filter "mask" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }} + {{- $maskImage := partial "inline/get-resource.html" $ctx }} + {{- $f = images.Mask $maskImage }} {{- else if eq $filter "opacity" }} {{- $ctx = merge $ctx (dict "argsRequired" 1) }} {{- template "validate-arg-count" $ctx }} @@ -322,11 +328,15 @@ Renders the given image using the given filter, if any. {{- end }} {{- /* Render. */}} +{{- $class := "di va b--black-20" }} +{{- if eq $filter "mask" }} + {{- $class = "di va" }} +{{- end }} {{- if $example }}

Original

- {{ $alt }} + {{ $alt }}

Processed

- {{ $alt }} + {{ $alt }} {{- else -}} {{ $alt }} {{- end }} @@ -354,15 +364,15 @@ Renders the given image using the given filter, if any. {{- $u := urls.Parse .src }} {{- $msg := "The %q shortcode was unable to resolve %s. See %s" }} {{- if $u.IsAbs }} - {{- with resources.GetRemote $u.String }} + {{- with try (resources.GetRemote $u.String) }} {{- with .Err }} {{- errorf "%s" . }} - {{- else }} + {{- else with .Value }} {{- /* This is a remote resource. */}} {{- $r = . }} + {{- else }} + {{- errorf $msg $.name $u.String $.position }} {{- end }} - {{- else }} - {{- errorf $msg $.name $u.String $.position }} {{- end }} {{- else }} {{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }} diff --git a/_vendor/modules.txt b/_vendor/modules.txt index a5092f11e..50178323d 100644 --- a/_vendor/modules.txt +++ b/_vendor/modules.txt @@ -1 +1 @@ -# github.com/gohugoio/gohugoioTheme v0.0.0-20250106044328-feb60697e056 +# github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743 diff --git a/archetypes/glossary.md b/archetypes/glossary.md new file mode 100644 index 000000000..b38ced64a --- /dev/null +++ b/archetypes/glossary.md @@ -0,0 +1,19 @@ +--- +title: {{ replace .File.ContentBaseName "-" " " }} +--- + + diff --git a/archetypes/showcase/index.md b/archetypes/showcase/index.md index 04d454575..cdd036722 100644 --- a/archetypes/showcase/index.md +++ b/archetypes/showcase/index.md @@ -20,7 +20,7 @@ byline: "[bep](https://github.com/bep), Hugo Lead" To complete this showcase: 1. Write the story about your site in this file. -2. Add a summary to the `bio.md` file in this folder. +2. Add a summary to the `bio.md` file in this directory. 3. Replace the `featured-template.png` with a screenshot of your site. You can rename it, but it must contain the word `featured`. 4. Create a new pull request in https://github.com/gohugoio/hugoDocs/pulls diff --git a/assets/images/examples/mask.png b/assets/images/examples/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..c3005a66949218c44670352d0b53bf53878786a3 GIT binary patch literal 137283 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�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
N1Co01*wcW8lXIgWT)|4}7v6X{UnVAil3 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 @@ -92,19 +92,19 @@ simple disableInlineCSS = true {{< /code-toggle >}} -### Twitter +### X enableDNT -: Enabling this for the twitter/tweet shortcode, the tweet and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads. +: Enabling this for the x shortcode, the post and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads. simple -: If simple mode is enabled, a static and no-JS version of a tweet will be built. +: If simple mode is enabled, a static and no-JS version of a post will be built. -**Note:** If you use the _simple mode_ for Twitter, you may want to disable the inline styles provided by Hugo: +**Note:** If you use the _simple mode_ for X, you may want to disable the inline styles provided by Hugo: {{< code-toggle file=hugo >}} [services] -[services.twitter] +[services.x] disableInlineCSS = true {{< /code-toggle >}} diff --git a/content/en/commands/hugo.md b/content/en/commands/hugo.md index ef0bca9a5..42f882ad7 100644 --- a/content/en/commands/hugo.md +++ b/content/en/commands/hugo.md @@ -72,7 +72,6 @@ hugo [flags] * [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell * [hugo config](/commands/hugo_config/) - Display site configuration * [hugo convert](/commands/hugo_convert/) - Convert front matter to another format -* [hugo deploy](/commands/hugo_deploy/) - Deploy your site to a cloud provider * [hugo env](/commands/hugo_env/) - Display version and environment info * [hugo gen](/commands/hugo_gen/) - Generate documentation and syntax highlighting styles * [hugo import](/commands/hugo_import/) - Import a site from another system diff --git a/content/en/commands/hugo_completion.md b/content/en/commands/hugo_completion.md index 96f53742a..ac60dc148 100644 --- a/content/en/commands/hugo_completion.md +++ b/content/en/commands/hugo_completion.md @@ -29,6 +29,7 @@ See each sub-command's help for details on how to use the generated script. -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_completion_bash.md b/content/en/commands/hugo_completion_bash.md index 60973415f..41fb47c0c 100644 --- a/content/en/commands/hugo_completion_bash.md +++ b/content/en/commands/hugo_completion_bash.md @@ -52,6 +52,7 @@ hugo completion bash -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_completion_fish.md b/content/en/commands/hugo_completion_fish.md index 92bbd6c22..7f971c3ca 100644 --- a/content/en/commands/hugo_completion_fish.md +++ b/content/en/commands/hugo_completion_fish.md @@ -43,6 +43,7 @@ hugo completion fish [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_completion_powershell.md b/content/en/commands/hugo_completion_powershell.md index f01442920..6ea17892b 100644 --- a/content/en/commands/hugo_completion_powershell.md +++ b/content/en/commands/hugo_completion_powershell.md @@ -40,6 +40,7 @@ hugo completion powershell [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_completion_zsh.md b/content/en/commands/hugo_completion_zsh.md index 142c53103..b9e79f9f3 100644 --- a/content/en/commands/hugo_completion_zsh.md +++ b/content/en/commands/hugo_completion_zsh.md @@ -54,6 +54,7 @@ hugo completion zsh [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_config.md b/content/en/commands/hugo_config.md index 8e8d745d2..2b4eaafa1 100644 --- a/content/en/commands/hugo_config.md +++ b/content/en/commands/hugo_config.md @@ -24,6 +24,7 @@ hugo config [command] [flags] --format string preferred file format (toml, yaml or json) (default "toml") -h, --help help for config --lang string the language to display config for. Defaults to the first language defined. + --printZero include config options with zero values (e.g. false, 0, "") in the output --renderSegments strings named segments to render (configured in the segments config) -t, --theme strings themes to use (located in /themes/THEMENAME/) ``` @@ -38,6 +39,7 @@ hugo config [command] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_config_mounts.md b/content/en/commands/hugo_config_mounts.md index 5af4819a2..06a781220 100644 --- a/content/en/commands/hugo_config_mounts.md +++ b/content/en/commands/hugo_config_mounts.md @@ -32,6 +32,7 @@ hugo config mounts [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_convert.md b/content/en/commands/hugo_convert.md index aeaa37766..a8d0b6a38 100644 --- a/content/en/commands/hugo_convert.md +++ b/content/en/commands/hugo_convert.md @@ -31,6 +31,7 @@ See convert's subcommands toJSON, toTOML and toYAML for more information. -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_convert_toJSON.md b/content/en/commands/hugo_convert_toJSON.md index 40403193c..fe81146f9 100644 --- a/content/en/commands/hugo_convert_toJSON.md +++ b/content/en/commands/hugo_convert_toJSON.md @@ -32,6 +32,7 @@ hugo convert toJSON [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file -o, --output string filesystem path to write files to --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) diff --git a/content/en/commands/hugo_convert_toTOML.md b/content/en/commands/hugo_convert_toTOML.md index 53ab82651..490b15ee6 100644 --- a/content/en/commands/hugo_convert_toTOML.md +++ b/content/en/commands/hugo_convert_toTOML.md @@ -32,6 +32,7 @@ hugo convert toTOML [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file -o, --output string filesystem path to write files to --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) diff --git a/content/en/commands/hugo_convert_toYAML.md b/content/en/commands/hugo_convert_toYAML.md index efb63a4e2..9b00ce247 100644 --- a/content/en/commands/hugo_convert_toYAML.md +++ b/content/en/commands/hugo_convert_toYAML.md @@ -32,6 +32,7 @@ hugo convert toYAML [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file -o, --output string filesystem path to write files to --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) diff --git a/content/en/commands/hugo_deploy.md b/content/en/commands/hugo_deploy.md index fce1e5422..696acf51f 100644 --- a/content/en/commands/hugo_deploy.md +++ b/content/en/commands/hugo_deploy.md @@ -42,6 +42,7 @@ hugo deploy [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_env.md b/content/en/commands/hugo_env.md index e216be416..7e21733a4 100644 --- a/content/en/commands/hugo_env.md +++ b/content/en/commands/hugo_env.md @@ -31,6 +31,7 @@ hugo env [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_gen.md b/content/en/commands/hugo_gen.md index 97cdbdb9d..d6333b52d 100644 --- a/content/en/commands/hugo_gen.md +++ b/content/en/commands/hugo_gen.md @@ -27,6 +27,7 @@ Generate documentation for your project using Hugo's documentation engine, inclu -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_gen_chromastyles.md b/content/en/commands/hugo_gen_chromastyles.md index 49cde6bb9..2863e46b4 100644 --- a/content/en/commands/hugo_gen_chromastyles.md +++ b/content/en/commands/hugo_gen_chromastyles.md @@ -37,6 +37,7 @@ hugo gen chromastyles [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_gen_doc.md b/content/en/commands/hugo_gen_doc.md index 180dc4f95..2c2c3a118 100644 --- a/content/en/commands/hugo_gen_doc.md +++ b/content/en/commands/hugo_gen_doc.md @@ -37,6 +37,7 @@ hugo gen doc [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_gen_man.md b/content/en/commands/hugo_gen_man.md index f33342c54..14fe859e3 100644 --- a/content/en/commands/hugo_gen_man.md +++ b/content/en/commands/hugo_gen_man.md @@ -34,6 +34,7 @@ hugo gen man [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_import.md b/content/en/commands/hugo_import.md index b20b58599..2b8e62951 100644 --- a/content/en/commands/hugo_import.md +++ b/content/en/commands/hugo_import.md @@ -29,6 +29,7 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_import_jekyll.md b/content/en/commands/hugo_import_jekyll.md index 14c57cc44..8746c156e 100644 --- a/content/en/commands/hugo_import_jekyll.md +++ b/content/en/commands/hugo_import_jekyll.md @@ -34,6 +34,7 @@ hugo import jekyll [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list.md b/content/en/commands/hugo_list.md index 726fe51a9..741ca1d68 100644 --- a/content/en/commands/hugo_list.md +++ b/content/en/commands/hugo_list.md @@ -29,6 +29,7 @@ List requires a subcommand, e.g. hugo list drafts -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list_all.md b/content/en/commands/hugo_list_all.md index 59846733d..e0f1efdcb 100644 --- a/content/en/commands/hugo_list_all.md +++ b/content/en/commands/hugo_list_all.md @@ -31,6 +31,7 @@ hugo list all [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list_drafts.md b/content/en/commands/hugo_list_drafts.md index 5f3bcd617..25ddc78d3 100644 --- a/content/en/commands/hugo_list_drafts.md +++ b/content/en/commands/hugo_list_drafts.md @@ -31,6 +31,7 @@ hugo list drafts [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list_expired.md b/content/en/commands/hugo_list_expired.md index c010d1159..1936b9920 100644 --- a/content/en/commands/hugo_list_expired.md +++ b/content/en/commands/hugo_list_expired.md @@ -31,6 +31,7 @@ hugo list expired [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list_future.md b/content/en/commands/hugo_list_future.md index 888784dcd..3152639c2 100644 --- a/content/en/commands/hugo_list_future.md +++ b/content/en/commands/hugo_list_future.md @@ -31,6 +31,7 @@ hugo list future [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_list_published.md b/content/en/commands/hugo_list_published.md index b8ec0e7b2..a7a08c7b4 100644 --- a/content/en/commands/hugo_list_published.md +++ b/content/en/commands/hugo_list_published.md @@ -31,6 +31,7 @@ hugo list published [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod.md b/content/en/commands/hugo_mod.md index 2c07b89cf..c037be6da 100644 --- a/content/en/commands/hugo_mod.md +++ b/content/en/commands/hugo_mod.md @@ -38,6 +38,7 @@ See https://gohugo.io/hugo-modules/ for more information. -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_clean.md b/content/en/commands/hugo_mod_clean.md index 7df51059f..6f49284b7 100644 --- a/content/en/commands/hugo_mod_clean.md +++ b/content/en/commands/hugo_mod_clean.md @@ -38,6 +38,7 @@ hugo mod clean [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_get.md b/content/en/commands/hugo_mod_get.md index f4ca6069a..08e1e5c84 100644 --- a/content/en/commands/hugo_mod_get.md +++ b/content/en/commands/hugo_mod_get.md @@ -62,6 +62,7 @@ hugo mod get [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_graph.md b/content/en/commands/hugo_mod_graph.md index 5b5a14d5d..b7bfc6bfe 100644 --- a/content/en/commands/hugo_mod_graph.md +++ b/content/en/commands/hugo_mod_graph.md @@ -39,6 +39,7 @@ hugo mod graph [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_init.md b/content/en/commands/hugo_mod_init.md index bf9651b59..0d9f1fd4e 100644 --- a/content/en/commands/hugo_mod_init.md +++ b/content/en/commands/hugo_mod_init.md @@ -43,6 +43,7 @@ hugo mod init [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_npm.md b/content/en/commands/hugo_mod_npm.md index a013fb91f..4c6b0c6a7 100644 --- a/content/en/commands/hugo_mod_npm.md +++ b/content/en/commands/hugo_mod_npm.md @@ -31,6 +31,7 @@ hugo mod npm [command] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_npm_pack.md b/content/en/commands/hugo_mod_npm_pack.md index 8f8738280..ffbf0703e 100644 --- a/content/en/commands/hugo_mod_npm_pack.md +++ b/content/en/commands/hugo_mod_npm_pack.md @@ -46,6 +46,7 @@ hugo mod npm pack [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_tidy.md b/content/en/commands/hugo_mod_tidy.md index c15ddb3c8..cff192130 100644 --- a/content/en/commands/hugo_mod_tidy.md +++ b/content/en/commands/hugo_mod_tidy.md @@ -32,6 +32,7 @@ hugo mod tidy [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_vendor.md b/content/en/commands/hugo_mod_vendor.md index ae112a36a..2b812e4c4 100644 --- a/content/en/commands/hugo_mod_vendor.md +++ b/content/en/commands/hugo_mod_vendor.md @@ -38,6 +38,7 @@ hugo mod vendor [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_mod_verify.md b/content/en/commands/hugo_mod_verify.md index 63dd28ce8..051477c90 100644 --- a/content/en/commands/hugo_mod_verify.md +++ b/content/en/commands/hugo_mod_verify.md @@ -37,6 +37,7 @@ hugo mod verify [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_new.md b/content/en/commands/hugo_new.md index 4d3021b44..2788ef168 100644 --- a/content/en/commands/hugo_new.md +++ b/content/en/commands/hugo_new.md @@ -34,6 +34,7 @@ Ensure you run this within the root directory of your site. -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_new_content.md b/content/en/commands/hugo_new_content.md index a8d2100f9..9624e9a61 100644 --- a/content/en/commands/hugo_new_content.md +++ b/content/en/commands/hugo_new_content.md @@ -46,6 +46,7 @@ hugo new content [path] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_new_site.md b/content/en/commands/hugo_new_site.md index cc0e63013..0f0096ae4 100644 --- a/content/en/commands/hugo_new_site.md +++ b/content/en/commands/hugo_new_site.md @@ -35,6 +35,7 @@ hugo new site [path] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_new_theme.md b/content/en/commands/hugo_new_theme.md index a79978c4a..b1c937bae 100644 --- a/content/en/commands/hugo_new_theme.md +++ b/content/en/commands/hugo_new_theme.md @@ -34,6 +34,7 @@ hugo new theme [name] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_server.md b/content/en/commands/hugo_server.md index a1c77d36f..4ab161f8f 100644 --- a/content/en/commands/hugo_server.md +++ b/content/en/commands/hugo_server.md @@ -50,7 +50,6 @@ hugo server [command] [flags] --liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1) --minify minify any supported output format (HTML, XML etc.) -N, --navigateToChanged navigate to changed content file on live browser reload - --noBuildLock don't create .hugo_build.lock file --noChmod don't sync permission mode of files --noHTTPCache prevent HTTP caching --noTimes don't sync modification time of files @@ -85,6 +84,7 @@ hugo server [command] [flags] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_server_trust.md b/content/en/commands/hugo_server_trust.md index 1a904e845..9d24e78e3 100644 --- a/content/en/commands/hugo_server_trust.md +++ b/content/en/commands/hugo_server_trust.md @@ -28,6 +28,7 @@ hugo server trust [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/commands/hugo_version.md b/content/en/commands/hugo_version.md index b1a6b971e..14cc92a00 100644 --- a/content/en/commands/hugo_version.md +++ b/content/en/commands/hugo_version.md @@ -31,6 +31,7 @@ hugo version [flags] [args] -e, --environment string build environment --ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern --logLevel string log level (debug|info|warn|error) + --noBuildLock don't create .hugo_build.lock file --quiet build in quiet mode -M, --renderToMemory render to memory (mostly useful when running the server) -s, --source string filesystem path to read files relative from diff --git a/content/en/content-management/archetypes.md b/content/en/content-management/archetypes.md index acf101fda..30747258c 100644 --- a/content/en/content-management/archetypes.md +++ b/content/en/content-management/archetypes.md @@ -15,7 +15,7 @@ aliases: [/content/archetypes/] ## Overview -A content file consists of [front matter] and markup. The markup is typically Markdown, but Hugo also supports other [content formats]. Front matter can be TOML, YAML, or JSON. +A content file consists of [front matter](g) and markup. The markup is typically Markdown, but Hugo also supports other [content formats](g). Front matter can be TOML, YAML, or JSON. The `hugo new content` command creates a new file in the `content` directory, using an archetype as a template. This is the default archetype: @@ -25,7 +25,7 @@ date = '{{ .Date }}' draft = true {{< /code-toggle >}} -When you create new content, Hugo evaluates the [template actions] within the archetype. For example: +When you create new content, Hugo evaluates the [template actions](g) within the archetype. For example: ```sh hugo new content posts/my-first-post.md @@ -39,7 +39,7 @@ date = '2023-08-24T11:49:46-07:00' draft = true {{< /code-toggle >}} -You can create an archetype for one or more [content types]. For example, use one archetype for posts, and use the default archetype for everything else: +You can create an archetype for one or more [content types](g). For example, use one archetype for posts, and use the default archetype for everything else: ```text archetypes/ @@ -59,18 +59,18 @@ hugo new content posts/my-first-post.md The archetype lookup order is: -1. archetypes/posts.md -1. archetypes/default.md -1. themes/my-theme/archetypes/posts.md -1. themes/my-theme/archetypes/default.md +1. `archetypes/posts.md` +1. `archetypes/default.md` +1. `themes/my-theme/archetypes/posts.md` +1. `themes/my-theme/archetypes/default.md` If none of these exists, Hugo uses a built-in default archetype. ## Functions and context -You can use any [template function] within an archetype. As shown above, the default archetype uses the [`replace`](/functions/strings/replace) function to replace hyphens with spaces when populating the title in front matter. +You can use any template [function](g) within an archetype. As shown above, the default archetype uses the [`replace`](/functions/strings/replace) function to replace hyphens with spaces when populating the title in front matter. -Archetypes receive the following [context]: +Archetypes receive the following [context](g): Date : (`string`) The current date and time, formatted in compliance with RFC3339. @@ -79,9 +79,7 @@ File : (`hugolib.fileInfo`) Returns file information for the current page. See [details](/methods/page/file). Type -: (`string`) The [content type] inferred from the top-level directory name, or as specified by the `--kind` flag passed to the `hugo new content` command. - -[content type]: /getting-started/glossary#content-type +: (`string`) The [content type](g) inferred from the top-level directory name, or as specified by the `--kind` flag passed to the `hugo new content` command. Site : (`page.Site`) The current site object. See [details](/methods/site/). @@ -130,11 +128,11 @@ One or more practical examples, each within a fenced code block. Additional information to clarify as needed. {{< /code >}} -Although you can include [template actions] within the content body, remember that Hugo evaluates these once---at the time of content creation. In most cases, place template actions in a [template] where Hugo evaluates the actions every time you [build](/getting-started/glossary/#build) the site. +Although you can include [template actions](g) within the content body, remember that Hugo evaluates these once---at the time of content creation. In most cases, place template actions in a [template](g) where Hugo evaluates the actions every time you [build](g) the site. ## Leaf bundles -You can also create archetypes for [leaf bundles](/getting-started/glossary/#leaf-bundle). +You can also create archetypes for [leaf bundles](g). For example, in a photography site you might have a section (content type) for galleries. Each gallery is leaf bundle with content and images. @@ -193,11 +191,3 @@ To create an article using the tutorials archetype: ```sh hugo new content --kind tutorials articles/something.md ``` - -[content formats]: /getting-started/glossary/#content-format -[content types]: /getting-started/glossary/#content-type -[context]: /getting-started/glossary/#context -[front matter]: /getting-started/glossary/#front-matter -[template actions]: /getting-started/glossary/#template-action -[template]: /getting-started/glossary/#template -[template function]: /getting-started/glossary/#function diff --git a/content/en/content-management/build-options.md b/content/en/content-management/build-options.md index a279fb651..e78462455 100644 --- a/content/en/content-management/build-options.md +++ b/content/en/content-management/build-options.md @@ -21,7 +21,6 @@ publishResources = true render = 'always' {{< /code-toggle >}} - list : When to include the page within page collections. Specify one of: @@ -115,7 +114,7 @@ public/ In the example above, note that: 1. Hugo did not publish an HTML file for the page. -2. Despite setting `publishResources` to `false` in front matter, Hugo published the [page resources] because we invoked the [`RelPermalink`] method on each resource. This is the expected behavior. +1. Despite setting `publishResources` to `false` in front matter, Hugo published the [page resources] because we invoked the [`RelPermalink`] method on each resource. This is the expected behavior. ## Example -- headless section @@ -181,7 +180,7 @@ public/ In the example above, note that: 1. Hugo did not publish an HTML file for the page. -2. Despite setting `publishResources` to `false` in front matter, Hugo correctly published the [page resources] because we invoked the [`RelPermalink`] method on each resource. This is the expected behavior. +1. Despite setting `publishResources` to `false` in front matter, Hugo correctly published the [page resources] because we invoked the [`RelPermalink`] method on each resource. This is the expected behavior. ## Example -- list without publishing diff --git a/content/en/content-management/content-adapters.md b/content/en/content-management/content-adapters.md index a88bdba5f..369e91a12 100644 --- a/content/en/content-management/content-adapters.md +++ b/content/en/content-management/content-adapters.md @@ -17,7 +17,7 @@ toc: true A content adapter is a template that dynamically creates pages when building a site. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML. -Unlike templates that reside in the layouts directory, content adapters reside in the content directory, no more than one per directory per language. When a content adapter creates a page, the page's [logical path] will be relative to the content adapter. +Unlike templates that reside in the `layouts` directory, content adapters reside in the `content` directory, no more than one per directory per language. When a content adapter creates a page, the page's [logical path](g) will be relative to the content adapter. ```text content/ @@ -33,7 +33,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 @@ -137,9 +137,8 @@ Key|Description|Required `dates.expiryDate`|The page expiry date as a `time.Time` value.|  `dates.lastmod`|The page last modification date as a `time.Time` value.|  `dates.publishDate`|The page publication date as a `time.Time` value.|  -`kind`|The [page kind]. Default is `page`.|  `params`|A map of page parameters.|  -`path`|The page's [logical path] relative to the content adapter. Do not include a leading slash or file extension.|:heavy_check_mark: +`path`|The page's [logical path](g) relative to the content adapter. Do not include a leading slash or file extension.|:heavy_check_mark: `title`|The page title.|  {{% note %}} @@ -158,7 +157,7 @@ Key|Description|Required `content.value`|The content value as a string or resource.|:heavy_check_mark: `name`|The resource name.|  `params`|A map of resource parameters.|  -`path`|The resources's [logical path] relative to the content adapter. Do not include a leading slash.|:heavy_check_mark: +`path`|The resources's [logical path](g) relative to the content adapter. Do not include a leading slash.|:heavy_check_mark: `title`|The resource title.|  {{% note %}} @@ -193,14 +192,14 @@ Step 3 {{/* Get remote data. */}} {{ $data := dict }} {{ $url := "https://gohugo.io/shared/examples/data/books.json" }} -{{ with resources.GetRemote $url }} +{{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "Unable to get remote resource %s: %s" $url . }} - {{ else }} + {{ else with .Value }} {{ $data = . | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %s" $url }} {{ end }} -{{ else }} - {{ errorf "Unable to get remote resource %s" $url }} {{ end }} {{/* Add pages and page resources. */}} @@ -223,10 +222,10 @@ Step 3 {{/* Add page resource. */}} {{ $item := . }} {{ with $url := $item.cover }} - {{ with resources.GetRemote $url }} + {{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "Unable to get remote resource %s: %s" $url . }} - {{ else }} + {{ else with .Value }} {{ $content := dict "mediaType" .MediaType.Type "value" .Content }} {{ $params := dict "alt" $item.title }} {{ $resource := dict @@ -235,9 +234,9 @@ Step 3 "path" (printf "%s/cover.%s" $item.title .MediaType.SubType) }} {{ $.AddResource $resource }} + {{ else }} + {{ errorf "Unable to get remote resource %s" $url }} {{ end }} - {{ else }} - {{ errorf "Unable to get remote resource %s" $url }} {{ end }} {{ end }} @@ -281,7 +280,7 @@ Step 4 With multilingual sites you can: 1. Create one content adapter for all languages using the [`EnableAllLanguages`](#enablealllanguages) method as described above. -2. Create content adapters unique to each language. See the examples below. +1. Create content adapters unique to each language. See the examples below. ### Translations by file name @@ -352,8 +351,6 @@ To detect page collisions, use the `--printPathWarnings` flag when building your [content formats]: /content-management/formats/#classification [front matter field]: /content-management/front-matter/#fields -[logical path]: /getting-started/glossary/#logical-path [media type]: https://en.wikipedia.org/wiki/Media_type -[page kind]: /getting-started/glossary/#page-kind [syntax]: /templates/introduction/ [template functions]: /functions/ diff --git a/content/en/content-management/cross-references.md b/content/en/content-management/cross-references.md index 124a3bf06..56f4c39e1 100644 --- a/content/en/content-management/cross-references.md +++ b/content/en/content-management/cross-references.md @@ -49,7 +49,7 @@ The pages can be referenced as follows: {{}} ``` -index.md can be reference either by its path or by its containing folder without the ending `/`. \_index.md can be referenced only by its containing folder: +`index.md` can be reference either by its path or by its containing directory without the ending `/`. `_index.md` can be referenced only by its containing directory: ```text {{}} <-- References /about/_index.md diff --git a/content/en/content-management/data-sources.md b/content/en/content-management/data-sources.md index c009fb7f3..1237e4f4d 100644 --- a/content/en/content-management/data-sources.md +++ b/content/en/content-management/data-sources.md @@ -12,21 +12,15 @@ toc: true aliases: [/extras/datafiles/,/extras/datadrivencontent/,/doc/datafiles/,/templates/data-templates/] --- -Hugo can access and [unmarshal] local and remote data sources including CSV, JSON, TOML, YAML, and XML. Use this data to augment existing content or to create new content. +Hugo can access and [unmarshal](g) local and remote data sources including CSV, JSON, TOML, YAML, and XML. Use this data to augment existing content or to create new content. -[unmarshal]: /getting-started/glossary/#unmarshal - -A data source might be a file in the data directory, a [global resource], a [page resource], or a [remote resource]. - -[global resource]: /getting-started/glossary/#global-resource -[page resource]: /getting-started/glossary/#page-resource -[remote resource]: /getting-started/glossary/#remote-resource +A data source might be a file in the `data` directory, a [global resource](g), a [page resource](g), or a [remote resource](g). ## Data directory -The data directory in the root of your project may contain one or more data files, in either a flat or nested tree. Hugo merges the data files to create a single data structure, accessible with the `Data` method on a `Site` object. +The `data` directory in the root of your project may contain one or more data files, in either a flat or nested tree. Hugo merges the data files to create a single data structure, accessible with the `Data` method on a `Site` object. -Hugo also merges data directories from themes and modules into this single data structure, where the data directory in the root of your project takes precedence. +Hugo also merges data directories from themes and modules into this single data structure, where the `data` directory in the root of your project takes precedence. {{% note %}} Hugo reads the combined data structure into memory and keeps it there for the entire build. For data that is infrequently accessed, use global or page resources instead. @@ -42,7 +36,7 @@ project/ ``` {{% note %}} -Do not place CSV files in the data directory. Access CSV files as page, global, or remote resources. +Do not place CSV files in the `data` directory. Access CSV files as page, global, or remote resources. {{% /note %}} See the documentation for the [`Data`] method on a `Site` object for details and examples. diff --git a/content/en/content-management/formats.md b/content/en/content-management/formats.md index 1132c888c..a02aa2545 100644 --- a/content/en/content-management/formats.md +++ b/content/en/content-management/formats.md @@ -29,7 +29,10 @@ content/ Regardless of content format, all content must have [front matter], preferably including both `title` and `date`. -Hugo selects the content renderer based on the `markup` identifier in front matter, falling back to the file extension. See the [classification](#classification) table below for a list of markup identifiers and recognized file extensions. +Hugo selects the content renderer based on the `markup` identifier in front matter, falling back to the file extension. See the [classification] table below for a list of markup identifiers and recognized file extensions. + +[classification]: #classification +[front matter]: /content-management/front-matter/ ## Formats @@ -37,7 +40,7 @@ Hugo selects the content renderer based on the `markup` identifier in front matt Create your content in [Markdown] preceded by front matter. -Markdown is Hugo's default content format. Hugo natively renders Markdown to HTML using [Goldmark]. Goldmark is fast and conforms to the [CommonMark] and [GitHub Flavored Markdown] specifications. You can [configure Goldmark] in your site configuration. +Markdown is Hugo's default content format. Hugo natively renders Markdown to HTML using [Goldmark]. Goldmark is fast and conforms to the [CommonMark] and [GitHub Flavored Markdown] specifications. You can configure Goldmark in your [site configuration][configure goldmark]. Hugo provides custom Markdown features including: @@ -48,24 +51,39 @@ Hugo provides custom Markdown features including: : Leverage the embedded Markdown extensions to create tables, definition lists, footnotes, task lists, inserted text, mark text, subscripts, superscripts, and more. [Mathematics] -: Include mathematical equations and expressions in Markdown using LaTeX or TeX typesetting syntax. +: Include mathematical equations and expressions in Markdown using LaTeX markup. [Render hooks] : Override the conversion of Markdown to HTML when rendering fenced code blocks, headings, images, and links. For example, render every standalone image as an HTML `figure` element. +[Attributes]: /content-management/markdown-attributes/ +[CommonMark]: https://spec.commonmark.org/current/ +[Extensions]: /getting-started/configuration-markup/#goldmark-extensions +[GitHub Flavored Markdown]: https://github.github.com/gfm/ +[Goldmark]: https://github.com/yuin/goldmark +[Markdown]: https://daringfireball.net/projects/markdown/ +[Mathematics]: /content-management/mathematics/ +[Render hooks]: /render-hooks/introduction/ +[configure goldmark]: /getting-started/configuration-markup/#goldmark + ### HTML Create your content in [HTML] preceded by front matter. The content is typically what you would place within an HTML document's `body` or `main` element. +[HTML]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content + ### Emacs Org Mode -Create your content in the [Emacs Org Mode] format preceded by front matter. You can use Org Mode keywords for front matter. See [details](/content-management/front-matter/#emacs-org-mode). +Create your content in the [Emacs Org Mode] format preceded by front matter. You can use Org Mode keywords for front matter. See [details]. + +[details]: /content-management/front-matter/#emacs-org-mode +[Emacs Org Mode]: https://orgmode.org/ ### 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. -You can [configure the AsciiDoc renderer] in your site configuration. +You can configure the AsciiDoc renderer in your [site configuration][configure asciidoc]. In its default configuration, Hugo passes these CLI flags when calling the Asciidoctor executable: @@ -79,6 +97,10 @@ The CLI flags passed to the Asciidoctor executable depend on configuration. You hugo --logLevel info ``` +[AsciiDoc]: https://asciidoc.org/ +[configure the AsciiDoc renderer]: /getting-started/configuration-markup/#asciidoc +[configure asciidoc]: /getting-started/configuration-markup/#asciidoc + ### 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. @@ -89,6 +111,8 @@ Hugo passes these CLI flags when calling the Pandoc executable: --mathjax ``` +[Pandoc]: https://pandoc.org/ + ### 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. @@ -99,6 +123,9 @@ Hugo passes these CLI flags when calling the rst2html executable: --leave-comments --initial-header-level=2 ``` +[Docutils]: https://docutils.sourceforge.io/ +[reStructuredText]: https://docutils.sourceforge.io/rst.html + ## Classification Content format|Media type|Identifier|File extensions @@ -116,22 +143,3 @@ When converting content to HTML, Hugo uses: - External renderers for AsciiDoc, Pandoc, and reStructuredText Native renderers are faster than external renderers. - -[AsciiDoc]: https://asciidoc.org/ -[Asciidoctor]: https://asciidoctor.org/ -[Attributes]: /content-management/markdown-attributes/ -[CommonMark]: https://spec.commonmark.org/current/ -[Docutils]: https://docutils.sourceforge.io/ -[Emacs Org Mode]: https://orgmode.org/ -[Extensions]: /getting-started/configuration-markup/#goldmark-extensions -[GitHub Flavored Markdown]: https://github.github.com/gfm/ -[Goldmark]: https://github.com/yuin/goldmark -[HTML]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics -[Markdown]: https://daringfireball.net/projects/markdown/ -[Mathematics]: /content-management/mathematics/ -[Pandoc]: https://pandoc.org/ -[Render hooks]: https://gohugo.io/render-hooks/introduction/ -[configure Goldmark]: /getting-started/configuration-markup/#goldmark -[configure the AsciiDoc renderer]: /getting-started/configuration-markup/#asciidoc -[front matter]: /content-management/front-matter/ -[reStructuredText]: https://docutils.sourceforge.io/rst.html diff --git a/content/en/content-management/front-matter.md b/content/en/content-management/front-matter.md index 5cbf645a5..dba77f1d1 100644 --- a/content/en/content-management/front-matter.md +++ b/content/en/content-management/front-matter.md @@ -39,15 +39,7 @@ weight = 10 author = 'John Smith' {{< /code-toggle >}} -Front matter fields may be [boolean], [integer], [float], [string], [arrays], or [maps]. Note that the TOML format also supports unquoted date/time values. - -[scalar]: /getting-started/glossary/#scalar -[arrays]: /getting-started/glossary/#array -[maps]: /getting-started/glossary/#map -[boolean]: /getting-started/glossary/#boolean -[integer]: /getting-started/glossary/#integer -[float]: /getting-started/glossary/#float -[string]: /getting-started/glossary/#string +Front matter fields may be [boolean](g), [integer](g), [float](g), [string](g), [arrays](g), or [maps](g). Note that the TOML format also supports unquoted date/time values. ## Fields @@ -82,7 +74,6 @@ The field names below are reserved. For example, you cannot create a custom fiel (`string`) The date associated with the page, typically the creation date. Note that the TOML format also supports unquoted date/time values. See the [dates](#dates) section for examples. Access this value from a template using the [`Date`] method on a `Page` object. - [`date`]: /methods/page/date/ ###### description @@ -113,22 +104,19 @@ If `true`, the page will not be rendered unless you pass the `--buildDrafts` fla ###### isCJKLanguage -(`bool`) Set to `true` if the content language is in the [CJK] family. This value determines how Hugo calculates word count, and affects the values returned by the [`WordCount`], [`FuzzyWordCount`], [`ReadingTime`], and [`Summary`] methods on a `Page` object. +(`bool`) Set to `true` if the content language is in the [CJK](g) family. This value determines how Hugo calculates word count, and affects the values returned by the [`WordCount`], [`FuzzyWordCount`], [`ReadingTime`], and [`Summary`] methods on a `Page` object. [`fuzzywordcount`]: /methods/page/wordcount/ [`readingtime`]: /methods/page/readingtime/ [`summary`]: /methods/page/summary/ [`wordcount`]: /methods/page/wordcount/ -[cjk]: /getting-started/glossary/#cjk ###### keywords -(`string array`) An array of keywords, typically rendered within a `meta` element within the `head` element of the published HTML file, or used as a [taxonomy] to classify content. Access these values from a template using the [`Keywords`] method on a `Page` object. +(`string array`) An array of keywords, typically rendered within a `meta` element within the `head` element of the published HTML file, or used as a [taxonomy](g) to classify content. Access these values from a template using the [`Keywords`] method on a `Page` object. [`keywords`]: /methods/page/keywords/ -[taxonomy]: /getting-started/glossary/#taxonomy -{{% comment %}} -{{% /comment %}} ###### lastmod @@ -154,7 +141,7 @@ lang [`layout`]: /methods/page/layout/ [template lookup order]: /templates/lookup-order/ -[target a specific template]: templates/lookup-order/#target-a-template +[target a specific template]: /templates/lookup-order/#target-a-template ###### linkTitle @@ -184,13 +171,11 @@ Alias to [lastmod](#lastmod). [output formats]: /templates/output-formats/ -{{% comment %}} -{{% /comment %}} ###### params @@ -254,9 +239,8 @@ Alias to [publishDate](#publishdate). ###### type -(`string`) The [content type], overriding the value derived from the top level section in which the page resides. Access this value from a template using the [`Type`] method on a `Page` object. +(`string`) The [content type](g), overriding the value derived from the top level section in which the page resides. Access this value from a template using the [`Type`] method on a `Page` object. -[content type]: /getting-started/glossary/#content-type [`type`]: /methods/page/type/ ###### unpublishdate @@ -268,10 +252,8 @@ Alias to [expirydate](#expirydate). (`string`) Overrides the entire URL path. Applicable to regular pages and section pages. See the [URL management] page for details. ###### weight -(`int`) The page [weight], used to order the page within a [page collection]. Access this value from a template using the [`Weight`] method on a `Page` object. +(`int`) The page [weight](g), used to order the page within a [page collection](g). Access this value from a template using the [`Weight`] method on a `Page` object. -[page collection]: /getting-started/glossary/#page-collection -[weight]: /getting-started/glossary/#weight [`weight`]: /methods/page/weight/ ## Parameters @@ -302,7 +284,7 @@ Parameter|Data type|Used by these embedded templates `images`|`[]string`|[`opengraph.html`], [`schema.html`], [`twitter_cards.html`] `videos`|`[]string`|[`opengraph.html`] -The embedded templates will skip a parameter if not provided in front matter, but will throw an error if the data type is unexpected. +The embedded templates will skip a parameter if not provided in front matter, but will throw an error if the data type is unexpected. [`opengraph.html`]: {{% eturl opengraph %}} [`schema.html`]: {{% eturl schema %}} @@ -332,7 +314,7 @@ genres = ['mystery','romance'] author = 'John Smith' {{< /code-toggle >}} -You can add taxonomy terms to the front matter of any these [page kinds]: +You can add taxonomy terms to the front matter of any these [page kinds](g): - `home` - `page` @@ -340,8 +322,6 @@ You can add taxonomy terms to the front matter of any these [page kinds]: - `taxonomy` - `term` -[page kinds]: /getting-started/glossary/#page-kind - Access taxonomy terms from a template using the [`Params`] or [`GetTerms`] method on a `Page` object. For example: {{< code file=layouts/_default/single.html >}} @@ -360,9 +340,7 @@ Access taxonomy terms from a template using the [`Params`] or [`GetTerms`] metho ## Cascade -Any [node] can pass down to its descendants a set of front matter values. - -[node]: /getting-started/glossary/#node +Any [node](g) can pass down to its descendants a set of front matter values. ### Target specific pages @@ -459,5 +437,5 @@ When populating a date field, whether a [custom page parameter](#parameters) or To override the default time zone, set the [`timeZone`](https://gohugo.io/getting-started/configuration/#timezone) in your site configuration. The order of precedence for determining the time zone is: 1. The time zone offset in the date/time string -2. The time zone specified in your site configuration -3. The `Etc/UTC` time zone +1. The time zone specified in your site configuration +1. The `Etc/UTC` time zone diff --git a/content/en/content-management/image-processing/index.md b/content/en/content-management/image-processing/index.md index 841f12863..69ee30dc8 100644 --- a/content/en/content-management/image-processing/index.md +++ b/content/en/content-management/image-processing/index.md @@ -88,15 +88,15 @@ Example 3: A more concise way to skip image rendering if the resource is not fou Example 4: Skips rendering if there's problem accessing a remote resource. ```go-html-template -{{ $u := "https://gohugo.io/img/hugo-logo.png" }} -{{ with resources.GetRemote $u }} +{{ $url := "https://gohugo.io/img/hugo-logo.png" }} +{{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} - {{ else }} + {{ else with .Value }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} {{ end }} -{{ else }} - {{ errorf "Unable to get remote resource %q" $u }} {{ end }} ``` @@ -482,7 +482,7 @@ To control tag availability, change the `excludeFields` or `includeFields` setti ## Smart cropping of images -By default, Hugo uses the [Smartcrop] library when cropping images with the `Crop` or`Fill` methods. You can set the anchor point manually, but in most cases the `Smart` option will make a good choice. +By default, Hugo uses the [Smartcrop] library when cropping images with the `Crop` or `Fill` methods. You can set the anchor point manually, but in most cases the `Smart` option will make a good choice. Examples using the sunset image from above: @@ -500,15 +500,14 @@ If you change image processing methods or options, or if you rename or remove im hugo --gc ``` - [`anchor`]: /content-management/image-processing#anchor [mounted]: /hugo-modules/configuration#module-configuration-mounts [page bundle]: /content-management/page-bundles/ [`lang.FormatNumber`]: /functions/lang/formatnumber/ [filters]: /functions/images/filter/#image-filters -[github.com/disintegration/imaging]: -[Smartcrop]: -[Exif]: +[github.com/disintegration/imaging]: https://github.com/disintegration/imaging#image-resizing +[Smartcrop]: https://github.com/muesli/smartcrop#smartcrop +[Exif]: https://en.wikipedia.org/wiki/Exif [`Process`]: #process [`Colors`]: #colors [`Crop`]: #crop diff --git a/content/en/content-management/markdown-attributes.md b/content/en/content-management/markdown-attributes.md index 9c62c4fba..a465f244d 100644 --- a/content/en/content-management/markdown-attributes.md +++ b/content/en/content-management/markdown-attributes.md @@ -45,7 +45,6 @@ title = true # default is true block = true # default is false {{< /code-toggle >}} - ## Standalone images By default, when the [Goldmark] Markdown renderer encounters a standalone image element (no other elements or text on the same line), it wraps the image element within a paragraph element per the [CommonMark specification]. diff --git a/content/en/content-management/mathematics.md b/content/en/content-management/mathematics.md index 3212fe251..5eda9ffa3 100644 --- a/content/en/content-management/mathematics.md +++ b/content/en/content-management/mathematics.md @@ -1,9 +1,9 @@ --- title: Mathematics in Markdown linkTitle: Mathematics -description: Include mathematical equations and expressions in your Markdown using LaTeX or TeX typesetting syntax. +description: Include mathematical equations and expressions in Markdown using LaTeX markup. categories: [content management] -keywords: [chemical,chemistry,latex,math,mathjax,tex,typesetting] +keywords: [katex,latex,math,mathjax,typesetting] menu: docs: parent: content-management @@ -15,18 +15,11 @@ math: true {{< new-in 0.122.0 >}} -\[ -\begin{aligned} -KL(\hat{y} || y) &= \sum_{c=1}^{M}\hat{y}_c \log{\frac{\hat{y}_c}{y_c}} \\ -JS(\hat{y} || y) &= \frac{1}{2}(KL(y||\frac{y+\hat{y}}{2}) + KL(\hat{y}||\frac{y+\hat{y}}{2})) -\end{aligned} -\] - ## Overview -Mathematical equations and expressions authored in [LaTeX] or [TeX] are common in academic and scientific publications. Your browser typically renders this mathematical markup using an open-source JavaScript display engine such as [MathJax] or [KaTeX]. +Mathematical equations and expressions written in [LaTeX] are common in academic and scientific publications. Your browser typically renders this mathematical markup using an open-source JavaScript display engine such as [MathJax] or [KaTeX]. -For example, this is the mathematical markup for the equations displayed at the top of this page: +For example, with this LaTeX markup: ```text \[ @@ -37,15 +30,30 @@ JS(\hat{y} || y) &= \frac{1}{2}(KL(y||\frac{y+\hat{y}}{2}) + KL(\hat{y}||\frac{y \] ``` +The MathJax display engine renders this: + +\[ +\begin{aligned} +KL(\hat{y} || y) &= \sum_{c=1}^{M}\hat{y}_c \log{\frac{\hat{y}_c}{y_c}} \\ +JS(\hat{y} || y) &= \frac{1}{2}(KL(y||\frac{y+\hat{y}}{2}) + KL(\hat{y}||\frac{y+\hat{y}}{2})) +\end{aligned} +\] + Equations and expressions can be displayed inline with other text, or as standalone blocks. Block presentation is also known as "display" mode. -Whether an equation or expression appears inline, or as a block, depends on the delimiters that surround the mathematical markup. Delimiters are defined in pairs, where each pair consists of an opening and closing delimiter. The opening and closing delimiters may be the same, or different. Common delimiter pairs are shown in [Step 1]. +Whether an equation or expression appears inline, or as a block, depends on the delimiters that surround the mathematical markup. Delimiters are defined in pairs, where each pair consists of an opening and closing delimiter. The opening and closing delimiters may be the same, or different. -The approach described below avoids reliance on platform-specific features like shortcodes or code block render hooks. Instead, it utilizes a standardized markup format for mathematical equations and expressions, compatible with the rendering engines used by GitHub, GitLab, [Microsoft VS Code], [Obsidian], [Typora], and others. +{{% note %}} +You can configure Hugo to render mathematical markup on the client-side using the MathJax or KaTeX display engine, or you can render the markup while building your site with the [`transform.ToMath`]function. + +The first approach is described below. + +[`transform.ToMath`]: /functions/transform/tomath/ +{{% /note %}} ## Setup -Follow these instructions to include mathematical equations and expressions in your Markdown using LaTeX or TeX typesetting syntax. +Follow these instructions to include mathematical equations and expressions in your Markdown using LaTeX markup. ###### Step 1 @@ -122,7 +130,7 @@ The example above loads the partial template if you have set the `math` paramete ###### Step 4 -Include mathematical equations and expressions in your Markdown using LaTeX or TeX typesetting syntax. +Include mathematical equations and expressions in Markdown using LaTeX markup. {{< code file=content/math-examples.md copy=true >}} This is an inline \(a^*=x-b^*\) equation. @@ -173,7 +181,7 @@ If you use the `$...$` delimiter pair for inline equations, and occasionally use ## Engines -MathJax and KaTeX are open-source JavaScript display engines. Both engines are fast, but at the time of this writing MathJax v3.2.2 is slightly faster than KaTeX v0.16.9. +MathJax and KaTeX are open-source JavaScript display engines. Both engines are fast, but at the time of this writing MathJax v3.2.2 is slightly faster than KaTeX v0.16.11. {{% note %}} If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation](https://github.com/KaTeX/KaTeX/issues/437). @@ -184,9 +192,9 @@ See the [inline delimiters](#inline-delimiters) section for details. To use KaTeX instead of MathJax, replace the partial template from [Step 2] with this: {{< code file=layouts/partials/math.html copy=true >}} - - - + + + diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index 7f560f53a..f3a5f7c12 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -30,6 +30,9 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term'] -- layouts/index.html -- {{ .Content }} -- content/_index.md -- +--- +title: home +--- a{{< comment >}}b{{< /comment >}}c ` @@ -87,6 +90,26 @@ E: An _emphasized_ word. ) } +func TestGistShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- content/_index.md -- +--- +title: home +--- +{{< gist jmooring 23932424365401ffa5e9d9810102a477 >}} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + b.AssertFileContent("public/index.html", ``) + b.AssertLogContains(`WARN The "gist" shortcode was deprecated in v0.143.0 and will be removed in a future release. See https://gohugo.io/shortcodes/gist for instructions to create a replacement.`) +} + func TestInstagramShortcode(t *testing.T) { t.Parallel() From 873a5cda1a781eb808078b59c8eca56078612ab7 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Thu, 23 Jan 2025 15:35:20 -0800 Subject: [PATCH 110/374] tpl/tplimpl: Improve shortcode test coverage --- hugolib/embedded_shortcodes_test.go | 93 ------ tpl/tplimpl/shortcodes_integration_test.go | 332 +++++++++++++++++++++ 2 files changed, 332 insertions(+), 93 deletions(-) delete mode 100644 hugolib/embedded_shortcodes_test.go diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go deleted file mode 100644 index 198c31ae8..000000000 --- a/hugolib/embedded_shortcodes_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hugolib - -import ( - "testing" - - "github.com/gohugoio/hugo/htesting" -) - -func TestEmbeddedShortcodes(t *testing.T) { - if !htesting.IsCI() { - t.Skip("skip on non-CI for now") - } - - t.Run("with theme", func(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -baseURL = "https://example.com" -disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "section"] -ignoreErrors = ["error-missing-instagram-accesstoken"] -[params] -foo = "bar" --- content/_index.md -- ---- -title: "Home" ---- - -## Figure - -{{< figure src="image.png" >}} - -## Gist - -{{< gist spf13 7896402 >}} - -## Highlight - -{{< highlight go >}} -package main -{{< /highlight >}} - -## Instagram - -{{< instagram BWNjjyYFxVx >}} - -## Tweet - -{{< tweet user="1626985695280603138" id="877500564405444608" >}} - -## Vimeo - -{{< vimeo 20097015 >}} - -## YouTube - -{{< youtube 0RKpf3rK57I >}} - -## Param - -Foo: {{< param foo >}} - --- layouts/index.html -- -Content: {{ .Content }}| -` - b := Test(t, files) - - b.AssertFileContent("public/index.html", ` -
-https://gist.github.com/spf13/7896402.js -main -https://t.co/X94FmYDEZJ -https://www.youtube.com/embed/0RKpf3rK57I -Foo: bar - - - -`) - }) -} diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index f3a5f7c12..55e850f26 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -90,6 +90,40 @@ E: An _emphasized_ word. ) } +func TestFigureShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- content/_index.md -- +--- +title: home +--- +{{< figure + src="a.jpg" + alt="alternate text" + width=600 + height=400 + loading="lazy" + class="my-class" + link="https://example.org" + target="_blank" + rel="noopener" + title="my-title" + caption="a **bold** word" + attr="an _emphasized_ word" + attrlink="https://example.org/foo" +>}} +-- layouts/index.html -- +Hash: {{ .Content | hash.XxHash }} +Content: {{ .Content }} +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "35b077dcb9887a84") +} + func TestGistShortcode(t *testing.T) { t.Parallel() @@ -110,6 +144,99 @@ title: home b.AssertLogContains(`WARN The "gist" shortcode was deprecated in v0.143.0 and will be removed in a future release. See https://gohugo.io/shortcodes/gist for instructions to create a replacement.`) } +func TestHighlightShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +-- layouts/_default/single.html -- +Hash: {{ .Content | hash.XxHash }} +Content: {{ .Content }} +-- content/p1.md -- +--- +title: p1 +--- +{{< highlight go >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p2.md -- +--- +title: p2 +--- +{{< highlight go "noClasses=false" >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p3.md -- +--- +title: p3 +--- +{{< highlight go "lineNos=inline" >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p4.md -- +--- +title: p4 +--- +{{< highlight go "lineNos=table" >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p5.md -- +--- +title: p5 +--- +{{< highlight go "anchorLineNos=true, hl_Lines=2-4, lineAnchors=foo, lineNoStart=42, lineNos=true, lineNumbersInTable=false, style=emacs, wrapperClass=my-class" >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p6.md -- +--- +title: p6 +--- +{{< highlight go "anchorlinenos=true, hl_lines=2-4, lineanchors=foo, linenostart=42, linenos=true, linenumbersintable=false, style=emacs, wrapperclass=my-class" >}} +func main() { + for i := 0; i < 3; i++ { + fmt.Println("Value of i:", i) + } +} +{{< /highlight >}} +-- content/p7.md -- +--- +title: p7 +--- +An inline {{< highlight go "hl_inline=true" >}}fmt.Println("Value of i:", i)Hello world!{{< /highlight >}} statement. +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/p1/index.html", "576ee13be18ddba2") + b.AssertFileContent("public/p2/index.html", "8774e8b4bf60aa9e") + b.AssertFileContent("public/p3/index.html", "7634b47df1859f58") + b.AssertFileContent("public/p4/index.html", "385a15e400df4e39") + b.AssertFileContent("public/p5/index.html", "b3a73f3eddc6e0c1") + b.AssertFileContent("public/p6/index.html", "b3a73f3eddc6e0c1") + b.AssertFileContent("public/p7/index.html", "f12eeaa4d6d9c7ac") +} + func TestInstagramShortcode(t *testing.T) { t.Parallel() @@ -137,6 +264,35 @@ Content: {{ .Content }} b.AssertFileContent("public/index.html", "2c1dce3881be0513") } +func TestParamShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +[params] +b = 2 +-- layouts/index.html -- +{{ .Content }} +-- content/_index.md -- +--- +title: home +params: + a: 1 +--- +A: {{% param "a" %}} +B: {{% param "b" %}} +` + + b := hugolib.Test(t, files) + + b.AssertFileExists("public/index.html", true) + b.AssertFileContent("public/index.html", + "A: 1", + "B: 2", + ) +} + func TestQRShortcode(t *testing.T) { t.Parallel() @@ -173,6 +329,136 @@ https://gohugo.io" ) } +func TestRefShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = 'https://example.org/' +disableKinds = ['rss','section','sitemap','taxonomy','term'] +defaultContentLanguageInSubdir = true +[markup.goldmark.extensions] +linkify = false +[languages.en] +weight = 1 +[languages.de] +weight = 2 +[outputs] +page = ['html','json'] +-- layouts/_default/home.html -- +{{ .Content }} +-- layouts/_default/single.html.html -- +{{ .Title }} +-- layouts/_default/single.json.json -- +{{ .Title }} +-- content/_index.en.md -- +--- +title: home +--- +A: {{% ref "/s1/p1.md" %}} + +B: {{% ref path="/s1/p1.md" %}} + +C: {{% ref path="/s1/p1.md" lang="en" %}} + +D: {{% ref path="/s1/p1.md" lang="de" %}} + +E: {{% ref path="/s1/p1.md" lang="en" outputFormat="html" %}} + +F: {{% ref path="/s1/p1.md" lang="de" outputFormat="html" %}} + +G: {{% ref path="/s1/p1.md" lang="en" outputFormat="json" %}} + +H: {{% ref path="/s1/p1.md" lang="de" outputFormat="json" %}} +-- content/s1/p1.en.md -- +--- +title: p1 (en) +--- +-- content/s1/p1.de.md -- +--- +title: p1 (de) +--- +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/en/index.html", + `p>A: https://example.org/en/s1/p1/

`, + `p>B: https://example.org/en/s1/p1/

`, + `p>C: https://example.org/en/s1/p1/

`, + `p>D: https://example.org/de/s1/p1/

`, + `p>E: https://example.org/en/s1/p1/

`, + `p>F: https://example.org/de/s1/p1/

`, + `p>G: https://example.org/en/s1/p1/index.json

`, + `p>H: https://example.org/de/s1/p1/index.json

`, + ) +} + +func TestRelRefShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = 'https://example.org/' +disableKinds = ['rss','section','sitemap','taxonomy','term'] +defaultContentLanguageInSubdir = true +[markup.goldmark.extensions] +linkify = false +[languages.en] +weight = 1 +[languages.de] +weight = 2 +[outputs] +page = ['html','json'] +-- layouts/_default/home.html -- +{{ .Content }} +-- layouts/_default/single.html.html -- +{{ .Title }} +-- layouts/_default/single.json.json -- +{{ .Title }} +-- content/_index.en.md -- +--- +title: home +--- +A: {{% relref "/s1/p1.md" %}} + +B: {{% relref path="/s1/p1.md" %}} + +C: {{% relref path="/s1/p1.md" lang="en" %}} + +D: {{% relref path="/s1/p1.md" lang="de" %}} + +E: {{% relref path="/s1/p1.md" lang="en" outputFormat="html" %}} + +F: {{% relref path="/s1/p1.md" lang="de" outputFormat="html" %}} + +G: {{% relref path="/s1/p1.md" lang="en" outputFormat="json" %}} + +H: {{% relref path="/s1/p1.md" lang="de" outputFormat="json" %}} +-- content/s1/p1.en.md -- +--- +title: p1 (en) +--- +-- content/s1/p1.de.md -- +--- +title: p1 (de) +--- +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/en/index.html", + `p>A: /en/s1/p1/

`, + `p>B: /en/s1/p1/

`, + `p>C: /en/s1/p1/

`, + `p>D: /de/s1/p1/

`, + `p>E: /en/s1/p1/

`, + `p>F: /de/s1/p1/

`, + `p>G: /en/s1/p1/index.json

`, + `p>H: /de/s1/p1/index.json

`, + ) +} + func TestVimeoShortcode(t *testing.T) { t.Parallel() @@ -352,3 +638,49 @@ title: home `WARN The "twitter_simple" shortcode was unable to retrieve the remote data.`, ) } + +func TestYouTubeShortcode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +privacy.youtube.privacyEnhanced = false +-- layouts/_default/single.html -- +Hash: {{ .Content | hash.XxHash }} +Content: {{ .Content }} +-- content/p1.md -- +--- +title: p1 +--- +{{< youtube 0RKpf3rK57I >}} +-- content/p2.md -- +--- +title: p2 +--- +{{< youtube + id="0RKpf3rK57I" + allowFullScreen=false + autoplay=true + class="my-class" + controls="false" + end=42 + loading="lazy" + loop=true + mute=true + start=6 + title="my title" +>}} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/p1/index.html", "515600e76b272f51") + b.AssertFileContent("public/p2/index.html", "b5ceeace7dfa797a") + + files = strings.ReplaceAll(files, "privacy.youtube.privacyEnhanced = false", "privacy.youtube.privacyEnhanced = true") + + b = hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", "e92c7f4b768d7e23") + b.AssertFileContent("public/p2/index.html", "c384e83e035b71d9") +} From bb7b3d3cdb2ee5531685db0adb27d620535697b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 24 Jan 2025 13:33:36 +0100 Subject: [PATCH 111/374] Fix cascade with overlapping sections Fixes #12465 --- hugolib/cascade_test.go | 41 +++++++++++++++++++++++++++++++++++++ hugolib/content_map_page.go | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index 47e53d927..f3060814c 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -876,3 +876,44 @@ Background: {{ .Params.background }}| b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg") } } + +// Issue #12465. +func TestCascadeOverlap(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','sitemap','taxonomy','term'] +-- layouts/_default/list.html -- +{{ .Title }} +-- layouts/_default/single.html -- +{{ .Title }} +-- content/s/_index.md -- +--- +title: s +cascade: + _build: + render: never +--- +-- content/s/p1.md -- +--- +title: p1 +--- +-- content/sx/_index.md -- +--- +title: sx +--- +-- content/sx/p2.md -- +--- +title: p2 +--- +` + + b := Test(t, files) + + b.AssertFileExists("public/s/index.html", false) + b.AssertFileExists("public/s/p1/index.html", false) + + b.AssertFileExists("public/sx/index.html", true) // failing + b.AssertFileExists("public/sx/p2/index.html", true) // failing +} diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 6927562f1..b930845e5 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1397,7 +1397,7 @@ func (sa *sitePagesAssembler) applyAggregates() error { pw.WalkContext.Data().Insert(keyPage, cascade) } } else { - _, data := pw.WalkContext.Data().LongestPrefix(keyPage) + _, data := pw.WalkContext.Data().LongestPrefix(paths.Dir(keyPage)) if data != nil { cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params]) } From c939c33fd3dca9a2b21960866031b5ec9dad0123 Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Thu, 23 Jan 2025 17:43:38 +0000 Subject: [PATCH 112/374] parser/pageparser: Don't allow parameters after closing tag in shortcodes Problem: Previously, the following self-closing shortcode syntax was incorrectly allowed: {{% sc / param %}} Solution: Only allow parameters before the self-closing tag --- parser/pageparser/pagelexer_shortcode.go | 1 + parser/pageparser/pageparser_shortcode_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/parser/pageparser/pagelexer_shortcode.go b/parser/pageparser/pagelexer_shortcode.go index def2f82c7..535d8192c 100644 --- a/parser/pageparser/pagelexer_shortcode.go +++ b/parser/pageparser/pagelexer_shortcode.go @@ -322,6 +322,7 @@ func lexInsideShortcode(l *pageLexer) stateFunc { } l.closingState++ l.isInline = false + l.elementStepNum = 0 l.emit(tScClose) case r == '\\': l.ignore() diff --git a/parser/pageparser/pageparser_shortcode_test.go b/parser/pageparser/pageparser_shortcode_test.go index 327da30ee..29626b6ad 100644 --- a/parser/pageparser/pageparser_shortcode_test.go +++ b/parser/pageparser/pageparser_shortcode_test.go @@ -126,6 +126,9 @@ var shortCodeLexerTests = []lexerTest{ {"self-closing with param", `{{< sc1 param1 />}}`, []typeText{ tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD, tstEOF, }, nil}, + {"self-closing with extra keyword", `{{< sc1 / keyword>}}`, []typeText{ + tstLeftNoMD, tstSC1, tstSCClose, nti(tError, "closing tag for shortcode 'keyword' does not match start tag"), + }, nil}, {"multiple self-closing with param", `{{< sc1 param1 />}}{{< sc1 param1 />}}`, []typeText{ tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD, tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD, tstEOF, From cd7dc7a37252b93799225af96cc011aaefdaa67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 23 Jan 2025 12:46:08 +0100 Subject: [PATCH 113/374] Fix some server rebuild issues for non-HTML custom output formats The failing test case here is * A custom search output format defined on the home page, marked as `noAlternative` and not `permalinkable` * In fast render mode, when making a change to a data source for that search output format, the JSON file was not refreshed. There are variants of the above, but the gist of it is: * The change set was correctly determined, but since the search JSON file was not in the recently visited browser stack, we skipped rendering it. Running with `hugo server --disableFastRender` would be a workaround for the above. This commit fixes this by: * Adding a check for the HTTP request header `Sec-Fetch-Mode = navigation` to the condition for if we should track server request as a user navigation (and not e.g. a HTTP request for a linked CSS stylesheet). * Making sure that we compare against the real relative URL for non-permalinkable output formats. Fixes #13014 --- commands/hugobuilder.go | 6 ++-- commands/server.go | 13 +++++---- common/types/evictingqueue.go | 37 ++++++++++++----------- common/types/evictingqueue_test.go | 4 +-- hugolib/hugo_sites.go | 20 +++++++------ hugolib/hugo_sites_build.go | 6 ++-- hugolib/integrationtest_builder.go | 4 +-- hugolib/page__paths.go | 3 +- hugolib/page__per_output.go | 10 ++++++- hugolib/rebuild_test.go | 47 ++++++++++++++++++++++++++++-- hugolib/site_render.go | 5 +++- 11 files changed, 109 insertions(+), 46 deletions(-) diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index dcc5c099b..4c2d865c0 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -62,7 +62,7 @@ type hugoBuilder struct { // Currently only set when in "fast render mode". changeDetector *fileChangeDetector - visitedURLs *types.EvictingStringQueue + visitedURLs *types.EvictingQueue[string] fullRebuildSem *semaphore.Weighted debounce func(f func()) @@ -1103,7 +1103,7 @@ func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) { if err != nil { return } - err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...) + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...) return } @@ -1119,7 +1119,7 @@ func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error } whatChanged := &hugolib.WhatChanged{} whatChanged.Add(ids...) - err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}) + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}) return } diff --git a/commands/server.go b/commands/server.go index c4a8ddd29..08ecd5bac 100644 --- a/commands/server.go +++ b/commands/server.go @@ -85,9 +85,9 @@ const ( ) func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder { - var visitedURLs *types.EvictingStringQueue + var visitedURLs *types.EvictingQueue[string] if s != nil && !s.disableFastRender { - visitedURLs = types.NewEvictingStringQueue(20) + visitedURLs = types.NewEvictingQueue[string](20) } return &hugoBuilder{ r: r, @@ -364,7 +364,10 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string } if f.c.fastRenderMode && f.c.errState.buildErr() == nil { - if strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") { + // Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate + // Fall back to the file extension if not set. + // The main take here is that we don't want to have CSS/JS files etc. partake in this logic. + if r.Header.Get("Sec-Fetch-Mode") == "navigate" || strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") { if !f.c.visitedURLs.Contains(requestURI) { // If not already on stack, re-render that single page. if err := f.c.partialReRender(requestURI); err != nil { @@ -838,7 +841,7 @@ func (c *serverCommand) partialReRender(urls ...string) (err error) { defer func() { c.errState.setWasErr(false) }() - visited := types.NewEvictingStringQueue(len(urls)) + visited := types.NewEvictingQueue[string](len(urls)) for _, url := range urls { visited.Add(url) } @@ -850,7 +853,7 @@ func (c *serverCommand) partialReRender(urls ...string) (err error) { } // Note: We do not set NoBuildLock as the file lock is not acquired at this stage. - err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()}) + err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyTouched: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()}) return } diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go index 5ab715aa8..c3598f19f 100644 --- a/common/types/evictingqueue.go +++ b/common/types/evictingqueue.go @@ -18,24 +18,24 @@ import ( "sync" ) -// EvictingStringQueue is a queue which automatically evicts elements from the head of +// EvictingQueue is a queue which automatically evicts elements from the head of // the queue when attempting to add new elements onto the queue and it is full. // This queue orders elements LIFO (last-in-first-out). It throws away duplicates. -// Note: This queue currently does not contain any remove (poll etc.) methods. -type EvictingStringQueue struct { +type EvictingQueue[T comparable] struct { size int - vals []string - set map[string]bool + vals []T + set map[T]bool mu sync.Mutex + zero T } -// NewEvictingStringQueue creates a new queue with the given size. -func NewEvictingStringQueue(size int) *EvictingStringQueue { - return &EvictingStringQueue{size: size, set: make(map[string]bool)} +// NewEvictingQueue creates a new queue with the given size. +func NewEvictingQueue[T comparable](size int) *EvictingQueue[T] { + return &EvictingQueue[T]{size: size, set: make(map[T]bool)} } // Add adds a new string to the tail of the queue if it's not already there. -func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue { +func (q *EvictingQueue[T]) Add(v T) *EvictingQueue[T] { q.mu.Lock() if q.set[v] { q.mu.Unlock() @@ -54,7 +54,7 @@ func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue { return q } -func (q *EvictingStringQueue) Len() int { +func (q *EvictingQueue[T]) Len() int { if q == nil { return 0 } @@ -64,7 +64,7 @@ func (q *EvictingStringQueue) Len() int { } // Contains returns whether the queue contains v. -func (q *EvictingStringQueue) Contains(v string) bool { +func (q *EvictingQueue[T]) Contains(v T) bool { if q == nil { return false } @@ -74,12 +74,12 @@ func (q *EvictingStringQueue) Contains(v string) bool { } // Peek looks at the last element added to the queue. -func (q *EvictingStringQueue) Peek() string { +func (q *EvictingQueue[T]) Peek() T { q.mu.Lock() l := len(q.vals) if l == 0 { q.mu.Unlock() - return "" + return q.zero } elem := q.vals[l-1] q.mu.Unlock() @@ -87,9 +87,12 @@ func (q *EvictingStringQueue) Peek() string { } // PeekAll looks at all the elements in the queue, with the newest first. -func (q *EvictingStringQueue) PeekAll() []string { +func (q *EvictingQueue[T]) PeekAll() []T { + if q == nil { + return nil + } q.mu.Lock() - vals := make([]string, len(q.vals)) + vals := make([]T, len(q.vals)) copy(vals, q.vals) q.mu.Unlock() for i, j := 0, len(vals)-1; i < j; i, j = i+1, j-1 { @@ -99,9 +102,9 @@ func (q *EvictingStringQueue) PeekAll() []string { } // PeekAllSet returns PeekAll as a set. -func (q *EvictingStringQueue) PeekAllSet() map[string]bool { +func (q *EvictingQueue[T]) PeekAllSet() map[T]bool { all := q.PeekAll() - set := make(map[string]bool) + set := make(map[T]bool) for _, v := range all { set[v] = true } diff --git a/common/types/evictingqueue_test.go b/common/types/evictingqueue_test.go index 7489ba88d..cd10d3d8e 100644 --- a/common/types/evictingqueue_test.go +++ b/common/types/evictingqueue_test.go @@ -23,7 +23,7 @@ import ( func TestEvictingStringQueue(t *testing.T) { c := qt.New(t) - queue := NewEvictingStringQueue(3) + queue := NewEvictingQueue[string](3) c.Assert(queue.Peek(), qt.Equals, "") queue.Add("a") @@ -53,7 +53,7 @@ func TestEvictingStringQueueConcurrent(t *testing.T) { var wg sync.WaitGroup val := "someval" - queue := NewEvictingStringQueue(3) + queue := NewEvictingQueue[string](3) for j := 0; j < 100; j++ { wg.Add(1) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 792d6a990..83e8caa7f 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -416,8 +416,8 @@ type BuildCfg struct { // Set in server mode when the last build failed for some reason. ErrRecovery bool - // Recently visited URLs. This is used for partial re-rendering. - RecentlyVisited *types.EvictingStringQueue + // Recently visited or touched URLs. This is used for partial re-rendering. + RecentlyTouched *types.EvictingQueue[string] // Can be set to build only with a sub set of the content source. ContentInclusionFilter *glob.FilenameFilter @@ -429,7 +429,7 @@ type BuildCfg struct { } // shouldRender returns whether this output format should be rendered or not. -func (cfg *BuildCfg) shouldRender(p *pageState) bool { +func (cfg *BuildCfg) shouldRender(infol logg.LevelLogger, p *pageState) bool { if p.skipRender() { return false } @@ -457,18 +457,20 @@ func (cfg *BuildCfg) shouldRender(p *pageState) bool { return false } - if p.outputFormat().IsHTML { - // This is fast render mode and the output format is HTML, - // rerender if this page is one of the recently visited. - return cfg.RecentlyVisited.Contains(p.RelPermalink()) + if relURL := p.getRelURL(); relURL != "" { + if cfg.RecentlyTouched.Contains(relURL) { + infol.Logf("render recently touched URL %q (%s)", relURL, p.outputFormat().Name) + return true + } } // In fast render mode, we want to avoid re-rendering the sitemaps etc. and // other big listings whenever we e.g. change a content file, - // but we want partial renders of the recently visited pages to also include + // but we want partial renders of the recently touched pages to also include // alternative formats of the same HTML page (e.g. RSS, JSON). for _, po := range p.pageOutputs { - if po.render && po.f.IsHTML && cfg.RecentlyVisited.Contains(po.RelPermalink()) { + if po.render && po.f.IsHTML && cfg.RecentlyTouched.Contains(po.getRelURL()) { + infol.Logf("render recently touched URL %q, %s version of %s", po.getRelURL(), po.f.Name, p.outputFormat().Name) return true } } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 7e7f61031..8f3b71baf 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -341,7 +341,7 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error { loggers.TimeTrackf(l, start, h.buildCounters.loggFields(), "") }() - siteRenderContext := &siteRenderContext{cfg: config, multihost: h.Configs.IsMultihost} + siteRenderContext := &siteRenderContext{cfg: config, infol: l, multihost: h.Configs.IsMultihost} renderErr := func(err error) error { if err == nil { @@ -902,12 +902,12 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo needsPagesAssemble = true - if config.RecentlyVisited != nil { + if config.RecentlyTouched != nil { // Fast render mode. Adding them to the visited queue // avoids rerendering them on navigation. for _, id := range changes { if p, ok := id.(page.Page); ok { - config.RecentlyVisited.Add(p.RelPermalink()) + config.RecentlyTouched.Add(p.RelPermalink()) } } } diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index 18785ce75..ff45ec275 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -487,11 +487,11 @@ func (s *IntegrationTestBuilder) BuildPartialE(urls ...string) (*IntegrationTest if !s.Cfg.Running { panic("BuildPartial can only be used in server mode") } - visited := types.NewEvictingStringQueue(len(urls)) + visited := types.NewEvictingQueue[string](len(urls)) for _, url := range urls { visited.Add(url) } - buildCfg := BuildCfg{RecentlyVisited: visited, PartialReRender: true} + buildCfg := BuildCfg{RecentlyTouched: visited, PartialReRender: true} return s, s.build(buildCfg) } diff --git a/hugolib/page__paths.go b/hugolib/page__paths.go index 6324b5871..62206cb15 100644 --- a/hugolib/page__paths.go +++ b/hugolib/page__paths.go @@ -71,11 +71,12 @@ func newPagePaths(ps *pageState) (pagePaths, error) { // Use the main format for permalinks, usually HTML. permalinksIndex := 0 if f.Permalinkable { - // Unless it's permalinkable + // Unless it's permalinkable. permalinksIndex = i } targets[f.Name] = targetPathsHolder{ + relURL: relPermalink, paths: paths, OutputFormat: pageOutputFormats[permalinksIndex], } diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index 7c6395e57..2915c6b8a 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -469,13 +469,21 @@ type pagePerOutputProviders interface { type targetPather interface { targetPaths() page.TargetPaths + getRelURL() string } type targetPathsHolder struct { - paths page.TargetPaths + // relURL is usually the same as OutputFormat.RelPermalink, but can be different + // for non-permalinkable output formats. These shares RelPermalink with the main (first) output format. + relURL string + paths page.TargetPaths page.OutputFormat } +func (t targetPathsHolder) getRelURL() string { + return t.relURL +} + func (t targetPathsHolder) targetPaths() page.TargetPaths { return t.paths } diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go index 793263e04..dc2c6524f 100644 --- a/hugolib/rebuild_test.go +++ b/hugolib/rebuild_test.go @@ -357,8 +357,8 @@ RegularPages: {{ range .Site.RegularPages }}{{ .RelPermalink }}|{{ end }}$ } func TestRebuildRenameDirectoryWithBranchBundleFastRender(t *testing.T) { - recentlyVisited := types.NewEvictingStringQueue(10).Add("/a/b/c/") - b := TestRunning(t, rebuildFilesSimple, func(cfg *IntegrationTestConfig) { cfg.BuildCfg = BuildCfg{RecentlyVisited: recentlyVisited} }) + recentlyVisited := types.NewEvictingQueue[string](10).Add("/a/b/c/") + b := TestRunning(t, rebuildFilesSimple, func(cfg *IntegrationTestConfig) { cfg.BuildCfg = BuildCfg{RecentlyTouched: recentlyVisited} }) b.RenameDir("content/mysection", "content/mysectionrenamed").Build() b.AssertFileContent("public/mysectionrenamed/index.html", "My Section") b.AssertFileContent("public/mysectionrenamed/mysectionbundle/index.html", "My Section Bundle") @@ -1181,6 +1181,49 @@ Content: {{ .Content }} b.AssertFileContent("public/index.html", "Content:

Home

") } +// Issue #13014. +func TestRebuildEditNotPermalinkableCustomOutputFormatTemplateInFastRenderMode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com/docs/" +disableLiveReload = true +[internal] +fastRenderMode = true +disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404"] +[outputFormats] + [outputFormats.SearchIndex] + baseName = 'Search' + isPlainText = true + mediaType = 'text/plain' + noAlternative = true + permalinkable = false + +[outputs] + home = ['HTML', 'SearchIndex'] +-- content/_index.md -- +--- +title: "Home" +--- +Home. +-- layouts/index.html -- +Home. +-- layouts/_default/index.searchindex.txt -- +Text. {{ .Title }}|{{ .RelPermalink }}| + +` + b := TestRunning(t, files, TestOptInfo()) + + b.AssertFileContent("public/search.txt", "Text.") + + b.EditFileReplaceAll("layouts/_default/index.searchindex.txt", "Text.", "Text Edited.").Build() + + b.BuildPartial("/docs/search.txt") + + b.AssertFileContent("public/search.txt", "Text Edited.") +} + func TestRebuildVariationsAssetsJSImport(t *testing.T) { t.Parallel() files := ` diff --git a/hugolib/site_render.go b/hugolib/site_render.go index e5b2b62ab..5ac3d5a75 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -20,6 +20,7 @@ import ( "strings" "sync" + "github.com/bep/logg" "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/hugolib/doctree" @@ -33,6 +34,8 @@ import ( type siteRenderContext struct { cfg *BuildCfg + infol logg.LevelLogger + // languageIdx is the zero based index of the site. languageIdx int @@ -86,7 +89,7 @@ func (s *Site) renderPages(ctx *siteRenderContext) error { Tree: s.pageMap.treePages, Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { if p, ok := n.(*pageState); ok { - if cfg.shouldRender(p) { + if cfg.shouldRender(ctx.infol, p) { select { case <-s.h.Done(): return true, nil From e08d9af21e9a29054c98c9a164fce2a0e8033fcf Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 24 Jan 2025 09:47:45 -0800 Subject: [PATCH 114/374] markup/goldmark: Trim space from blockquote render hook text Closes #13302 --- markup/goldmark/blockquotes/blockquotes.go | 4 ++-- markup/goldmark/blockquotes/blockquotes_integration_test.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/markup/goldmark/blockquotes/blockquotes.go b/markup/goldmark/blockquotes/blockquotes.go index 064200d5e..539cd1875 100644 --- a/markup/goldmark/blockquotes/blockquotes.go +++ b/markup/goldmark/blockquotes/blockquotes.go @@ -69,7 +69,7 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N return ast.WalkContinue, nil } - text := ctx.PopRenderedString() + text := strings.TrimSpace(ctx.PopRenderedString()) ordinal := ctx.GetAndIncrementOrdinal(ast.KindBlockquote) @@ -90,7 +90,7 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N // tag if the first line of the blockquote content does not have a // closing p tag. At some point we might want to move this to the // parser. - before, after, found := strings.Cut(strings.TrimSpace(text), "\n") + before, after, found := strings.Cut(text, "\n") if found { if strings.HasSuffix(before, "

") { text = after diff --git a/markup/goldmark/blockquotes/blockquotes_integration_test.go b/markup/goldmark/blockquotes/blockquotes_integration_test.go index 93fe5b27d..6f7914d07 100644 --- a/markup/goldmark/blockquotes/blockquotes_integration_test.go +++ b/markup/goldmark/blockquotes/blockquotes_integration_test.go @@ -76,7 +76,7 @@ title: "p1" "Blockquote Alert: |

This is a note with some whitespace after the alert type.

|alert|", "Blockquote Alert: |

This is a tip.

", "Blockquote Alert: |

This is a caution with some whitespace before the alert type.

|alert|", - "Blockquote: |

A regular blockquote.

\n|regular|", + "Blockquote: |

A regular blockquote.

|regular|", "Blockquote Alert Attributes: |

This is a tip with attributes.

|map[class:foo bar id:baz]|", filepath.FromSlash("/content/p1.md:19:3"), "Blockquote Alert Page: |

This is a tip with attributes.

|p1|p1|", @@ -155,6 +155,7 @@ AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|AlertSign: {{ .AlertSi // Issue 12913 // Issue 13119 +// Issue 13302 func TestBlockquoteRenderHookTextParsing(t *testing.T) { t.Parallel() @@ -225,6 +226,7 @@ title: home > [!sixteen] _title_ > line one +> seventeen ` b := hugolib.Test(t, files) @@ -246,5 +248,6 @@ title: home "AlertType: fourteen|AlertTitle: title|Text:

\"alt\"

|", "AlertType: fifteen|AlertTitle: title|Text: |", "AlertType: sixteen|AlertTitle: title|Text:

line one

|", + "AlertType: |AlertTitle: |Text:

seventeen

|", ) } From 6c68142cc1338640e2bfe2add661a7b4d7bee6ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 29 Jan 2025 15:48:19 +0100 Subject: [PATCH 115/374] Fix TailwindCSS related server rebuild issue Fixes #13316 --- hugolib/hugo_sites_build.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 8f3b71baf..d86274791 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -27,6 +27,7 @@ import ( "github.com/bep/logg" "github.com/gohugoio/hugo/bufferpool" + "github.com/gohugoio/hugo/cache/dynacache" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs/files" @@ -1062,6 +1063,18 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo resourceFiles := h.fileEventsContentPaths(addedOrChangedContent) + defer func() { + // See issue 13316. + h.MemCache.DrainEvictedIdentitiesMatching(func(ki dynacache.KeyIdentity) bool { + for _, c := range changes { + if c.IdentifierBase() == ki.Identity.IdentifierBase() { + return true + } + } + return false + }) + }() + changed := &WhatChanged{ needsPagesAssembly: needsPagesAssemble, identitySet: make(identity.Identities), From 33b46d8a418f98cc132250e0ec49e1c153e908d1 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Thu, 30 Jan 2025 00:22:49 -0800 Subject: [PATCH 116/374] resources: Remove debug statement Closes #13320 --- .../cssjs/tailwindcss_integration_test.go | 6 +++--- resources/transform.go | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/resources/resource_transformers/cssjs/tailwindcss_integration_test.go b/resources/resource_transformers/cssjs/tailwindcss_integration_test.go index c0d6d3dff..ae70042a0 100644 --- a/resources/resource_transformers/cssjs/tailwindcss_integration_test.go +++ b/resources/resource_transformers/cssjs/tailwindcss_integration_test.go @@ -36,8 +36,8 @@ func TestTailwindV4Basic(t *testing.T) { "url": "https://github.com/bep/hugo-starter-tailwind-basic.git" }, "devDependencies": { - "@tailwindcss/cli": "^4.0.0-alpha.26", - "tailwindcss": "^4.0.0-alpha.26" + "@tailwindcss/cli": "^4.0.1", + "tailwindcss": "^4.0.1" }, "name": "hugo-starter-tailwind-basic", "version": "0.1.0" @@ -68,5 +68,5 @@ CSS: {{ $css.Content | safeCSS }}| LogLevel: logg.LevelInfo, }).Build() - b.AssertFileContent("public/index.html", "/*! tailwindcss v4.0.0") + b.AssertFileContent("public/index.html", "/*! tailwindcss v4.") } diff --git a/resources/transform.go b/resources/transform.go index 73f3b85d2..abb55f3cc 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -388,7 +388,6 @@ func (r *resourceAdapter) getImageOps() images.ImageResourceOps { if r.MediaType().SubType == "svg" { panic("this method is only available for raster images. To determine if an image is SVG, you can do {{ if eq .MediaType.SubType \"svg\" }}{{ end }}") } - fmt.Println(r.MediaType().SubType) panic("this method is only available for image resources") } r.init(false, false) From 329b2342f05fdc350711b7ea2928b70cafd25336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 30 Jan 2025 18:34:50 +0100 Subject: [PATCH 117/374] Fix "concurrent map iteration and map write" in pages from data Fixes #13254 --- common/maps/maps_test.go | 4 ++ common/maps/params.go | 41 ++++++++++++++++++++- resources/page/pagemeta/page_frontmatter.go | 5 ++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index b4f9c5a3d..40c8ac824 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -73,10 +73,14 @@ func TestPrepareParams(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { // PrepareParams modifies input. + prepareClone := PrepareParamsClone(test.input) PrepareParams(test.input) if !reflect.DeepEqual(test.expected, test.input) { t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input) } + if !reflect.DeepEqual(test.expected, prepareClone) { + t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, prepareClone) + } }) } } diff --git a/common/maps/params.go b/common/maps/params.go index a8cbba555..819f796e4 100644 --- a/common/maps/params.go +++ b/common/maps/params.go @@ -303,7 +303,7 @@ func toMergeStrategy(v any) ParamsMergeStrategy { } // PrepareParams -// * makes all the keys in the given map lower cased and will do so +// * makes all the keys in the given map lower cased and will do so recursively. // * This will modify the map given. // * Any nested map[interface{}]interface{}, map[string]interface{},map[string]string will be converted to Params. // * Any _merge value will be converted to proper type and value. @@ -343,3 +343,42 @@ func PrepareParams(m Params) { } } } + +// PrepareParamsClone is like PrepareParams, but it does not modify the input. +func PrepareParamsClone(m Params) Params { + m2 := make(Params) + for k, v := range m { + var retyped bool + lKey := strings.ToLower(k) + if lKey == MergeStrategyKey { + v = toMergeStrategy(v) + retyped = true + } else { + switch vv := v.(type) { + case map[any]any: + var p Params = cast.ToStringMap(v) + v = PrepareParamsClone(p) + retyped = true + case map[string]any: + var p Params = v.(map[string]any) + v = PrepareParamsClone(p) + retyped = true + case map[string]string: + p := make(Params) + for k, v := range vv { + p[k] = v + } + v = p + PrepareParams(p) + retyped = true + } + } + + if retyped || k != lKey { + m2[lKey] = v + } else { + m2[k] = v + } + } + return m2 +} diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go index 21789909e..60ba0e00a 100644 --- a/resources/page/pagemeta/page_frontmatter.go +++ b/resources/page/pagemeta/page_frontmatter.go @@ -158,8 +158,11 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo if p.Params == nil { p.Params = make(maps.Params) + } else if pagesFromData { + p.Params = maps.PrepareParamsClone(p.Params) + } else { + maps.PrepareParams(p.Params) } - maps.PrepareParams(p.Params) if p.Content.Markup == "" && p.Content.MediaType == "" { if ext == "" { From 13b208e2f70fd0bfad8a4ae33a30afb5fd4b7477 Mon Sep 17 00:00:00 2001 From: Alex Shpak Date: Fri, 31 Jan 2025 00:56:12 +0100 Subject: [PATCH 118/374] tpl/tplimpl: Remove leading whitespaces produced by Youtube shortcode --- tpl/tplimpl/embedded/templates/shortcodes/youtube.html | 2 +- tpl/tplimpl/shortcodes_integration_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html index b61c6130a..afb1d132f 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html @@ -102,7 +102,7 @@ Renders an embedded YouTube video. {{- $allow := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }} {{- $referrerpolicy := "strict-origin-when-cross-origin" }} - {{- /* Render. */}} + {{- /* Render. */ -}}
Date: Wed, 29 Jan 2025 20:13:25 +0100 Subject: [PATCH 119/374] Don't re-render aliases on server rebuilds This can lead to stale aliases when rebuilding, but that's a trade-off we need to take for snappier rebuilds on bigger sites. Note that it should be possible to detect alias changes, but I'm not sure it's worth it. --- hugolib/site.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugolib/site.go b/hugolib/site.go index ebe4a771b..7c09ba346 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -1542,7 +1542,7 @@ func (s *Site) render(ctx *siteRenderContext) (err error) { return err } - if ctx.outIdx == 0 { + if ctx.outIdx == 0 && s.h.buildCounter.Load() == 0 { // Note that even if disableAliases is set, the aliases themselves are // preserved on page. The motivation with this is to be able to generate // 301 redirects in a .htaccess file and similar using a custom output format. From db28695ff505f84aee69c72dcc9e192f674c86a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 31 Jan 2025 10:35:01 +0100 Subject: [PATCH 120/374] Fix some server/watch rebuild issues Two issues: 1. Fixe potential edit-loop in server/watch mode (see below) 2. Drain the cache eviction stack before we start calculating the change set. This should allow more fine grained rebuilds for bigger sites and/or in low memory situations. The fix in 6c68142cc1338640e2bfe2add661a7b4d7bee6ab wasn't really fixing the complete problem. In Hugo we have some steps that takes more time than others, one example being CSS building with TailwindCSS. The symptom here is that sometimes when you: 1. Edit content or templates that does not trigger a CSS rebuild => Snappy rebuild. 2. Edit stylesheet or add a CSS class to template that triggers a CSS rebuild => relatively slow rebuild (expected) 3. Then back to content editing or template edits that should not trigger a CSS rebuild => relatively slow rebuild (not expected) This commit fixes this by pulling the dynacache GC step up and merge it with the cache buster step. Fixes #13316 --- cache/dynacache/dynacache.go | 16 ++++-- cache/dynacache/dynacache_test.go | 4 +- hugolib/content_map_page.go | 53 ++++++++++--------- hugolib/hugo_sites_build.go | 30 +++++------ identity/identity.go | 3 ++ .../resource_factories/bundler/bundler.go | 4 ++ 6 files changed, 59 insertions(+), 51 deletions(-) diff --git a/cache/dynacache/dynacache.go b/cache/dynacache/dynacache.go index a906a0dd3..25d0f9b29 100644 --- a/cache/dynacache/dynacache.go +++ b/cache/dynacache/dynacache.go @@ -176,11 +176,12 @@ func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManag } // ClearOnRebuild prepares the cache for a new rebuild taking the given changeset into account. -func (c *Cache) ClearOnRebuild(changeset ...identity.Identity) { +// predicate is optional and will clear any entry for which it returns true. +func (c *Cache) ClearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ NumWorkers: len(c.partitions), Handle: func(ctx context.Context, partition PartitionManager) error { - partition.clearOnRebuild(changeset...) + partition.clearOnRebuild(predicate, changeset...) return nil }, }) @@ -479,7 +480,12 @@ func (p *Partition[K, V]) clearMatching(predicate func(k, v any) bool) { }) } -func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) { +func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { + if predicate == nil { + predicate = func(k, v any) bool { + return false + } + } opts := p.getOptions() if opts.ClearWhen == ClearNever { return @@ -525,7 +531,7 @@ func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) { // Second pass needs to be done in a separate loop to catch any // elements marked as stale in the other partitions. p.c.DeleteFunc(func(key K, v V) bool { - if shouldDelete(key, v) { + if predicate(key, v) || shouldDelete(key, v) { p.trace.Log( logg.StringFunc( func() string { @@ -601,7 +607,7 @@ type PartitionManager interface { adjustMaxSize(addend int) int getMaxSize() int getOptions() OptionsPartition - clearOnRebuild(changeset ...identity.Identity) + clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) clearMatching(predicate func(k, v any) bool) clearStale() } diff --git a/cache/dynacache/dynacache_test.go b/cache/dynacache/dynacache_test.go index 87239479b..14abf240d 100644 --- a/cache/dynacache/dynacache_test.go +++ b/cache/dynacache/dynacache_test.go @@ -147,13 +147,13 @@ func TestClear(t *testing.T) { c.Assert(cache.Keys(predicateAll), qt.HasLen, 4) - cache.ClearOnRebuild() + cache.ClearOnRebuild(nil) // Stale items are always cleared. c.Assert(cache.Keys(predicateAll), qt.HasLen, 2) cache = newTestCache(t) - cache.ClearOnRebuild(identity.StringIdentity("changed")) + cache.ClearOnRebuild(nil, identity.StringIdentity("changed")) c.Assert(cache.Keys(nil), qt.HasLen, 1) diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index b930845e5..9485f2e13 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1123,6 +1123,9 @@ func (h *HugoSites) resolveAndClearStateForIdentities( l logg.LevelLogger, cachebuster func(s string) bool, changes []identity.Identity, ) error { + // Drain the cache eviction stack to start fresh. + h.Deps.MemCache.DrainEvictedIdentities() + h.Log.Debug().Log(logg.StringFunc( func() string { var sb strings.Builder @@ -1163,17 +1166,32 @@ func (h *HugoSites) resolveAndClearStateForIdentities( } // The order matters here: - // 1. Handle the cache busters first, as those may produce identities for the page reset step. + // 1. Then GC the cache, which may produce changes. // 2. Then reset the page outputs, which may mark some resources as stale. - // 3. Then GC the cache. - if cachebuster != nil { - if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { - ll := l.WithField("substep", "gc dynacache cachebuster") - h.dynacacheGCCacheBuster(cachebuster) - return ll, nil - }); err != nil { - return err + if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { + ll := l.WithField("substep", "gc dynacache") + + predicate := func(k any, v any) bool { + if cachebuster != nil { + if s, ok := k.(string); ok { + return cachebuster(s) + } + } + return false } + + h.MemCache.ClearOnRebuild(predicate, changes...) + h.Log.Trace(logg.StringFunc(func() string { + var sb strings.Builder + sb.WriteString("dynacache keys:\n") + for _, key := range h.MemCache.Keys(nil) { + sb.WriteString(fmt.Sprintf(" %s\n", key)) + } + return sb.String() + })) + return ll, nil + }); err != nil { + return err } // Drain the cache eviction stack. @@ -1238,23 +1256,6 @@ func (h *HugoSites) resolveAndClearStateForIdentities( return err } - if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { - ll := l.WithField("substep", "gc dynacache") - - h.MemCache.ClearOnRebuild(changes...) - h.Log.Trace(logg.StringFunc(func() string { - var sb strings.Builder - sb.WriteString("dynacache keys:\n") - for _, key := range h.MemCache.Keys(nil) { - sb.WriteString(fmt.Sprintf(" %s\n", key)) - } - return sb.String() - })) - return ll, nil - }); err != nil { - return err - } - return nil } diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index d86274791..e066aa100 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -27,7 +27,6 @@ import ( "github.com/bep/logg" "github.com/gohugoio/hugo/bufferpool" - "github.com/gohugoio/hugo/cache/dynacache" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs/files" @@ -828,6 +827,11 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo addedContentPaths []*paths.Path ) + var ( + addedOrChangedContent []pathChange + changes []identity.Identity + ) + for _, ev := range eventInfos { cpss := h.BaseFs.ResolvePaths(ev.Name) pss := make([]*paths.Path, len(cpss)) @@ -854,6 +858,13 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo if err == nil && g != nil { cacheBusters = append(cacheBusters, g) } + + if ev.added { + changes = append(changes, identity.StructuralChangeAdd) + } + if ev.removed { + changes = append(changes, identity.StructuralChangeRemove) + } } if ev.removed { @@ -865,11 +876,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } - var ( - addedOrChangedContent []pathChange - changes []identity.Identity - ) - // Find the most specific identity possible. handleChange := func(pathInfo *paths.Path, delete, isDir bool) { switch pathInfo.Component() { @@ -1063,18 +1069,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo resourceFiles := h.fileEventsContentPaths(addedOrChangedContent) - defer func() { - // See issue 13316. - h.MemCache.DrainEvictedIdentitiesMatching(func(ki dynacache.KeyIdentity) bool { - for _, c := range changes { - if c.IdentifierBase() == ki.Identity.IdentifierBase() { - return true - } - } - return false - }) - }() - changed := &WhatChanged{ needsPagesAssembly: needsPagesAssemble, identitySet: make(identity.Identities), diff --git a/identity/identity.go b/identity/identity.go index e38a8f0f2..53169dfe1 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -33,6 +33,9 @@ const ( // GenghisKhan is an Identity everyone relates to. GenghisKhan = StringIdentity("__genghiskhan") + + StructuralChangeAdd = StringIdentity("__structural_change_add") + StructuralChangeRemove = StringIdentity("__structural_change_remove") ) var NopManager = new(nopManager) diff --git a/resources/resource_factories/bundler/bundler.go b/resources/resource_factories/bundler/bundler.go index dd0f1a4e1..8b268ebbe 100644 --- a/resources/resource_factories/bundler/bundler.go +++ b/resources/resource_factories/bundler/bundler.go @@ -95,6 +95,10 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou } idm := c.rs.Cfg.NewIdentityManager("concat") + + // Re-create on structural changes. + idm.AddIdentity(identity.StructuralChangeAdd, identity.StructuralChangeRemove) + // Add the concatenated resources as dependencies to the composite resource // so that we can track changes to the individual resources. idm.AddIdentityForEach(identity.ForEeachIdentityProviderFunc( From ee48d9692af281180aea00645d86f3231a5231df Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Sat, 1 Feb 2025 15:56:04 +0000 Subject: [PATCH 121/374] releaser: Bump versions for release of 0.143.0 [ci skip] --- common/hugo/version_current.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 6bb143da1..b2e098978 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -19,5 +19,5 @@ var CurrentVersion = Version{ Major: 0, Minor: 143, PatchLevel: 0, - Suffix: "-DEV", + Suffix: "", } From 05e067ced8aa9ee3f21814131f81213e7c65a632 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Sat, 1 Feb 2025 16:09:38 +0000 Subject: [PATCH 122/374] releaser: Prepare repository for 0.144.0-DEV [ci skip] --- common/hugo/version_current.go | 4 ++-- hugoreleaser.env | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index b2e098978..8faa58ab6 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 143, + Minor: 144, PatchLevel: 0, - Suffix: "", + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index 5bd6f37dc..19e7290c8 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.142.0 -HUGORELEASER_COMMITISH=1f746a872442e66b6afd47c8c04ac42dc92cdb6f +HUGORELEASER_TAG=v0.143.0 +HUGORELEASER_COMMITISH=ee48d9692af281180aea00645d86f3231a5231df + From 835579b338deca98751fbcf3444928f26e293543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 2 Feb 2025 11:48:37 +0100 Subject: [PATCH 123/374] Re-introduce the LRU-evicted identities in change set calculation This is a follow up to db28695ff505f84aee69c72dcc9e192f674c86a1 -- that commit dropped the cache items evicted in the LRU process. This was done as performance optimization for large Hugo sites. That made much sense, but now there's a slight chance that we miss out on a change when rebuilding. This commit fixes this by applying the same logic to the evicted items as if they were still in the cache. This should preserve the performance gains in db28695ff505f84aee69c72dcc9e192f674c86a1 and close the hole for the possible false negatives. --- hugolib/content_map_page.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 9485f2e13..fcc650c39 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -1124,7 +1124,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities( cachebuster func(s string) bool, changes []identity.Identity, ) error { // Drain the cache eviction stack to start fresh. - h.Deps.MemCache.DrainEvictedIdentities() + evictedStart := h.Deps.MemCache.DrainEvictedIdentities() h.Log.Debug().Log(logg.StringFunc( func() string { @@ -1200,6 +1200,27 @@ func (h *HugoSites) resolveAndClearStateForIdentities( for _, c := range evicted { changes = append(changes, c.Identity) } + + if len(evictedStart) > 0 { + // In low memory situations and/or very big sites, there can be a lot of unrelated evicted items, + // but there's a chance that some of them are related to the changes we are about to process, + // so check. + depsFinder := identity.NewFinder(identity.FinderConfig{}) + var addends []identity.Identity + for _, ev := range evictedStart { + for _, id := range changes { + if cachebuster != nil && cachebuster(ev.Key.(string)) { + addends = append(addends, ev.Identity) + break + } + if r := depsFinder.Contains(id, ev.Identity, -1); r > 0 { + addends = append(addends, ev.Identity) + break + } + } + } + changes = append(changes, addends...) + } } else { // Mass eviction, we might as well invalidate everything. changes = []identity.Identity{identity.GenghisKhan} From 7104de83ce561cbf95e4cff28f7528d761cd548c Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Sun, 2 Feb 2025 08:55:47 -0800 Subject: [PATCH 124/374] common/hugo: Adjust deprecation timing and message Closes #13333 --- common/hugo/hugo.go | 10 +++++----- common/hugo/hugo_test.go | 8 ++++---- tpl/debug/debug.go | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index eecf4bc2f..8b32432db 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -418,7 +418,7 @@ func Deprecate(item, alternative string, version string) { func DeprecateLevel(item, alternative, version string, level logg.Level) { var msg string if level == logg.LevelError { - msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in Hugo %s. %s", item, version, CurrentVersion.Next().ReleaseVersion(), alternative) + msg = fmt.Sprintf("%s was deprecated in Hugo %s and subsequently removed. %s", item, version, alternative) } else { msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative) } @@ -434,11 +434,11 @@ func deprecationLogLevelFromVersion(ver string) logg.Level { to := CurrentVersion minorDiff := to.Minor - from.Minor switch { - case minorDiff >= 12: - // Start failing the build after about a year. + case minorDiff >= 15: + // Start failing the build after about 15 months. return logg.LevelError - case minorDiff >= 6: - // Start printing warnings after about six months. + case minorDiff >= 3: + // Start printing warnings after about 3 months. return logg.LevelWarn default: return logg.LevelInfo diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go index feb52075d..f938073da 100644 --- a/common/hugo/hugo_test.go +++ b/common/hugo/hugo_test.go @@ -57,11 +57,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) { c.Assert(deprecationLogLevelFromVersion("0.55.0"), qt.Equals, logg.LevelError) ver := CurrentVersion c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) - ver.Minor -= 1 - c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) - ver.Minor -= 6 + ver.Minor -= 3 c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn) - ver.Minor -= 6 + ver.Minor -= 4 + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn) + ver.Minor -= 13 c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError) // Added just to find the threshold for where we can remove deprecated items. diff --git a/tpl/debug/debug.go b/tpl/debug/debug.go index fd676f0e2..3909cbfa2 100644 --- a/tpl/debug/debug.go +++ b/tpl/debug/debug.go @@ -171,7 +171,7 @@ func (ns *Namespace) TestDeprecationInfo(item, alternative string) string { // Internal template func, used in tests only. func (ns *Namespace) TestDeprecationWarn(item, alternative string) string { v := hugo.CurrentVersion - v.Minor -= 6 + v.Minor -= 3 hugo.Deprecate(item, alternative, v.String()) return "" } @@ -179,7 +179,7 @@ func (ns *Namespace) TestDeprecationWarn(item, alternative string) string { // Internal template func, used in tests only. func (ns *Namespace) TestDeprecationErr(item, alternative string) string { v := hugo.CurrentVersion - v.Minor -= 12 + v.Minor -= 15 hugo.Deprecate(item, alternative, v.String()) return "" } From 3bd73d262d4229b0779df5cfa99c6f7c17c9c955 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Mon, 3 Feb 2025 19:34:16 +0200 Subject: [PATCH 125/374] mage: Simplify magefile --- magefile.go | 73 +++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 56 deletions(-) diff --git a/magefile.go b/magefile.go index 9919e0185..120e23666 100644 --- a/magefile.go +++ b/magefile.go @@ -1,11 +1,9 @@ //go:build mage -// +build mage package main import ( "bytes" - "errors" "fmt" "os" "path" @@ -36,10 +34,6 @@ func init() { if exe := os.Getenv("GOEXE"); exe != "" { goexe = exe } - - // We want to use Go 1.11 modules even if the source lives inside GOPATH. - // The default is "auto". - os.Setenv("GO111MODULE", "on") } func runWith(env map[string]string, cmd string, inArgs ...any) error { @@ -122,10 +116,10 @@ func HugoNoGitInfo() error { return Hugo() } -var docker = sh.RunCmd("docker") - // Build hugo Docker container func Docker() error { + docker := sh.RunCmd("docker") + if err := docker("build", "-t", "hugo", "."); err != nil { return err } @@ -148,7 +142,7 @@ func Check() { fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS) } - if isCi() && isDarwin() { + if isCI() && isDarwin() { // Skip on macOS in CI (disk space issues) } else { mg.Deps(Fmt, Vet) @@ -200,56 +194,19 @@ func Fmt() error { return nil } -var ( - pkgPrefixLen = len("github.com/gohugoio/hugo") - pkgs []string - pkgsInit sync.Once -) +const pkgPrefixLen = len("github.com/gohugoio/hugo") -func hugoPackages() ([]string, error) { - var err error - pkgsInit.Do(func() { - var s string - s, err = sh.Output(goexe, "list", "./...") - if err != nil { - return - } - pkgs = strings.Split(s, "\n") - for i := range pkgs { - pkgs[i] = "." + pkgs[i][pkgPrefixLen:] - } - }) - return pkgs, err -} - -// Run golint linter -func Lint() error { - pkgs, err := hugoPackages() +var hugoPackages = sync.OnceValues(func() ([]string, error) { + s, err := sh.Output(goexe, "list", "./...") if err != nil { - return err + return nil, err } - failed := false - for _, pkg := range pkgs { - // We don't actually want to fail this target if we find golint errors, - // so we don't pass -set_exit_status, but we still print out any failures. - if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil { - fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err) - failed = true - } + pkgs := strings.Split(s, "\n") + for i := range pkgs { + pkgs[i] = "." + pkgs[i][pkgPrefixLen:] } - if failed { - return errors.New("errors running golint") - } - return nil -} - -func isCi() bool { - return os.Getenv("CI") != "" -} - -func isDarwin() bool { - return runtime.GOOS == "darwin" -} + return pkgs, nil +}) // Run go vet linter func Vet() error { @@ -270,7 +227,7 @@ func TestCoverHTML() error { return err } defer f.Close() - if _, err := f.Write([]byte("mode: count")); err != nil { + if _, err := f.WriteString("mode: count"); err != nil { return err } pkgs, err := hugoPackages() @@ -320,6 +277,10 @@ func isUnix() bool { return runtime.GOOS != "windows" } +func isDarwin() bool { + return runtime.GOOS == "darwin" +} + func isCI() bool { return os.Getenv("CI") != "" } From 760c13a7acf179499dbb18940c9feb41fa7587b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 2 Feb 2025 18:49:04 +0100 Subject: [PATCH 126/374] Fix RSS with baseURL with sub dir when render hooks is enabled Fixes #13332 --- hugolib/rss_test.go | 48 ++++++++++++++++++++++++ transform/urlreplacers/absurl.go | 4 +- transform/urlreplacers/absurlreplacer.go | 19 +++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go index 0c3c21b90..34c2be393 100644 --- a/hugolib/rss_test.go +++ b/hugolib/rss_test.go @@ -96,3 +96,51 @@ Figure: b.AssertFileContent("public/index.xml", "img src="http://example.com/images/sunset.jpg") } + +// Issue 13332. +func TestRSSCanonifyURLsSubDir(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = 'https://example.org/subdir' +disableKinds = ['section','sitemap','taxonomy','term'] +[markup.goldmark.renderHooks.image] +enableDefault = true +[markup.goldmark.renderHooks.link] +enableDefault = true +-- layouts/_default/_markup/render-image.html -- +{{- $u := urls.Parse .Destination -}} +{{- $src := $u.String | relURL -}} + + +{{- /**/ -}} +-- layouts/_default/home.html -- +{{ .Content }}| +-- layouts/_default/single.html -- +{{ .Content }}| +-- layouts/_default/rss.xml -- +{{ with site.GetPage "/s1/p2" }} + {{ .Content | transform.XMLEscape | safeHTML }} +{{ end }} +-- content/s1/p1.md -- +--- +title: p1 +--- +-- content/s1/p2/index.md -- +--- +title: p2 +--- +![alt](a.jpg) + +[p1](/s1/p1) +-- content/s1/p2/a.jpg -- +` + + b := Test(t, files) + + b.AssertFileContent("public/index.xml", "https://example.org/subdir/s1/p1/") + b.AssertFileContent("public/index.xml", + "img src="https://example.org/subdir/a.jpg", + "img srcset="https://example.org/subdir/a.jpg" src="https://example.org/subdir/a.jpg 2x") +} diff --git a/transform/urlreplacers/absurl.go b/transform/urlreplacers/absurl.go index 029d94da2..17fe15327 100644 --- a/transform/urlreplacers/absurl.go +++ b/transform/urlreplacers/absurl.go @@ -13,7 +13,9 @@ package urlreplacers -import "github.com/gohugoio/hugo/transform" +import ( + "github.com/gohugoio/hugo/transform" +) var ar = newAbsURLReplacer() diff --git a/transform/urlreplacers/absurlreplacer.go b/transform/urlreplacers/absurlreplacer.go index a875e6fa8..601fd9a1f 100644 --- a/transform/urlreplacers/absurlreplacer.go +++ b/transform/urlreplacers/absurlreplacer.go @@ -16,9 +16,11 @@ package urlreplacers import ( "bytes" "io" + "net/url" "unicode" "unicode/utf8" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/transform" ) @@ -31,6 +33,9 @@ type absurllexer struct { // path may be set to a "." relative path path []byte + // The root path, without leading slash. + root []byte + pos int // input position start int // item start position @@ -119,6 +124,9 @@ func checkCandidateBase(l *absurllexer) { } l.pos += relURLPrefixLen l.w.Write(l.path) + if len(l.root) > 0 && bytes.HasPrefix(l.content[l.pos:], l.root) { + l.pos += len(l.root) + } l.start = l.pos } @@ -174,7 +182,11 @@ func checkCandidateSrcset(l *absurllexer) { for i, f := range fields { if f[0] == '/' { l.w.Write(l.path) - l.w.Write(f[1:]) + n := 1 + if len(l.root) > 0 && bytes.HasPrefix(f[n:], l.root) { + n += len(l.root) + } + l.w.Write(f[n:]) } else { l.w.Write(f) @@ -229,10 +241,15 @@ func (l *absurllexer) replace() { } func doReplace(path string, ct transform.FromTo, quotes [][]byte) { + var root string + if u, err := url.Parse(path); err == nil { + root = paths.TrimLeading(u.Path) + } lexer := &absurllexer{ content: ct.From().Bytes(), w: ct.To(), path: []byte(path), + root: []byte(root), quotes: quotes, } From 0270364a347b2ece97e0321782b21904db515ecc Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Tue, 4 Feb 2025 08:57:38 +0000 Subject: [PATCH 127/374] releaser: Bump versions for release of 0.143.1 [ci skip] --- common/hugo/version_current.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 8faa58ab6..59270b744 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 144, - PatchLevel: 0, - Suffix: "-DEV", + Minor: 143, + PatchLevel: 1, + Suffix: "", } From 377287a614821aa024385772db64721b0fd24fc1 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Tue, 4 Feb 2025 09:14:03 +0000 Subject: [PATCH 128/374] releaser: Prepare repository for 0.144.0-DEV [ci skip] --- common/hugo/version_current.go | 6 +++--- hugoreleaser.env | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 59270b744..8faa58ab6 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 143, - PatchLevel: 1, - Suffix: "", + Minor: 144, + PatchLevel: 0, + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index 19e7290c8..9905fff87 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.143.0 -HUGORELEASER_COMMITISH=ee48d9692af281180aea00645d86f3231a5231df +HUGORELEASER_TAG=v0.143.1 +HUGORELEASER_COMMITISH=0270364a347b2ece97e0321782b21904db515ecc + From e865d59844744e1446ebcfccdadebac6802aa34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 4 Feb 2025 11:18:26 +0100 Subject: [PATCH 129/374] Fix shortcode name in error message on self-closing shortcodes with no .Inner Fixes #13344 --- hugolib/shortcode.go | 6 +++++- hugolib/shortcode_test.go | 30 +++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 69e891adb..9a2bb7aeb 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -650,7 +650,11 @@ Loop: // return that error, more specific continue } - return nil, fmt.Errorf("%s: shortcode %q does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided", errorPrefix, next.ValStr(source)) + name := sc.name + if name == "" { + name = next.ValStr(source) + } + return nil, fmt.Errorf("%s: shortcode %q does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided", errorPrefix, name) } } if next.IsRightShortcodeDelim() { diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go index 92812bf66..5799de452 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -831,19 +831,35 @@ title: "Hugo Rocks!" func TestShortcodeNoInner(t *testing.T) { t.Parallel() - b := newTestSitesBuilder(t) - - b.WithContent("mypage.md", `--- + files := ` +-- hugo.toml -- +baseURL = "https://example.org" +disableKinds = ["term", "taxonomy", "home", "section"] +-- content/mypage.md -- +--- title: "No Inner!" --- + {{< noinner >}}{{< /noinner >}} +-- layouts/shortcodes/noinner.html -- +No inner here. +-- layouts/_default/single.html -- +Content: {{ .Content }}| -`).WithTemplatesAdded( - "layouts/shortcodes/noinner.html", `No inner here.`) +` - err := b.BuildE(BuildCfg{}) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`"content/mypage.md:4:16": failed to extract shortcode: shortcode "noinner" does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided`)) + b, err := TestE(t, files) + + assert := func() { + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`failed to extract shortcode: shortcode "noinner" does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided`)) + } + + assert() + + b, err = TestE(t, strings.Replace(files, `{{< noinner >}}{{< /noinner >}}`, `{{< noinner />}}`, 1)) + + assert() } func TestShortcodeStableOutputFormatTemplates(t *testing.T) { From 029d1e0ced541450fe7f056eaa438a1f6ac28685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 4 Feb 2025 18:21:24 +0100 Subject: [PATCH 130/374] Add some more server options/improvements New options: * `FromHeaders`: Server header matching for redirects * `FromRe`: Regexp with group support, i.e. it replaces $1, $2 in To with the group matches. Note that if both `From` and `FromRe` is set, both must match. Also * Allow redirects to non HTML URLs as long as the Sec-Fetch-Mode is set to navigate on the request. * Detect and stop redirect loops. This was all done while testing out InertiaJS with Hugo. So, after this commit, this setup will support the main parts of the protocol that Inertia uses: ```toml [server] [[server.headers]] for = '/**/inertia.json' [server.headers.values] Content-Type = 'text/html' X-Inertia = 'true' Vary = 'Accept' [[server.redirects]] force = true from = '/**/' fromRe = "^/(.*)/$" fromHeaders = { "X-Inertia" = "true" } status = 301 to = '/$1/inertia.json' ``` Unfortunately, a provider like Netlify does not support redirects matching by request headers. It should be possible with some edge function, but then again, I'm not sure that InertiaJS is a very good fit with the common Hugo use cases. But this commit should be generally useful. --- commands/server.go | 125 +++++++++++++++++++++--------------- config/commonConfig.go | 115 ++++++++++++++++++++++++++------- config/commonConfig_test.go | 76 ++++++++++++---------- 3 files changed, 208 insertions(+), 108 deletions(-) diff --git a/commands/server.go b/commands/server.go index 08ecd5bac..828c78a3a 100644 --- a/commands/server.go +++ b/commands/server.go @@ -84,6 +84,10 @@ const ( configChangeGoWork = "go work file" ) +const ( + hugoHeaderRedirect = "X-Hugo-Redirect" +) + func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder { var visitedURLs *types.EvictingQueue[string] if s != nil && !s.disableFastRender { @@ -307,67 +311,65 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string w.Header().Set(header.Key, header.Value) } - if redirect := serverConfig.MatchRedirect(requestURI); !redirect.IsZero() { - // fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) - doRedirect := true - // This matches Netlify's behavior and is needed for SPA behavior. - // See https://docs.netlify.com/routing/redirects/rewrites-proxies/ - if !redirect.Force { - path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path())) - if root != "" { - path = filepath.Join(root, path) - } - var fs afero.Fs - f.c.withConf(func(conf *commonConfig) { - fs = conf.fs.PublishDirServer - }) - - fi, err := fs.Stat(path) - - if err == nil { - if fi.IsDir() { - // There will be overlapping directories, so we - // need to check for a file. - _, err = fs.Stat(filepath.Join(path, "index.html")) - doRedirect = err != nil - } else { - doRedirect = false + if canRedirect(requestURI, r) { + if redirect := serverConfig.MatchRedirect(requestURI, r.Header); !redirect.IsZero() { + doRedirect := true + // This matches Netlify's behavior and is needed for SPA behavior. + // See https://docs.netlify.com/routing/redirects/rewrites-proxies/ + if !redirect.Force { + path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path())) + if root != "" { + path = filepath.Join(root, path) } - } - } + var fs afero.Fs + f.c.withConf(func(conf *commonConfig) { + fs = conf.fs.PublishDirServer + }) + + fi, err := fs.Stat(path) - if doRedirect { - switch redirect.Status { - case 404: - w.WriteHeader(404) - file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path())) if err == nil { - defer file.Close() - io.Copy(w, file) - } else { - fmt.Fprintln(w, "

Page Not Found

") + if fi.IsDir() { + // There will be overlapping directories, so we + // need to check for a file. + _, err = fs.Stat(filepath.Join(path, "index.html")) + doRedirect = err != nil + } else { + doRedirect = false + } } - return - case 200: - if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil { - requestURI = redirect.To - r = r2 - } - default: - w.Header().Set("Content-Type", "") - http.Redirect(w, r, redirect.To, redirect.Status) - return + } + if doRedirect { + w.Header().Set(hugoHeaderRedirect, "true") + switch redirect.Status { + case 404: + w.WriteHeader(404) + file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path())) + if err == nil { + defer file.Close() + io.Copy(w, file) + } else { + fmt.Fprintln(w, "

Page Not Found

") + } + return + case 200: + if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil { + requestURI = redirect.To + r = r2 + } + default: + w.Header().Set("Content-Type", "") + http.Redirect(w, r, redirect.To, redirect.Status) + return + + } } } - } if f.c.fastRenderMode && f.c.errState.buildErr() == nil { - // Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate - // Fall back to the file extension if not set. - // The main take here is that we don't want to have CSS/JS files etc. partake in this logic. - if r.Header.Get("Sec-Fetch-Mode") == "navigate" || strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") { + if isNavigation(requestURI, r) { if !f.c.visitedURLs.Contains(requestURI) { // If not already on stack, re-render that single page. if err := f.c.partialReRender(requestURI); err != nil { @@ -1233,3 +1235,24 @@ func formatByteCount(b uint64) string { return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) } + +func canRedirect(requestURIWithoutQuery string, r *http.Request) bool { + if r.Header.Get(hugoHeaderRedirect) != "" { + return false + } + return isNavigation(requestURIWithoutQuery, r) +} + +// Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate +// Fall back to the file extension if not set. +// The main take here is that we don't want to have CSS/JS files etc. partake in this logic. +func isNavigation(requestURIWithoutQuery string, r *http.Request) bool { + return r.Header.Get("Sec-Fetch-Mode") == "navigate" || isPropablyHTMLRequest(requestURIWithoutQuery) +} + +func isPropablyHTMLRequest(requestURIWithoutQuery string) bool { + if strings.HasSuffix(requestURIWithoutQuery, "/") || strings.HasSuffix(requestURIWithoutQuery, "html") || strings.HasSuffix(requestURIWithoutQuery, "htm") { + return true + } + return !strings.Contains(requestURIWithoutQuery, ".") +} diff --git a/config/commonConfig.go b/config/commonConfig.go index 9dea4a2fc..9125f256a 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -15,6 +15,7 @@ package config import ( "fmt" + "net/http" "regexp" "sort" "strings" @@ -226,7 +227,22 @@ type Server struct { Redirects []Redirect compiledHeaders []glob.Glob - compiledRedirects []glob.Glob + compiledRedirects []redirect +} + +type redirect struct { + from glob.Glob + fromRe *regexp.Regexp + headers map[string]glob.Glob +} + +func (r redirect) matchHeader(header http.Header) bool { + for k, v := range r.headers { + if !v.Match(header.Get(k)) { + return false + } + } + return true } func (s *Server) CompileConfig(logger loggers.Logger) error { @@ -234,10 +250,41 @@ func (s *Server) CompileConfig(logger loggers.Logger) error { return nil } for _, h := range s.Headers { - s.compiledHeaders = append(s.compiledHeaders, glob.MustCompile(h.For)) + g, err := glob.Compile(h.For) + if err != nil { + return fmt.Errorf("failed to compile Headers glob %q: %w", h.For, err) + } + s.compiledHeaders = append(s.compiledHeaders, g) } for _, r := range s.Redirects { - s.compiledRedirects = append(s.compiledRedirects, glob.MustCompile(r.From)) + if r.From == "" && r.FromRe == "" { + return fmt.Errorf("redirects must have either From or FromRe set") + } + rd := redirect{ + headers: make(map[string]glob.Glob), + } + if r.From != "" { + g, err := glob.Compile(r.From) + if err != nil { + return fmt.Errorf("failed to compile Redirect glob %q: %w", r.From, err) + } + rd.from = g + } + if r.FromRe != "" { + re, err := regexp.Compile(r.FromRe) + if err != nil { + return fmt.Errorf("failed to compile Redirect regexp %q: %w", r.FromRe, err) + } + rd.fromRe = re + } + for k, v := range r.FromHeaders { + g, err := glob.Compile(v) + if err != nil { + return fmt.Errorf("failed to compile Redirect header glob %q: %w", v, err) + } + rd.headers[k] = g + } + s.compiledRedirects = append(s.compiledRedirects, rd) } return nil @@ -266,22 +313,42 @@ func (s *Server) MatchHeaders(pattern string) []types.KeyValueStr { return matches } -func (s *Server) MatchRedirect(pattern string) Redirect { +func (s *Server) MatchRedirect(pattern string, header http.Header) Redirect { if s.compiledRedirects == nil { return Redirect{} } pattern = strings.TrimSuffix(pattern, "index.html") - for i, g := range s.compiledRedirects { + for i, r := range s.compiledRedirects { redir := s.Redirects[i] - // No redirect to self. - if redir.To == pattern { - return Redirect{} + var found bool + + if r.from != nil { + if r.from.Match(pattern) { + found = header == nil || r.matchHeader(header) + // We need to do regexp group replacements if needed. + } } - if g.Match(pattern) { + if r.fromRe != nil { + m := r.fromRe.FindStringSubmatch(pattern) + if m != nil { + if !found { + found = header == nil || r.matchHeader(header) + } + + if found { + // Replace $1, $2 etc. in To. + for i, g := range m[1:] { + redir.To = strings.ReplaceAll(redir.To, fmt.Sprintf("$%d", i+1), g) + } + } + } + } + + if found { return redir } } @@ -295,8 +362,22 @@ type Headers struct { } type Redirect struct { + // From is the Glob pattern to match. + // One of From or FromRe must be set. From string - To string + + // FromRe is the regexp to match. + // This regexp can contain group matches (e.g. $1) that can be used in the To field. + // One of From or FromRe must be set. + FromRe string + + // To is the target URL. + To string + + // Headers to match for the redirect. + // This maps the HTTP header name to a Glob pattern with values to match. + // If the map is empty, the redirect will always be triggered. + FromHeaders map[string]string // HTTP status code to use for the redirect. // A status code of 200 will trigger a URL rewrite. @@ -383,17 +464,7 @@ func DecodeServer(cfg Provider) (Server, error) { _ = mapstructure.WeakDecode(cfg.GetStringMap("server"), s) for i, redir := range s.Redirects { - // Get it in line with the Hugo server for OK responses. - // We currently treat the 404 as a special case, they are always "ugly", so keep them as is. - if redir.Status != 404 { - redir.To = strings.TrimSuffix(redir.To, "index.html") - if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") { - // There are some tricky infinite loop situations when dealing - // when the target does not have a trailing slash. - // This can certainly be handled better, but not time for that now. - return Server{}, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To) - } - } + redir.To = strings.TrimSuffix(redir.To, "index.html") s.Redirects[i] = redir } @@ -401,7 +472,7 @@ func DecodeServer(cfg Provider) (Server, error) { // Set up a default redirect for 404s. s.Redirects = []Redirect{ { - From: "**", + From: "/**", To: "/404.html", Status: 404, }, diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go index 425d3e970..ce68cec15 100644 --- a/config/commonConfig_test.go +++ b/config/commonConfig_test.go @@ -71,7 +71,28 @@ X-Content-Type-Options = "nosniff" [[server.redirects]] from = "/foo/**" -to = "/foo/index.html" +to = "/baz/index.html" +status = 200 + +[[server.redirects]] +from = "/loop/**" +to = "/loop/foo/" +status = 200 + +[[server.redirects]] +from = "/b/**" +fromRe = "/b/(.*)/" +to = "/baz/$1/" +status = 200 + +[[server.redirects]] +fromRe = "/c/(.*)/" +to = "/boo/$1/" +status = 200 + +[[server.redirects]] +fromRe = "/d/(.*)/" +to = "/boo/$1/" status = 200 [[server.redirects]] @@ -79,11 +100,6 @@ from = "/google/**" to = "https://google.com/" status = 301 -[[server.redirects]] -from = "/**" -to = "/default/index.html" -status = 301 - `, "toml") @@ -100,45 +116,35 @@ status = 301 {Key: "X-XSS-Protection", Value: "1; mode=block"}, }) - c.Assert(s.MatchRedirect("/foo/bar/baz"), qt.DeepEquals, Redirect{ + c.Assert(s.MatchRedirect("/foo/bar/baz", nil), qt.DeepEquals, Redirect{ From: "/foo/**", - To: "/foo/", + To: "/baz/", Status: 200, }) - c.Assert(s.MatchRedirect("/someother"), qt.DeepEquals, Redirect{ - From: "/**", - To: "/default/", - Status: 301, + c.Assert(s.MatchRedirect("/foo/bar/", nil), qt.DeepEquals, Redirect{ + From: "/foo/**", + To: "/baz/", + Status: 200, }) - c.Assert(s.MatchRedirect("/google/foo"), qt.DeepEquals, Redirect{ + c.Assert(s.MatchRedirect("/b/c/", nil), qt.DeepEquals, Redirect{ + From: "/b/**", + FromRe: "/b/(.*)/", + To: "/baz/c/", + Status: 200, + }) + + c.Assert(s.MatchRedirect("/c/d/", nil).To, qt.Equals, "/boo/d/") + c.Assert(s.MatchRedirect("/c/d/e/", nil).To, qt.Equals, "/boo/d/e/") + + c.Assert(s.MatchRedirect("/someother", nil), qt.DeepEquals, Redirect{}) + + c.Assert(s.MatchRedirect("/google/foo", nil), qt.DeepEquals, Redirect{ From: "/google/**", To: "https://google.com/", Status: 301, }) - - // No redirect loop, please. - c.Assert(s.MatchRedirect("/default/index.html"), qt.DeepEquals, Redirect{}) - c.Assert(s.MatchRedirect("/default/"), qt.DeepEquals, Redirect{}) - - for _, errorCase := range []string{ - `[[server.redirects]] -from = "/**" -to = "/file" -status = 301`, - `[[server.redirects]] -from = "/**" -to = "/foo/file.html" -status = 301`, - } { - - cfg, err := FromConfigString(errorCase, "toml") - c.Assert(err, qt.IsNil) - _, err = DecodeServer(cfg) - c.Assert(err, qt.Not(qt.IsNil)) - - } } func TestBuildConfigCacheBusters(t *testing.T) { From eb7a5aabaaa025b6633b4aff3267b21ebdffb8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 7 Feb 2025 17:09:27 +0100 Subject: [PATCH 131/374] Move "print unused templates" after renderDeferred Fixes #13355 --- hugolib/hugo_sites_build.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index e066aa100..44598526c 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -170,10 +170,6 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { h.SendError(fmt.Errorf("render: %w", err)) } - if err := h.postRenderOnce(); err != nil { - h.SendError(fmt.Errorf("postRenderOnce: %w", err)) - } - // Make sure to write any build stats to disk first so it's available // to the post processors. if err := h.writeBuildStats(); err != nil { @@ -184,6 +180,10 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { h.SendError(fmt.Errorf("renderDeferred: %w", err)) } + if err := h.postRenderOnce(); err != nil { + h.SendError(fmt.Errorf("postRenderOnce: %w", err)) + } + if err := h.postProcess(infol); err != nil { h.SendError(fmt.Errorf("postProcess: %w", err)) } From 1dd27be81aafc44fc3207be675cbac754710be90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 7 Feb 2025 22:25:19 +0100 Subject: [PATCH 132/374] Update CONTRIBUTING.md --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b5666963..a1f56927d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -93,6 +93,7 @@ Most title/subjects should have a lower-cased prefix with a colon and one whites * If this commit touches many packages without a common functional topic, prefix with `all:` (e.g. `all: Reformat Go code`) * If this is a documentation update, prefix with `docs:`. * If nothing of the above applies, just leave the prefix out. +* Note that the above excludes nouns seen in other repositories, e.g. "chore:". Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*. Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*. From a352e69b02ca7d5ea59ba175889365bbbb70303c Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 7 Feb 2025 09:36:29 -0800 Subject: [PATCH 133/374] commands: Validate style argument passed to gen chromastyles Closes #13357 --- commands/gen.go | 5 +++++ testscripts/commands/gen.txt | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/commands/gen.go b/commands/gen.go index 67aa7a896..3cb05927d 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -21,6 +21,7 @@ import ( "os" "path" "path/filepath" + "slices" "strings" "github.com/alecthomas/chroma/v2" @@ -60,6 +61,10 @@ func newGenCommand() *genCommand { See https://xyproto.github.io/splash/docs/all.html for a preview of the available styles`, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + style = strings.ToLower(style) + if !slices.Contains(styles.Names(), style) { + return fmt.Errorf("invalid style: %s", style) + } builder := styles.Get(style).Builder() if highlightStyle != "" { builder.Add(chroma.LineHighlight, highlightStyle) diff --git a/testscripts/commands/gen.txt b/testscripts/commands/gen.txt index 092b4e129..2d57c8ec0 100644 --- a/testscripts/commands/gen.txt +++ b/testscripts/commands/gen.txt @@ -11,4 +11,6 @@ hugo gen man --dir manpages hugo gen chromastyles -h stdout 'Generate CSS stylesheet for the Chroma code highlighter' hugo gen chromastyles --style monokai -stdout '/\* LineHighlight \*/ \.chroma \.hl \{ background-color:#3c3d38 \}' +stdout 'Generated using: hugo gen chromastyles --style monokai' +! hugo gen chromastyles --style __invalid_style__ +stderr 'invalid style: __invalid_style__' From f0c1852978bb7042c0d290c683fdb813fd30107e Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Fri, 7 Feb 2025 11:06:29 -0800 Subject: [PATCH 134/374] helpers: Add Chroma styles to docs.yaml Closes #13360 --- docs/data/docs.yaml | 71 ++++++++++++++++++++++++++++++++++++++++++- helpers/docshelper.go | 6 +++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/data/docs.yaml b/docs/data/docs.yaml index dc61df0e8..6fc02c63a 100644 --- a/docs/data/docs.yaml +++ b/docs/data/docs.yaml @@ -958,6 +958,73 @@ chroma: - Aliases: - zig Name: Zig + styles: + - abap + - algol + - algol_nu + - arduino + - autumn + - average + - base16-snazzy + - borland + - bw + - catppuccin-frappe + - catppuccin-latte + - catppuccin-macchiato + - catppuccin-mocha + - colorful + - doom-one + - doom-one2 + - dracula + - emacs + - evergarden + - friendly + - fruity + - github + - github-dark + - gruvbox + - gruvbox-light + - hr_high_contrast + - hrdark + - igor + - lovelace + - manni + - modus-operandi + - modus-vivendi + - monokai + - monokailight + - murphy + - native + - nord + - nordic + - onedark + - onesenterprise + - paraiso-dark + - paraiso-light + - pastie + - perldoc + - pygments + - rainbow_dash + - rose-pine + - rose-pine-dawn + - rose-pine-moon + - rrt + - solarized-dark + - solarized-dark256 + - solarized-light + - swapoff + - tango + - tokyonight-day + - tokyonight-moon + - tokyonight-night + - tokyonight-storm + - trac + - vim + - vs + - vulcan + - witchhazel + - xcode + - xcode-dark config: HTTPCache: cache: @@ -1737,7 +1804,9 @@ config: headers: null redirects: - force: false - from: '**' + from: /** + fromHeaders: null + fromRe: "" status: 404 to: /404.html services: diff --git a/helpers/docshelper.go b/helpers/docshelper.go index 35d07d366..5edbef118 100644 --- a/helpers/docshelper.go +++ b/helpers/docshelper.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" "github.com/gohugoio/hugo/docshelper" ) @@ -30,7 +31,10 @@ func init() { } - return docshelper.DocProvider{"chroma": map[string]any{"lexers": chromaLexers}} + return docshelper.DocProvider{"chroma": map[string]any{ + "lexers": chromaLexers, + "styles": styles.Names(), + }} } docshelper.AddDocProviderFunc(docsProvider) From 4245a4514d58c0896900bc242390b31e92005d43 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Sat, 8 Feb 2025 11:29:42 +0200 Subject: [PATCH 135/374] all: Remove deprecated build tags --- commands/deploy.go | 1 - commands/deploy_off.go | 1 - common/hugo/vars_extended.go | 1 - common/hugo/vars_regular.go | 1 - common/hugo/vars_withdeploy.go | 1 - common/hugo/vars_withdeploy_off.go | 1 - deploy/cloudfront.go | 1 - deploy/deploy.go | 1 - deploy/deploy_azure.go | 1 - deploy/deploy_test.go | 1 - deploy/deployconfig/deployConfig_test.go | 1 - deploy/google.go | 1 - main_withdeploy_off_test.go | 1 - main_withdeploy_test.go | 1 - resources/image_extended_test.go | 1 - resources/images/webp/webp.go | 1 - resources/images/webp/webp_notavailable.go | 1 - resources/resource_transformers/tocss/scss/client_extended.go | 1 - .../resource_transformers/tocss/scss/client_notavailable.go | 1 - resources/resource_transformers/tocss/scss/tocss.go | 1 - 20 files changed, 20 deletions(-) diff --git a/commands/deploy.go b/commands/deploy.go index eb419daba..3e9d3df20 100644 --- a/commands/deploy.go +++ b/commands/deploy.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package commands diff --git a/commands/deploy_off.go b/commands/deploy_off.go index 32a08da2e..8f5eaa2de 100644 --- a/commands/deploy_off.go +++ b/commands/deploy_off.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !withdeploy -// +build !withdeploy // Copyright 2024 The Hugo Authors. All rights reserved. // diff --git a/common/hugo/vars_extended.go b/common/hugo/vars_extended.go index edbaff243..ab01e2647 100644 --- a/common/hugo/vars_extended.go +++ b/common/hugo/vars_extended.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package hugo diff --git a/common/hugo/vars_regular.go b/common/hugo/vars_regular.go index 223df4b6c..a78aeb0b6 100644 --- a/common/hugo/vars_regular.go +++ b/common/hugo/vars_regular.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !extended -// +build !extended package hugo diff --git a/common/hugo/vars_withdeploy.go b/common/hugo/vars_withdeploy.go index 88ce9a1cd..4e0c3efbb 100644 --- a/common/hugo/vars_withdeploy.go +++ b/common/hugo/vars_withdeploy.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package hugo diff --git a/common/hugo/vars_withdeploy_off.go b/common/hugo/vars_withdeploy_off.go index 935568027..36e9bd874 100644 --- a/common/hugo/vars_withdeploy_off.go +++ b/common/hugo/vars_withdeploy_off.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !withdeploy -// +build !withdeploy package hugo diff --git a/deploy/cloudfront.go b/deploy/cloudfront.go index 29c0a7f8c..3202a73ea 100644 --- a/deploy/cloudfront.go +++ b/deploy/cloudfront.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package deploy diff --git a/deploy/deploy.go b/deploy/deploy.go index 0c379ffb8..4b90881a6 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package deploy diff --git a/deploy/deploy_azure.go b/deploy/deploy_azure.go index 521d8f094..b1ce7358c 100644 --- a/deploy/deploy_azure.go +++ b/deploy/deploy_azure.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !solaris && withdeploy -// +build !solaris,withdeploy package deploy diff --git a/deploy/deploy_test.go b/deploy/deploy_test.go index f290d6c81..3bbc9ff70 100644 --- a/deploy/deploy_test.go +++ b/deploy/deploy_test.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package deploy diff --git a/deploy/deployconfig/deployConfig_test.go b/deploy/deployconfig/deployConfig_test.go index 8a3ad2d8a..38d0aadd6 100644 --- a/deploy/deployconfig/deployConfig_test.go +++ b/deploy/deployconfig/deployConfig_test.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package deployconfig diff --git a/deploy/google.go b/deploy/google.go index e4683aa81..5b302e95b 100644 --- a/deploy/google.go +++ b/deploy/google.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package deploy diff --git a/main_withdeploy_off_test.go b/main_withdeploy_off_test.go index 490ccd693..968c0c957 100644 --- a/main_withdeploy_off_test.go +++ b/main_withdeploy_off_test.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !withdeploy -// +build !withdeploy package main diff --git a/main_withdeploy_test.go b/main_withdeploy_test.go index de50313c3..004893bdf 100644 --- a/main_withdeploy_test.go +++ b/main_withdeploy_test.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build withdeploy -// +build withdeploy package main diff --git a/resources/image_extended_test.go b/resources/image_extended_test.go index f481be9bb..5865ce75b 100644 --- a/resources/image_extended_test.go +++ b/resources/image_extended_test.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package resources_test diff --git a/resources/images/webp/webp.go b/resources/images/webp/webp.go index 28336d2e0..d7407214f 100644 --- a/resources/images/webp/webp.go +++ b/resources/images/webp/webp.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package webp diff --git a/resources/images/webp/webp_notavailable.go b/resources/images/webp/webp_notavailable.go index 70407f94e..b617eeb51 100644 --- a/resources/images/webp/webp_notavailable.go +++ b/resources/images/webp/webp_notavailable.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !extended -// +build !extended package webp diff --git a/resources/resource_transformers/tocss/scss/client_extended.go b/resources/resource_transformers/tocss/scss/client_extended.go index c9b347c4c..bd2330f6d 100644 --- a/resources/resource_transformers/tocss/scss/client_extended.go +++ b/resources/resource_transformers/tocss/scss/client_extended.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package scss diff --git a/resources/resource_transformers/tocss/scss/client_notavailable.go b/resources/resource_transformers/tocss/scss/client_notavailable.go index efd79109b..783a7d7db 100644 --- a/resources/resource_transformers/tocss/scss/client_notavailable.go +++ b/resources/resource_transformers/tocss/scss/client_notavailable.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !extended -// +build !extended package scss diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go index 89e95b2f8..2976578af 100644 --- a/resources/resource_transformers/tocss/scss/tocss.go +++ b/resources/resource_transformers/tocss/scss/tocss.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package scss From c2fb22120924b66ba6d89dacca6487ff12e78e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 7 Feb 2025 10:29:35 +0100 Subject: [PATCH 136/374] Add ContentTypes to config This is an empty struct for now, but we will most likely expand on that. ``` [contentTypes] [contentTypes.'text/markdown'] ``` The above means that only Markdown will be considered a content type. E.g. HTML will be treated as plain text. Fixes #12274 --- common/hreflect/helpers.go | 10 ++ config/allconfig/allconfig.go | 7 +- .../allconfig/allconfig_integration_test.go | 21 +++- config/allconfig/alldecoders.go | 9 ++ config/allconfig/configlanguage.go | 2 +- hugolib/content_map_test.go | 48 ++++++++ media/config.go | 115 +++++++++++++----- media/mediaType_test.go | 8 -- parser/lowercase_camel_json.go | 2 +- resources/page/page_nop.go | 5 +- source/fileInfo.go | 1 + tpl/reflect/reflect.go | 6 +- 12 files changed, 182 insertions(+), 52 deletions(-) diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index fc83165c7..4d7339b5b 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -74,6 +74,16 @@ func IsTruthful(in any) bool { } } +// IsMap reports whether v is a map. +func IsMap(v any) bool { + return reflect.ValueOf(v).Kind() == reflect.Map +} + +// IsSlice reports whether v is a slice. +func IsSlice(v any) bool { + return reflect.ValueOf(v).Kind() == reflect.Slice +} + var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem() // IsTruthfulValue returns whether the given value has a meaningful truth value. diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 3c2eddcab..3021028fd 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -128,6 +128,9 @@ type Config struct { // {"identifiers": ["markup"] } Markup markup_config.Config `mapstructure:"-"` + // ContentTypes are the media types that's considered content in Hugo. + ContentTypes *config.ConfigNamespace[map[string]media.ContentTypeConfig, media.ContentTypes] `mapstructure:"-"` + // The mediatypes configuration section maps the MIME type (a string) to a configuration object for that type. // {"identifiers": ["mediatypes"], "refs": ["types:media:type"] } MediaTypes *config.ConfigNamespace[map[string]media.MediaTypeConfig, media.Types] `mapstructure:"-"` @@ -433,7 +436,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error { IgnoredLogs: ignoredLogIDs, KindOutputFormats: kindOutputFormats, DefaultOutputFormat: defaultOutputFormat, - ContentTypes: media.DefaultContentTypes.FromTypes(c.MediaTypes.Config), CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle), IsUglyURLSection: isUglyURL, IgnoreFile: ignoreFile, @@ -471,7 +473,6 @@ type ConfigCompiled struct { ServerInterface string KindOutputFormats map[string]output.Formats DefaultOutputFormat output.Format - ContentTypes media.ContentTypes DisabledKinds map[string]bool DisabledLanguages map[string]bool IgnoredLogs map[string]bool @@ -839,7 +840,7 @@ func (c *Configs) Init() error { c.Languages = languages c.LanguagesDefaultFirst = languagesDefaultFirst - c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: c.Base.C.ContentTypes.IsContentSuffix} + c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: c.Base.ContentTypes.Config.IsContentSuffix} c.configLangs = make([]config.AllProvider, len(c.Languages)) for i, l := range c.LanguagesDefaultFirst { diff --git a/config/allconfig/allconfig_integration_test.go b/config/allconfig/allconfig_integration_test.go index 162a36cdb..cae04ba85 100644 --- a/config/allconfig/allconfig_integration_test.go +++ b/config/allconfig/allconfig_integration_test.go @@ -7,6 +7,7 @@ import ( qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/media" ) func TestDirsMount(t *testing.T) { @@ -97,7 +98,7 @@ suffixes = ["html", "xhtml"] b := hugolib.Test(t, files) conf := b.H.Configs.Base - contentTypes := conf.C.ContentTypes + contentTypes := conf.ContentTypes.Config b.Assert(contentTypes.HTML.Suffixes(), qt.DeepEquals, []string{"html", "xhtml"}) b.Assert(contentTypes.Markdown.Suffixes(), qt.DeepEquals, []string{"md", "mdown", "markdown"}) @@ -215,3 +216,21 @@ weight = 3 b := hugolib.Test(t, files) b.Assert(b.H.Configs.LanguageConfigSlice[0].Title, qt.Equals, `TITLE_DE`) } + +func TestContentTypesDefault(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.com" + + +` + + b := hugolib.Test(t, files) + + ct := b.H.Configs.Base.ContentTypes + c := ct.Config + s := ct.SourceStructure.(map[string]media.ContentTypeConfig) + + b.Assert(c.IsContentFile("foo.md"), qt.Equals, true) + b.Assert(len(s), qt.Equals, 6) +} diff --git a/config/allconfig/alldecoders.go b/config/allconfig/alldecoders.go index 60e571999..0bf8508d9 100644 --- a/config/allconfig/alldecoders.go +++ b/config/allconfig/alldecoders.go @@ -163,6 +163,15 @@ var allDecoderSetups = map[string]decodeWeight{ return err }, }, + "contenttypes": { + key: "contenttypes", + weight: 100, // This needs to be decoded after media types. + decode: func(d decodeWeight, p decodeConfig) error { + var err error + p.c.ContentTypes, err = media.DecodeContentTypes(p.p.GetStringMap(d.key), p.c.MediaTypes.Config) + return err + }, + }, "mediatypes": { key: "mediatypes", decode: func(d decodeWeight, p decodeConfig) error { diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go index deec61449..6990a3590 100644 --- a/config/allconfig/configlanguage.go +++ b/config/allconfig/configlanguage.go @@ -145,7 +145,7 @@ func (c ConfigLanguage) NewIdentityManager(name string, opts ...identity.Manager } func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider { - return c.config.C.ContentTypes + return c.config.ContentTypes.Config } // GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use. diff --git a/hugolib/content_map_test.go b/hugolib/content_map_test.go index 6a1245846..4cbdaf53a 100644 --- a/hugolib/content_map_test.go +++ b/hugolib/content_map_test.go @@ -501,3 +501,51 @@ func (n *testContentNode) resetBuildState() { func (n *testContentNode) MarkStale() { } + +// Issue 12274. +func TestHTMLNotContent(t *testing.T) { + filesTemplate := ` +-- hugo.toml.temp -- +[contentTypes] +[contentTypes."text/markdown"] +# Emopty for now. +-- hugo.yaml.temp -- +contentTypes: + text/markdown: {} +-- hugo.json.temp -- +{ + "contentTypes": { + "text/markdown": {} + } +} +-- content/p1/index.md -- +--- +title: p1 +--- +-- content/p1/a.html -- +

a

+-- content/p1/b.html -- +

b

+-- content/p1/c.html -- +

c

+-- layouts/_default/single.html -- +|{{ (.Resources.Get "a.html").RelPermalink -}} +|{{ (.Resources.Get "b.html").RelPermalink -}} +|{{ (.Resources.Get "c.html").Publish }} +` + + for _, format := range []string{"toml", "yaml", "json"} { + format := format + t.Run(format, func(t *testing.T) { + t.Parallel() + + files := strings.Replace(filesTemplate, format+".temp", format, 1) + b := Test(t, files) + + b.AssertFileContent("public/p1/index.html", "|/p1/a.html|/p1/b.html|") + b.AssertFileContent("public/p1/a.html", "

a

") + b.AssertFileContent("public/p1/b.html", "

b

") + b.AssertFileContent("public/p1/c.html", "

c

") + }) + } +} diff --git a/media/config.go b/media/config.go index e00837e5e..e50d8499d 100644 --- a/media/config.go +++ b/media/config.go @@ -71,11 +71,15 @@ func init() { EmacsOrgMode: Builtin.EmacsOrgModeType, } - DefaultContentTypes.init() + DefaultContentTypes.init(nil) } var DefaultContentTypes ContentTypes +type ContentTypeConfig struct { + // Empty for now. +} + // ContentTypes holds the media types that are considered content in Hugo. type ContentTypes struct { HTML Type @@ -85,13 +89,36 @@ type ContentTypes struct { ReStructuredText Type EmacsOrgMode Type + types Types + // Created in init(). - types Types extensionSet map[string]bool } -func (t *ContentTypes) init() { - t.types = Types{t.HTML, t.Markdown, t.AsciiDoc, t.Pandoc, t.ReStructuredText, t.EmacsOrgMode} +func (t *ContentTypes) init(types Types) { + sort.Slice(t.types, func(i, j int) bool { + return t.types[i].Type < t.types[j].Type + }) + + if tt, ok := types.GetByType(t.HTML.Type); ok { + t.HTML = tt + } + if tt, ok := types.GetByType(t.Markdown.Type); ok { + t.Markdown = tt + } + if tt, ok := types.GetByType(t.AsciiDoc.Type); ok { + t.AsciiDoc = tt + } + if tt, ok := types.GetByType(t.Pandoc.Type); ok { + t.Pandoc = tt + } + if tt, ok := types.GetByType(t.ReStructuredText.Type); ok { + t.ReStructuredText = tt + } + if tt, ok := types.GetByType(t.EmacsOrgMode.Type); ok { + t.EmacsOrgMode = tt + } + t.extensionSet = make(map[string]bool) for _, mt := range t.types { for _, suffix := range mt.Suffixes() { @@ -135,32 +162,6 @@ func (t ContentTypes) Types() Types { return t.types } -// FromTypes creates a new ContentTypes updated with the values from the given Types. -func (t ContentTypes) FromTypes(types Types) ContentTypes { - if tt, ok := types.GetByType(t.HTML.Type); ok { - t.HTML = tt - } - if tt, ok := types.GetByType(t.Markdown.Type); ok { - t.Markdown = tt - } - if tt, ok := types.GetByType(t.AsciiDoc.Type); ok { - t.AsciiDoc = tt - } - if tt, ok := types.GetByType(t.Pandoc.Type); ok { - t.Pandoc = tt - } - if tt, ok := types.GetByType(t.ReStructuredText.Type); ok { - t.ReStructuredText = tt - } - if tt, ok := types.GetByType(t.EmacsOrgMode.Type); ok { - t.EmacsOrgMode = tt - } - - t.init() - - return t -} - // Hold the configuration for a given media type. type MediaTypeConfig struct { // The file suffixes used for this media type. @@ -169,6 +170,58 @@ type MediaTypeConfig struct { Delimiter string } +var defaultContentTypesConfig = map[string]ContentTypeConfig{ + Builtin.HTMLType.Type: {}, + Builtin.MarkdownType.Type: {}, + Builtin.AsciiDocType.Type: {}, + Builtin.PandocType.Type: {}, + Builtin.ReStructuredTextType.Type: {}, + Builtin.EmacsOrgModeType.Type: {}, +} + +// DecodeContentTypes decodes the given map of content types. +func DecodeContentTypes(in map[string]any, types Types) (*config.ConfigNamespace[map[string]ContentTypeConfig, ContentTypes], error) { + buildConfig := func(v any) (ContentTypes, any, error) { + var s map[string]ContentTypeConfig + c := DefaultContentTypes + m, err := maps.ToStringMapE(v) + if err != nil { + return c, nil, err + } + if len(m) == 0 { + s = defaultContentTypesConfig + } else { + s = make(map[string]ContentTypeConfig) + m = maps.CleanConfigStringMap(m) + for k, v := range m { + var ctc ContentTypeConfig + if err := mapstructure.WeakDecode(v, &ctc); err != nil { + return c, nil, err + } + s[k] = ctc + } + } + + for k := range s { + mediaType, found := types.GetByType(k) + if !found { + return c, nil, fmt.Errorf("unknown media type %q", k) + } + c.types = append(c.types, mediaType) + } + + c.init(types) + + return c, s, nil + } + + ns, err := config.DecodeNamespace[map[string]ContentTypeConfig](in, buildConfig) + if err != nil { + return nil, fmt.Errorf("failed to decode media types: %w", err) + } + return ns, nil +} + // DecodeTypes decodes the given map of media types. func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTypeConfig, Types], error) { buildConfig := func(v any) (Types, any, error) { @@ -220,6 +273,6 @@ func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTyp // TODO(bep) get rid of this. var DefaultPathParser = &paths.PathParser{ IsContentExt: func(ext string) bool { - return DefaultContentTypes.IsContentSuffix(ext) + panic("not supported") }, } diff --git a/media/mediaType_test.go b/media/mediaType_test.go index fb3eb664f..3b8e099b8 100644 --- a/media/mediaType_test.go +++ b/media/mediaType_test.go @@ -214,11 +214,3 @@ func BenchmarkTypeOps(b *testing.B) { } } - -func TestIsContentFile(t *testing.T) { - c := qt.New(t) - - c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("my/file.md")), qt.Equals, true) - c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("my/file.ad")), qt.Equals, true) - c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("textfile.txt")), qt.Equals, false) -} diff --git a/parser/lowercase_camel_json.go b/parser/lowercase_camel_json.go index c61a4078e..468c1a8fe 100644 --- a/parser/lowercase_camel_json.go +++ b/parser/lowercase_camel_json.go @@ -107,7 +107,7 @@ func (c ReplacingJSONMarshaller) MarshalJSON() ([]byte, error) { var removeZeroVAlues func(m map[string]any) removeZeroVAlues = func(m map[string]any) { for k, v := range m { - if !hreflect.IsTruthful(v) { + if !hreflect.IsMap(v) && !hreflect.IsTruthful(v) { delete(m, k) } else { switch vv := v.(type) { diff --git a/resources/page/page_nop.go b/resources/page/page_nop.go index af9f2682d..398a7df02 100644 --- a/resources/page/page_nop.go +++ b/resources/page/page_nop.go @@ -21,7 +21,6 @@ import ( "html/template" "time" - "github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/tableofcontents" @@ -59,8 +58,6 @@ var ( // PageNop implements Page, but does nothing. type nopPage int -var noOpPathInfo = media.DefaultPathParser.Parse(files.ComponentFolderContent, "no-op.md") - func (p *nopPage) Aliases() []string { return nil } @@ -338,7 +335,7 @@ func (p *nopPage) Path() string { } func (p *nopPage) PathInfo() *paths.Path { - return noOpPathInfo + return nil } func (p *nopPage) Permalink() string { diff --git a/source/fileInfo.go b/source/fileInfo.go index 8994dec97..8403c8088 100644 --- a/source/fileInfo.go +++ b/source/fileInfo.go @@ -132,6 +132,7 @@ func (fi *File) p() *paths.Path { return fi.fim.Meta().PathInfo.Unnormalized() } +// Used in tests. func NewFileInfoFrom(path, filename string) *File { meta := &hugofs.FileMeta{ Filename: filename, diff --git a/tpl/reflect/reflect.go b/tpl/reflect/reflect.go index 07834be1c..c19c8c178 100644 --- a/tpl/reflect/reflect.go +++ b/tpl/reflect/reflect.go @@ -14,7 +14,7 @@ package reflect import ( - "reflect" + "github.com/gohugoio/hugo/common/hreflect" ) // New returns a new instance of the reflect-namespaced template functions. @@ -27,10 +27,10 @@ type Namespace struct{} // IsMap reports whether v is a map. func (ns *Namespace) IsMap(v any) bool { - return reflect.ValueOf(v).Kind() == reflect.Map + return hreflect.IsMap(v) } // IsSlice reports whether v is a slice. func (ns *Namespace) IsSlice(v any) bool { - return reflect.ValueOf(v).Kind() == reflect.Slice + return hreflect.IsSlice(v) } From d58c0198d30d8de6eef3cbc5f6e30e4b2ae4beee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:36:23 +0000 Subject: [PATCH 137/374] build(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6 Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.5 to 1.0.6. - [Release notes](https://github.com/spf13/pflag/releases) - [Commits](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6) --- updated-dependencies: - dependency-name: github.com/spf13/pflag dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index edd36ad05..a6a3338e4 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.8.1 github.com/spf13/fsync v0.10.1 - github.com/spf13/pflag v1.0.5 + github.com/spf13/pflag v1.0.6 github.com/tdewolff/minify/v2 v2.20.37 github.com/tdewolff/parse/v2 v2.7.15 github.com/tetratelabs/wazero v1.8.2 diff --git a/go.sum b/go.sum index 3afca7c06..7505c9593 100644 --- a/go.sum +++ b/go.sum @@ -225,8 +225,6 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= -github.com/gohugoio/hashstructure v0.1.0 h1:kBSTMLMyTXbrJVAxaKI+wv30MMJJxn9Q8kfQtJaZ400= -github.com/gohugoio/hashstructure v0.1.0/go.mod h1:8ohPTAfQLTs2WdzB6k9etmQYclDUeNsIHGPAFejbsEA= github.com/gohugoio/hashstructure v0.3.0 h1:orHavfqnBv0ffQmobOp41Y9HKEMcjrR/8EFAzpngmGs= github.com/gohugoio/hashstructure v0.3.0/go.mod h1:8ohPTAfQLTs2WdzB6k9etmQYclDUeNsIHGPAFejbsEA= github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= @@ -432,8 +430,9 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE= github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= From b772f0e3d2a6c5fcd955ffe0e43229b57a2b6951 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:36:36 +0000 Subject: [PATCH 138/374] build(deps): bump github.com/getkin/kin-openapi from 0.123.0 to 0.129.0 Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.123.0 to 0.129.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.123.0...v0.129.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 9 +++++---- go.sum | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index a6a3338e4..7466e8fd4 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/fortytw2/leaktest v1.3.0 github.com/frankban/quicktest v1.14.6 github.com/fsnotify/fsnotify v1.8.0 - github.com/getkin/kin-openapi v0.123.0 + github.com/getkin/kin-openapi v0.129.0 github.com/ghodss/yaml v1.0.0 github.com/gobuffalo/flect v1.0.3 github.com/gobwas/glob v0.2.3 @@ -124,8 +124,8 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/swag v0.22.8 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.8 // indirect @@ -135,7 +135,6 @@ require ( github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/yaml v0.2.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -145,6 +144,8 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect + github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 7505c9593..6fb9153d9 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8= -github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30= +github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -213,10 +213,10 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= -github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= @@ -336,8 +336,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -390,6 +388,10 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= +github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= +github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -866,7 +868,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From b50ab04031e1fb3a7c3047dd2086db0e960abc29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:28:01 +0000 Subject: [PATCH 139/374] build(deps): bump github.com/aws/aws-sdk-go-v2 from 1.32.4 to 1.36.1 Bumps [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) from 1.32.4 to 1.36.1. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/changelog-template.json) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.32.4...v1.36.1) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7466e8fd4..7d585eb58 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 github.com/alecthomas/chroma/v2 v2.15.0 github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c - github.com/aws/aws-sdk-go-v2 v1.32.4 + github.com/aws/aws-sdk-go-v2 v1.36.1 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.41.0 github.com/bep/clocks v0.5.0 github.com/bep/debounce v1.2.0 @@ -118,7 +118,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.22.0 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect diff --git a/go.sum b/go.sum index 6fb9153d9..781453ab3 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtS github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.4 h1:S13INUiTxgrPueTmrm5DZ+MiAo99zYzHEFh1UNkOxNE= -github.com/aws/aws-sdk-go-v2 v1.32.4/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= +github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= +github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= @@ -119,8 +119,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrA github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= -github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo= From 22ee0918f3faf05f6a8bb6e0f596980896eb6c46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2025 09:34:27 +0000 Subject: [PATCH 140/374] build(deps): bump github.com/aws/aws-sdk-go-v2/service/cloudfront Bumps [github.com/aws/aws-sdk-go-v2/service/cloudfront](https://github.com/aws/aws-sdk-go-v2) from 1.41.0 to 1.44.9. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/changelog-template.json) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.41.0...service/macie2/v1.44.9) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/cloudfront dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7d585eb58..3011f246a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/alecthomas/chroma/v2 v2.15.0 github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c github.com/aws/aws-sdk-go-v2 v1.36.1 - github.com/aws/aws-sdk-go-v2/service/cloudfront v1.41.0 + github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.9 github.com/bep/clocks v0.5.0 github.com/bep/debounce v1.2.0 github.com/bep/gitmap v1.6.0 @@ -106,8 +106,8 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect diff --git a/go.sum b/go.sum index 781453ab3..710194633 100644 --- a/go.sum +++ b/go.sum @@ -93,16 +93,16 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 h1:A2w6m6Tmr+BNXjDsr7M90zkWjsu4JXHwrzPg235STs4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23/go.mod h1:35EVp9wyeANdujZruvHiQUAo9E3vbhnIO1mTCAxMlY0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 h1:pgYW9FCabt2M25MoHYCfMrVY2ghiiBKYWUVXfwZs+sU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23/go.mod h1:c48kLgzO19wAu3CPkDWC28JbaJ+hfQlsdl7I2+oqIbk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.41.0 h1:sLXpWohpuSh6fSvI7q/D5k3yUB9KtUyIEUDAQnasG0c= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.41.0/go.mod h1:GM6Olux4KAMUmRw0XgadfpN1cOpm5eWYZ31PAj59JSk= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.9 h1:4PpVlmo6btpGYqq+SRkxbDKvfFrn9Fq0IWkqww4o8Nc= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.9/go.mod h1:uBca+/1aH5v/RYWXqyymLrsbmx1vU9bBxeurlC627Gc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= From f02da7fbcea4991a977445efaa70c4e73f3d4d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20S=C3=BC=C3=9F?= Date: Sun, 9 Feb 2025 22:40:28 +0100 Subject: [PATCH 141/374] parser: Handle org-mode filetags as slice This adds support for filetags by slicing them according to [the org mode tag specification](https://orgmode.org/guide/Tags.html). Can be used to create taxonomies based on org-mode tags --- parser/metadecoders/decoder.go | 4 ++++ parser/metadecoders/decoder_test.go | 2 ++ 2 files changed, 6 insertions(+) diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index 5dac23f03..eb33f1ee9 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -251,6 +251,10 @@ func (d Decoder) unmarshalORG(data []byte, v any) error { frontMatter[k[:len(k)-2]] = strings.Fields(v) } else if strings.Contains(v, "\n") { frontMatter[k] = strings.Split(v, "\n") + } else if k == "filetags" { + trimmed := strings.TrimPrefix(v, ":") + trimmed = strings.TrimSuffix(trimmed, ":") + frontMatter[k] = strings.Split(trimmed, ":") } else if k == "date" || k == "lastmod" || k == "publishdate" || k == "expirydate" { frontMatter[k] = parseORGDate(v) } else { diff --git a/parser/metadecoders/decoder_test.go b/parser/metadecoders/decoder_test.go index 49f7868cc..f0ebe57e5 100644 --- a/parser/metadecoders/decoder_test.go +++ b/parser/metadecoders/decoder_test.go @@ -131,6 +131,8 @@ func TestUnmarshalToInterface(t *testing.T) { {[]byte("#+a: foo bar\n#+a: baz"), ORG, map[string]any{"a": []string{string("foo bar"), string("baz")}}}, {[]byte(`#+DATE: <2020-06-26 Fri>`), ORG, map[string]any{"date": "2020-06-26"}}, {[]byte(`#+LASTMOD: <2020-06-26 Fri>`), ORG, map[string]any{"lastmod": "2020-06-26"}}, + {[]byte(`#+FILETAGS: :work:`), ORG, map[string]any{"filetags": []string{"work"}}}, + {[]byte(`#+FILETAGS: :work:fun:`), ORG, map[string]any{"filetags": []string{"work", "fun"}}}, {[]byte(`#+PUBLISHDATE: <2020-06-26 Fri>`), ORG, map[string]any{"publishdate": "2020-06-26"}}, {[]byte(`#+EXPIRYDATE: <2020-06-26 Fri>`), ORG, map[string]any{"expirydate": "2020-06-26"}}, {[]byte(`a = "b"`), TOML, expect}, From 2d86a0512cc8b5a0284e0b0b2e6a653ae9c3aa6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:07:26 +0000 Subject: [PATCH 142/374] build(deps): bump github.com/bep/simplecobra from 0.4.0 to 0.5.0 Bumps [github.com/bep/simplecobra](https://github.com/bep/simplecobra) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/bep/simplecobra/releases) - [Commits](https://github.com/bep/simplecobra/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: github.com/bep/simplecobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3011f246a..9bd96649f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 github.com/bep/overlayfs v0.9.2 - github.com/bep/simplecobra v0.4.0 + github.com/bep/simplecobra v0.5.0 github.com/bep/tmc v0.5.1 github.com/cespare/xxhash/v2 v2.3.0 github.com/clbanning/mxj/v2 v2.7.0 diff --git a/go.sum b/go.sum index 710194633..efd3f334e 100644 --- a/go.sum +++ b/go.sum @@ -147,8 +147,8 @@ github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69 github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY= github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs= github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40= -github.com/bep/simplecobra v0.4.0 h1:ufX/6WcOtEVJdCd7hsztTWURlZkOaWYOD+zCqrM8qUE= -github.com/bep/simplecobra v0.4.0/go.mod h1:evSM6iQqRwqpV7W4H4DlYFfe9mZ0x6Hj5GEOnIV7dI4= +github.com/bep/simplecobra v0.5.0 h1:b6rElBI1mtSrhhIvk/5KrAddKwiNXjxhChs9W1zRep4= +github.com/bep/simplecobra v0.5.0/go.mod h1:QdxH1jmpb2DB+rkDYbuk7EM7XiluIt9+ACJmoY92igg= github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg= From a591c4406a77fe9a462e215e02afb8267df33813 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 08:56:13 +0000 Subject: [PATCH 143/374] build(deps): bump github.com/gohugoio/hashstructure from 0.3.0 to 0.5.0 Bumps [github.com/gohugoio/hashstructure](https://github.com/gohugoio/hashstructure) from 0.3.0 to 0.5.0. - [Release notes](https://github.com/gohugoio/hashstructure/releases) - [Commits](https://github.com/gohugoio/hashstructure/compare/v0.3.0...v0.5.0) --- updated-dependencies: - dependency-name: github.com/gohugoio/hashstructure dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9bd96649f..22b95f4f9 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/gobuffalo/flect v1.0.3 github.com/gobwas/glob v0.2.3 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e - github.com/gohugoio/hashstructure v0.3.0 + github.com/gohugoio/hashstructure v0.5.0 github.com/gohugoio/httpcache v0.7.0 github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0 github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0 diff --git a/go.sum b/go.sum index efd3f334e..7f0ed9c0d 100644 --- a/go.sum +++ b/go.sum @@ -225,8 +225,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= -github.com/gohugoio/hashstructure v0.3.0 h1:orHavfqnBv0ffQmobOp41Y9HKEMcjrR/8EFAzpngmGs= -github.com/gohugoio/hashstructure v0.3.0/go.mod h1:8ohPTAfQLTs2WdzB6k9etmQYclDUeNsIHGPAFejbsEA= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0 h1:MNdY6hYCTQEekY0oAfsxWZU1CDt6iH+tMLgyMJQh/sg= From 0cdcc2b584cff6620058492293c09eb185cb0b88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 08:56:05 +0000 Subject: [PATCH 144/374] build(deps): bump golang.org/x/mod from 0.22.0 to 0.23.0 Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.22.0 to 0.23.0. - [Commits](https://github.com/golang/mod/compare/v0.22.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 22b95f4f9..28bb51bcf 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 golang.org/x/image v0.22.0 - golang.org/x/mod v0.22.0 + golang.org/x/mod v0.23.0 golang.org/x/net v0.34.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 diff --git a/go.sum b/go.sum index 7f0ed9c0d..d06611ec5 100644 --- a/go.sum +++ b/go.sum @@ -550,8 +550,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 5e4ffa0e892a0668ade6fe0c0b766881a55c7efd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 10:45:17 +0000 Subject: [PATCH 145/374] build(deps): bump golang.org/x/image from 0.22.0 to 0.24.0 Bumps [golang.org/x/image](https://github.com/golang/image) from 0.22.0 to 0.24.0. - [Commits](https://github.com/golang/image/compare/v0.22.0...v0.24.0) --- updated-dependencies: - dependency-name: golang.org/x/image dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 28bb51bcf..3c9a07193 100644 --- a/go.mod +++ b/go.mod @@ -75,11 +75,11 @@ require ( go.uber.org/automaxprocs v1.5.3 gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 - golang.org/x/image v0.22.0 + golang.org/x/image v0.24.0 golang.org/x/mod v0.23.0 golang.org/x/net v0.34.0 - golang.org/x/sync v0.10.0 - golang.org/x/text v0.21.0 + golang.org/x/sync v0.11.0 + golang.org/x/text v0.22.0 golang.org/x/tools v0.29.0 google.golang.org/api v0.206.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index d06611ec5..107ca0b54 100644 --- a/go.sum +++ b/go.sum @@ -522,8 +522,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= -golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= +golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= +golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -619,8 +619,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -688,8 +688,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From e6feb9e0be52c542c354f737fca9863eccb7102c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 10 Feb 2025 15:42:21 +0100 Subject: [PATCH 146/374] commands: Move the CHMOD event filter up To prevent ghost rebuilds (from VSCode and possibly others). Fixes #13373 --- commands/hugobuilder.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index 4c2d865c0..778d85ca9 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -663,7 +663,20 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, var n int for _, ev := range evs { keep := true - if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { + // Write and rename operations are often followed by CHMOD. + // There may be valid use cases for rebuilding the site on CHMOD, + // but that will require more complex logic than this simple conditional. + // On OS X this seems to be related to Spotlight, see: + // https://github.com/go-fsnotify/fsnotify/issues/15 + // A workaround is to put your site(s) on the Spotlight exception list, + // but that may be a little mysterious for most end users. + // So, for now, we skip reload on CHMOD. + // We do have to check for WRITE though. On slower laptops a Chmod + // could be aggregated with other important events, and we still want + // to rebuild on those + if ev.Op == fsnotify.Chmod { + keep = false + } else if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { if _, err := os.Stat(ev.Name); err != nil { keep = false } @@ -805,21 +818,6 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, continue } - // Write and rename operations are often followed by CHMOD. - // There may be valid use cases for rebuilding the site on CHMOD, - // but that will require more complex logic than this simple conditional. - // On OS X this seems to be related to Spotlight, see: - // https://github.com/go-fsnotify/fsnotify/issues/15 - // A workaround is to put your site(s) on the Spotlight exception list, - // but that may be a little mysterious for most end users. - // So, for now, we skip reload on CHMOD. - // We do have to check for WRITE though. On slower laptops a Chmod - // could be aggregated with other important events, and we still want - // to rebuild on those - if ev.Op&(fsnotify.Chmod|fsnotify.Write|fsnotify.Create) == fsnotify.Chmod { - continue - } - walkAdder := func(path string, f hugofs.FileMetaInfo) error { if f.IsDir() { c.r.logger.Println("adding created directory to watchlist", path) From 9b5f786df8fde5e5bd6311f210b95c85ff21eeea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 10 Feb 2025 18:50:56 +0100 Subject: [PATCH 147/374] Deprecate kind, lang, and path from front matter These were added to the page meta object when we implemented "pages from data", but were not meant to be used in front matter. That is not supported, so we might as well add validation. Fixes #12484 --- commands/commandeer.go | 2 +- common/hugo/hugo.go | 9 ++++++++ common/loggers/loggerglobal.go | 12 ++++++++-- config/allconfig/load.go | 2 +- config/privacy/privacyConfig_test.go | 8 ++----- hugolib/page__meta.go | 7 ++++++ hugolib/page_test.go | 26 ++++++++++++++++++++++ hugolib/testhelpers_test.go | 2 -- tpl/tplimpl/shortcodes_integration_test.go | 2 +- 9 files changed, 57 insertions(+), 13 deletions(-) diff --git a/commands/commandeer.go b/commands/commandeer.go index c53235cef..697ece1f0 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -448,7 +448,7 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { return err } // Set up the global logger early to allow info deprecations during config load. - loggers.InitGlobalLogger(r.logger.Level(), false) + loggers.SetGlobalLogger(r.logger) r.changesFromBuild = make(chan []identity.Identity, 10) diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index 8b32432db..815c25fa7 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -414,6 +414,15 @@ func Deprecate(item, alternative string, version string) { DeprecateLevel(item, alternative, version, level) } +// DeprecateLevelMin informs about a deprecation starting at the given version, but with a minimum log level. +func DeprecateLevelMin(item, alternative string, version string, minLevel logg.Level) { + level := deprecationLogLevelFromVersion(version) + if level < minLevel { + level = minLevel + } + DeprecateLevel(item, alternative, version, level) +} + // DeprecateLevel informs about a deprecation logging at the given level. func DeprecateLevel(item, alternative, version string, level logg.Level) { var msg string diff --git a/common/loggers/loggerglobal.go b/common/loggers/loggerglobal.go index c3e2970d0..b8c9a6931 100644 --- a/common/loggers/loggerglobal.go +++ b/common/loggers/loggerglobal.go @@ -21,7 +21,15 @@ import ( "github.com/bep/logg" ) -func InitGlobalLogger(level logg.Level, panicOnWarnings bool) { +// SetGlobalLogger sets the global logger. +// This is used in a few places in Hugo, e.g. deprecated functions. +func SetGlobalLogger(logger Logger) { + logMu.Lock() + defer logMu.Unlock() + log = logger +} + +func initGlobalLogger(level logg.Level, panicOnWarnings bool) { logMu.Lock() defer logMu.Unlock() var logHookLast func(e *logg.Entry) error @@ -50,5 +58,5 @@ func Log() Logger { var log Logger func init() { - InitGlobalLogger(logg.LevelWarn, false) + initGlobalLogger(logg.LevelWarn, false) } diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 999e03645..9e9c7a42a 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -91,7 +91,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) { return nil, fmt.Errorf("failed to init config: %w", err) } - loggers.InitGlobalLogger(d.Logger.Level(), configs.Base.PanicOnWarning) + loggers.SetGlobalLogger(d.Logger) return configs, nil } diff --git a/config/privacy/privacyConfig_test.go b/config/privacy/privacyConfig_test.go index 6cde91165..1dd20215b 100644 --- a/config/privacy/privacyConfig_test.go +++ b/config/privacy/privacyConfig_test.go @@ -36,10 +36,6 @@ respectDoNotTrack = true [privacy.instagram] disable = true simple = true -[privacy.twitter] -disable = true -enableDNT = true -simple = true [privacy.x] disable = true enableDNT = true @@ -63,8 +59,8 @@ simple = true got := []bool{ pc.Disqus.Disable, pc.GoogleAnalytics.Disable, pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable, - pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT, - pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple, + pc.Instagram.Simple, + pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple, pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable, pc.X.Disable, pc.X.EnableDNT, pc.X.Simple, } diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 38fe6db56..20ce1aa59 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -21,6 +21,7 @@ import ( "strings" "time" + "github.com/bep/logg" "github.com/gobuffalo/flect" "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/markup/converter" @@ -32,6 +33,7 @@ import ( "github.com/gohugoio/hugo/common/constants" "github.com/gohugoio/hugo/common/hashing" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/paths" @@ -486,6 +488,11 @@ params: continue } + if loki == "path" || loki == "kind" || loki == "lang" { + // See issue 12484. + hugo.DeprecateLevelMin(loki+" in front matter", "", "v0.144.0", logg.LevelWarn) + } + switch loki { case "title": pcfg.Title = cast.ToString(v) diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 39a16d948..0fec397e0 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1942,3 +1942,29 @@ Hugo: h-Home| `, ) } + +// See #12484 +func TestPageFrontMatterDeprecatePathKindLang(t *testing.T) { + // This cannot be parallel as it depends on output from the global logger. + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "home", "section"] +-- content/p1.md -- +--- +title: "p1" +kind: "page" +lang: "en" +path: "mypath" +--- +-- layouts/_default/single.html -- +Title: {{ .Title }} +` + b := Test(t, files, TestOptWarn()) + b.AssertFileContent("public/mypath/index.html", "p1") + b.AssertLogContains( + "deprecated: kind in front matter was deprecated", + "deprecated: lang in front matter was deprecated", + "deprecated: path in front matter was deprecated", + ) +} diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go index 9fdb63238..2007b658d 100644 --- a/hugolib/testhelpers_test.go +++ b/hugolib/testhelpers_test.go @@ -260,8 +260,6 @@ disable = false respectDoNotTrack = true [privacy.instagram] simple = true -[privacy.twitter] -enableDNT = true [privacy.x] enableDNT = true [privacy.vimeo] diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index 0578e2d8b..b8a4ad833 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -22,7 +22,7 @@ import ( ) func TestCommentShortcode(t *testing.T) { - t.Parallel() + // This cannot be parallel as it depends on output from the global logger. files := ` -- hugo.toml -- From fd8b0fbf8a4efbe20c6a5143d5a9f308f08cab7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 12 Feb 2025 10:35:51 +0100 Subject: [PATCH 148/374] Upgrade to Go 1.24 Fixes #13381 --- .circleci/config.yml | 4 +- .github/workflows/test.yml | 2 +- go.mod | 2 +- scripts/fork_go_templates/main.go | 3 +- tpl/internal/go_templates/cfg/cfg.go | 2 + .../go_templates/htmltemplate/clone_test.go | 4 +- .../go_templates/htmltemplate/content_test.go | 6 +- .../go_templates/htmltemplate/css_test.go | 4 +- .../go_templates/htmltemplate/escape.go | 19 +- .../go_templates/htmltemplate/escape_test.go | 10 +- .../go_templates/htmltemplate/example_test.go | 5 + .../htmltemplate/examplefiles_test.go | 3 + .../go_templates/htmltemplate/exec_test.go | 50 +- .../go_templates/htmltemplate/html_test.go | 4 +- tpl/internal/go_templates/htmltemplate/js.go | 11 +- .../go_templates/htmltemplate/js_test.go | 11 +- .../go_templates/htmltemplate/multi_test.go | 6 +- .../htmltemplate/template_test.go | 4 + .../htmltemplate/transition_test.go | 5 +- .../go_templates/htmltemplate/url_test.go | 4 +- tpl/internal/go_templates/testenv/exec.go | 44 +- tpl/internal/go_templates/testenv/testenv.go | 222 +++++---- .../go_templates/testenv/testenv_notwin.go | 5 +- .../go_templates/testenv/testenv_test.go | 78 +++- .../go_templates/testenv/testenv_windows.go | 29 +- tpl/internal/go_templates/texttemplate/doc.go | 30 +- .../go_templates/texttemplate/example_test.go | 2 +- .../texttemplate/examplefiles_test.go | 3 + .../go_templates/texttemplate/exec.go | 63 ++- .../go_templates/texttemplate/exec_test.go | 59 +++ .../texttemplate/hugo_template.go | 18 +- .../go_templates/texttemplate/link_test.go | 8 +- .../go_templates/texttemplate/multi_test.go | 39 +- .../go_templates/texttemplate/parse/lex.go | 1 + .../texttemplate/parse/lex_test.go | 10 + .../texttemplate/parse/parse_test.go | 439 ++++++------------ .../go_templates/texttemplate/template.go | 9 +- 37 files changed, 652 insertions(+), 566 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7a47a64b..06e643bdd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ parameters: defaults: &defaults resource_class: large docker: - - image: bepsays/ci-hugoreleaser:1.22301.20401 + - image: bepsays/ci-hugoreleaser:1.22400.20000 environment: &buildenv GOMODCACHE: /root/project/gomodcache version: 2 @@ -58,7 +58,7 @@ jobs: environment: <<: [*buildenv] docker: - - image: bepsays/ci-hugoreleaser-linux-arm64:1.22301.20401 + - image: bepsays/ci-hugoreleaser-linux-arm64:1.22400.20000 steps: - *restore-cache - &attach-workspace diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb9d71f06..c49c12371 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: test: strategy: matrix: - go-version: [1.22.x, 1.23.x] + go-version: [1.23.x, 1.24.x] os: [ubuntu-latest, windows-latest] # macos disabled for now because of disk space issues. runs-on: ${{ matrix.os }} steps: diff --git a/go.mod b/go.mod index 3c9a07193..f375cbe1a 100644 --- a/go.mod +++ b/go.mod @@ -170,4 +170,4 @@ require ( software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect ) -go 1.22.6 +go 1.23 diff --git a/scripts/fork_go_templates/main.go b/scripts/fork_go_templates/main.go index c4dad3224..cd461043c 100644 --- a/scripts/fork_go_templates/main.go +++ b/scripts/fork_go_templates/main.go @@ -16,7 +16,7 @@ import ( ) func main() { - // The current is built with 6885bad7dd86880be6929c02085e5c7a67ff2887 go1.23.0 + // The current is built with 3901409b5d [release-branch.go1.24] go1.24.0 // TODO(bep) preserve the staticcheck.conf file. fmt.Println("Forking ...") defer fmt.Println("Done ...") @@ -216,6 +216,7 @@ func rewrite(filename, rule string) { } func goimports(dir string) { + // Needs go install golang.org/x/tools/cmd/goimports@latest cmf, _ := hexec.SafeCommand("goimports", "-w", dir) out, err := cmf.CombinedOutput() if err != nil { diff --git a/tpl/internal/go_templates/cfg/cfg.go b/tpl/internal/go_templates/cfg/cfg.go index 08d210b79..932976972 100644 --- a/tpl/internal/go_templates/cfg/cfg.go +++ b/tpl/internal/go_templates/cfg/cfg.go @@ -37,12 +37,14 @@ const KnownEnv = ` GOARCH GOARM GOARM64 + GOAUTH GOBIN GOCACHE GOCACHEPROG GOENV GOEXE GOEXPERIMENT + GOFIPS140 GOFLAGS GOGCCFLAGS GOHOSTARCH diff --git a/tpl/internal/go_templates/htmltemplate/clone_test.go b/tpl/internal/go_templates/htmltemplate/clone_test.go index e42177a6b..7db335b5b 100644 --- a/tpl/internal/go_templates/htmltemplate/clone_test.go +++ b/tpl/internal/go_templates/htmltemplate/clone_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template diff --git a/tpl/internal/go_templates/htmltemplate/content_test.go b/tpl/internal/go_templates/htmltemplate/content_test.go index e886ee8ff..fac4774cc 100644 --- a/tpl/internal/go_templates/htmltemplate/content_test.go +++ b/tpl/internal/go_templates/htmltemplate/content_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template @@ -428,7 +428,7 @@ func TestStringer(t *testing.T) { if err := tmpl.Execute(b, s); err != nil { t.Fatal(err) } - expect := "string=3" + var expect = "string=3" if b.String() != expect { t.Errorf("expected %q got %q", expect, b.String()) } diff --git a/tpl/internal/go_templates/htmltemplate/css_test.go b/tpl/internal/go_templates/htmltemplate/css_test.go index f2b2add3a..f44568930 100644 --- a/tpl/internal/go_templates/htmltemplate/css_test.go +++ b/tpl/internal/go_templates/htmltemplate/css_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template diff --git a/tpl/internal/go_templates/htmltemplate/escape.go b/tpl/internal/go_templates/htmltemplate/escape.go index 334bbce0f..f5d5674fa 100644 --- a/tpl/internal/go_templates/htmltemplate/escape.go +++ b/tpl/internal/go_templates/htmltemplate/escape.go @@ -9,6 +9,7 @@ import ( "fmt" "html" "io" + "maps" "regexp" template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate" @@ -145,7 +146,7 @@ func (e *escaper) escape(c context, n parse.Node) context { return c case *parse.ContinueNode: c.n = n - e.rangeContext.continues = append(e.rangeContext.breaks, c) + e.rangeContext.continues = append(e.rangeContext.continues, c) return context{state: stateDead} case *parse.IfNode: return e.escapeBranch(c, &n.BranchNode, "if") @@ -588,22 +589,14 @@ func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter f e1 := makeEscaper(e.ns) e1.rangeContext = e.rangeContext // Make type inferences available to f. - for k, v := range e.output { - e1.output[k] = v - } + maps.Copy(e1.output, e.output) c = e1.escapeList(c, n) ok := filter != nil && filter(&e1, c) if ok { // Copy inferences and edits from e1 back into e. - for k, v := range e1.output { - e.output[k] = v - } - for k, v := range e1.derived { - e.derived[k] = v - } - for k, v := range e1.called { - e.called[k] = v - } + maps.Copy(e.output, e1.output) + maps.Copy(e.derived, e1.derived) + maps.Copy(e.called, e1.called) for k, v := range e1.actionNodeEdits { e.editActionNode(k, v) } diff --git a/tpl/internal/go_templates/htmltemplate/escape_test.go b/tpl/internal/go_templates/htmltemplate/escape_test.go index 66bf50203..a5a810ffc 100644 --- a/tpl/internal/go_templates/htmltemplate/escape_test.go +++ b/tpl/internal/go_templates/htmltemplate/escape_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template @@ -944,6 +944,7 @@ func TestEscapeSet(t *testing.T) { t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) } } + } func TestErrors(t *testing.T) { @@ -1064,6 +1065,10 @@ func TestErrors(t *testing.T) { "{{range .Items}}{{end}}", "z:1:29: at range loop continue: {{range}} branches end in different contexts", }, + { + "{{range .Items}}{{if .X}}{{break}}{{end}}{{if .Z}}{{continue}}{{end}}{{end}}", + "z:1:54: at range loop continue: {{range}} branches end in different contexts", + }, { "no rows
// // + } func Example_autoescaping() { @@ -120,6 +124,7 @@ func Example_escape() { // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E // %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E + } func ExampleTemplate_Delims() { diff --git a/tpl/internal/go_templates/htmltemplate/examplefiles_test.go b/tpl/internal/go_templates/htmltemplate/examplefiles_test.go index 24b22d984..43cc3bf01 100644 --- a/tpl/internal/go_templates/htmltemplate/examplefiles_test.go +++ b/tpl/internal/go_templates/htmltemplate/examplefiles_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 +// +build go1.13 + package template_test import ( diff --git a/tpl/internal/go_templates/htmltemplate/exec_test.go b/tpl/internal/go_templates/htmltemplate/exec_test.go index e01813e68..e349ca7cd 100644 --- a/tpl/internal/go_templates/htmltemplate/exec_test.go +++ b/tpl/internal/go_templates/htmltemplate/exec_test.go @@ -325,16 +325,12 @@ var execTests = []execTest{ {"$.U.V", "{{$.U.V}}", "v", tVal, true}, {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true}, - { - "nested assignment", + {"nested assignment", "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}", - "3", tVal, true, - }, - { - "nested assignment changes the last declaration", + "3", tVal, true}, + {"nested assignment changes the last declaration", "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}", - "1", tVal, true, - }, + "1", tVal, true}, // Type with String method. {"V{6666}.String()", "-{{.V0}}-", "-{6666}-", tVal, true}, // NOTE: -<6666>- in text/template @@ -381,21 +377,15 @@ var execTests = []execTest{ {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true}, {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true}, {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, - { - "method on chained var", + {"method on chained var", "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true, - }, - { - "chained method", + "true", tVal, true}, + {"chained method", "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true, - }, - { - "chained method on variable", + "true", tVal, true}, + {"chained method on variable", "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", - "true", tVal, true, - }, + "true", tVal, true}, {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true}, @@ -481,14 +471,10 @@ var execTests = []execTest{ {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, // HTML. - { - "html", `{{html ""}}`, - "<script>alert("XSS");</script>", nil, true, - }, - { - "html pipeline", `{{printf "" | html}}`, - "<script>alert("XSS");</script>", nil, true, - }, + {"html", `{{html ""}}`, + "<script>alert("XSS");</script>", nil, true}, + {"html pipeline", `{{printf "" | html}}`, + "<script>alert("XSS");</script>", nil, true}, {"html", `{{html .PS}}`, "a string", tVal, true}, {"html typed nil", `{{html .NIL}}`, "<nil>", tVal, true}, {"html untyped nil", `{{html .Empty0}}`, "<nil>", tVal, true}, // NOTE: "<no value>" in text/template @@ -854,7 +840,7 @@ var delimPairs = []string{ func TestDelims(t *testing.T) { const hello = "Hello, world" - value := struct{ Str string }{hello} + var value = struct{ Str string }{hello} for i := 0; i < len(delimPairs); i += 2 { text := ".Str" left := delimPairs[i+0] @@ -877,7 +863,7 @@ func TestDelims(t *testing.T) { if err != nil { t.Fatalf("delim %q text %q parse err %s", left, text, err) } - b := new(strings.Builder) + var b = new(strings.Builder) err = tmpl.Execute(b, value) if err != nil { t.Fatalf("delim %q exec err %s", left, err) @@ -978,7 +964,7 @@ const treeTemplate = ` ` func TestTree(t *testing.T) { - tree := &Tree{ + var tree = &Tree{ 1, &Tree{ 2, &Tree{ @@ -1229,7 +1215,7 @@ var cmpTests = []cmpTest{ func TestComparison(t *testing.T) { b := new(strings.Builder) - cmpStruct := struct { + var cmpStruct = struct { Uthree, Ufour uint NegOne, Three int Ptr, NilPtr *int diff --git a/tpl/internal/go_templates/htmltemplate/html_test.go b/tpl/internal/go_templates/htmltemplate/html_test.go index bea668ef9..2809ee1e2 100644 --- a/tpl/internal/go_templates/htmltemplate/html_test.go +++ b/tpl/internal/go_templates/htmltemplate/html_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template diff --git a/tpl/internal/go_templates/htmltemplate/js.go b/tpl/internal/go_templates/htmltemplate/js.go index 1b56abeb1..b658aeabc 100644 --- a/tpl/internal/go_templates/htmltemplate/js.go +++ b/tpl/internal/go_templates/htmltemplate/js.go @@ -10,6 +10,7 @@ import ( "fmt" htmltemplate "html/template" "reflect" + "regexp" "strings" "unicode/utf8" ) @@ -145,6 +146,8 @@ func indirectToJSONMarshaler(a any) any { return v.Interface() } +var scriptTagRe = regexp.MustCompile("(?i)<(/?)script") + // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has // neither side-effects nor free variables outside (NaN, Infinity). func jsValEscaper(args ...any) string { @@ -182,9 +185,9 @@ func jsValEscaper(args ...any) string { // In particular we: // * replace "*/" comment end tokens with "* /", which does not // terminate the comment - // * replace "", `--\u003e`}, // From https://code.google.com/p/doctype/wiki/ArticleUtf7 - { - "+ADw-script+AD4-alert(1)+ADw-/script+AD4-", + {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-", `\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`, }, // Invalid UTF-8 sequence diff --git a/tpl/internal/go_templates/htmltemplate/multi_test.go b/tpl/internal/go_templates/htmltemplate/multi_test.go index c320c4353..f3e629b88 100644 --- a/tpl/internal/go_templates/htmltemplate/multi_test.go +++ b/tpl/internal/go_templates/htmltemplate/multi_test.go @@ -4,8 +4,8 @@ // Tests for multiple-template execution, copied from text/template. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template @@ -268,7 +268,7 @@ func TestIssue19294(t *testing.T) { // by the contents of "stylesheet", but if the internal map associating // names with templates is built in the wrong order, the empty block // looks non-empty and this doesn't happen. - inlined := map[string]string{ + var inlined = map[string]string{ "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, "xhtml": `{{block "stylesheet" .}}{{end}}`, } diff --git a/tpl/internal/go_templates/htmltemplate/template_test.go b/tpl/internal/go_templates/htmltemplate/template_test.go index 3a0f7463e..0838308a1 100644 --- a/tpl/internal/go_templates/htmltemplate/template_test.go +++ b/tpl/internal/go_templates/htmltemplate/template_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 +// +build go1.13 + package template_test import ( @@ -15,6 +18,7 @@ import ( ) func TestTemplateClone(t *testing.T) { + orig := New("name") clone, err := orig.Clone() if err != nil { diff --git a/tpl/internal/go_templates/htmltemplate/transition_test.go b/tpl/internal/go_templates/htmltemplate/transition_test.go index 9eb36558b..0bd38800f 100644 --- a/tpl/internal/go_templates/htmltemplate/transition_test.go +++ b/tpl/internal/go_templates/htmltemplate/transition_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template @@ -43,6 +43,7 @@ func TestFindEndTag(t *testing.T) { } func BenchmarkTemplateSpecialTags(b *testing.B) { + r := struct { Name, Gift string }{"Aunt Mildred", "bone china tea set"} diff --git a/tpl/internal/go_templates/htmltemplate/url_test.go b/tpl/internal/go_templates/htmltemplate/url_test.go index c7e67c211..72c8a4fe9 100644 --- a/tpl/internal/go_templates/htmltemplate/url_test.go +++ b/tpl/internal/go_templates/htmltemplate/url_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template diff --git a/tpl/internal/go_templates/testenv/exec.go b/tpl/internal/go_templates/testenv/exec.go index 7f6ad5cac..7b251b602 100644 --- a/tpl/internal/go_templates/testenv/exec.go +++ b/tpl/internal/go_templates/testenv/exec.go @@ -31,20 +31,17 @@ import ( // If exec is not supported, testenv.SyscallIsNotSupported will return true // for the resulting error. func MustHaveExec(t testing.TB) { - tryExecOnce.Do(func() { - tryExecErr = tryExec() - }) - if tryExecErr != nil { - t.Skipf("skipping test: cannot exec subprocess on %s/%s: %v", runtime.GOOS, runtime.GOARCH, tryExecErr) + if err := tryExec(); err != nil { + msg := fmt.Sprintf("cannot exec subprocess on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + if t == nil { + panic(msg) + } + t.Helper() + t.Skip("skipping test:", msg) } } -var ( - tryExecOnce sync.Once - tryExecErr error -) - -func tryExec() error { +var tryExec = sync.OnceValue(func() error { switch runtime.GOOS { case "wasip1", "js", "ios": default: @@ -70,15 +67,37 @@ func tryExec() error { // We know that this is a test executable. We should be able to run it with a // no-op flag to check for overall exec support. - exe, err := os.Executable() + exe, err := exePath() if err != nil { return fmt.Errorf("can't probe for exec support: %w", err) } cmd := exec.Command(exe, "-test.list=^$") cmd.Env = origEnv return cmd.Run() +}) + +// Executable is a wrapper around [MustHaveExec] and [os.Executable]. +// It returns the path name for the executable that started the current process, +// or skips the test if the current system can't start new processes, +// or fails the test if the path can not be obtained. +func Executable(t testing.TB) string { + MustHaveExec(t) + + exe, err := exePath() + if err != nil { + msg := fmt.Sprintf("os.Executable error: %v", err) + if t == nil { + panic(msg) + } + t.Fatal(msg) + } + return exe } +var exePath = sync.OnceValues(func() (string, error) { + return os.Executable() +}) + var execPaths sync.Map // path -> error // MustHaveExecPath checks that the current system can start the named executable @@ -93,6 +112,7 @@ func MustHaveExecPath(t testing.TB, path string) { err, _ = execPaths.LoadOrStore(path, err) } if err != nil { + t.Helper() t.Skipf("skipping test: %s: %s", path, err) } } diff --git a/tpl/internal/go_templates/testenv/testenv.go b/tpl/internal/go_templates/testenv/testenv.go index e8577d2ea..a46c8f5a8 100644 --- a/tpl/internal/go_templates/testenv/testenv.go +++ b/tpl/internal/go_templates/testenv/testenv.go @@ -12,7 +12,6 @@ package testenv import ( "bytes" - "errors" "flag" "fmt" "os" @@ -43,15 +42,22 @@ func Builder() string { // HasGoBuild reports whether the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. -// Modified by Hugo (not needed) func HasGoBuild() bool { - return false + if os.Getenv("GO_GCFLAGS") != "" { + // It's too much work to require every caller of the go command + // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). + // For now, if $GO_GCFLAGS is set, report that we simply can't + // run go build. + return false + } + + return tryGoBuild() == nil } -var ( - goBuildOnce sync.Once - goBuildErr error -) +var tryGoBuild = sync.OnceValue(func() error { + // Removed by Hugo, not used. + return nil +}) // MustHaveGoBuild checks that the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. @@ -63,7 +69,7 @@ func MustHaveGoBuild(t testing.TB) { } if !HasGoBuild() { t.Helper() - t.Skipf("skipping test: 'go build' unavailable: %v", goBuildErr) + t.Skipf("skipping test: 'go build' unavailable: %v", tryGoBuild()) } } @@ -77,6 +83,7 @@ func HasGoRun() bool { // If not, MustHaveGoRun calls t.Skip with an explanation. func MustHaveGoRun(t testing.TB) { if !HasGoRun() { + t.Helper() t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) } } @@ -96,6 +103,7 @@ func HasParallelism() bool { // threads in parallel. If not, MustHaveParallelism calls t.Skip with an explanation. func MustHaveParallelism(t testing.TB) { if !HasParallelism() { + t.Helper() t.Skipf("skipping test: no parallelism available on %s/%s", runtime.GOOS, runtime.GOARCH) } } @@ -119,82 +127,67 @@ func GoToolPath(t testing.TB) string { return path } -var ( - gorootOnce sync.Once - gorootPath string - gorootErr error -) +var findGOROOT = sync.OnceValues(func() (path string, err error) { + if path := runtime.GOROOT(); path != "" { + // If runtime.GOROOT() is non-empty, assume that it is valid. + // + // (It might not be: for example, the user may have explicitly set GOROOT + // to the wrong directory. But this case is + // rare, and if that happens the user can fix what they broke.) + return path, nil + } -func findGOROOT() (string, error) { - gorootOnce.Do(func() { - gorootPath = runtime.GOROOT() - if gorootPath != "" { - // If runtime.GOROOT() is non-empty, assume that it is valid. - // - // (It might not be: for example, the user may have explicitly set GOROOT - // to the wrong directory. But this case is - // rare, and if that happens the user can fix what they broke.) - return + // runtime.GOROOT doesn't know where GOROOT is (perhaps because the test + // binary was built with -trimpath). + // + // Since this is internal/testenv, we can cheat and assume that the caller + // is a test of some package in a subdirectory of GOROOT/src. ('go test' + // runs the test in the directory containing the packaged under test.) That + // means that if we start walking up the tree, we should eventually find + // GOROOT/src/go.mod, and we can report the parent directory of that. + // + // Notably, this works even if we can't run 'go env GOROOT' as a + // subprocess. + + cwd, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("finding GOROOT: %w", err) + } + + dir := cwd + for { + parent := filepath.Dir(dir) + if parent == dir { + // dir is either "." or only a volume name. + return "", fmt.Errorf("failed to locate GOROOT/src in any parent directory") } - // runtime.GOROOT doesn't know where GOROOT is (perhaps because the test - // binary was built with -trimpath). - // - // Since this is internal/testenv, we can cheat and assume that the caller - // is a test of some package in a subdirectory of GOROOT/src. ('go test' - // runs the test in the directory containing the packaged under test.) That - // means that if we start walking up the tree, we should eventually find - // GOROOT/src/go.mod, and we can report the parent directory of that. - // - // Notably, this works even if we can't run 'go env GOROOT' as a - // subprocess. + if base := filepath.Base(dir); base != "src" { + dir = parent + continue // dir cannot be GOROOT/src if it doesn't end in "src". + } - cwd, err := os.Getwd() + b, err := os.ReadFile(filepath.Join(dir, "go.mod")) if err != nil { - gorootErr = fmt.Errorf("finding GOROOT: %w", err) - return - } - - dir := cwd - for { - parent := filepath.Dir(dir) - if parent == dir { - // dir is either "." or only a volume name. - gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory") - return - } - - if base := filepath.Base(dir); base != "src" { + if os.IsNotExist(err) { dir = parent - continue // dir cannot be GOROOT/src if it doesn't end in "src". + continue } + return "", fmt.Errorf("finding GOROOT: %w", err) + } + goMod := string(b) - b, err := os.ReadFile(filepath.Join(dir, "go.mod")) - if err != nil { - if os.IsNotExist(err) { - dir = parent - continue - } - gorootErr = fmt.Errorf("finding GOROOT: %w", err) - return - } - goMod := string(b) - - for goMod != "" { - var line string - line, goMod, _ = strings.Cut(goMod, "\n") - fields := strings.Fields(line) - if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" { - // Found "module std", which is the module declaration in GOROOT/src! - gorootPath = parent - return - } + for goMod != "" { + var line string + line, goMod, _ = strings.Cut(goMod, "\n") + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" { + // Found "module std", which is the module declaration in GOROOT/src! + return parent, nil } } - }) - - return gorootPath, gorootErr -} + } +}) // GOROOT reports the path to the directory containing the root of the Go // project source tree. This is normally equivalent to runtime.GOROOT, but @@ -217,28 +210,22 @@ func GOROOT(t testing.TB) string { // GoTool reports the path to the Go tool. func GoTool() (string, error) { - if !HasGoBuild() { - return "", errors.New("platform cannot run go tool") - } - goToolOnce.Do(func() { - goToolPath, goToolErr = exec.LookPath("go") - }) - return goToolPath, goToolErr + // Removed by Hugo, not used. + return "", nil } -var ( - goToolOnce sync.Once - goToolPath string - goToolErr error -) +var goTool = sync.OnceValues(func() (string, error) { + return exec.LookPath("go") +}) -// HasSrc reports whether the entire source tree is available under GOROOT. -func HasSrc() bool { +// MustHaveSource checks that the entire source tree is available under GOROOT. +// If not, it calls t.Skip with an explanation. +func MustHaveSource(t testing.TB) { switch runtime.GOOS { case "ios": - return false + t.Helper() + t.Skip("skipping test: no source tree on " + runtime.GOOS) } - return true } // HasExternalNetwork reports whether the current system can use @@ -263,41 +250,39 @@ func MustHaveExternalNetwork(t testing.TB) { // HasCGO reports whether the current system can use cgo. func HasCGO() bool { - hasCgoOnce.Do(func() { - goTool, err := GoTool() - if err != nil { - return - } - cmd := exec.Command(goTool, "env", "CGO_ENABLED") - cmd.Env = origEnv - out, err := cmd.Output() - if err != nil { - panic(fmt.Sprintf("%v: %v", cmd, out)) - } - hasCgo, err = strconv.ParseBool(string(bytes.TrimSpace(out))) - if err != nil { - panic(fmt.Sprintf("%v: non-boolean output %q", cmd, out)) - } - }) - return hasCgo + return hasCgo() } -var ( - hasCgoOnce sync.Once - hasCgo bool -) +var hasCgo = sync.OnceValue(func() bool { + goTool, err := goTool() + if err != nil { + return false + } + cmd := exec.Command(goTool, "env", "CGO_ENABLED") + cmd.Env = origEnv + out, err := cmd.Output() + if err != nil { + panic(fmt.Sprintf("%v: %v", cmd, out)) + } + ok, err := strconv.ParseBool(string(bytes.TrimSpace(out))) + if err != nil { + panic(fmt.Sprintf("%v: non-boolean output %q", cmd, out)) + } + return ok +}) // MustHaveCGO calls t.Skip if cgo is not available. func MustHaveCGO(t testing.TB) { if !HasCGO() { + t.Helper() t.Skipf("skipping test: no cgo") } } // CanInternalLink reports whether the current system can link programs with // internal linking. -// Modified by Hugo (not needed) func CanInternalLink(withCgo bool) bool { + // Removed by Hugo, not used. return false } @@ -306,6 +291,7 @@ func CanInternalLink(withCgo bool) bool { // If not, MustInternalLink calls t.Skip with an explanation. func MustInternalLink(t testing.TB, withCgo bool) { if !CanInternalLink(withCgo) { + t.Helper() if withCgo && CanInternalLink(false) { t.Skipf("skipping test: internal linking on %s/%s is not supported with cgo", runtime.GOOS, runtime.GOARCH) } @@ -316,15 +302,15 @@ func MustInternalLink(t testing.TB, withCgo bool) { // MustInternalLinkPIE checks whether the current system can link PIE binary using // internal linking. // If not, MustInternalLinkPIE calls t.Skip with an explanation. -// Modified by Hugo (not needed) func MustInternalLinkPIE(t testing.TB) { + // Removed by Hugo, not used. } // MustHaveBuildMode reports whether the current system can build programs in // the given build mode. // If not, MustHaveBuildMode calls t.Skip with an explanation. -// Modified by Hugo (not needed) func MustHaveBuildMode(t testing.TB, buildmode string) { + // Removed by Hugo, not used. } // HasSymlink reports whether the current system can use os.Symlink. @@ -338,6 +324,7 @@ func HasSymlink() bool { func MustHaveSymlink(t testing.TB) { ok, reason := hasSymlink() if !ok { + t.Helper() t.Skipf("skipping test: cannot make symlinks on %s/%s: %s", runtime.GOOS, runtime.GOARCH, reason) } } @@ -354,6 +341,7 @@ func HasLink() bool { // If not, MustHaveLink calls t.Skip with an explanation. func MustHaveLink(t testing.TB) { if !HasLink() { + t.Helper() t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) } } @@ -361,15 +349,15 @@ func MustHaveLink(t testing.TB) { var flaky = flag.Bool("flaky", false, "run known-flaky tests too") func SkipFlaky(t testing.TB, issue int) { - t.Helper() if !*flaky { + t.Helper() t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) } } func SkipFlakyNet(t testing.TB) { - t.Helper() if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { + t.Helper() t.Skip("skipping test on builder known to have frequent network failures") } } @@ -455,6 +443,6 @@ func SyscallIsNotSupported(err error) bool { // ParallelOn64Bit calls t.Parallel() unless there is a case that cannot be parallel. // This function should be used when it is necessary to avoid t.Parallel on // 32-bit machines, typically because the test uses lots of memory. -// Disabled by Hugo. func ParallelOn64Bit(t *testing.T) { + // Removed by Hugo, not used. } diff --git a/tpl/internal/go_templates/testenv/testenv_notwin.go b/tpl/internal/go_templates/testenv/testenv_notwin.go index 30e159a6e..9dddea94d 100644 --- a/tpl/internal/go_templates/testenv/testenv_notwin.go +++ b/tpl/internal/go_templates/testenv/testenv_notwin.go @@ -11,9 +11,10 @@ import ( "os" "path/filepath" "runtime" + "sync" ) -func hasSymlink() (ok bool, reason string) { +var hasSymlink = sync.OnceValues(func() (ok bool, reason string) { switch runtime.GOOS { case "plan9": return false, "" @@ -43,4 +44,4 @@ func hasSymlink() (ok bool, reason string) { } return true, "" -} +}) diff --git a/tpl/internal/go_templates/testenv/testenv_test.go b/tpl/internal/go_templates/testenv/testenv_test.go index 4aabc00a6..afe159531 100644 --- a/tpl/internal/go_templates/testenv/testenv_test.go +++ b/tpl/internal/go_templates/testenv/testenv_test.go @@ -15,6 +15,7 @@ import ( ) func TestGoToolLocation(t *testing.T) { + t.Skip("This test is not relevant for Hugo") testenv.MustHaveGoBuild(t) var exeSuffix string @@ -54,8 +55,83 @@ func TestGoToolLocation(t *testing.T) { } } -// Modified by Hugo (not needed) func TestHasGoBuild(t *testing.T) { + if !testenv.HasGoBuild() { + switch runtime.GOOS { + case "js", "wasip1": + // No exec syscall, so these shouldn't be able to 'go build'. + t.Logf("HasGoBuild is false on %s", runtime.GOOS) + return + } + + b := testenv.Builder() + if b == "" { + // We shouldn't make assumptions about what kind of sandbox or build + // environment external Go users may be running in. + t.Skipf("skipping: 'go build' unavailable") + } + + // Since we control the Go builders, we know which ones ought + // to be able to run 'go build'. Check that they can. + // + // (Note that we don't verify that any builders *can't* run 'go build'. + // If a builder starts running 'go build' tests when it shouldn't, + // we will presumably find out about it when those tests fail.) + switch runtime.GOOS { + case "ios": + if isCorelliumBuilder(b) { + // The corellium environment is self-hosting, so it should be able + // to build even though real "ios" devices can't exec. + } else { + // The usual iOS sandbox does not allow the app to start another + // process. If we add builders on stock iOS devices, they presumably + // will not be able to exec, so we may as well allow that now. + t.Logf("HasGoBuild is false on %s", b) + return + } + case "android": + panic("Removed by Hugo, should not be used") + } + + if strings.Contains(b, "-noopt") { + // The -noopt builder sets GO_GCFLAGS, which causes tests of 'go build' to + // be skipped. + t.Logf("HasGoBuild is false on %s", b) + return + } + + t.Fatalf("HasGoBuild unexpectedly false on %s", b) + } + + t.Logf("HasGoBuild is true; checking consistency with other functions") + + hasExec := false + hasExecGo := false + t.Run("MustHaveExec", func(t *testing.T) { + testenv.MustHaveExec(t) + hasExec = true + }) + t.Run("MustHaveExecPath", func(t *testing.T) { + testenv.MustHaveExecPath(t, "go") + hasExecGo = true + }) + if !hasExec { + t.Errorf(`MustHaveExec(t) skipped unexpectedly`) + } + if !hasExecGo { + t.Errorf(`MustHaveExecPath(t, "go") skipped unexpectedly`) + } + + dir := t.TempDir() + mainGo := filepath.Join(dir, "main.go") + if err := os.WriteFile(mainGo, []byte("package main\nfunc main() {}\n"), 0o644); err != nil { + t.Fatal(err) + } + cmd := testenv.Command(t, "go", "build", "-o", os.DevNull, mainGo) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v: %v\n%s", cmd, err, out) + } } func TestMustHaveExec(t *testing.T) { diff --git a/tpl/internal/go_templates/testenv/testenv_windows.go b/tpl/internal/go_templates/testenv/testenv_windows.go index 4802b1395..eed53cdfb 100644 --- a/tpl/internal/go_templates/testenv/testenv_windows.go +++ b/tpl/internal/go_templates/testenv/testenv_windows.go @@ -5,16 +5,14 @@ package testenv import ( + "errors" "os" "path/filepath" "sync" "syscall" ) -var symlinkOnce sync.Once -var winSymlinkErr error - -func initWinHasSymlink() { +var hasSymlink = sync.OnceValues(func() (bool, string) { tmpdir, err := os.MkdirTemp("", "symtest") if err != nil { panic("failed to create temp directory: " + err.Error()) @@ -22,26 +20,13 @@ func initWinHasSymlink() { defer os.RemoveAll(tmpdir) err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) - if err != nil { - err = err.(*os.LinkError).Err - switch err { - case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: - winSymlinkErr = err - } - } -} - -func hasSymlink() (ok bool, reason string) { - symlinkOnce.Do(initWinHasSymlink) - - switch winSymlinkErr { - case nil: + switch { + case err == nil: return true, "" - case syscall.EWINDOWS: + case errors.Is(err, syscall.EWINDOWS): return false, ": symlinks are not supported on your version of Windows" - case syscall.ERROR_PRIVILEGE_NOT_HELD: + case errors.Is(err, syscall.ERROR_PRIVILEGE_NOT_HELD): return false, ": you don't have enough privileges to create symlinks" } - return false, "" -} +}) diff --git a/tpl/internal/go_templates/texttemplate/doc.go b/tpl/internal/go_templates/texttemplate/doc.go index b3ffaabb1..7b63bb76a 100644 --- a/tpl/internal/go_templates/texttemplate/doc.go +++ b/tpl/internal/go_templates/texttemplate/doc.go @@ -98,7 +98,8 @@ data, defined in detail in the corresponding sections that follow. {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}} {{range pipeline}} T1 {{end}} - The value of the pipeline must be an array, slice, map, or channel. + The value of the pipeline must be an array, slice, map, iter.Seq, + iter.Seq2, integer or channel. If the value of the pipeline has length zero, nothing is output; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. If the value is a map and the @@ -106,7 +107,8 @@ data, defined in detail in the corresponding sections that follow. visited in sorted key order. {{range pipeline}} T1 {{else}} T0 {{end}} - The value of the pipeline must be an array, slice, map, or channel. + The value of the pipeline must be an array, slice, map, iter.Seq, + iter.Seq2, integer or channel. If the value of the pipeline has length zero, dot is unaffected and T0 is executed; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. @@ -162,37 +164,55 @@ An argument is a simple value, denoted by one of the following. the host machine's ints are 32 or 64 bits. - The keyword nil, representing an untyped Go nil. - The character '.' (period): + . + The result is the value of dot. - A variable name, which is a (possibly empty) alphanumeric string preceded by a dollar sign, such as + $piOver2 + or + $ + The result is the value of the variable. Variables are described below. - The name of a field of the data, which must be a struct, preceded by a period, such as + .Field + The result is the value of the field. Field invocations may be chained: + .Field1.Field2 + Fields can also be evaluated on variables, including chaining: + $x.Field1.Field2 - The name of a key of the data, which must be a map, preceded by a period, such as + .Key + The result is the map element value indexed by the key. Key invocations may be chained and combined with fields to any depth: + .Field1.Key1.Field2.Key2 + Although the key must be an alphanumeric identifier, unlike with field names they do not need to start with an upper case letter. Keys can also be evaluated on variables, including chaining: + $x.key1.key2 - The name of a niladic method of the data, preceded by a period, such as + .Method + The result is the value of invoking the method with dot as the receiver, dot.Method(). Such a method must have one return value (of any type) or two return values, the second of which is an error. @@ -200,16 +220,22 @@ An argument is a simple value, denoted by one of the following. and an error is returned to the caller as the value of Execute. Method invocations may be chained and combined with fields and keys to any depth: + .Field1.Key1.Method1.Field2.Key2.Method2 + Methods can also be evaluated on variables, including chaining: + $x.Method1.Field - The name of a niladic function, such as + fun + The result is the value of invoking the function, fun(). The return types and values behave as in methods. Functions and function names are described below. - A parenthesized instance of one the above, for grouping. The result may be accessed by a field or map key invocation. + print (.F1 arg1) (.F2 arg2) (.StructValuedMethod "arg").Field diff --git a/tpl/internal/go_templates/texttemplate/example_test.go b/tpl/internal/go_templates/texttemplate/example_test.go index 975ceea93..295a810b8 100644 --- a/tpl/internal/go_templates/texttemplate/example_test.go +++ b/tpl/internal/go_templates/texttemplate/example_test.go @@ -35,7 +35,7 @@ Josie Name, Gift string Attended bool } - recipients := []Recipient{ + var recipients = []Recipient{ {"Aunt Mildred", "bone china tea set", true}, {"Uncle John", "moleskin pants", false}, {"Cousin Rodney", "", false}, diff --git a/tpl/internal/go_templates/texttemplate/examplefiles_test.go b/tpl/internal/go_templates/texttemplate/examplefiles_test.go index 6534ee331..bc91e87f9 100644 --- a/tpl/internal/go_templates/texttemplate/examplefiles_test.go +++ b/tpl/internal/go_templates/texttemplate/examplefiles_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 +// +build go1.13 + package template_test import ( diff --git a/tpl/internal/go_templates/texttemplate/exec.go b/tpl/internal/go_templates/texttemplate/exec.go index bd8c82bd7..f710a25ec 100644 --- a/tpl/internal/go_templates/texttemplate/exec.go +++ b/tpl/internal/go_templates/texttemplate/exec.go @@ -395,6 +395,22 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { s.walk(elem, r.List) } switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if len(r.Pipe.Decl) > 1 { + s.errorf("can't use %v to iterate over more than one variable", val) + break + } + run := false + for v := range val.Seq() { + run = true + // Pass element as second value, as we do for channels. + oneIteration(reflect.Value{}, v) + } + if !run { + break + } + return case reflect.Array, reflect.Slice: if val.Len() == 0 { break @@ -434,6 +450,43 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { return case reflect.Invalid: break // An invalid value is likely a nil map, etc. and acts like an empty map. + case reflect.Func: + if val.Type().CanSeq() { + if len(r.Pipe.Decl) > 1 { + s.errorf("can't use %v iterate over more than one variable", val) + break + } + run := false + for v := range val.Seq() { + run = true + // Pass element as second value, + // as we do for channels. + oneIteration(reflect.Value{}, v) + } + if !run { + break + } + return + } + if val.Type().CanSeq2() { + run := false + for i, v := range val.Seq2() { + run = true + if len(r.Pipe.Decl) > 1 { + oneIteration(i, v) + } else { + // If there is only one range variable, + // oneIteration will use the + // second value. + oneIteration(reflect.Value{}, i) + } + } + if !run { + break + } + return + } + fallthrough default: s.errorf("range can't iterate over %v", val) } @@ -757,7 +810,7 @@ func (s *state) evalCallOld(dot, fun reflect.Value, isBuiltin bool, node parse.N return v } } - if final != missingVal { + if !final.Equal(missingVal) { // The last argument to and/or is coming from // the pipeline. We didn't short circuit on an earlier // argument, so we are going to return this one. @@ -803,7 +856,13 @@ func (s *state) evalCallOld(dot, fun reflect.Value, isBuiltin bool, node parse.N // Special case for the "call" builtin. // Insert the name of the callee function as the first argument. if isBuiltin && name == "call" { - calleeName := args[0].String() + var calleeName string + if len(args) == 0 { + // final must be present or we would have errored out above. + calleeName = final.String() + } else { + calleeName = args[0].String() + } argv = append([]reflect.Value{reflect.ValueOf(calleeName)}, argv...) fun = reflect.ValueOf(call) } diff --git a/tpl/internal/go_templates/texttemplate/exec_test.go b/tpl/internal/go_templates/texttemplate/exec_test.go index efbaa9eec..742e85605 100644 --- a/tpl/internal/go_templates/texttemplate/exec_test.go +++ b/tpl/internal/go_templates/texttemplate/exec_test.go @@ -13,6 +13,7 @@ import ( "flag" "fmt" "io" + "iter" "reflect" "strings" "sync" @@ -412,6 +413,9 @@ var execTests = []execTest{ {"Interface Call", `{{stringer .S}}`, "foozle", map[string]any{"S": bytes.NewBufferString("foozle")}, true}, {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, {"call nil", "{{call nil}}", "", tVal, false}, + {"empty call", "{{call}}", "", tVal, false}, + {"empty call after pipe valid", "{{.ErrFunc | call}}", "bla", tVal, true}, + {"empty call after pipe invalid", "{{1 | call}}", "", tVal, false}, // Erroneous function calls (check args). {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, @@ -618,6 +622,30 @@ var execTests = []execTest{ {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true}, {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true}, + {"range iter.Seq[int]", `{{range $i := .}}{{$i}}{{end}}`, "01", fVal1(2), true}, + {"i = range iter.Seq[int]", `{{$i := 0}}{{range $i = .}}{{$i}}{{end}}`, "01", fVal1(2), true}, + {"range iter.Seq[int] over two var", `{{range $i, $c := .}}{{$c}}{{end}}`, "", fVal1(2), false}, + {"i, c := range iter.Seq2[int,int]", `{{range $i, $c := .}}{{$i}}{{$c}}{{end}}`, "0112", fVal2(2), true}, + {"i, c = range iter.Seq2[int,int]", `{{$i := 0}}{{$c := 0}}{{range $i, $c = .}}{{$i}}{{$c}}{{end}}`, "0112", fVal2(2), true}, + {"i = range iter.Seq2[int,int]", `{{$i := 0}}{{range $i = .}}{{$i}}{{end}}`, "01", fVal2(2), true}, + {"i := range iter.Seq2[int,int]", `{{range $i := .}}{{$i}}{{end}}`, "01", fVal2(2), true}, + {"i,c,x range iter.Seq2[int,int]", `{{$i := 0}}{{$c := 0}}{{$x := 0}}{{range $i, $c = .}}{{$i}}{{$c}}{{end}}`, "0112", fVal2(2), true}, + {"i,x range iter.Seq[int]", `{{$i := 0}}{{$x := 0}}{{range $i = .}}{{$i}}{{end}}`, "01", fVal1(2), true}, + {"range iter.Seq[int] else", `{{range $i := .}}{{$i}}{{else}}empty{{end}}`, "empty", fVal1(0), true}, + {"range iter.Seq2[int,int] else", `{{range $i := .}}{{$i}}{{else}}empty{{end}}`, "empty", fVal2(0), true}, + {"range int8", rangeTestInt, rangeTestData[int8](), int8(5), true}, + {"range int16", rangeTestInt, rangeTestData[int16](), int16(5), true}, + {"range int32", rangeTestInt, rangeTestData[int32](), int32(5), true}, + {"range int64", rangeTestInt, rangeTestData[int64](), int64(5), true}, + {"range int", rangeTestInt, rangeTestData[int](), int(5), true}, + {"range uint8", rangeTestInt, rangeTestData[uint8](), uint8(5), true}, + {"range uint16", rangeTestInt, rangeTestData[uint16](), uint16(5), true}, + {"range uint32", rangeTestInt, rangeTestData[uint32](), uint32(5), true}, + {"range uint64", rangeTestInt, rangeTestData[uint64](), uint64(5), true}, + {"range uint", rangeTestInt, rangeTestData[uint](), uint(5), true}, + {"range uintptr", rangeTestInt, rangeTestData[uintptr](), uintptr(5), true}, + {"range uintptr(0)", `{{range $v := .}}{{print $v}}{{else}}empty{{end}}`, "empty", uintptr(0), true}, + {"range 5", `{{range $v := 5}}{{printf "%T%d" $v $v}}{{end}}`, rangeTestData[int](), nil, true}, // Cute examples. {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, @@ -722,6 +750,37 @@ var execTests = []execTest{ {"issue60801", "{{$k := 0}}{{$v := 0}}{{range $k, $v = .AI}}{{$k}}={{$v}} {{end}}", "0=3 1=4 2=5 ", tVal, true}, } +func fVal1(i int) iter.Seq[int] { + return func(yield func(int) bool) { + for v := range i { + if !yield(v) { + break + } + } + } +} + +func fVal2(i int) iter.Seq2[int, int] { + return func(yield func(int, int) bool) { + for v := range i { + if !yield(v, v+1) { + break + } + } + } +} + +const rangeTestInt = `{{range $v := .}}{{printf "%T%d" $v $v}}{{end}}` + +func rangeTestData[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr]() string { + I := T(5) + var buf strings.Builder + for i := T(0); i < I; i++ { + fmt.Fprintf(&buf, "%T%d", i, i) + } + return buf.String() +} + func zeroArgs() string { return "zeroArgs" } diff --git a/tpl/internal/go_templates/texttemplate/hugo_template.go b/tpl/internal/go_templates/texttemplate/hugo_template.go index 36962c444..d179cb8c9 100644 --- a/tpl/internal/go_templates/texttemplate/hugo_template.go +++ b/tpl/internal/go_templates/texttemplate/hugo_template.go @@ -304,14 +304,14 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node } }() } + if args != nil { args = args[1:] // Zeroth arg is function name/node; not passed to function. } - typ := fun.Type() - numFirst := len(first) + numFirst := len(first) // Added for Hugo numIn := len(args) + numFirst // Added for Hugo - if final != missingVal { + if !isMissing(final) { numIn++ } numFixed := len(args) + len(first) // Adjusted for Hugo @@ -346,7 +346,7 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node return v } } - if final != missingVal { + if !final.Equal(missingVal) { // The last argument to and/or is coming from // the pipeline. We didn't short circuit on an earlier // argument, so we are going to return this one. @@ -373,7 +373,7 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node } } // Add final value if necessary. - if final != missingVal { + if !isMissing(final) { t := typ.In(typ.NumIn() - 1) if typ.IsVariadic() { if numIn-1 < numFixed { @@ -392,7 +392,13 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node // Special case for the "call" builtin. // Insert the name of the callee function as the first argument. if isBuiltin && name == "call" { - calleeName := args[0].String() + var calleeName string + if len(args) == 0 { + // final must be present or we would have errored out above. + calleeName = final.String() + } else { + calleeName = args[0].String() + } argv = append([]reflect.Value{reflect.ValueOf(calleeName)}, argv...) fun = reflect.ValueOf(call) } diff --git a/tpl/internal/go_templates/texttemplate/link_test.go b/tpl/internal/go_templates/texttemplate/link_test.go index 63418cd90..23f6a31fa 100644 --- a/tpl/internal/go_templates/texttemplate/link_test.go +++ b/tpl/internal/go_templates/texttemplate/link_test.go @@ -2,16 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 +// +build go1.13 + package template_test import ( "bytes" + "github.com/gohugoio/hugo/tpl/internal/go_templates/testenv" "os" "os/exec" "path/filepath" "testing" - - "github.com/gohugoio/hugo/tpl/internal/go_templates/testenv" ) // Issue 36021: verify that text/template doesn't prevent the linker from removing @@ -42,7 +44,7 @@ func main() { ` td := t.TempDir() - if err := os.WriteFile(filepath.Join(td, "x.go"), []byte(prog), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(td, "x.go"), []byte(prog), 0644); err != nil { t.Fatal(err) } cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "x.exe", "x.go") diff --git a/tpl/internal/go_templates/texttemplate/multi_test.go b/tpl/internal/go_templates/texttemplate/multi_test.go index 6358383e4..6e383c131 100644 --- a/tpl/internal/go_templates/texttemplate/multi_test.go +++ b/tpl/internal/go_templates/texttemplate/multi_test.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !windows -// +build !windows +//go:build go1.13 && !windows +// +build go1.13,!windows package template @@ -11,11 +11,10 @@ package template import ( "fmt" + "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" "os" "strings" "testing" - - "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" ) const ( @@ -32,32 +31,22 @@ type multiParseTest struct { } var multiParseTests = []multiParseTest{ - { - "empty", "", noError, + {"empty", "", noError, nil, - nil, - }, - { - "one", `{{define "foo"}} FOO {{end}}`, noError, + nil}, + {"one", `{{define "foo"}} FOO {{end}}`, noError, []string{"foo"}, - []string{" FOO "}, - }, - { - "two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, + []string{" FOO "}}, + {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, []string{"foo", "bar"}, - []string{" FOO ", " BAR "}, - }, + []string{" FOO ", " BAR "}}, // errors - { - "missing end", `{{define "foo"}} FOO `, hasError, + {"missing end", `{{define "foo"}} FOO `, hasError, nil, + nil}, + {"malformed name", `{{define "foo}} FOO `, hasError, nil, - }, - { - "malformed name", `{{define "foo}} FOO `, hasError, - nil, - nil, - }, + nil}, } func TestMultiParse(t *testing.T) { @@ -443,7 +432,7 @@ func TestIssue19294(t *testing.T) { // by the contents of "stylesheet", but if the internal map associating // names with templates is built in the wrong order, the empty block // looks non-empty and this doesn't happen. - inlined := map[string]string{ + var inlined = map[string]string{ "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, "xhtml": `{{block "stylesheet" .}}{{end}}`, } diff --git a/tpl/internal/go_templates/texttemplate/parse/lex.go b/tpl/internal/go_templates/texttemplate/parse/lex.go index 70fc86b63..a00f48e65 100644 --- a/tpl/internal/go_templates/texttemplate/parse/lex.go +++ b/tpl/internal/go_templates/texttemplate/parse/lex.go @@ -352,6 +352,7 @@ func lexComment(l *lexer) stateFn { if !delim { return l.errorf("comment ends before closing delimiter") } + l.line += strings.Count(l.input[l.start:l.pos], "\n") i := l.thisItem(itemComment) if trimSpace { l.pos += trimMarkerLen diff --git a/tpl/internal/go_templates/texttemplate/parse/lex_test.go b/tpl/internal/go_templates/texttemplate/parse/lex_test.go index 11e88792e..b1ea69967 100644 --- a/tpl/internal/go_templates/texttemplate/parse/lex_test.go +++ b/tpl/internal/go_templates/texttemplate/parse/lex_test.go @@ -548,6 +548,16 @@ var lexPosTests = []lexTest{ {itemRightDelim, 11, "}}", 2}, {itemEOF, 13, "", 2}, }}, + {"longcomment", "{{/*\n*/}}\n{{undefinedFunction \"test\"}}", []item{ + {itemComment, 2, "/*\n*/", 1}, + {itemText, 9, "\n", 2}, + {itemLeftDelim, 10, "{{", 3}, + {itemIdentifier, 12, "undefinedFunction", 3}, + {itemSpace, 29, " ", 3}, + {itemString, 30, "\"test\"", 3}, + {itemRightDelim, 36, "}}", 3}, + {itemEOF, 38, "", 3}, + }}, } // The other tests don't check position, to make the test cases easier to construct. diff --git a/tpl/internal/go_templates/texttemplate/parse/parse_test.go b/tpl/internal/go_templates/texttemplate/parse/parse_test.go index 80e7f53fa..47951a9c9 100644 --- a/tpl/internal/go_templates/texttemplate/parse/parse_test.go +++ b/tpl/internal/go_templates/texttemplate/parse/parse_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 +// +build go1.13 + package parse import ( @@ -33,9 +36,9 @@ var numberTests = []numberTest{ {"7_3", true, true, true, false, 73, 73, 73, 0}, {"0b10_010_01", true, true, true, false, 73, 73, 73, 0}, {"0B10_010_01", true, true, true, false, 73, 73, 73, 0}, - {"073", true, true, true, false, 0o73, 0o73, 0o73, 0}, - {"0o73", true, true, true, false, 0o73, 0o73, 0o73, 0}, - {"0O73", true, true, true, false, 0o73, 0o73, 0o73, 0}, + {"073", true, true, true, false, 073, 073, 073, 0}, + {"0o73", true, true, true, false, 073, 073, 073, 0}, + {"0O73", true, true, true, false, 073, 073, 073, 0}, {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0}, {"0X73", true, true, true, false, 0x73, 0x73, 0x73, 0}, {"0x7_3", true, true, true, false, 0x73, 0x73, 0x73, 0}, @@ -61,7 +64,7 @@ var numberTests = []numberTest{ {"-12+0i", true, false, true, true, -12, 0, -12, -12}, {"13+0i", true, true, true, true, 13, 13, 13, 13}, // funny bases - {"0123", true, true, true, false, 0o123, 0o123, 0o123, 0}, + {"0123", true, true, true, false, 0123, 0123, 0123, 0}, {"-0x0", true, true, true, false, 0, 0, 0, 0}, {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, // character constants @@ -176,150 +179,78 @@ const ( ) var parseTests = []parseTest{ - { - "empty", "", noError, - ``, - }, - { - "comment", "{{/*\n\n\n*/}}", noError, - ``, - }, - { - "spaces", " \t\n", noError, - `" \t\n"`, - }, - { - "text", "some text", noError, - `"some text"`, - }, - { - "emptyAction", "{{}}", hasError, - `{{}}`, - }, - { - "field", "{{.X}}", noError, - `{{.X}}`, - }, - { - "simple command", "{{printf}}", noError, - `{{printf}}`, - }, - { - "$ invocation", "{{$}}", noError, - "{{$}}", - }, - { - "variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, - "{{with $x := 3}}{{$x 23}}{{end}}", - }, - { - "variable with fields", "{{$.I}}", noError, - "{{$.I}}", - }, - { - "multi-word command", "{{printf `%d` 23}}", noError, - "{{printf `%d` 23}}", - }, - { - "pipeline", "{{.X|.Y}}", noError, - `{{.X | .Y}}`, - }, - { - "pipeline with decl", "{{$x := .X|.Y}}", noError, - `{{$x := .X | .Y}}`, - }, - { - "nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError, - `{{.X (.Y .Z) (.A | .B .C) (.E)}}`, - }, - { - "field applied to parentheses", "{{(.Y .Z).Field}}", noError, - `{{(.Y .Z).Field}}`, - }, - { - "simple if", "{{if .X}}hello{{end}}", noError, - `{{if .X}}"hello"{{end}}`, - }, - { - "if with else", "{{if .X}}true{{else}}false{{end}}", noError, - `{{if .X}}"true"{{else}}"false"{{end}}`, - }, - { - "if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError, - `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`, - }, - { - "if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError, - `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`, - }, - { - "simple range", "{{range .X}}hello{{end}}", noError, - `{{range .X}}"hello"{{end}}`, - }, - { - "chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, - `{{range .X.Y.Z}}"hello"{{end}}`, - }, - { - "nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, - `{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`, - }, - { - "range with else", "{{range .X}}true{{else}}false{{end}}", noError, - `{{range .X}}"true"{{else}}"false"{{end}}`, - }, - { - "range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, - `{{range .X | .M}}"true"{{else}}"false"{{end}}`, - }, - { - "range []int", "{{range .SI}}{{.}}{{end}}", noError, - `{{range .SI}}{{.}}{{end}}`, - }, - { - "range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError, - `{{range $x := .SI}}{{.}}{{end}}`, - }, - { - "range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, - `{{range $x, $y := .SI}}{{.}}{{end}}`, - }, - { - "range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError, - `{{range .SI}}{{.}}{{break}}{{end}}`, - }, - { - "range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError, - `{{range .SI}}{{.}}{{continue}}{{end}}`, - }, - { - "constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, - `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`, - }, - { - "template", "{{template `x`}}", noError, - `{{template "x"}}`, - }, - { - "template with arg", "{{template `x` .Y}}", noError, - `{{template "x" .Y}}`, - }, - { - "with", "{{with .X}}hello{{end}}", noError, - `{{with .X}}"hello"{{end}}`, - }, - { - "with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, - `{{with .X}}"hello"{{else}}"goodbye"{{end}}`, - }, - { - "with with else with", "{{with .X}}hello{{else with .Y}}goodbye{{end}}", noError, - `{{with .X}}"hello"{{else}}{{with .Y}}"goodbye"{{end}}{{end}}`, - }, - { - "with else chain", "{{with .X}}X{{else with .Y}}Y{{else with .Z}}Z{{end}}", noError, - `{{with .X}}"X"{{else}}{{with .Y}}"Y"{{else}}{{with .Z}}"Z"{{end}}{{end}}{{end}}`, - }, + {"empty", "", noError, + ``}, + {"comment", "{{/*\n\n\n*/}}", noError, + ``}, + {"spaces", " \t\n", noError, + `" \t\n"`}, + {"text", "some text", noError, + `"some text"`}, + {"emptyAction", "{{}}", hasError, + `{{}}`}, + {"field", "{{.X}}", noError, + `{{.X}}`}, + {"simple command", "{{printf}}", noError, + `{{printf}}`}, + {"$ invocation", "{{$}}", noError, + "{{$}}"}, + {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, + "{{with $x := 3}}{{$x 23}}{{end}}"}, + {"variable with fields", "{{$.I}}", noError, + "{{$.I}}"}, + {"multi-word command", "{{printf `%d` 23}}", noError, + "{{printf `%d` 23}}"}, + {"pipeline", "{{.X|.Y}}", noError, + `{{.X | .Y}}`}, + {"pipeline with decl", "{{$x := .X|.Y}}", noError, + `{{$x := .X | .Y}}`}, + {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError, + `{{.X (.Y .Z) (.A | .B .C) (.E)}}`}, + {"field applied to parentheses", "{{(.Y .Z).Field}}", noError, + `{{(.Y .Z).Field}}`}, + {"simple if", "{{if .X}}hello{{end}}", noError, + `{{if .X}}"hello"{{end}}`}, + {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, + `{{if .X}}"true"{{else}}"false"{{end}}`}, + {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError, + `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`}, + {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError, + `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`}, + {"simple range", "{{range .X}}hello{{end}}", noError, + `{{range .X}}"hello"{{end}}`}, + {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, + `{{range .X.Y.Z}}"hello"{{end}}`}, + {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, + `{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`}, + {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, + `{{range .X}}"true"{{else}}"false"{{end}}`}, + {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, + `{{range .X | .M}}"true"{{else}}"false"{{end}}`}, + {"range []int", "{{range .SI}}{{.}}{{end}}", noError, + `{{range .SI}}{{.}}{{end}}`}, + {"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError, + `{{range $x := .SI}}{{.}}{{end}}`}, + {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, + `{{range $x, $y := .SI}}{{.}}{{end}}`}, + {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError, + `{{range .SI}}{{.}}{{break}}{{end}}`}, + {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError, + `{{range .SI}}{{.}}{{continue}}{{end}}`}, + {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, + `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, + {"template", "{{template `x`}}", noError, + `{{template "x"}}`}, + {"template with arg", "{{template `x` .Y}}", noError, + `{{template "x" .Y}}`}, + {"with", "{{with .X}}hello{{end}}", noError, + `{{with .X}}"hello"{{end}}`}, + {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, + `{{with .X}}"hello"{{else}}"goodbye"{{end}}`}, + {"with with else with", "{{with .X}}hello{{else with .Y}}goodbye{{end}}", noError, + `{{with .X}}"hello"{{else}}{{with .Y}}"goodbye"{{end}}{{end}}`}, + {"with else chain", "{{with .X}}X{{else with .Y}}Y{{else with .Z}}Z{{end}}", noError, + `{{with .X}}"X"{{else}}{{with .Y}}"Y"{{else}}{{with .Z}}"Z"{{end}}{{end}}{{end}}`}, // Trimming spaces. {"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`}, {"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`}, @@ -328,24 +259,18 @@ var parseTests = []parseTest{ {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`}, {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`}, {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`}, - { - "block definition", `{{block "foo" .}}hello{{end}}`, noError, - `{{template "foo" .}}`, - }, + {"block definition", `{{block "foo" .}}hello{{end}}`, noError, + `{{template "foo" .}}`}, {"newline in assignment", "{{ $x \n := \n 1 \n }}", noError, "{{$x := 1}}"}, {"newline in empty action", "{{\n}}", hasError, "{{\n}}"}, {"newline in pipeline", "{{\n\"x\"\n|\nprintf\n}}", noError, `{{"x" | printf}}`}, {"newline in comment", "{{/*\nhello\n*/}}", noError, ""}, {"newline in comment", "{{-\n/*\nhello\n*/\n-}}", noError, ""}, - { - "spaces around continue", "{{range .SI}}{{.}}{{ continue }}{{end}}", noError, - `{{range .SI}}{{.}}{{continue}}{{end}}`, - }, - { - "spaces around break", "{{range .SI}}{{.}}{{ break }}{{end}}", noError, - `{{range .SI}}{{.}}{{break}}{{end}}`, - }, + {"spaces around continue", "{{range .SI}}{{.}}{{ continue }}{{end}}", noError, + `{{range .SI}}{{.}}{{continue}}{{end}}`}, + {"spaces around break", "{{range .SI}}{{.}}{{ break }}{{end}}", noError, + `{{range .SI}}{{.}}{{break}}{{end}}`}, // Errors. {"unclosed action", "hello{{range", hasError, ""}, @@ -487,7 +412,7 @@ func TestKeywordsAndFuncs(t *testing.T) { { // 'break' is a defined function, don't treat it as a keyword: it should // accept an argument successfully. - funcsWithKeywordFunc := map[string]any{ + var funcsWithKeywordFunc = map[string]any{ "break": func(in any) any { return in }, } tmpl, err := New("").Parse(inp, "", "", make(map[string]*Tree), funcsWithKeywordFunc) @@ -574,168 +499,104 @@ func TestErrorContextWithTreeCopy(t *testing.T) { // All failures, and the result is a string that must appear in the error message. var errorTests = []parseTest{ // Check line numbers are accurate. - { - "unclosed1", + {"unclosed1", "line1\n{{", - hasError, `unclosed1:2: unclosed action`, - }, - { - "unclosed2", + hasError, `unclosed1:2: unclosed action`}, + {"unclosed2", "line1\n{{define `x`}}line2\n{{", - hasError, `unclosed2:3: unclosed action`, - }, - { - "unclosed3", + hasError, `unclosed2:3: unclosed action`}, + {"unclosed3", "line1\n{{\"x\"\n\"y\"\n", - hasError, `unclosed3:4: unclosed action started at unclosed3:2`, - }, - { - "unclosed4", + hasError, `unclosed3:4: unclosed action started at unclosed3:2`}, + {"unclosed4", "{{\n\n\n\n\n", - hasError, `unclosed4:6: unclosed action started at unclosed4:1`, - }, - { - "var1", + hasError, `unclosed4:6: unclosed action started at unclosed4:1`}, + {"var1", "line1\n{{\nx\n}}", - hasError, `var1:3: function "x" not defined`, - }, + hasError, `var1:3: function "x" not defined`}, // Specific errors. - { - "function", + {"function", "{{foo}}", - hasError, `function "foo" not defined`, - }, - { - "comment1", + hasError, `function "foo" not defined`}, + {"comment1", "{{/*}}", - hasError, `comment1:1: unclosed comment`, - }, - { - "comment2", + hasError, `comment1:1: unclosed comment`}, + {"comment2", "{{/*\nhello\n}}", - hasError, `comment2:1: unclosed comment`, - }, - { - "lparen", + hasError, `comment2:1: unclosed comment`}, + {"lparen", "{{.X (1 2 3}}", - hasError, `unclosed left paren`, - }, - { - "rparen", + hasError, `unclosed left paren`}, + {"rparen", "{{.X 1 2 3 ) }}", - hasError, "unexpected right paren", - }, - { - "rparen2", + hasError, "unexpected right paren"}, + {"rparen2", "{{(.X 1 2 3", - hasError, `unclosed action`, - }, - { - "space", + hasError, `unclosed action`}, + {"space", "{{`x`3}}", - hasError, `in operand`, - }, - { - "idchar", + hasError, `in operand`}, + {"idchar", "{{a#}}", - hasError, `'#'`, - }, - { - "charconst", + hasError, `'#'`}, + {"charconst", "{{'a}}", - hasError, `unterminated character constant`, - }, - { - "stringconst", + hasError, `unterminated character constant`}, + {"stringconst", `{{"a}}`, - hasError, `unterminated quoted string`, - }, - { - "rawstringconst", + hasError, `unterminated quoted string`}, + {"rawstringconst", "{{`a}}", - hasError, `unterminated raw quoted string`, - }, - { - "number", + hasError, `unterminated raw quoted string`}, + {"number", "{{0xi}}", - hasError, `number syntax`, - }, - { - "multidefine", + hasError, `number syntax`}, + {"multidefine", "{{define `a`}}a{{end}}{{define `a`}}b{{end}}", - hasError, `multiple definition of template`, - }, - { - "eof", + hasError, `multiple definition of template`}, + {"eof", "{{range .X}}", - hasError, `unexpected EOF`, - }, - { - "variable", + hasError, `unexpected EOF`}, + {"variable", // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration. "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}", - hasError, `unexpected ":="`, - }, - { - "multidecl", + hasError, `unexpected ":="`}, + {"multidecl", "{{$a,$b,$c := 23}}", - hasError, `too many declarations`, - }, - { - "undefvar", + hasError, `too many declarations`}, + {"undefvar", "{{$a}}", - hasError, `undefined variable`, - }, - { - "wrongdot", + hasError, `undefined variable`}, + {"wrongdot", "{{true.any}}", - hasError, `unexpected . after term`, - }, - { - "wrongpipeline", + hasError, `unexpected . after term`}, + {"wrongpipeline", "{{12|false}}", - hasError, `non executable command in pipeline`, - }, - { - "emptypipeline", + hasError, `non executable command in pipeline`}, + {"emptypipeline", `{{ ( ) }}`, - hasError, `missing value for parenthesized pipeline`, - }, - { - "multilinerawstring", + hasError, `missing value for parenthesized pipeline`}, + {"multilinerawstring", "{{ $v := `\n` }} {{", - hasError, `multilinerawstring:2: unclosed action`, - }, - { - "rangeundefvar", + hasError, `multilinerawstring:2: unclosed action`}, + {"rangeundefvar", "{{range $k}}{{end}}", - hasError, `undefined variable`, - }, - { - "rangeundefvars", + hasError, `undefined variable`}, + {"rangeundefvars", "{{range $k, $v}}{{end}}", - hasError, `undefined variable`, - }, - { - "rangemissingvalue1", + hasError, `undefined variable`}, + {"rangemissingvalue1", "{{range $k,}}{{end}}", - hasError, `missing value for range`, - }, - { - "rangemissingvalue2", + hasError, `missing value for range`}, + {"rangemissingvalue2", "{{range $k, $v := }}{{end}}", - hasError, `missing value for range`, - }, - { - "rangenotvariable1", + hasError, `missing value for range`}, + {"rangenotvariable1", "{{range $k, .}}{{end}}", - hasError, `range can only initialize variables`, - }, - { - "rangenotvariable2", + hasError, `range can only initialize variables`}, + {"rangenotvariable2", "{{range $k, 123 := .}}{{end}}", - hasError, `range can only initialize variables`, - }, + hasError, `range can only initialize variables`}, } func TestErrors(t *testing.T) { diff --git a/tpl/internal/go_templates/texttemplate/template.go b/tpl/internal/go_templates/texttemplate/template.go index 536932a60..0be64eee2 100644 --- a/tpl/internal/go_templates/texttemplate/template.go +++ b/tpl/internal/go_templates/texttemplate/template.go @@ -6,6 +6,7 @@ package template import ( "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" + "maps" "reflect" "sync" ) @@ -102,12 +103,8 @@ func (t *Template) Clone() (*Template, error) { } t.muFuncs.RLock() defer t.muFuncs.RUnlock() - for k, v := range t.parseFuncs { - nt.parseFuncs[k] = v - } - for k, v := range t.execFuncs { - nt.execFuncs[k] = v - } + maps.Copy(nt.parseFuncs, t.parseFuncs) + maps.Copy(nt.execFuncs, t.execFuncs) return nt, nil } From ee438606ddec5362a0fa2685ec5f5c48a0154c40 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 12 Feb 2025 13:30:21 -0800 Subject: [PATCH 149/374] commands: Use punctuation consistently in short description --- commands/gen.go | 4 ++-- commands/mod.go | 28 +++++++++++++-------------- commands/release.go | 2 +- commands/server.go | 2 +- testscripts/commands/hugo__errors.txt | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/commands/gen.go b/commands/gen.go index 3cb05927d..5e49d9e7d 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -149,7 +149,7 @@ url: %s return &simpleCommand{ name: "doc", - short: "Generate Markdown documentation for the Hugo CLI.", + short: "Generate Markdown documentation for the Hugo CLI", long: `Generate Markdown documentation for the Hugo CLI. This command is, mostly, used to create up-to-date documentation of Hugo's command-line interface for https://gohugo.io/. @@ -201,7 +201,7 @@ url: %s newDocsHelper := func() simplecobra.Commander { return &simpleCommand{ name: "docshelper", - short: "Generate some data files for the Hugo docs.", + short: "Generate some data files for the Hugo docs", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { r.Println("Generate docs data to", docsHelperTarget) diff --git a/commands/mod.go b/commands/mod.go index dda7840cc..58155f9be 100644 --- a/commands/mod.go +++ b/commands/mod.go @@ -44,16 +44,16 @@ func newModCommands() *modCommands { npmCommand := &simpleCommand{ name: "npm", - short: "Various npm helpers.", + short: "Various npm helpers", long: `Various npm (Node package manager) helpers.`, commands: []simplecobra.Commander{ &simpleCommand{ name: "pack", - short: "Experimental: Prepares and writes a composite package.json file for your project.", + short: "Experimental: Prepares and writes a composite package.json file for your project", long: `Prepares and writes a composite package.json file for your project. On first run it creates a "package.hugo.json" in the project root if not already there. This file will be used as a template file -with the base dependency set. +with the base dependency set. This set will be merged with all "package.hugo.json" files found in the dependency tree, picking the version closest to the project. @@ -80,12 +80,12 @@ so this may/will change in future versions of Hugo. commands: []simplecobra.Commander{ &simpleCommand{ name: "init", - short: "Initialize this project as a Hugo Module.", + short: "Initialize this project as a Hugo Module", long: `Initialize this project as a Hugo Module. It will try to guess the module path, but you may help by passing it as an argument, e.g: - + hugo mod init github.com/gohugoio/testshortcodes - + Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module inside a subfolder on GitHub, as one example. `, @@ -111,7 +111,7 @@ so this may/will change in future versions of Hugo. }, &simpleCommand{ name: "verify", - short: "Verify dependencies.", + short: "Verify dependencies", long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`, withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions @@ -129,7 +129,7 @@ so this may/will change in future versions of Hugo. }, &simpleCommand{ name: "graph", - short: "Print a module dependency graph.", + short: "Print a module dependency graph", long: `Print a module dependency graph with information about module status (disabled, vendored). Note that for vendored modules, that is the version listed and not the one from go.mod. `, @@ -149,7 +149,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "clean", - short: "Delete the Hugo Module cache for the current project.", + short: "Delete the Hugo Module cache for the current project", long: `Delete the Hugo Module cache for the current project.`, withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions @@ -175,7 +175,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "tidy", - short: "Remove unused entries in go.mod and go.sum.", + short: "Remove unused entries in go.mod and go.sum", withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions applyLocalFlagsBuildConfig(cmd, r) @@ -190,7 +190,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "vendor", - short: "Vendor all module dependencies into the _vendor directory.", + short: "Vendor all module dependencies into the _vendor directory", long: `Vendor all module dependencies into the _vendor directory. If a module is vendored, that is where Hugo will look for it's dependencies. `, @@ -209,16 +209,16 @@ Note that for vendored modules, that is the version listed and not the one from &simpleCommand{ name: "get", - short: "Resolves dependencies in your current Hugo Project.", + short: "Resolves dependencies in your current Hugo project", long: ` -Resolves dependencies in your current Hugo Project. +Resolves dependencies in your current Hugo project. Some examples: Install the latest version possible for a given module: hugo mod get github.com/gohugoio/testshortcodes - + Install a specific version: hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 diff --git a/commands/release.go b/commands/release.go index b05226d35..059f04eb8 100644 --- a/commands/release.go +++ b/commands/release.go @@ -32,7 +32,7 @@ func newReleaseCommand() simplecobra.Commander { return &simpleCommand{ name: "release", - short: "Release a new version of Hugo.", + short: "Release a new version of Hugo", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { rel, err := releaser.New(skipPush, try, step) if err != nil { diff --git a/commands/server.go b/commands/server.go index 828c78a3a..d42ce6d29 100644 --- a/commands/server.go +++ b/commands/server.go @@ -119,7 +119,7 @@ func newServerCommand() *serverCommand { commands: []simplecobra.Commander{ &simpleCommand{ name: "trust", - short: "Install the local CA in the system trust store.", + short: "Install the local CA in the system trust store", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { action := "-install" if uninstall { diff --git a/testscripts/commands/hugo__errors.txt b/testscripts/commands/hugo__errors.txt index 2400ce69b..975d11616 100644 --- a/testscripts/commands/hugo__errors.txt +++ b/testscripts/commands/hugo__errors.txt @@ -2,7 +2,7 @@ # The hugo mod get command handles flags a little special, but the -h flag should print the help. hugo mod get -h -stdout 'Resolves dependencies in your current Hugo Project' +stdout 'Resolves dependencies in your current Hugo project' # Invalid flag. Should print an error message to stderr and the help to stdout. ! hugo --asdf @@ -15,4 +15,4 @@ stdout 'hugo is the main command' stderr 'failed to load config' -- hugo.toml -- -invalid: toml \ No newline at end of file +invalid: toml From 34dcac53bfd1d573b2c641801c84515d94739252 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 12 Feb 2025 22:02:49 -0800 Subject: [PATCH 150/374] config: Remove unused code --- config/defaultConfigProvider.go | 37 --------------------------------- 1 file changed, 37 deletions(-) diff --git a/config/defaultConfigProvider.go b/config/defaultConfigProvider.go index f840c6cb0..bb7c47412 100644 --- a/config/defaultConfigProvider.go +++ b/config/defaultConfigProvider.go @@ -15,7 +15,6 @@ package config import ( "fmt" - "sort" "strings" "sync" @@ -26,42 +25,6 @@ import ( "github.com/gohugoio/hugo/common/maps" ) -var ( - - // ConfigRootKeysSet contains all of the config map root keys. - ConfigRootKeysSet = map[string]bool{ - "build": true, - "caches": true, - "cascade": true, - "frontmatter": true, - "languages": true, - "imaging": true, - "markup": true, - "mediatypes": true, - "menus": true, - "minify": true, - "module": true, - "outputformats": true, - "params": true, - "permalinks": true, - "related": true, - "sitemap": true, - "privacy": true, - "security": true, - "taxonomies": true, - } - - // ConfigRootKeys is a sorted version of ConfigRootKeysSet. - ConfigRootKeys []string -) - -func init() { - for k := range ConfigRootKeysSet { - ConfigRootKeys = append(ConfigRootKeys, k) - } - sort.Strings(ConfigRootKeys) -} - // New creates a Provider backed by an empty maps.Params. func New() Provider { return &defaultConfigProvider{ From c054e18827aee0f2d8b90ae1ece093f4321478a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:08:31 +0000 Subject: [PATCH 151/374] build(deps): bump github.com/bep/imagemeta from 0.8.3 to 0.8.4 Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.8.3 to 0.8.4. - [Release notes](https://github.com/bep/imagemeta/releases) - [Commits](https://github.com/bep/imagemeta/compare/v0.8.3...v0.8.4) --- updated-dependencies: - dependency-name: github.com/bep/imagemeta dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f375cbe1a..5b8f19cdf 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/bep/golibsass v1.2.0 github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 - github.com/bep/imagemeta v0.8.3 + github.com/bep/imagemeta v0.8.4 github.com/bep/lazycache v0.7.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 diff --git a/go.sum b/go.sum index 107ca0b54..a88d39027 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY= github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI= github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o= github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q= -github.com/bep/imagemeta v0.8.3 h1:68XqpYXjWW9mFjdGurutDmAKBJa9y2aknEBHwY/+3zw= -github.com/bep/imagemeta v0.8.3/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s= +github.com/bep/imagemeta v0.8.4 h1:hA2liDZ4a3BINdsg4dD7kyR3MXNqw8Z00U3znCvtH3M= +github.com/bep/imagemeta v0.8.4/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s= github.com/bep/lazycache v0.7.0 h1:VM257SkkjcR9z55eslXTkUIX8QMNKoqQRNKV/4xIkCY= github.com/bep/lazycache v0.7.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= From a024bc7d76fcc5e49e8210f9b0896db9ef21861a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 13 Feb 2025 10:40:34 +0100 Subject: [PATCH 152/374] Squashed 'docs/' changes from 73a01565c..d1a251933 d1a251933 theme: Optimize for printing d906488d5 theme: Add border to highlighted code blocks within list items 9630c38cb theme: Display short command on commands list 7f81dd997 content: Regen commands markup a84008e99 content: Cleanup primary list page 0874b03e9 content: Publish QR code examples to the images/qr directory ef1ce3498 Restore static/img 489a68458 content: Restore KeyCDN screen captures 7d6e6184b theme: Move search button in navbar to the right on smaller screens b03b96082 theme: Misc style adjustments ecad97821 theme: Misc fixes cf5e07b0f content: Restore example data and images c7265041f content: Reformat css.TailwindCSS setup steps 3226e668f theme: Make the search button more compact on mobile 1f6614ee8 content: Update css.TailwindCSS example 49a2e7d71 theme: Improve search output 7e6b81ffa misc: Create issue template 826740223 theme: Misc search improvements 75f32b2f6 theme: Remove the search alert fd3de4ac2 config: Enable link render hook warnings 769e387cd Adjust logic to mark current section for Algolia ba73ce646 Adjust markup to make it easier to match in Algolia 5accec5f1 Misc adjustments eb5842566 Some content adjustments bdf97b7b4 Add new theme b19d68ee5 Remove old theme a04e96e55 Rewrite the css.TailwindCSS page 1c46f1864 Update deprecation notes aedcb444c Fix typo c3290b876 Changes related to release of v0.144.0 a0012fcce netlify: Hugo 0.143.1 9e4d73e87 Include sections in quick-reference page collection 4591058f5 Miscellaneous edits 612b8528f glossary: More edits 1f0c54e60 Fit typo 6f14084c1 Move glossary to quick reference section 2d94905be Improve branch bundle admonition 2ea56bef3 Fix typos found with codespell 71c5fe951 Close shortcode e95d06592 Document the responseHeaders option for resources.GetRemote da7c12aaa Deprecate gist shortcode c2d3e2c25 netlify: Bump to 0.143.0 367d3a7ab Improve new-in shortcode 1a7413a16 Clean up shortcode documentation d847892aa Improve glossary 6a7fd42ff Update pages describing Store methods and functions fa7643d1b Revert "Improve link render hook performance" fe4c86ec4 Reformat MathJax example cea44922a Update MathJax example to use safe mode 0b6d0292c Update KaTeX example to use latest version fd4508645 Improve link render hook performance 23aeb5bd0 Update purgecss example f7ef83e56 Refer each Hugo Pipe page to the corresponding function 81b91ef4e Remove related pages from pages for deprecated functions and methods 53c7627d5 Remove body from deprecated functions and methods d9cf034c8 Consolidate css.ToCSS information b7ed108f9 Improve js.Build example and presentation of options 86a4a5088 Improve babel example and description of options d1874c5f5 Consistently use "edition" when referring to standard, extended, etc. 92f03a350 Revise description of privacy settings 152a92d80 Update Last.md 82a2365bd Add print-only QR code example d22cadc25 Update mathematics.md b8160af03 Delete page self-reference in page/type.md ce24fe4e0 Update param.md 51cb92180 Improve description of output format template selection 78bcf358e Update Tailwind CSS npm package version to 4.0 2ca9da4af Include winget command to uninstall git-subtree-dir: docs git-subtree-split: d1a2519330f3647447053c89c49d6098af29519f --- .codespellrc | 13 + .cspell.json | 5 + .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/default.md | 6 + .github/workflows/spellcheck.yml | 5 + .gitignore | 15 +- LICENSE.md | 202 +- README.md | 9 + .../gohugoioTheme/assets/css/_algolia.css | 11 - .../gohugoioTheme/assets/css/_animation.css | 21 - .../gohugoioTheme/assets/css/_carousel.css | 25 - .../gohugoioTheme/assets/css/_code.css | 89 - .../assets/css/_color-scheme.css | 38 - .../gohugoioTheme/assets/css/_columns.css | 11 - .../assets/css/_content-tables.css | 28 - .../gohugoioTheme/assets/css/_content.css | 53 - .../assets/css/_definition-lists.css | 9 - .../assets/css/_documentation-styles.css | 54 - .../gohugoioTheme/assets/css/_fluid-type.css | 10 - .../gohugoioTheme/assets/css/_font-family.css | 74 - .../gohugoioTheme/assets/css/_header-link.css | 15 - .../css/_hugo-internal-template-styling.css | 52 - .../gohugoioTheme/assets/css/_no-js.css | 7 - .../gohugoioTheme/assets/css/_print.css | 7 - .../assets/css/_right-sidebar.css | 11 - .../gohugoioTheme/assets/css/_shame.css | 25 - .../assets/css/_social-icons.css | 52 - .../assets/css/_stickyheader.css | 15 - .../gohugoioTheme/assets/css/_svg.css | 1 - .../gohugoioTheme/assets/css/_tabs.css | 34 - .../gohugoioTheme/assets/css/_tachyons.css | 94 - .../gohugoioTheme/assets/css/_variables.css | 16 - .../gohugoioTheme/assets/css/main.css | 39 - .../assets/images/sponsors/linode-logo.svg | 1 - .../gohugoio/gohugoioTheme/assets/index.js | 10 - .../gohugoioTheme/assets/js/clipboardjs.js | 30 - .../gohugoioTheme/assets/js/docsearch.js | 8 - .../gohugoioTheme/assets/js/filesaver.js | 0 .../gohugoioTheme/assets/js/lazysizes.js | 3 - .../gohugoio/gohugoioTheme/assets/js/main.js | 22 - .../gohugoioTheme/assets/js/menutoggle.js | 31 - .../gohugoio/gohugoioTheme/assets/js/nojs.js | 1 - .../gohugoioTheme/assets/js/scrolldir.js | 1 - .../gohugoioTheme/assets/js/smoothscroll.js | 80 - .../gohugoio/gohugoioTheme/assets/js/tabs.js | 43 - .../gohugoioTheme/assets/output/css/app.css | 5349 ----------------- .../gohugoioTheme/assets/output/js/app.js | 17 - .../gohugoio/gohugoioTheme/config.toml | 4 - .../gohugoio/gohugoioTheme/layouts/404.html | 10 - .../_markup/render-codeblock-mermaid.html | 4 - .../_default/_markup/render-heading.html | 5 - .../layouts/_default/_markup/render-link.html | 245 - .../layouts/_default/baseof.html | 122 - .../layouts/_default/documentation-home.html | 4 - .../gohugoioTheme/layouts/_default/list.html | 5 - .../gohugoioTheme/layouts/_default/page.html | 48 - .../layouts/_default/single.html | 29 - .../layouts/_default/taxonomy.html | 4 - .../gohugoioTheme/layouts/_default/terms.html | 48 - .../gohugoio/gohugoioTheme/layouts/index.html | 27 - .../layouts/maintenance/list.html | 78 - .../gohugoioTheme/layouts/news/list.html | 64 - .../gohugoioTheme/layouts/news/list.xml | 75 - .../gohugoioTheme/layouts/news/single.html | 85 - .../partials/boxes-section-summaries.html | 46 - .../layouts/partials/boxes-small-news.html | 1 - .../components/author-github-data-card.html | 43 - .../components/author-github-data.html | 36 - .../partials/docs/functions-return-type.html | 6 - .../layouts/partials/docs/page-meta-data.html | 3 - .../layouts/partials/entry-summary.html | 13 - .../layouts/partials/head-additions.html | 1 - .../gohugoioTheme/layouts/partials/hero.html | 18 - .../home-page-sections/features-icons.html | 34 - .../home-page-sections/features-single.html | 32 - .../home-page-sections/installation.html | 38 - .../open-source-involvement.html | 59 - .../partials/home-page-sections/showcase.html | 44 - .../partials/home-page-sections/sponsors.html | 60 - .../partials/home-page-sections/tweets.html | 27 - .../partials/hooks/after-body-start.html | 1 - .../partials/hooks/before-body-end.html | 7 - .../layouts/partials/icon-link.html | 1 - .../partials/maintenance-pages-table.html | 24 - .../gohugoioTheme/layouts/partials/math.html | 9 - .../partials/nav-links-docs-mobile.html | 11 - .../layouts/partials/nav-links-docs.html | 23 - .../partials/nav-links-global-mobile.html | 11 - .../layouts/partials/nav-links.html | 37 - .../layouts/partials/nav-mobile.html | 12 - .../layouts/partials/nav-top.html | 16 - .../opengraph/get-featured-image.html | 24 - .../layouts/partials/opengraph/opengraph.html | 42 - .../partials/opengraph/twitter_cards.html | 22 - .../layouts/partials/page-edit.html | 3 - .../layouts/partials/page-header.html | 20 - .../layouts/partials/pagelayout.html | 36 - ...ious-next-links-in-section-with-title.html | 14 - .../previous-next-links-in-section.html | 16 - .../layouts/partials/previous-next-links.html | 25 - .../layouts/partials/related.html | 22 - .../layouts/partials/right-sidebar.html | 29 - .../layouts/partials/site-footer.html | 48 - .../layouts/partials/site-manifest.html | 7 - .../layouts/partials/site-nav.html | 38 - .../layouts/partials/site-scripts.html | 17 - .../layouts/partials/site-search.html | 6 - .../layouts/partials/social-follow.html | 15 - .../layouts/partials/summary.html | 13 - .../partials/svg/Twitter_Logo_Blue.svg | 1 - .../layouts/partials/svg/apple.svg | 1 - .../layouts/partials/svg/clipboard.svg | 3 - .../layouts/partials/svg/clippy.svg | 3 - .../layouts/partials/svg/cloud.svg | 13 - .../layouts/partials/svg/content.svg | 18 - .../layouts/partials/svg/design.svg | 5 - .../layouts/partials/svg/exclamation.svg | 0 .../layouts/partials/svg/facebook.svg | 1 - .../layouts/partials/svg/focus.svg | 11 - .../layouts/partials/svg/freebsd.svg | 3 - .../layouts/partials/svg/functions.svg | 14 - .../layouts/partials/svg/github-corner.svg | 1 - .../layouts/partials/svg/github-squared.svg | 3 - .../layouts/partials/svg/gitter.svg | 57 - .../layouts/partials/svg/gme.svg | 1 - .../layouts/partials/svg/godoc-icon.html | 35 - .../layouts/partials/svg/gopher-2.svg | 27 - .../layouts/partials/svg/gopher-front.svg | 33 - .../layouts/partials/svg/gopher-homepage.svg | 58 - .../layouts/partials/svg/gopher-side_path.svg | 42 - .../layouts/partials/svg/gopher-small.svg | 50 - .../layouts/partials/svg/gopher.svg | 65 - .../layouts/partials/svg/hugo-h-only.svg | 9 - .../layouts/partials/svg/hugo.svg | 10 - .../partials/svg/ic_arrow_drop_down.svg | 4 - .../layouts/partials/svg/ic_arrow_drop_up.svg | 4 - .../svg/ic_chevron_left_black_24px.svg | 4 - .../svg/ic_chevron_right_black_24px.svg | 4 - .../layouts/partials/svg/idea.svg | 61 - .../layouts/partials/svg/instagram.svg | 1 - .../layouts/partials/svg/javascript.svg | 24 - .../layouts/partials/svg/json.svg | 7 - .../layouts/partials/svg/link-ext.svg | 3 - .../layouts/partials/svg/link-permalink.svg | 1 - .../gohugoioTheme/layouts/partials/svg/md.svg | 4 - .../layouts/partials/svg/mdsolid.svg | 7 - .../layouts/partials/svg/newlogo.svg | 35 - .../layouts/partials/svg/sass.svg | 3 - .../layouts/partials/svg/search.svg | 1 - .../layouts/partials/svg/twitter.svg | 1 - .../layouts/partials/svg/website.svg | 5 - .../layouts/partials/svg/windows.svg | 1 - .../layouts/partials/svg/yaml.svg | 1 - .../gohugoioTheme/layouts/partials/tags.html | 37 - .../gohugoio/gohugoioTheme/layouts/robots.txt | 8 - .../layouts/shortcodes/articlelist.html | 18 - .../layouts/shortcodes/chroma-lexers.html | 6 - .../layouts/shortcodes/code.html | 35 - .../layouts/shortcodes/funcsig.html | 4 - .../layouts/shortcodes/gomodules-info.html | 18 - .../layouts/shortcodes/new-in.html | 34 - .../layouts/shortcodes/note.html | 7 - .../layouts/shortcodes/readfile.html | 29 - .../layouts/shortcodes/todo.html | 1 - .../gohugoioTheme/layouts/showcase/list.html | 46 - .../layouts/showcase/single.html | 106 - .../layouts/template-func/page.html | 55 - .../gohugoio/gohugoioTheme/package.json | 34 - .../gohugoioTheme/static/apple-touch-icon.png | Bin 6238 -> 0 bytes .../gohugoioTheme/static/dist/app.bundle.js | 22 - .../gohugoioTheme/static/dist/main.css | 1 - .../gohugoio/gohugoioTheme/static/favicon.ico | Bin 15086 -> 0 bytes .../static/fonts/muli-latin-200.woff | Bin 20892 -> 0 bytes .../static/fonts/muli-latin-200.woff2 | Bin 16936 -> 0 bytes .../static/fonts/muli-latin-200italic.woff | Bin 21496 -> 0 bytes .../static/fonts/muli-latin-200italic.woff2 | Bin 17320 -> 0 bytes .../static/fonts/muli-latin-300.woff | Bin 20932 -> 0 bytes .../static/fonts/muli-latin-300.woff2 | Bin 16872 -> 0 bytes .../static/fonts/muli-latin-300italic.woff | Bin 21520 -> 0 bytes .../static/fonts/muli-latin-300italic.woff2 | Bin 17332 -> 0 bytes .../static/fonts/muli-latin-400.woff | Bin 21240 -> 0 bytes .../static/fonts/muli-latin-400.woff2 | Bin 17172 -> 0 bytes .../static/fonts/muli-latin-400italic.woff | Bin 21964 -> 0 bytes .../static/fonts/muli-latin-400italic.woff2 | Bin 17732 -> 0 bytes .../static/fonts/muli-latin-600.woff | Bin 21208 -> 0 bytes .../static/fonts/muli-latin-600.woff2 | Bin 17080 -> 0 bytes .../static/fonts/muli-latin-600italic.woff | Bin 21924 -> 0 bytes .../static/fonts/muli-latin-600italic.woff2 | Bin 17776 -> 0 bytes .../static/fonts/muli-latin-700.woff | Bin 21220 -> 0 bytes .../static/fonts/muli-latin-700.woff2 | Bin 17128 -> 0 bytes .../static/fonts/muli-latin-700italic.woff | Bin 21960 -> 0 bytes .../static/fonts/muli-latin-700italic.woff2 | Bin 17756 -> 0 bytes .../static/fonts/muli-latin-800.woff | Bin 21244 -> 0 bytes .../static/fonts/muli-latin-800.woff2 | Bin 17140 -> 0 bytes .../static/fonts/muli-latin-800italic.woff | Bin 21872 -> 0 bytes .../static/fonts/muli-latin-800italic.woff2 | Bin 17644 -> 0 bytes .../static/fonts/muli-latin-900.woff | Bin 21676 -> 0 bytes .../static/fonts/muli-latin-900.woff2 | Bin 17436 -> 0 bytes .../static/fonts/muli-latin-900italic.woff | Bin 22220 -> 0 bytes .../static/fonts/muli-latin-900italic.woff2 | Bin 17948 -> 0 bytes .../gohugoioTheme/static/images/Github.svg | 1 - .../static/images/gohugoio-card.png | Bin 218051 -> 0 bytes .../images/home-page-templating-example.png | Bin 88131 -> 0 bytes .../homepage-screenshot-hugo-themes.jpg | Bin 32994 -> 0 bytes ...emes_not-optimized-according-to-google.jpg | Bin 88453 -> 0 bytes .../static/images/icon-built-in-templates.svg | 14 - .../static/images/icon-content-management.svg | 11 - .../gohugoioTheme/static/images/icon-fast.svg | 4 - .../static/images/icon-h/hugo-h-1.svg | 11 - .../static/images/icon-h/hugo-h-2.svg | 62 - .../static/images/icon-h/hugo-h-3.svg | 62 - .../static/images/icon-h/hugo-h-4.svg | 62 - .../static/images/icon-multilingual.svg | 12 - .../static/images/icon-multilingual2.svg | 4 - .../static/images/icon-search.png | Bin 337 -> 0 bytes .../static/images/icon-shortcodes.svg | 28 - .../static/images/netlify-dark.svg | 1 - .../static/images/site-hierarchy.svg | 634 -- .../gohugoio/gohugoioTheme/theme.toml | 15 - _vendor/modules.txt | 1 - archetypes/glossary.md | 15 +- assets/css/components/all.css | 7 + .../css/components/chroma.css | 30 +- assets/css/components/chroma_dark.css | 85 + assets/css/components/content.css | 48 + assets/css/components/fonts.css | 15 + assets/css/components/helpers.css | 19 + assets/css/components/highlight.css | 11 + assets/css/components/shortcodes.css | 4 + assets/css/components/tableofcontents.css | 14 + assets/css/components/view-transitions.css | 22 + assets/css/styles.css | 123 + assets/images/hugo-github-screenshot.png | Bin 0 -> 243609 bytes .../sponsors/Route4MeLogoBlueOnWhite.svg | 0 .../images/sponsors/bep-consulting.svg | 0 .../images/sponsors/butter-dark.svg | 0 .../images/sponsors/butter-light.svg | 0 .../images/sponsors/cloudcannon-blue.svg | 0 .../images/sponsors/cloudcannon-white.svg | 0 .../images/sponsors/esolia-logo.svg | 0 .../images/sponsors/goland.svg | 2 +- .../images/sponsors/graitykit-dark.svg | 0 assets/images/sponsors/linode-logo.svg | 1 + .../linode-logo_standard_light_medium.png | Bin .../images/sponsors/your-company-dark.svg | 0 .../images/sponsors/your-company.svg | 0 assets/js/alpinejs/data/explorer.js | 123 + assets/js/alpinejs/data/index.js | 3 + assets/js/alpinejs/data/navbar.js | 10 + assets/js/alpinejs/data/search.js | 109 + assets/js/alpinejs/data/toc.js | 71 + assets/js/alpinejs/magics/helpers.js | 36 + assets/js/alpinejs/magics/index.js | 1 + assets/js/alpinejs/stores/index.js | 1 + assets/js/alpinejs/stores/nav.js | 94 + assets/js/body-start.js | 6 + assets/js/head-early.js | 16 + assets/js/helpers/bridgeTurboAndAlpine.js | 67 + assets/js/helpers/helpers.js | 17 + assets/js/helpers/index.js | 2 + assets/js/main.js | 89 + assets/js/turbo.js | 1 + assets/jsconfig.json | 10 + .../opengraph/gohugoio-card-base-1.png | Bin .../opengraph/mulish-black.ttf | Bin config/_default/languages.toml | 4 - config/_default/markup.toml | 40 - config/_default/menus/menus.en.toml | 158 - config/_default/params.toml | 28 - config/_default/security.toml | 9 - config/development/params.toml | 1 - config/production/config.toml | 6 - config/production/params.toml | 2 - content/LICENSE.md | 201 + content/en/_common/_index.md | 13 + content/en/_common/scratch-pad-scope.md | 21 + content/en/_common/store-methods.md | 86 + content/en/about/_index.md | 4 +- content/en/about/features.md | 56 +- content/en/about/privacy.md | 120 +- content/en/commands/_index.md | 12 + content/en/commands/hugo_gen.md | 2 +- content/en/commands/hugo_gen_doc.md | 2 +- content/en/commands/hugo_mod.md | 16 +- content/en/commands/hugo_mod_clean.md | 2 +- content/en/commands/hugo_mod_get.md | 6 +- content/en/commands/hugo_mod_graph.md | 2 +- content/en/commands/hugo_mod_init.md | 6 +- content/en/commands/hugo_mod_npm.md | 4 +- content/en/commands/hugo_mod_npm_pack.md | 6 +- content/en/commands/hugo_mod_tidy.md | 2 +- content/en/commands/hugo_mod_vendor.md | 2 +- content/en/commands/hugo_mod_verify.md | 2 +- content/en/commands/hugo_server.md | 2 +- content/en/commands/hugo_server_trust.md | 2 +- content/en/content-management/_index.md | 2 +- content/en/content-management/archetypes.md | 4 +- .../en/content-management/content-adapters.md | 2 +- .../en/content-management/cross-references.md | 2 +- content/en/content-management/formats.md | 2 +- content/en/content-management/front-matter.md | 4 +- .../image-processing/index.md | 4 +- content/en/content-management/mathematics.md | 31 +- content/en/content-management/multilingual.md | 2 +- content/en/content-management/page-bundles.md | 10 +- .../en/content-management/page-resources.md | 4 +- content/en/content-management/sections.md | 4 +- content/en/content-management/shortcodes.md | 242 +- content/en/content-management/urls.md | 4 +- content/en/contribute/_index.md | 4 +- content/en/contribute/development.md | 2 +- content/en/contribute/documentation.md | 29 +- content/en/documentation.md | 4 +- .../functions/_common/highlighting-options.md | 2 +- content/en/functions/_index.md | 4 +- .../en/functions/collections/Dictionary.md | 2 +- content/en/functions/collections/Last.md | 2 + .../en/functions/collections/NewScratch.md | 21 +- content/en/functions/collections/Where.md | 4 +- content/en/functions/crypto/FNV32a.md | 13 +- content/en/functions/css/PostCSS.md | 21 +- content/en/functions/css/Sass.md | 36 +- content/en/functions/css/TailwindCSS.md | 149 +- content/en/functions/data/GetCSV.md | 8 +- content/en/functions/data/GetJSON.md | 8 +- content/en/functions/debug/Timer.md | 2 +- content/en/functions/fmt/Warnidf.md | 2 +- content/en/functions/go-template/try.md | 2 +- content/en/functions/hugo/IsDevelopment.md | 2 +- content/en/functions/hugo/IsExtended.md | 2 +- content/en/functions/hugo/IsMultihost.md | 2 +- content/en/functions/hugo/IsMultilingual.md | 2 +- content/en/functions/hugo/IsServer.md | 2 +- content/en/functions/hugo/Store.md | 24 +- content/en/functions/images/AutoOrient.md | 2 +- content/en/functions/images/Dither.md | 2 +- content/en/functions/images/Mask.md | 2 +- content/en/functions/images/Opacity.md | 2 +- content/en/functions/images/Padding.md | 2 +- content/en/functions/images/Process.md | 2 +- content/en/functions/images/QR.md | 41 +- content/en/functions/images/Text.md | 2 +- content/en/functions/js/Babel.md | 63 +- content/en/functions/js/Batch.md | 2 +- content/en/functions/js/Build.md | 37 +- content/en/functions/js/_common/options.md | 75 +- content/en/functions/math/Acos.md | 2 +- content/en/functions/math/Asin.md | 2 +- content/en/functions/math/Atan.md | 2 +- content/en/functions/math/Atan2.md | 2 +- content/en/functions/math/Cos.md | 2 +- content/en/functions/math/Pi.md | 2 +- content/en/functions/math/Product.md | 2 +- content/en/functions/math/Rand.md | 2 +- content/en/functions/math/Sin.md | 2 +- content/en/functions/math/Sum.md | 2 +- content/en/functions/math/Tan.md | 2 +- content/en/functions/math/ToDegrees.md | 2 +- content/en/functions/math/ToRadians.md | 2 +- content/en/functions/resources/Babel.md | 81 +- content/en/functions/resources/Concat.md | 2 +- content/en/functions/resources/GetRemote.md | 104 +- content/en/functions/resources/PostCSS.md | 122 +- content/en/functions/resources/PostProcess.md | 93 +- content/en/functions/resources/ToCSS.md | 218 +- content/en/functions/strings/Diff/index.md | 2 +- content/en/functions/strings/TrimSpace.md | 2 +- content/en/functions/templates/Defer.md | 2 +- content/en/functions/transform/ToMath.md | 2 +- content/en/functions/transform/Unmarshal.md | 2 +- content/en/functions/transform/XMLEscape.md | 2 +- content/en/getting-started/_index.md | 4 +- .../en/getting-started/configuration-build.md | 16 +- .../getting-started/configuration-markup.md | 10 +- content/en/getting-started/configuration.md | 30 +- .../external-learning-resources/index.md | 1 + content/en/getting-started/glossary/action.md | 5 - .../en/getting-started/glossary/archetype.md | 5 - .../en/getting-started/glossary/argument.md | 5 - content/en/getting-started/glossary/array.md | 5 - .../en/getting-started/glossary/boolean.md | 5 - .../getting-started/glossary/branch-bundle.md | 5 - content/en/getting-started/glossary/build.md | 5 - content/en/getting-started/glossary/cache.md | 5 - content/en/getting-started/glossary/chain.md | 5 - content/en/getting-started/glossary/cjk.md | 5 - content/en/getting-started/glossary/cli.md | 5 - .../en/getting-started/glossary/collection.md | 5 - .../glossary/content-adapter.md | 5 - .../glossary/content-format.md | 5 - .../getting-started/glossary/content-type.md | 5 - .../getting-started/glossary/content-view.md | 5 - .../en/getting-started/glossary/context.md | 5 - .../glossary/default-sort-order.md | 5 - .../en/getting-started/glossary/element.md | 5 - content/en/getting-started/glossary/field.md | 5 - content/en/getting-started/glossary/flag.md | 5 - .../glossary/floating-point.md | 5 - .../en/getting-started/glossary/fragment.md | 5 - .../getting-started/glossary/front-matter.md | 5 - .../en/getting-started/glossary/function.md | 5 - .../glossary/global-resource.md | 10 - .../glossary/headless-bundle.md | 5 - .../en/getting-started/glossary/identifier.md | 5 - .../en/getting-started/glossary/integer.md | 5 - .../glossary/internationalization.md | 5 - .../glossary/interpreted-string-literal.md | 5 - .../en/getting-started/glossary/interval.md | 11 - content/en/getting-started/glossary/kind.md | 5 - .../getting-started/glossary/leaf-bundle.md | 5 - content/en/getting-started/glossary/lexer.md | 5 - .../en/getting-started/glossary/list-page.md | 5 - .../getting-started/glossary/list-template.md | 5 - .../getting-started/glossary/localization.md | 5 - .../getting-started/glossary/logical-path.md | 7 - content/en/getting-started/glossary/map.md | 5 - .../glossary/markdown-attribute.md | 5 - .../en/getting-started/glossary/marshal.md | 5 - content/en/getting-started/glossary/method.md | 5 - content/en/getting-started/glossary/module.md | 5 - content/en/getting-started/glossary/node.md | 5 - content/en/getting-started/glossary/object.md | 5 - .../glossary/ordered-taxonomy.md | 8 - .../getting-started/glossary/output-format.md | 7 - .../getting-started/glossary/page-bundle.md | 5 - .../glossary/page-collection.md | 5 - .../en/getting-started/glossary/page-kind.md | 7 - .../getting-started/glossary/page-resource.md | 7 - content/en/getting-started/glossary/pager.md | 5 - .../en/getting-started/glossary/paginate.md | 5 - .../en/getting-started/glossary/pagination.md | 5 - .../en/getting-started/glossary/paginator.md | 5 - .../en/getting-started/glossary/parameter.md | 5 - .../en/getting-started/glossary/partial.md | 5 - .../en/getting-started/glossary/permalink.md | 5 - .../en/getting-started/glossary/pipeline.md | 7 - .../glossary/raw-string-literal.md | 5 - .../getting-started/glossary/regular-page.md | 5 - .../glossary/relative-permalink.md | 5 - .../glossary/remote-resource.md | 5 - .../getting-started/glossary/render-hook.md | 5 - .../getting-started/glossary/resource-type.md | 8 - .../en/getting-started/glossary/resource.md | 7 - content/en/getting-started/glossary/scalar.md | 5 - .../getting-started/glossary/scratch-pad.md | 8 - .../getting-started/glossary/section-page.md | 5 - .../en/getting-started/glossary/section.md | 5 - .../en/getting-started/glossary/shortcode.md | 5 - content/en/getting-started/glossary/slice.md | 5 - content/en/getting-started/glossary/string.md | 5 - .../glossary/taxonomic-weight.md | 5 - .../glossary/taxonomy-object.md | 5 - .../getting-started/glossary/taxonomy-page.md | 5 - .../en/getting-started/glossary/taxonomy.md | 5 - .../glossary/template-action.md | 5 - .../en/getting-started/glossary/template.md | 5 - .../en/getting-started/glossary/term-page.md | 5 - content/en/getting-started/glossary/term.md | 5 - content/en/getting-started/glossary/theme.md | 5 - content/en/getting-started/glossary/token.md | 5 - .../en/getting-started/glossary/unmarshal.md | 5 - .../en/getting-started/glossary/variable.md | 5 - content/en/getting-started/glossary/walk.md | 5 - content/en/getting-started/glossary/weight.md | 5 - .../getting-started/glossary/weighted-page.md | 5 - content/en/getting-started/usage.md | 2 +- content/en/hosting-and-deployment/_index.md | 6 +- .../hosting-on-github/index.md | 2 +- .../index.md} | 8 +- .../hosting-on-keycdn/keycdn-pull-zone.png | Bin .../hosting-on-keycdn/secret-api-key.png | Bin .../hosting-on-keycdn/secret-zone-id.png | Bin content/en/hugo-modules/_index.md | 4 +- content/en/hugo-modules/configuration.md | 8 +- content/en/hugo-modules/use-modules.md | 2 +- content/en/hugo-pipes/_index.md | 2 +- content/en/hugo-pipes/bundling.md | 14 +- content/en/hugo-pipes/fingerprint.md | 20 +- content/en/hugo-pipes/minification.md | 17 +- content/en/hugo-pipes/postcss.md | 122 +- content/en/hugo-pipes/postprocess.md | 95 +- content/en/hugo-pipes/resource-from-string.md | 24 +- .../en/hugo-pipes/resource-from-template.md | 28 +- .../en/hugo-pipes/transpile-sass-to-css.md | 201 +- .../en/installation/_common/01-editions.md | 6 +- .../installation/_common/02-prerequisites.md | 2 +- content/en/installation/_index.md | 2 +- content/en/installation/windows.md | 6 + content/en/maintenance/_index.md | 10 - content/en/methods/_index.md | 4 +- content/en/methods/page/Ancestors.md | 2 +- .../en/methods/page/ContentWithoutSummary.md | 2 +- content/en/methods/page/CurrentSection.md | 2 +- content/en/methods/page/File.md | 4 +- content/en/methods/page/FirstSection.md | 2 +- content/en/methods/page/InSection.md | 2 +- content/en/methods/page/IsAncestor.md | 2 +- content/en/methods/page/IsDescendant.md | 2 +- content/en/methods/page/Pages.md | 2 +- content/en/methods/page/Parent.md | 2 +- content/en/methods/page/Path.md | 2 +- content/en/methods/page/RegularPages.md | 2 +- content/en/methods/page/RenderShortcodes.md | 2 +- content/en/methods/page/Resources.md | 2 +- content/en/methods/page/Scratch.md | 37 +- content/en/methods/page/Sections.md | 2 +- content/en/methods/page/Sitemap.md | 6 +- content/en/methods/page/Store.md | 101 +- content/en/methods/page/Title.md | 2 +- content/en/methods/page/Type.md | 1 - .../page/_common/definition-of-section.md | 5 - .../methods/page/_common/scratch-methods.md | 86 - content/en/methods/pager/PageSize.md | 23 +- content/en/methods/pager/PagerSize.md | 2 +- content/en/methods/resource/Colors.md | 4 +- content/en/methods/resource/Data.md | 33 +- content/en/methods/resource/Err.md | 2 +- content/en/methods/shortcode/Scratch.md | 18 +- content/en/methods/shortcode/Store.md | 15 +- content/en/methods/shortcode/_index.md | 1 + content/en/methods/site/DisqusShortname.md | 2 +- content/en/methods/site/GoogleAnalytics.md | 2 +- content/en/methods/site/IsDevelopment.md | 6 +- content/en/methods/site/IsMultiLingual.md | 26 +- content/en/methods/site/IsServer.md | 6 +- content/en/methods/site/LastChange.md | 13 +- content/en/methods/site/Lastmod.md | 2 +- content/en/methods/site/Store.md | 12 +- content/en/methods/taxonomy/Page.md | 2 +- content/en/myshowcase/index.md | 1 + content/en/news/_index.md | 2 + content/en/quick-reference/_index.md | 6 +- .../glossary/_index.md | 12 +- content/en/quick-reference/glossary/action.md | 5 + .../en/quick-reference/glossary/archetype.md | 6 + .../en/quick-reference/glossary/argument.md | 5 + content/en/quick-reference/glossary/array.md | 6 + .../glossary/asset-pipeline.md | 5 + .../glossary/bool.md | 2 +- .../en/quick-reference/glossary/boolean.md | 5 + .../quick-reference/glossary/branch-bundle.md | 6 + content/en/quick-reference/glossary/build.md | 5 + .../glossary/bundle.md | 0 content/en/quick-reference/glossary/cache.md | 5 + content/en/quick-reference/glossary/chain.md | 5 + content/en/quick-reference/glossary/cjk.md | 5 + content/en/quick-reference/glossary/cli.md | 5 + .../en/quick-reference/glossary/collection.md | 5 + .../glossary/content-adapter.md | 6 + .../glossary/content-format.md | 6 + .../quick-reference/glossary/content-type.md | 6 + .../quick-reference/glossary/content-view.md | 6 + .../en/quick-reference/glossary/context.md | 6 + .../glossary/default-sort-order.md | 5 + .../en/quick-reference/glossary/duration.md | 5 + .../en/quick-reference/glossary/element.md | 5 + .../glossary/embedded-template.md | 5 + .../glossary/environment.md | 2 +- content/en/quick-reference/glossary/field.md | 5 + content/en/quick-reference/glossary/flag.md | 6 + .../glossary/float.md | 0 .../glossary/floating-point.md | 5 + .../en/quick-reference/glossary/fragment.md | 5 + .../quick-reference/glossary/front-matter.md | 6 + .../en/quick-reference/glossary/function.md | 6 + content/en/quick-reference/glossary/glob.md | 6 + .../glossary/global-resource.md | 5 + .../glossary/headless-bundle.md | 6 + content/en/quick-reference/glossary/i18n.md | 5 + .../en/quick-reference/glossary/identifier.md | 5 + .../glossary/int.md | 2 +- .../en/quick-reference/glossary/integer.md | 5 + .../glossary/internationalization.md | 5 + .../glossary/interpreted-string-literal.md | 6 + .../en/quick-reference/glossary/interval.md | 11 + content/en/quick-reference/glossary/kind.md | 5 + content/en/quick-reference/glossary/l10n.md | 5 + .../glossary/layout.md | 2 +- .../quick-reference/glossary/leaf-bundle.md | 6 + content/en/quick-reference/glossary/lexer.md | 5 + .../en/quick-reference/glossary/list-page.md | 5 + .../quick-reference/glossary/list-template.md | 5 + .../quick-reference/glossary/localization.md | 6 + .../quick-reference/glossary/logical-path.md | 8 + content/en/quick-reference/glossary/map.md | 6 + .../glossary/markdown-attribute.md | 6 + .../en/quick-reference/glossary/marshal.md | 6 + content/en/quick-reference/glossary/method.md | 5 + content/en/quick-reference/glossary/module.md | 6 + content/en/quick-reference/glossary/node.md | 5 + .../glossary/noop.md | 0 content/en/quick-reference/glossary/object.md | 5 + .../glossary/ordered-taxonomy.md | 8 + .../quick-reference/glossary/output-format.md | 6 + .../quick-reference/glossary/page-bundle.md | 6 + .../glossary/page-collection.md | 5 + .../en/quick-reference/glossary/page-kind.md | 6 + .../quick-reference/glossary/page-resource.md | 5 + content/en/quick-reference/glossary/pager.md | 5 + .../en/quick-reference/glossary/paginate.md | 5 + .../en/quick-reference/glossary/pagination.md | 6 + .../en/quick-reference/glossary/paginator.md | 5 + .../en/quick-reference/glossary/parameter.md | 5 + .../en/quick-reference/glossary/partial.md | 5 + .../en/quick-reference/glossary/permalink.md | 5 + .../glossary/pipe.md | 2 +- .../en/quick-reference/glossary/pipeline.md | 7 + .../en/quick-reference/glossary/pretty-url.md | 5 + .../glossary/publish.md | 2 +- .../glossary/raw-string-literal.md | 6 + .../quick-reference/glossary/regular-page.md | 5 + .../glossary/relative-permalink.md | 5 + .../glossary/remote-resource.md | 5 + .../quick-reference/glossary/render-hook.md | 6 + .../quick-reference/glossary/resource-type.md | 8 + .../en/quick-reference/glossary/resource.md | 7 + content/en/quick-reference/glossary/scalar.md | 5 + content/en/quick-reference/glossary/scope.md | 5 + .../quick-reference/glossary/scratch-pad.md | 8 + .../quick-reference/glossary/section-page.md | 5 + .../en/quick-reference/glossary/section.md | 5 + .../en/quick-reference/glossary/segment.md | 5 + .../en/quick-reference/glossary/shortcode.md | 6 + content/en/quick-reference/glossary/slice.md | 6 + content/en/quick-reference/glossary/string.md | 5 + .../glossary/taxonomic-weight.md | 6 + .../glossary/taxonomy-object.md | 5 + .../quick-reference/glossary/taxonomy-page.md | 5 + .../en/quick-reference/glossary/taxonomy.md | 5 + .../glossary/template-action.md | 6 + .../en/quick-reference/glossary/template.md | 6 + .../en/quick-reference/glossary/term-page.md | 5 + content/en/quick-reference/glossary/term.md | 6 + content/en/quick-reference/glossary/theme.md | 5 + content/en/quick-reference/glossary/token.md | 5 + .../glossary/type.md | 0 .../en/quick-reference/glossary/ugly-url.md | 5 + .../en/quick-reference/glossary/unmarshal.md | 6 + .../en/quick-reference/glossary/variable.md | 5 + content/en/quick-reference/glossary/walk.md | 5 + content/en/quick-reference/glossary/weight.md | 5 + .../quick-reference/glossary/weighted-page.md | 5 + .../glossary/zero-time.md | 0 content/en/quick-reference/methods.md | 4 +- .../en/quick-reference/page-collections.md | 4 +- content/en/render-hooks/_common/pageinner.md | 2 +- content/en/render-hooks/_index.md | 2 +- content/en/render-hooks/blockquotes.md | 6 +- content/en/render-hooks/code-blocks.md | 2 +- content/en/render-hooks/headings.md | 2 +- content/en/render-hooks/images.md | 4 +- content/en/render-hooks/links.md | 4 +- content/en/render-hooks/passthrough.md | 2 +- content/en/render-hooks/tables.md | 2 +- content/en/shortcodes/_index.md | 2 +- content/en/shortcodes/comment.md | 4 +- content/en/shortcodes/details.md | 2 +- content/en/shortcodes/gist.md | 20 +- content/en/shortcodes/param.md | 4 +- content/en/shortcodes/qr.md | 8 +- content/en/shortcodes/vimeo.md | 2 +- content/en/shortcodes/x.md | 2 +- content/en/shortcodes/youtube.md | 16 +- content/en/templates/404.md | 24 +- content/en/templates/_index.md | 4 +- content/en/templates/embedded.md | 29 +- content/en/templates/home.md | 78 +- content/en/templates/output-formats.md | 69 +- content/en/templates/section.md | 2 +- content/en/templates/shortcode.md | 551 +- content/en/templates/sitemap.md | 6 +- content/en/templates/types/index.md | 16 +- content/en/tools/_index.md | 4 +- content/en/tools/search.md | 2 +- content/en/troubleshooting/_index.md | 2 +- content/en/troubleshooting/deprecation.md | 10 +- content/en/troubleshooting/faq.md | 6 +- .../gohugoioTheme/data => data}/sponsors.toml | 0 go.mod | 2 - hugo.toml | 228 +- hugo.work | 2 +- layouts/404.html | 22 + layouts/_default/_markup/render-heading.html | 10 + layouts/_default/_markup/render-link.html | 6 +- layouts/_default/baseof.html | 67 + layouts/_default/list.html | 67 + layouts/_default/single.html | 71 + .../layouts => layouts}/index.headers | 0 layouts/index.html | 52 + .../layouts => layouts}/index.redir | 0 .../layouts => layouts}/index.rss.xml | 4 +- .../partials/docs/functions-aliases.html | 6 +- .../partials/docs/functions-return-type.html | 6 + .../partials/docs/functions-signatures.html | 6 +- .../helpers/funcs/color-from-string.html | 25 + .../helpers/funcs/get-github-info.html | 28 + .../helpers/funcs}/get-remote-data.html | 0 .../partials/helpers}/gtag.html | 6 +- layouts/partials/helpers/linkcss.html | 28 + layouts/partials/helpers/linkjs.html | 15 + layouts/partials/helpers/picture.html | 25 + layouts/partials/layouts/blocks/alert.html | 27 + layouts/partials/layouts/blocks/modal.html | 30 + layouts/partials/layouts/breadcrumbs.html | 44 + layouts/partials/layouts/docsheader.html | 9 + layouts/partials/layouts/explorer.html | 47 + layouts/partials/layouts/footer.html | 72 + layouts/partials/layouts/head/head-js.html | 11 + layouts/partials/layouts/head/head.html | 52 + .../partials/layouts/header/githubstars.html | 16 + layouts/partials/layouts/header/header.html | 44 + layouts/partials/layouts/header/qr.html | 25 + layouts/partials/layouts/header/theme.html | 35 + layouts/partials/layouts/home/features.html | 56 + layouts/partials/layouts/home/opensource.html | 110 + layouts/partials/layouts/home/sponsors.html | 43 + layouts/partials/layouts/hooks/body-end.html | 1 + .../partials/layouts/hooks/body-start.html | 3 + layouts/partials/layouts/icons.html | 53 + layouts/partials/layouts/in-this-section.html | 32 + layouts/partials/layouts/page-edit.html | 24 + layouts/partials/layouts/related.html | 37 + layouts/partials/layouts/search/button.html | 19 + layouts/partials/layouts/search/input.html | 4 + layouts/partials/layouts/search/results.html | 82 + layouts/partials/layouts/toc.html | 42 + layouts/partials/news/get-news-items.html | 45 + .../opengraph/get-featured-image.html | 26 + layouts/partials/opengraph/opengraph.html | 84 + layouts/shortcodes/chroma-lexers.html | 7 + .../shortcodes/code-toggle.html | 60 +- layouts/shortcodes/code.html | 38 + .../shortcodes/datatable-filtered.html | 0 .../shortcodes/datatable.html | 0 .../shortcodes/deprecated-in.html | 15 +- .../layouts => layouts}/shortcodes/eturl.html | 0 layouts/shortcodes/glossary-term.html | 6 +- layouts/shortcodes/glossary.html | 23 +- layouts/shortcodes/gomodules-info.html | 12 + .../layouts => layouts}/shortcodes/hl.html | 0 .../layouts => layouts}/shortcodes/img.html | 0 .../shortcodes/imgproc.html | 0 .../shortcodes/include.html | 5 +- .../shortcodes/list-pages-in-section.html | 0 .../shortcodes/module-mounts-note.html | 0 layouts/shortcodes/new-in.html | 66 + layouts/shortcodes/note.html | 7 + .../shortcodes/quick-reference.html | 0 layouts/shortcodes/readfile.html | 1 + netlify.toml | 37 +- package.json | 22 + pull-theme.sh | 4 - src/css/_chroma.css | 43 - src/package-lock.json | 3 - .../android-chrome-144x144.png | Bin .../android-chrome-192x192.png | Bin .../android-chrome-256x256.png | Bin .../android-chrome-36x36.png | Bin .../android-chrome-48x48.png | Bin .../android-chrome-72x72.png | Bin .../android-chrome-96x96.png | Bin static/apple-touch-icon.png | Bin 7993 -> 6238 bytes .../static => static}/browserconfig.xml | 0 static/css/hugofont.css | 184 - static/css/style.css | 684 --- .../static => static}/favicon-16x16.png | Bin .../static => static}/favicon-32x32.png | Bin static/favicon.ico | Bin 15086 -> 15086 bytes .../fonts/Mulish-Italic-VariableFont_wght.ttf | Bin 0 -> 219768 bytes static/fonts/Mulish-VariableFont_wght.ttf | Bin 0 -> 212500 bytes static/fonts/hugo.eot | Bin 16380 -> 0 bytes static/fonts/hugo.svg | 63 - static/fonts/hugo.ttf | Bin 16228 -> 0 bytes static/fonts/hugo.woff | Bin 11728 -> 0 bytes static/images/blog/hugo-26-poster.png | Bin 69207 -> 0 bytes static/images/blog/hugo-27-poster.png | Bin 79893 -> 0 bytes static/images/blog/hugo-28-poster.png | Bin 116760 -> 0 bytes static/images/blog/hugo-29-poster.png | Bin 123034 -> 0 bytes static/images/blog/hugo-30-poster.png | Bin 123192 -> 0 bytes static/images/blog/hugo-31-poster.png | Bin 65077 -> 0 bytes static/images/blog/hugo-32-poster.png | Bin 95867 -> 0 bytes static/images/blog/hugo-bug-poster.png | Bin 74141 -> 0 bytes static/images/blog/hugo-http2-push.png | Bin 20544 -> 0 bytes static/images/blog/sunset.jpg | Bin 34584 -> 0 bytes .../contribute/development/accept-cla.png | Bin 24972 -> 0 bytes .../development/copy-remote-url.png | Bin 7232 -> 0 bytes .../development/forking-a-repository.png | Bin 4608 -> 0 bytes .../development/open-pull-request.png | Bin 46508 -> 0 bytes static/images/gohugoio-card-1.png | Bin 73881 -> 0 bytes .../static => static}/images/gopher-hero.svg | 0 .../images/gopher-side_color.svg | 0 .../amplify-build-settings.png | Bin 67178 -> 0 bytes .../amplify-connect-repo.gif | Bin 2880775 -> 0 bytes .../amplify-gettingstarted.png | Bin 57947 -> 0 bytes .../hosting-on-azure/basic-app-details.png | Bin 54346 -> 0 bytes .../hosting-on-azure/create-in-portal.png | Bin 63683 -> 0 bytes .../bitbucket-blog-post.png | Bin 37585 -> 0 bytes .../bitbucket-create-repo.png | Bin 24689 -> 0 bytes static/images/hugo-content-bundles.png | Bin 34394 -> 0 bytes .../images/hugo-logo-wide.svg | 0 static/images/icon-custom-outputs.svg | 36 - .../static => static}/manifest.json | 0 .../static => static}/mstile-144x144.png | Bin .../static => static}/mstile-150x150.png | Bin .../static => static}/mstile-310x310.png | Bin static/npmjs/index.html | 6 - .../static => static}/safari-pinned-tab.svg | 0 static/shared/branding/hugo-tall.png | Bin 9971 -> 0 bytes .../shared/branding/made-with-hugo-dark.png | Bin 8764 -> 0 bytes .../branding/made-with-hugo-long-dark.png | Bin 9116 -> 0 bytes .../shared/branding/made-with-hugo-long.png | Bin 9318 -> 0 bytes static/shared/branding/made-with-hugo.png | Bin 8900 -> 0 bytes .../shared/branding/powered-by-hugo-dark.png | Bin 3545 -> 0 bytes .../branding/powered-by-hugo-long-dark.png | Bin 3857 -> 0 bytes .../shared/branding/powered-by-hugo-long.png | Bin 3773 -> 0 bytes static/shared/branding/powered-by-hugo.png | Bin 3527 -> 0 bytes tailwind.config.js | 1 + 817 files changed, 5301 insertions(+), 14766 deletions(-) create mode 100644 .codespellrc create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/default.md delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/index.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/filesaver.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/config.toml delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/404.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-codeblock-mermaid.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-heading.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-link.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/baseof.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/documentation-home.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/list.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/page.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/single.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/taxonomy.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/terms.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/index.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/maintenance/list.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/news/list.xml delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/news/single.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/boxes-section-summaries.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/boxes-small-news.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data-card.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/components/author-github-data.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-return-type.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/page-meta-data.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/entry-summary.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/head-additions.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/hero.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/features-icons.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/features-single.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/installation.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/open-source-involvement.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/showcase.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/sponsors.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/home-page-sections/tweets.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/hooks/after-body-start.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/hooks/before-body-end.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/exclamation.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/package.json delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/dist/app.bundle.js delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/dist/main.css delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/favicon.ico delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-200.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-200.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-200italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-200italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-300.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-300.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-300italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-300italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-400.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-400.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-400italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-400italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-600.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-600.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-600italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-600italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-700.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-700.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-700italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-700italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-800.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-800.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-800italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-800italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-900.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-900.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-900italic.woff delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/fonts/muli-latin-900italic.woff2 delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/Github.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/gohugoio-card.png delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/home-page-templating-example.png delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/homepage-screenshot-hugo-themes.jpg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/homepage-screenshot-hugo-themes_not-optimized-according-to-google.jpg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-built-in-templates.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-content-management.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-fast.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-h/hugo-h-1.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-h/hugo-h-2.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-h/hugo-h-3.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-h/hugo-h-4.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-multilingual.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-multilingual2.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-search.png delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/icon-shortcodes.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/netlify-dark.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/static/images/site-hierarchy.svg delete mode 100644 _vendor/github.com/gohugoio/gohugoioTheme/theme.toml delete mode 100644 _vendor/modules.txt create mode 100644 assets/css/components/all.css rename _vendor/github.com/gohugoio/gohugoioTheme/assets/css/_chroma.css => assets/css/components/chroma.css (74%) create mode 100644 assets/css/components/chroma_dark.css create mode 100644 assets/css/components/content.css create mode 100644 assets/css/components/fonts.css create mode 100644 assets/css/components/helpers.css create mode 100644 assets/css/components/highlight.css create mode 100644 assets/css/components/shortcodes.css create mode 100644 assets/css/components/tableofcontents.css create mode 100644 assets/css/components/view-transitions.css create mode 100644 assets/css/styles.css create mode 100644 assets/images/hugo-github-screenshot.png rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/Route4MeLogoBlueOnWhite.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/bep-consulting.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/butter-dark.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/butter-light.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/cloudcannon-blue.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/cloudcannon-white.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/esolia-logo.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/goland.svg (98%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/graitykit-dark.svg (100%) create mode 100644 assets/images/sponsors/linode-logo.svg rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/linode-logo_standard_light_medium.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/your-company-dark.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/images/sponsors/your-company.svg (100%) create mode 100644 assets/js/alpinejs/data/explorer.js create mode 100644 assets/js/alpinejs/data/index.js create mode 100644 assets/js/alpinejs/data/navbar.js create mode 100644 assets/js/alpinejs/data/search.js create mode 100644 assets/js/alpinejs/data/toc.js create mode 100644 assets/js/alpinejs/magics/helpers.js create mode 100644 assets/js/alpinejs/magics/index.js create mode 100644 assets/js/alpinejs/stores/index.js create mode 100644 assets/js/alpinejs/stores/nav.js create mode 100644 assets/js/body-start.js create mode 100644 assets/js/head-early.js create mode 100644 assets/js/helpers/bridgeTurboAndAlpine.js create mode 100644 assets/js/helpers/helpers.js create mode 100644 assets/js/helpers/index.js create mode 100644 assets/js/main.js create mode 100644 assets/js/turbo.js create mode 100644 assets/jsconfig.json rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/opengraph/gohugoio-card-base-1.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/assets => assets}/opengraph/mulish-black.ttf (100%) delete mode 100644 config/_default/languages.toml delete mode 100644 config/_default/markup.toml delete mode 100644 config/_default/menus/menus.en.toml delete mode 100644 config/_default/params.toml delete mode 100644 config/_default/security.toml delete mode 100644 config/development/params.toml delete mode 100644 config/production/config.toml delete mode 100644 config/production/params.toml create mode 100644 content/LICENSE.md create mode 100644 content/en/_common/_index.md create mode 100644 content/en/_common/scratch-pad-scope.md create mode 100644 content/en/_common/store-methods.md create mode 100644 content/en/commands/_index.md delete mode 100644 content/en/getting-started/glossary/action.md delete mode 100644 content/en/getting-started/glossary/archetype.md delete mode 100644 content/en/getting-started/glossary/argument.md delete mode 100644 content/en/getting-started/glossary/array.md delete mode 100644 content/en/getting-started/glossary/boolean.md delete mode 100644 content/en/getting-started/glossary/branch-bundle.md delete mode 100644 content/en/getting-started/glossary/build.md delete mode 100644 content/en/getting-started/glossary/cache.md delete mode 100644 content/en/getting-started/glossary/chain.md delete mode 100644 content/en/getting-started/glossary/cjk.md delete mode 100644 content/en/getting-started/glossary/cli.md delete mode 100644 content/en/getting-started/glossary/collection.md delete mode 100644 content/en/getting-started/glossary/content-adapter.md delete mode 100644 content/en/getting-started/glossary/content-format.md delete mode 100644 content/en/getting-started/glossary/content-type.md delete mode 100644 content/en/getting-started/glossary/content-view.md delete mode 100644 content/en/getting-started/glossary/context.md delete mode 100644 content/en/getting-started/glossary/default-sort-order.md delete mode 100644 content/en/getting-started/glossary/element.md delete mode 100644 content/en/getting-started/glossary/field.md delete mode 100644 content/en/getting-started/glossary/flag.md delete mode 100644 content/en/getting-started/glossary/floating-point.md delete mode 100644 content/en/getting-started/glossary/fragment.md delete mode 100644 content/en/getting-started/glossary/front-matter.md delete mode 100644 content/en/getting-started/glossary/function.md delete mode 100644 content/en/getting-started/glossary/global-resource.md delete mode 100644 content/en/getting-started/glossary/headless-bundle.md delete mode 100644 content/en/getting-started/glossary/identifier.md delete mode 100644 content/en/getting-started/glossary/integer.md delete mode 100644 content/en/getting-started/glossary/internationalization.md delete mode 100644 content/en/getting-started/glossary/interpreted-string-literal.md delete mode 100644 content/en/getting-started/glossary/interval.md delete mode 100644 content/en/getting-started/glossary/kind.md delete mode 100644 content/en/getting-started/glossary/leaf-bundle.md delete mode 100644 content/en/getting-started/glossary/lexer.md delete mode 100644 content/en/getting-started/glossary/list-page.md delete mode 100644 content/en/getting-started/glossary/list-template.md delete mode 100644 content/en/getting-started/glossary/localization.md delete mode 100644 content/en/getting-started/glossary/logical-path.md delete mode 100644 content/en/getting-started/glossary/map.md delete mode 100644 content/en/getting-started/glossary/markdown-attribute.md delete mode 100644 content/en/getting-started/glossary/marshal.md delete mode 100644 content/en/getting-started/glossary/method.md delete mode 100644 content/en/getting-started/glossary/module.md delete mode 100644 content/en/getting-started/glossary/node.md delete mode 100644 content/en/getting-started/glossary/object.md delete mode 100644 content/en/getting-started/glossary/ordered-taxonomy.md delete mode 100644 content/en/getting-started/glossary/output-format.md delete mode 100644 content/en/getting-started/glossary/page-bundle.md delete mode 100644 content/en/getting-started/glossary/page-collection.md delete mode 100644 content/en/getting-started/glossary/page-kind.md delete mode 100644 content/en/getting-started/glossary/page-resource.md delete mode 100644 content/en/getting-started/glossary/pager.md delete mode 100644 content/en/getting-started/glossary/paginate.md delete mode 100644 content/en/getting-started/glossary/pagination.md delete mode 100644 content/en/getting-started/glossary/paginator.md delete mode 100644 content/en/getting-started/glossary/parameter.md delete mode 100644 content/en/getting-started/glossary/partial.md delete mode 100644 content/en/getting-started/glossary/permalink.md delete mode 100644 content/en/getting-started/glossary/pipeline.md delete mode 100644 content/en/getting-started/glossary/raw-string-literal.md delete mode 100644 content/en/getting-started/glossary/regular-page.md delete mode 100644 content/en/getting-started/glossary/relative-permalink.md delete mode 100644 content/en/getting-started/glossary/remote-resource.md delete mode 100644 content/en/getting-started/glossary/render-hook.md delete mode 100644 content/en/getting-started/glossary/resource-type.md delete mode 100644 content/en/getting-started/glossary/resource.md delete mode 100644 content/en/getting-started/glossary/scalar.md delete mode 100644 content/en/getting-started/glossary/scratch-pad.md delete mode 100644 content/en/getting-started/glossary/section-page.md delete mode 100644 content/en/getting-started/glossary/section.md delete mode 100644 content/en/getting-started/glossary/shortcode.md delete mode 100644 content/en/getting-started/glossary/slice.md delete mode 100644 content/en/getting-started/glossary/string.md delete mode 100644 content/en/getting-started/glossary/taxonomic-weight.md delete mode 100644 content/en/getting-started/glossary/taxonomy-object.md delete mode 100644 content/en/getting-started/glossary/taxonomy-page.md delete mode 100644 content/en/getting-started/glossary/taxonomy.md delete mode 100644 content/en/getting-started/glossary/template-action.md delete mode 100644 content/en/getting-started/glossary/template.md delete mode 100644 content/en/getting-started/glossary/term-page.md delete mode 100644 content/en/getting-started/glossary/term.md delete mode 100644 content/en/getting-started/glossary/theme.md delete mode 100644 content/en/getting-started/glossary/token.md delete mode 100644 content/en/getting-started/glossary/unmarshal.md delete mode 100644 content/en/getting-started/glossary/variable.md delete mode 100644 content/en/getting-started/glossary/walk.md delete mode 100644 content/en/getting-started/glossary/weight.md delete mode 100644 content/en/getting-started/glossary/weighted-page.md rename content/en/hosting-and-deployment/{hosting-on-keycdn.md => hosting-on-keycdn/index.md} (90%) rename {static/images => content/en}/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png (100%) rename {static/images => content/en}/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png (100%) rename {static/images => content/en}/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png (100%) delete mode 100644 content/en/maintenance/_index.md delete mode 100644 content/en/methods/page/_common/definition-of-section.md delete mode 100644 content/en/methods/page/_common/scratch-methods.md rename content/en/{getting-started => quick-reference}/glossary/_index.md (58%) create mode 100644 content/en/quick-reference/glossary/action.md create mode 100644 content/en/quick-reference/glossary/archetype.md create mode 100644 content/en/quick-reference/glossary/argument.md create mode 100644 content/en/quick-reference/glossary/array.md create mode 100644 content/en/quick-reference/glossary/asset-pipeline.md rename content/en/{getting-started => quick-reference}/glossary/bool.md (51%) create mode 100644 content/en/quick-reference/glossary/boolean.md create mode 100644 content/en/quick-reference/glossary/branch-bundle.md create mode 100644 content/en/quick-reference/glossary/build.md rename content/en/{getting-started => quick-reference}/glossary/bundle.md (100%) create mode 100644 content/en/quick-reference/glossary/cache.md create mode 100644 content/en/quick-reference/glossary/chain.md create mode 100644 content/en/quick-reference/glossary/cjk.md create mode 100644 content/en/quick-reference/glossary/cli.md create mode 100644 content/en/quick-reference/glossary/collection.md create mode 100644 content/en/quick-reference/glossary/content-adapter.md create mode 100644 content/en/quick-reference/glossary/content-format.md create mode 100644 content/en/quick-reference/glossary/content-type.md create mode 100644 content/en/quick-reference/glossary/content-view.md create mode 100644 content/en/quick-reference/glossary/context.md create mode 100644 content/en/quick-reference/glossary/default-sort-order.md create mode 100644 content/en/quick-reference/glossary/duration.md create mode 100644 content/en/quick-reference/glossary/element.md create mode 100644 content/en/quick-reference/glossary/embedded-template.md rename content/en/{getting-started => quick-reference}/glossary/environment.md (70%) create mode 100644 content/en/quick-reference/glossary/field.md create mode 100644 content/en/quick-reference/glossary/flag.md rename content/en/{getting-started => quick-reference}/glossary/float.md (100%) create mode 100644 content/en/quick-reference/glossary/floating-point.md create mode 100644 content/en/quick-reference/glossary/fragment.md create mode 100644 content/en/quick-reference/glossary/front-matter.md create mode 100644 content/en/quick-reference/glossary/function.md create mode 100644 content/en/quick-reference/glossary/glob.md create mode 100644 content/en/quick-reference/glossary/global-resource.md create mode 100644 content/en/quick-reference/glossary/headless-bundle.md create mode 100644 content/en/quick-reference/glossary/i18n.md create mode 100644 content/en/quick-reference/glossary/identifier.md rename content/en/{getting-started => quick-reference}/glossary/int.md (50%) create mode 100644 content/en/quick-reference/glossary/integer.md create mode 100644 content/en/quick-reference/glossary/internationalization.md create mode 100644 content/en/quick-reference/glossary/interpreted-string-literal.md create mode 100644 content/en/quick-reference/glossary/interval.md create mode 100644 content/en/quick-reference/glossary/kind.md create mode 100644 content/en/quick-reference/glossary/l10n.md rename content/en/{getting-started => quick-reference}/glossary/layout.md (52%) create mode 100644 content/en/quick-reference/glossary/leaf-bundle.md create mode 100644 content/en/quick-reference/glossary/lexer.md create mode 100644 content/en/quick-reference/glossary/list-page.md create mode 100644 content/en/quick-reference/glossary/list-template.md create mode 100644 content/en/quick-reference/glossary/localization.md create mode 100644 content/en/quick-reference/glossary/logical-path.md create mode 100644 content/en/quick-reference/glossary/map.md create mode 100644 content/en/quick-reference/glossary/markdown-attribute.md create mode 100644 content/en/quick-reference/glossary/marshal.md create mode 100644 content/en/quick-reference/glossary/method.md create mode 100644 content/en/quick-reference/glossary/module.md create mode 100644 content/en/quick-reference/glossary/node.md rename content/en/{getting-started => quick-reference}/glossary/noop.md (100%) create mode 100644 content/en/quick-reference/glossary/object.md create mode 100644 content/en/quick-reference/glossary/ordered-taxonomy.md create mode 100644 content/en/quick-reference/glossary/output-format.md create mode 100644 content/en/quick-reference/glossary/page-bundle.md create mode 100644 content/en/quick-reference/glossary/page-collection.md create mode 100644 content/en/quick-reference/glossary/page-kind.md create mode 100644 content/en/quick-reference/glossary/page-resource.md create mode 100644 content/en/quick-reference/glossary/pager.md create mode 100644 content/en/quick-reference/glossary/paginate.md create mode 100644 content/en/quick-reference/glossary/pagination.md create mode 100644 content/en/quick-reference/glossary/paginator.md create mode 100644 content/en/quick-reference/glossary/parameter.md create mode 100644 content/en/quick-reference/glossary/partial.md create mode 100644 content/en/quick-reference/glossary/permalink.md rename content/en/{getting-started => quick-reference}/glossary/pipe.md (50%) create mode 100644 content/en/quick-reference/glossary/pipeline.md create mode 100644 content/en/quick-reference/glossary/pretty-url.md rename content/en/{getting-started => quick-reference}/glossary/publish.md (57%) create mode 100644 content/en/quick-reference/glossary/raw-string-literal.md create mode 100644 content/en/quick-reference/glossary/regular-page.md create mode 100644 content/en/quick-reference/glossary/relative-permalink.md create mode 100644 content/en/quick-reference/glossary/remote-resource.md create mode 100644 content/en/quick-reference/glossary/render-hook.md create mode 100644 content/en/quick-reference/glossary/resource-type.md create mode 100644 content/en/quick-reference/glossary/resource.md create mode 100644 content/en/quick-reference/glossary/scalar.md create mode 100644 content/en/quick-reference/glossary/scope.md create mode 100644 content/en/quick-reference/glossary/scratch-pad.md create mode 100644 content/en/quick-reference/glossary/section-page.md create mode 100644 content/en/quick-reference/glossary/section.md create mode 100644 content/en/quick-reference/glossary/segment.md create mode 100644 content/en/quick-reference/glossary/shortcode.md create mode 100644 content/en/quick-reference/glossary/slice.md create mode 100644 content/en/quick-reference/glossary/string.md create mode 100644 content/en/quick-reference/glossary/taxonomic-weight.md create mode 100644 content/en/quick-reference/glossary/taxonomy-object.md create mode 100644 content/en/quick-reference/glossary/taxonomy-page.md create mode 100644 content/en/quick-reference/glossary/taxonomy.md create mode 100644 content/en/quick-reference/glossary/template-action.md create mode 100644 content/en/quick-reference/glossary/template.md create mode 100644 content/en/quick-reference/glossary/term-page.md create mode 100644 content/en/quick-reference/glossary/term.md create mode 100644 content/en/quick-reference/glossary/theme.md create mode 100644 content/en/quick-reference/glossary/token.md rename content/en/{getting-started => quick-reference}/glossary/type.md (100%) create mode 100644 content/en/quick-reference/glossary/ugly-url.md create mode 100644 content/en/quick-reference/glossary/unmarshal.md create mode 100644 content/en/quick-reference/glossary/variable.md create mode 100644 content/en/quick-reference/glossary/walk.md create mode 100644 content/en/quick-reference/glossary/weight.md create mode 100644 content/en/quick-reference/glossary/weighted-page.md rename content/en/{getting-started => quick-reference}/glossary/zero-time.md (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/data => data}/sponsors.toml (100%) create mode 100644 layouts/404.html create mode 100644 layouts/_default/_markup/render-heading.html create mode 100644 layouts/_default/baseof.html create mode 100644 layouts/_default/list.html create mode 100644 layouts/_default/single.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/index.headers (100%) create mode 100644 layouts/index.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/index.redir (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/index.rss.xml (95%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/partials/docs/functions-aliases.html (56%) create mode 100644 layouts/partials/docs/functions-return-type.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/partials/docs/functions-signatures.html (63%) create mode 100644 layouts/partials/helpers/funcs/color-from-string.html create mode 100644 layouts/partials/helpers/funcs/get-github-info.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities => layouts/partials/helpers/funcs}/get-remote-data.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials => layouts/partials/helpers}/gtag.html (80%) create mode 100644 layouts/partials/helpers/linkcss.html create mode 100644 layouts/partials/helpers/linkjs.html create mode 100644 layouts/partials/helpers/picture.html create mode 100644 layouts/partials/layouts/blocks/alert.html create mode 100644 layouts/partials/layouts/blocks/modal.html create mode 100644 layouts/partials/layouts/breadcrumbs.html create mode 100644 layouts/partials/layouts/docsheader.html create mode 100644 layouts/partials/layouts/explorer.html create mode 100644 layouts/partials/layouts/footer.html create mode 100644 layouts/partials/layouts/head/head-js.html create mode 100644 layouts/partials/layouts/head/head.html create mode 100644 layouts/partials/layouts/header/githubstars.html create mode 100644 layouts/partials/layouts/header/header.html create mode 100644 layouts/partials/layouts/header/qr.html create mode 100644 layouts/partials/layouts/header/theme.html create mode 100644 layouts/partials/layouts/home/features.html create mode 100644 layouts/partials/layouts/home/opensource.html create mode 100644 layouts/partials/layouts/home/sponsors.html create mode 100644 layouts/partials/layouts/hooks/body-end.html create mode 100644 layouts/partials/layouts/hooks/body-start.html create mode 100644 layouts/partials/layouts/icons.html create mode 100644 layouts/partials/layouts/in-this-section.html create mode 100644 layouts/partials/layouts/page-edit.html create mode 100644 layouts/partials/layouts/related.html create mode 100644 layouts/partials/layouts/search/button.html create mode 100644 layouts/partials/layouts/search/input.html create mode 100644 layouts/partials/layouts/search/results.html create mode 100644 layouts/partials/layouts/toc.html create mode 100644 layouts/partials/news/get-news-items.html create mode 100644 layouts/partials/opengraph/get-featured-image.html create mode 100644 layouts/partials/opengraph/opengraph.html create mode 100644 layouts/shortcodes/chroma-lexers.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/code-toggle.html (59%) create mode 100644 layouts/shortcodes/code.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/datatable-filtered.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/datatable.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/deprecated-in.html (61%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/eturl.html (100%) create mode 100644 layouts/shortcodes/gomodules-info.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/hl.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/img.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/imgproc.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/include.html (76%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/list-pages-in-section.html (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/module-mounts-note.html (100%) create mode 100644 layouts/shortcodes/new-in.html create mode 100644 layouts/shortcodes/note.html rename {_vendor/github.com/gohugoio/gohugoioTheme/layouts => layouts}/shortcodes/quick-reference.html (100%) create mode 100644 layouts/shortcodes/readfile.html create mode 100644 package.json delete mode 100755 pull-theme.sh delete mode 100644 src/css/_chroma.css delete mode 100644 src/package-lock.json rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-144x144.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-192x192.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-256x256.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-36x36.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-48x48.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-72x72.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/android-chrome-96x96.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/browserconfig.xml (100%) delete mode 100644 static/css/hugofont.css delete mode 100644 static/css/style.css rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/favicon-16x16.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/favicon-32x32.png (100%) create mode 100644 static/fonts/Mulish-Italic-VariableFont_wght.ttf create mode 100644 static/fonts/Mulish-VariableFont_wght.ttf delete mode 100644 static/fonts/hugo.eot delete mode 100644 static/fonts/hugo.svg delete mode 100644 static/fonts/hugo.ttf delete mode 100644 static/fonts/hugo.woff delete mode 100644 static/images/blog/hugo-26-poster.png delete mode 100644 static/images/blog/hugo-27-poster.png delete mode 100644 static/images/blog/hugo-28-poster.png delete mode 100644 static/images/blog/hugo-29-poster.png delete mode 100644 static/images/blog/hugo-30-poster.png delete mode 100644 static/images/blog/hugo-31-poster.png delete mode 100644 static/images/blog/hugo-32-poster.png delete mode 100644 static/images/blog/hugo-bug-poster.png delete mode 100644 static/images/blog/hugo-http2-push.png delete mode 100644 static/images/blog/sunset.jpg delete mode 100644 static/images/contribute/development/accept-cla.png delete mode 100644 static/images/contribute/development/copy-remote-url.png delete mode 100644 static/images/contribute/development/forking-a-repository.png delete mode 100644 static/images/contribute/development/open-pull-request.png delete mode 100644 static/images/gohugoio-card-1.png rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/images/gopher-hero.svg (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/images/gopher-side_color.svg (100%) delete mode 100644 static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png delete mode 100644 static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif delete mode 100644 static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png delete mode 100644 static/images/hosting-and-deployment/hosting-on-azure/basic-app-details.png delete mode 100644 static/images/hosting-and-deployment/hosting-on-azure/create-in-portal.png delete mode 100644 static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-blog-post.png delete mode 100644 static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-create-repo.png delete mode 100644 static/images/hugo-content-bundles.png rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/images/hugo-logo-wide.svg (100%) delete mode 100644 static/images/icon-custom-outputs.svg rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/manifest.json (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/mstile-144x144.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/mstile-150x150.png (100%) rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/mstile-310x310.png (100%) delete mode 100644 static/npmjs/index.html rename {_vendor/github.com/gohugoio/gohugoioTheme/static => static}/safari-pinned-tab.svg (100%) delete mode 100644 static/shared/branding/hugo-tall.png delete mode 100644 static/shared/branding/made-with-hugo-dark.png delete mode 100644 static/shared/branding/made-with-hugo-long-dark.png delete mode 100644 static/shared/branding/made-with-hugo-long.png delete mode 100644 static/shared/branding/made-with-hugo.png delete mode 100644 static/shared/branding/powered-by-hugo-dark.png delete mode 100644 static/shared/branding/powered-by-hugo-long-dark.png delete mode 100644 static/shared/branding/powered-by-hugo-long.png delete mode 100644 static/shared/branding/powered-by-hugo.png create mode 100644 tailwind.config.js diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..564fc77c0 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,13 @@ +# Config file for codespell. +# https://github.com/codespell-project/codespell#using-a-config-file + +[codespell] + +# Comma separated list of dirs to be skipped. +skip = _vendor,.cspell.json,chroma.css,chroma_dark.css + +# Comma separated list of words to be ignored. Words must be lowercased. +ignore-words-list = abl,edn,te,ue,trys,januar,womens,crossreferences + +# Check file names as well. +check-filenames = true diff --git a/.cspell.json b/.cspell.json index 6596a160c..8d51085ab 100644 --- a/.cspell.json +++ b/.cspell.json @@ -68,6 +68,7 @@ "# ----------------------------------------------------------------------", "# cspell: ignore hugo terminology", "# ----------------------------------------------------------------------", + "alignx", "attrlink", "canonify", "codeowners", @@ -85,6 +86,7 @@ "stringifier", "struct", "toclevels", + "unmarshal", "unpublishdate", "zgotmplz", "# ----------------------------------------------------------------------", @@ -158,6 +160,8 @@ "# cspell: ignore miscellaneous", "# ----------------------------------------------------------------------", "achristie", + "ccpa", + "crpa", "ddmaurier", "dring", "fleqn", @@ -171,6 +175,7 @@ "rsmith", "tdewolff", "tjones", + "vcard", "wcag", "xfeff" ] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/default.md b/.github/ISSUE_TEMPLATE/default.md new file mode 100644 index 000000000..ada35b3a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/default.md @@ -0,0 +1,6 @@ +--- +name: Default +about: This is the default issue template. +labels: + - NeedsTriage +--- diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 86f8f53a5..e01ab1764 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -20,3 +20,8 @@ jobs: incremental_files_only: true inline: warning strict: false + - uses: codespell-project/actions-codespell@v2 + with: + check_filenames: true + check_hidden: true + # by default, codespell uses configuration from the .codespellrc diff --git a/.gitignore b/.gitignore index f9cab2f80..debc86d46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ +.DS_Store +.hugo_build.lock /.idea /.vscode -/public /dist -node_modules +/public +hugo_stats.json +node_modules/ nohup.out -.DS_Store -trace.out -.hugo_build.lock -resources/_gen/images/ \ No newline at end of file +package-lock.json +public/ +resources/ +trace.out \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index b09cd7856..979711275 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,201 +1,3 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +See [content/LICENSE.md](content/LICENSE.md) for the license of the content of this repository. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +The theme (layouts, CSS, JavaScript etc.) of this repository has no open source license. It is custom made for the Hugo sites and is not meant for reuse. \ No newline at end of file diff --git a/README.md b/README.md index 7550a93cd..64ba31a48 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,12 @@ Please see the [contributing] section for guidelines, examples, and process. [friends]: https://github.com/gohugoio/hugo/graphs/contributors [go]: https://go.dev/ [contributing]: https://gohugo.io/contribute/documentation + +# Install + +```bash +npm i +hugo server +``` + +**Note:** We're working on removing the need to run `npm i` for local development. Stay tuned. \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css deleted file mode 100644 index 0122f9758..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css +++ /dev/null @@ -1,11 +0,0 @@ -.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-webkit-input-placeholder{color:#aaa}.searchbox__input:-ms-input-placeholder{color:#aaa}.searchbox__input::-ms-input-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}}@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} -/*# sourceMappingURL=docsearch.min.css.map */ -a.algolia-docsearch-suggestion { - text-decoration: none !important; -} -.algolia-docsearch-suggestion--category-header { - background: #0594cb; - padding-left: .25rem !important; - color: white !important; - border-radius: 3px; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css deleted file mode 100644 index 997931ac4..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css +++ /dev/null @@ -1,21 +0,0 @@ -.animated { - animation-duration: .5s; - animation-fill-mode: forwards; - animation-timing-function: ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -.fadeIn { - animation-name: fadeIn; -} -.animated-delay-1 { - animation-delay: 0.5s; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css deleted file mode 100644 index 11fae8702..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css +++ /dev/null @@ -1,25 +0,0 @@ -/* These styles enhance the home page carousel, located here: themes/gohugoioTheme/layouts/partials/home-page-sections/showcase.html */ -.overflow-x-scroll{ - -webkit-overflow-scrolling: touch; -} -.row { - transition: 450ms transform; - font-size: 0; -} -.tile { - transition: 450ms all; -} -.details { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(0,0,0,0.9)), to(rgba(0,0,0,0))); - background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0) 100%); - transition: 450ms opacity; -} -.tile:hover .details { - opacity: 1; -} -.row:hover .tile { - opacity: 0.3; -} -.row:hover .tile:hover { - opacity: 1; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css deleted file mode 100644 index 9906a13d0..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css +++ /dev/null @@ -1,89 +0,0 @@ -.chroma .lntable pre { - padding: 0; - margin: 0; - border: 0; -} - -.chroma .lntable pre code { - padding: 0; - margin: 0; -} - -code { - padding: 2px 3px; - margin: 0; - font-size: 93.75%; - background-color: rgba(27,31,35,0.05); - border-radius: 3px; -} - -pre code { - display: block; - padding: 1.5em 1.5em; - font-size: .875rem; - line-height: 2; - overflow-x: auto; -} - -pre { - background-color: #fff; - color: #333; - white-space: pre; - hyphens: none; - position: relative; - border-width: 1px; - border-color: #ccc; - border-style: solid; -} - -/* The Pygments highlighter comes with its own styles. */ -.highlight pre { - background-color: inherit; - color: inherit; - padding: 0.5em; - font-size: .875rem; -} - - -/*We are adding the copy button content here so we can change it with javascript. See the "Clipboard scripts"*/ -.copy:after { - content: "Copy" -} -.copied:after { - content: "Copied" -} - -@media (--breakpoint-large) { - .full-width - { - /*width: 100vw; - position: relative; - left: 50%; - right: 50%; - margin-left: -50vw; - margin-right: -50vw;*/ - /*width: 60vw;*/ - /*position: relative; - left: 50%; - right: 50%;*/ - /*margin-left: -30vw;*/ - margin-right: -30vw; - max-width: 100vw; - } -} - -.code-block .line-numbers-rows { - background: #2f3a46; - border: none; - bottom: -50px; - color: #98a4b3; - left: -178px; - padding: 50px 0; - top: -50px; - width: 138px -} - -.code-block .line-numbers-rows>span:before { - color: inherit; - padding-right: 30px -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css deleted file mode 100644 index 1d61a7725..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css +++ /dev/null @@ -1,38 +0,0 @@ -.primary-color {color: var(--primary-color)} -.bg-primary-color {background-color: var(--primary-color)} -.hover-bg-primary-color:hover {background-color: var(--primary-color)} - -.primary-color-dark {color: var(--primary-color-dark)} -.bg-primary-color-dark {background-color: var(--primary-color-dark)} -.hover-bg-primary-color-dark:hover {background-color: var(--primary-color-dark)} - -.primary-color-light {color: var(--primary-color-light)} -.bg-primary-color-light {background-color: var(--primary-color-light)} -.hover-bg-primary-color-light:hover {background-color: var(--primary-color-light)} - -.accent-color {color: var(--accent-color)} -.bg-accent-color {background-color: var(--accent-color)} -.hover-bg-accent-color:hover {background-color: var(--accent-color)} - -.accent-color-light {color: var(--accent-color-light)} -.hover-accent-color-light:hover {color: var(--accent-color-light)} -.bg-accent-color-light {background-color: var(--accent-color-light)} -.hover-bg-accent-color-light:hover {background-color: var(--accent-color-light)} - -.accent-color-dark {color: var(--accent-color-dark)} -.bg-accent-color-dark {background-color: var(--accent-color-dark)} -.hover-bg-accent-color-dark:hover {background-color: var(--accent-color-dark)} - -.text-color-primary {color: var(--text-color-primary)} -.text-on-primary-color {color: var(--text-on-primary-color)} -.text-color-secondary {color: var(--text-color-secondary)} -.text-color-disabled {color: var(--text-color-disabled)} -.divider-color {color: var(--divider-color)} -.warn-color {color: var(--warn-color)} - - -.nested-links a { - color: var(--primary-color); - text-decoration: none; - -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css deleted file mode 100644 index e1e938c74..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css +++ /dev/null @@ -1,11 +0,0 @@ -.column-count-2 {column-count: 1} -.column-gap-1 {column-gap: 0} -.break-inside-avoid {break-inside: auto} - - -@media (--breakpoint-large) { - .column-count-3-l {column-count: 3} - .column-count-2-l {column-count: 2} - .column-gap-1-l {column-gap: 1} - .break-inside-avoid-l {break-inside: avoid} -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css deleted file mode 100644 index 4e092e8bf..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css +++ /dev/null @@ -1,28 +0,0 @@ -.prose table { - width: 100%; - margin-bottom: 3em; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; - border: 1px solid var(--light-gray); - & th { - background-color: var(--primary-color); - border-bottom: 1px solid var(--primary-color); - color: white; - font-weight: 400; - - text-align: left; - padding: .375em .5em; - } - - & td, & tc { - padding: .75em .5em; - text-align: left; - border-right: 1px solid var(--light-gray); - } - -} - -.prose table tr:nth-child(even) { - background-color: var(--light-gray); -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css deleted file mode 100644 index 9306f8b2f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css +++ /dev/null @@ -1,53 +0,0 @@ -.prose ul, .prose ol { - margin-bottom: 2em; -} -.prose ul li, .prose ol li { - margin-bottom: .5em; -} -.prose li:hover { - background-color: var(--light-gray) -} -.prose ::selection { - background: var(--primary-color); /* WebKit/Blink Browsers */ - color: white; -} - -.prose-glossary h3 { - margin-top: 0; - font-size: 1.125rem; -} - -.prose-glossary h3:first-of-type { - margin-top: 3em; -} - -.prose-glossary h3 ~ p { - margin: 0.5em 0 2em 0; -} - -body { - -line-height: 1.45; - -} - -p {margin-bottom: 1.3em;} - -h1, h2, h3, h4 { -margin: 1.414em 0 0.5em; - -line-height: 1.2; -} - -h1 { -margin-top: 0; -font-size: 2.441em; -} - -h2 {font-size: 1.953em;} - -h3 {font-size: 1.563em;} - -h4 {font-size: 1.25em;} - -small, .font_small {font-size: 0.8em;} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css deleted file mode 100644 index e28f67d4b..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css +++ /dev/null @@ -1,9 +0,0 @@ - -dl dt { - font-weight: bold; - font-size: 1.125rem; -} -dd { - margin: .5em 0 2em 0; - padding: 0; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css deleted file mode 100644 index 0ea8e9b72..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css +++ /dev/null @@ -1,54 +0,0 @@ -.note, -.warning { - - border-left-width: 4px; - border-left-style: solid; - position: relative; - border-color: var(--primary-color); - - display: block; -} -.note #exclamation-icon, -.warning #exclamation-icon { - - fill: var(--primary-color); - position: absolute; - top: 35%; - left: -12px; - /*background-color: white;*/ -} - - .admonition-content { - display: block; - margin: 0px; - padding: .125em 1em; - /*margin-left: 1em;*/ - margin-top: 2em; - margin-bottom: 2em; - overflow-x: auto; - /*font-size: .9375em;*/ - background-color: var(--black-05); - } - - - .hide-child-menu .child-menu { - display: none; - } - .hide-child-menu:hover .child-menu, - .hide-child-menu:focus .child-menu, - .hide-child-menu:active .child-menu { - display: block; - } - - -/*documentation-copy headings exaggerate spacing and size to chunk content */ - .documentation-copy h2 { - margin-top: 3em; - &.minor { - font-size: inherit; - margin-top: inherit; - border-bottom: none; - } - } - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css deleted file mode 100644 index da9f04c81..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css +++ /dev/null @@ -1,10 +0,0 @@ -.f2-fluid { - font-size: 2.25rem; -} - -@media (--breakpoint-large) { - .f2-fluid { - font-size: 1.25rem; - font-size: calc(0.875rem + 0.5 * ((100vw - 20rem) / 60)); - } -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css deleted file mode 100644 index 85b3b6c95..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css +++ /dev/null @@ -1,74 +0,0 @@ -/* From https://www.cssfontstack.com */ -code, .code, pre code, .highlight pre { - font-family: 'inconsolata',Menlo,Monaco,'Courier New',monospace; -} - -.sans-serif { - font-family: 'Muli', Avenir, 'Helvetica Neue', Helvetica, Roboto, Noto, 'Segoe UI', Arial, sans-serif; -} - - -.serif { - font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif; -} - -/* Monospaced Typefaces (for code) */ - - -.courier { - font-family: 'Courier Next', - courier, - monospace; -} - - -/* Sans-Serif Typefaces */ - -.helvetica { - font-family: 'helvetica neue', helvetica, - sans-serif; -} - -.avenir { - font-family: 'avenir next', avenir, - sans-serif; -} - - -/* Serif Typefaces */ - -.athelas { - font-family: athelas, - georgia, - serif; -} - -.georgia { - font-family: georgia, - serif; -} - -.times { - font-family: times, - serif; -} - -.bodoni { - font-family: "Bodoni MT", - serif; -} - -.calisto { - font-family: "Calisto MT", - serif; -} - -.garamond { - font-family: garamond, - serif; -} - -.baskerville { - font-family: baskerville, - serif; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css deleted file mode 100644 index 56a16be6d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css +++ /dev/null @@ -1,15 +0,0 @@ -.header-link:after { - position: relative; - left: 0.5em; - opacity: 0; - font-size: 0.8em; - -moz-transition: opacity 0.2s ease-in-out 0.1s; - -ms-transition: opacity 0.2s ease-in-out 0.1s; -} -h2:hover .header-link, -h3:hover .header-link, -h4:hover .header-link, -h5:hover .header-link, -h6:hover .header-link { - opacity: 1; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css deleted file mode 100644 index 0b1df9610..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css +++ /dev/null @@ -1,52 +0,0 @@ -/* pagination.html: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/template_embedded.go#L117 */ -.pagination { - margin: 3rem 0; -} - -.pagination li { - display: inline-block; - margin-right: .375rem; - font-size: .875rem; - margin-bottom: 2.5em; -} -.pagination li a { - padding: .5rem .625rem; - background-color: white; - color: #333; - border: 1px solid #ddd; - border-radius: 3px; - text-decoration: none; -} -.pagination li.disabled { - display: none; -} -.pagination li.active a:link, -.pagination li.active a:active, -.pagination li.active a:visited { - background-color: #ddd; -} - -/* Hides non-meaningful TOC items*/ -#TableOfContents ul li ul li ul li{ - display: none; - } - - -#TableOfContents ul li { - color: black; - display: block; - margin-bottom: .375em; - line-height: 1.375; -} - -#TableOfContents ul li a{ - width: 100%; - padding: .25em .375em; - margin-left: -.375em; - -} -#TableOfContents ul li a:hover { - background-color: #999; - color: white; - -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css deleted file mode 100644 index 7991450fe..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css +++ /dev/null @@ -1,7 +0,0 @@ -.no-js .needs-js { - opacity: 0 -} -.js .needs-js { - opacity: 1; - transition: opacity .15s ease-in; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css deleted file mode 100644 index c0be3af61..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css +++ /dev/null @@ -1,7 +0,0 @@ -@media print { - #page-footer, - body > footer, - body > nav { - display: none; - } -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css deleted file mode 100644 index 757457b2d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css +++ /dev/null @@ -1,11 +0,0 @@ -#right-sidebar { - scrollbar-width: none; /* hide scrollbar: Firefox */ - -ms-overflow-style: none; /* hide scrollbar: Internet Explorer 10+ */ - height: calc(100vh - 9rem); - overflow-y: auto; -} - -#right-sidebar::-webkit-scrollbar { /* hide scrollbar: WebKit */ - width: 0; - height: 0; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css deleted file mode 100644 index 30e0c412d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css +++ /dev/null @@ -1,25 +0,0 @@ -/* -Make h6 elements behave like dt elements. Initially implemented to support -linkable glossary entries. - -Yes, it's a hack. That's why it's in the shame file. -*/ - -h6 { - margin-top: 0; - margin-bottom: 0; - font-size: 1.125rem; -} - -h6:first-of-type { - margin-top: 3em; -} - -h6 ~ p { - margin: 0.5em 0 2em 0; -} - -/* QR codes */ -img.qrcode { - width: initial; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css deleted file mode 100644 index 6cfa7b1b4..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css +++ /dev/null @@ -1,52 +0,0 @@ -.facebook, -.twitter, -.instagram, -.youtube { - fill: #bababa; -} -.facebook:hover { - fill: #3b5998; -} - -.twitter { - fill: #55acee; -} - -.twitter:hover { - fill: #bababa; -} - -.instagram:hover { - fill: #e95950; -} - -.youtube:hover { - fill: #bb0000; -} - -.mstdn { - display: inline-block; - background-color: #282c37; - color: #d9e1e8; - text-decoration: none; - padding: 4px 10px 4px 30px; - border-radius: 4px; - font-size: 16px; - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"); - background-size: 16px; - background-repeat: no-repeat; - background-position: top 50% left 8px; - transition: all 0.5s; -} -.mstdn:hover { - background-color: #484c56; -} - -.mstdn > span { - color: #9baec8; - font-size: 12px; - padding-left: 3px; -} -.mstdn > span:before { - content: "@"; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css deleted file mode 100644 index 7759bed96..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css +++ /dev/null @@ -1,15 +0,0 @@ - -@media (min-width: 75em) { - - [data-scrolldir="down"] .sticky { - position: fixed; - top:100px; - right:0; - } - - [data-scrolldir="up"] .sticky { - position: fixed; - top:100px; - right:0; - } -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css deleted file mode 100644 index 299a4a963..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css +++ /dev/null @@ -1 +0,0 @@ -.fill-current { fill: currentColor; } diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css deleted file mode 100644 index 6e0022cc9..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css +++ /dev/null @@ -1,34 +0,0 @@ -.tab-button{ - margin-bottom:1px; - position: relative; - z-index: 1; - color:#333; - border-color:#ccc; - outline: none; - background-color:white; -} -.tab-pane code{ - background:#f1f2f2; - border-radius:0; -} -.tab-pane .chroma{ - background:none; - padding:0; -} -.tab-button.active{ - border-bottom-color:#f1f2f2; - background-color: #f1f2f2; -} -.tab-content .tab-pane{ - display: none; -} -.tab-content .tab-pane.active{ - display: block; -} -/* Treatment of copy buttons inside a tab module */ -.tab-content .copy, .tab-content .copied{ - display: none; -} -.tab-content .tab-pane.active + .copy, .tab-content .tab-pane.active + .copied{ - display: block; -} \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css deleted file mode 100644 index d697c4d85..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css +++ /dev/null @@ -1,94 +0,0 @@ -/*! TACHYONS v4.7.0 | http://tachyons.io */ - -/* - * NOTE: The Tachyons folder is for backup/reference only. This file references the module - * ________ ______ - * ___ __/_____ _________ /______ ______________________ - * __ / _ __ `/ ___/_ __ \_ / / / __ \_ __ \_ ___/ - * _ / / /_/ // /__ _ / / / /_/ // /_/ / / / /(__ ) - * /_/ \__,_/ \___/ /_/ /_/_\__, / \____//_/ /_//____/ - * /____/ - * - * TABLE OF CONTENTS - * - * 1. External Library Includes - * - Normalize.css | http://normalize.css.github.io - * 2. Tachyons Modules - * 3. Variables - * - Media Queries - * - Colors - * 4. Debugging - * - Debug all - * - Debug children - * - */ - - -/* External Library Includes */ -@import 'tachyons/src/_normalize'; - - -/* Modules */ -@import 'tachyons/src/_box-sizing'; -/*@import 'tachyons/src/_aspect-ratios';*/ -@import 'tachyons/src/_images'; -@import 'tachyons/src/_background-size'; -@import 'tachyons/src/_background-position'; -/*@import 'tachyons/src/_outlines';*/ -@import 'tachyons/src/_borders'; -@import 'tachyons/src/_border-colors'; -@import 'tachyons/src/_border-radius'; -@import 'tachyons/src/_border-style'; -@import 'tachyons/src/_border-widths'; -@import 'tachyons/src/_box-shadow'; -/*@import 'tachyons/src/_code';*/ -@import 'tachyons/src/_coordinates'; -@import 'tachyons/src/_clears'; -@import 'tachyons/src/_display'; -@import 'tachyons/src/_flexbox'; -@import 'tachyons/src/_floats'; -/*@import 'tachyons/src/_font-family';*/ -@import 'tachyons/src/_font-style'; -@import 'tachyons/src/_font-weight'; -@import 'tachyons/src/_forms'; -@import 'tachyons/src/_heights'; -@import 'tachyons/src/_letter-spacing'; -@import 'tachyons/src/_line-height'; -@import 'tachyons/src/_links'; -@import 'tachyons/src/_lists'; -@import 'tachyons/src/_max-widths'; -@import 'tachyons/src/_widths'; -@import 'tachyons/src/_overflow'; -@import 'tachyons/src/_position'; -@import 'tachyons/src/_opacity'; -/*@import 'tachyons/src/_rotations';*/ -@import 'tachyons/src/_skins'; -@import 'tachyons/src/_skins-pseudo'; -@import 'tachyons/src/_spacing'; -@import 'tachyons/src/_negative-margins'; -@import 'tachyons/src/_tables'; -@import 'tachyons/src/_text-decoration'; -@import 'tachyons/src/_text-align'; -@import 'tachyons/src/_text-transform'; -@import 'tachyons/src/_type-scale'; -@import 'tachyons/src/_typography'; -@import 'tachyons/src/_utilities'; -@import 'tachyons/src/_visibility'; -@import 'tachyons/src/_white-space'; -@import 'tachyons/src/_vertical-align'; -@import 'tachyons/src/_hovers'; -@import 'tachyons/src/_z-index'; -@import 'tachyons/src/_nested'; -/*@import 'tachyons/src/_styles';*/ - -/* Variables */ -/* Importing here will allow you to override any variables in the modules */ -@import 'tachyons/src/_colors'; -@import 'tachyons/src/_media-queries'; - -/* Debugging */ -/*@import 'tachyons/src/_debug-children'; -@import 'tachyons/src/_debug-grid';*/ - -/* Uncomment out the line below to help debug layout issues */ -/* @import 'tachyons/src/_debug'; */ diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css deleted file mode 100644 index 8701b1530..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css +++ /dev/null @@ -1,16 +0,0 @@ -:root { - --primary-color: #0594CB; - --primary-color-dark: #0A1922; - --primary-color-light: #f9f9f9; - --accent-color: #EBB951; - --accent-color-light: #FF4088; - --accent-color-dark: #33ba91; - --text-color-primary: #373737; - --text-on-primary-color: #fff; - --text-color-secondary: #ccc; - --text-color-disabled: #F7f7f7; - --divider-color: #f6f6f6; - --warn-color: red; - - --blue: var(--primary-color); -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css deleted file mode 100644 index fd0f2a503..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css +++ /dev/null @@ -1,39 +0,0 @@ -/*Base Styles*/ -@import '_tachyons'; - -/* purgecss start ignore */ -@import '_header-link'; -@import '_animation'; -@import '_documentation-styles'; - -@import 'docsearch.js/dist/cdn/docsearch.min'; -@import '_carousel'; -@import '_code'; -@import '_tabs'; -@import '_color-scheme'; -@import '_columns'; -@import '_content'; -@import '_content-tables'; -@import '_definition-lists'; -@import '_fluid-type'; -@import '_font-family'; -@import '_hugo-internal-template-styling'; -@import '_no-js'; -@import '_social-icons'; -@import '_stickyheader'; -@import '_right-sidebar'; -@import '_svg'; -@import '_chroma'; -@import '_variables'; -@import '_print'; -@import '_shame'; - -.nested-blockquote blockquote { - border-left: 4px solid var(--primary-color); - padding-left: 1em; -} - -.mw-90 { - max-width:90%; -} -/* purgecss end ignore */ diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg b/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg deleted file mode 100644 index 7060e856f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js deleted file mode 100644 index c89af041b..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js +++ /dev/null @@ -1,10 +0,0 @@ -require('typeface-muli'); -import styles from './css/main.css'; -import './js/clipboardjs.js'; -import './js/docsearch.js'; -import './js/lazysizes.js'; -import './js/menutoggle.js'; -import './js/scrolldir.js'; -import './js/smoothscroll.js'; -import './js/tabs.js'; -import './js/nojs.js'; diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js deleted file mode 100644 index ffae31c7f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js +++ /dev/null @@ -1,30 +0,0 @@ -var Clipboard = require('clipboard/dist/clipboard.js'); -new Clipboard('.copy', { - target: function(trigger) { - if(trigger.classList.contains('copy-toggle')){ - return trigger.previousElementSibling; - } - return trigger.nextElementSibling; - } - }).on('success', function(e) { - successMessage(e.trigger, 'Copied!'); - e.clearSelection(); - }).on('error', function(e) { - successMessage(e.trigger, fallbackMessage(e.action)); -}); - -function successMessage(elem, msg) { - elem.setAttribute('class', 'copied bg-primary-color-dark f6 absolute top-0 right-0 lh-solid hover-bg-primary-color-dark bn white ph3 pv2'); - elem.setAttribute('aria-label', msg); -} - -function fallbackMessage(elem, action) { - var actionMsg = ''; - var actionKey = (action === 'cut' ? 'X' : 'C'); - if (isMac) { - actionMsg = 'Press ⌘-' + actionKey; - } else { - actionMsg = 'Press Ctrl-' + actionKey; - } - return actionMsg; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js deleted file mode 100644 index e14fb2994..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js +++ /dev/null @@ -1,8 +0,0 @@ -var docsearch = require('docsearch.js/dist/cdn/docsearch.js'); -docsearch({ - appId: 'D1BPLZHGYQ', - apiKey: '6df94e1e5d55d258c56f60d974d10314', - indexName: 'hugodocs', - inputSelector: '#search-input', - debug: true, // Set debug to true if you want to inspect the dropdown -}); diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/filesaver.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/filesaver.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js deleted file mode 100644 index 4eb3950af..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js +++ /dev/null @@ -1,3 +0,0 @@ -var lazysizes = require('lazysizes'); -// var lsnoscript = require('lazysizes/plugins/noscript/ls.noscript.js'); -var unveilhooks = require('lazysizes/plugins/unveilhooks/ls.unveilhooks.js'); diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js deleted file mode 100644 index f6d3eac9f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js +++ /dev/null @@ -1,22 +0,0 @@ -import styles from './../css/main.css'; -import './clipboardjs.js' -import './codeblocks.js' -import './docsearch.js' -import './lazysizes.js' -import './menutoggle.js' -import './scrolldir.js' -import './smoothscroll.js' -import './tabs.js' -import './nojs.js' - -// TO use jQuery, just call the modules you want -// var $ = require('jquery/src/core'); -// require('jquery/src/core/init'); -// require('jquery/src/manipulation'); - -// OR, use all of them -// var $ = require('jquery/src/jquery'); - -// And write your code -// $('body').append('

Jquery is working

'); -// diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js deleted file mode 100644 index 40bda0ce9..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js +++ /dev/null @@ -1,31 +0,0 @@ -// Grab any element that has the 'js-toggle' class and add an event listener for the toggleClass function -var toggleBtns = document.getElementsByClassName('js-toggle') - for (var i = 0; i < toggleBtns.length; i++) { - toggleBtns[i].addEventListener('click', toggleClass, false) - } - -function toggleClass() { - // Define the data target via the dataset "target" (e.g. data-target=".docsmenu") - var content = this.dataset.target.split(' ') - // Find any menu items that are open - var mobileCurrentlyOpen = document.querySelector('.mobilemenu:not(.dn)') - var desktopCurrentlyOpen = document.querySelector('.desktopmenu:not(.dn)') - var desktopActive = document.querySelector('.desktopmenu:not(.dn)') - - // Loop through the targets' divs - for (var i = 0; i < content.length; i++) { - var matches = document.querySelectorAll(content[i]); - //for each, if the div has the 'dn' class (which is "display:none;"), remove it, otherwise, add that class - [].forEach.call(matches, function(dom) { - dom.classList.contains('dn') ? - dom.classList.remove('dn') : - dom.classList.add('dn'); - return false; - }); - // close the currently open menu items - if (mobileCurrentlyOpen) mobileCurrentlyOpen.classList.add('dn') - if (desktopCurrentlyOpen) desktopCurrentlyOpen.classList.add('dn') - if (desktopActive) desktopActive.classList.remove('db') - - } - } diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js deleted file mode 100644 index 50b5126a9..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js +++ /dev/null @@ -1 +0,0 @@ -document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/, 'js'); diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js deleted file mode 100644 index 0b69978cd..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js +++ /dev/null @@ -1 +0,0 @@ -var scrollDir = require('scrolldir/dist/scrolldir.auto.min.js'); diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js deleted file mode 100644 index 4bb2d99b8..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js +++ /dev/null @@ -1,80 +0,0 @@ -// query selector targets Hugo TOC -(function() { - - 'use strict'; - - // Feature Test - if ('querySelector' in document && 'addEventListener' in window && Array.prototype.forEach) { - - // Function to animate the scroll - var smoothScroll = function(anchor, duration) { - - // Calculate how far and how fast to scroll - var startLocation = window.pageYOffset; - var endLocation = anchor.offsetTop; - var distance = endLocation - startLocation; - var increments = distance / (duration / 16); - var stopAnimation; - - // Scroll the page by an increment, and check if it's time to stop - var animateScroll = function() { - window.scrollBy(0, increments); - stopAnimation(); - }; - - // If scrolling down - if (increments >= 0) { - // Stop animation when you reach the anchor OR the bottom of the page - stopAnimation = function() { - var travelled = window.pageYOffset; - if ((travelled >= (endLocation - increments)) || ((window.innerHeight + travelled) >= document.body.offsetHeight)) { - clearInterval(runAnimation); - } - }; - } - // If scrolling up - else { - // Stop animation when you reach the anchor OR the top of the page - stopAnimation = function() { - var travelled = window.pageYOffset; - if (travelled <= (endLocation || 0)) { - clearInterval(runAnimation); - } - }; - } - - // Loop the animation function - var runAnimation = setInterval(animateScroll, 16); - - }; - - // Define smooth scroll links - var scrollToggle = document.querySelectorAll('#TableOfContents ul li a'); - - // For each smooth scroll link - [].forEach.call(scrollToggle, function(toggle) { - - // When the smooth scroll link is clicked - toggle.addEventListener('click', function(e) { - - // Prevent the default link behavior - e.preventDefault(); - - // Get anchor link and calculate distance from the top - var dataID = toggle.getAttribute('href'); - var dataTarget = document.querySelector(dataID); - var dataSpeed = toggle.getAttribute('data-speed'); - - // If the anchor exists - if (dataTarget) { - // Scroll to the anchor - smoothScroll(dataTarget, dataSpeed || 500); - } - - }, false); - - }); - - } - -})(); diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js deleted file mode 100644 index a689d474e..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Scripts which manages Code Toggle tabs. - */ -var i; -// store tabs variable -var allTabs = document.querySelectorAll("[data-toggle-tab]"); -var allPanes = document.querySelectorAll("[data-pane]"); - -function toggleTabs(event) { - - if(event.target){ - event.preventDefault(); - var clickedTab = event.currentTarget; - var targetKey = clickedTab.getAttribute("data-toggle-tab") - }else { - var targetKey = event - } - // We store the config language selected in users' localStorage - if(window.localStorage){ - window.localStorage.setItem("configLangPref", targetKey) - } - var selectedTabs = document.querySelectorAll("[data-toggle-tab='" + targetKey + "']"); - var selectedPanes = document.querySelectorAll("[data-pane='" + targetKey + "']"); - - for (var i = 0; i < allTabs.length; i++) { - allTabs[i].classList.remove("active"); - allPanes[i].classList.remove("active"); - } - - for (var i = 0; i < selectedTabs.length; i++) { - selectedTabs[i].classList.add("active"); - selectedPanes[i].classList.add("active"); - } - -} - -for (i = 0; i < allTabs.length; i++) { - allTabs[i].addEventListener("click", toggleTabs) -} -// Upon page load, if user has a preferred language in its localStorage, tabs are set to it. -if(window.localStorage.getItem('configLangPref')) { - toggleTabs(window.localStorage.getItem('configLangPref')) -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css b/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css deleted file mode 100644 index 4fd374a1f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css +++ /dev/null @@ -1,5349 +0,0 @@ -/* muli-200normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 200; - src: - local('Muli Extra Light '), - local('Muli-Extra Light'), - url(/fonts/muli-latin-200.woff2) format('woff2'), - url(/fonts/muli-latin-200.woff) format('woff'); /* Modern Browsers */ -} -/* muli-200italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 200; - src: - local('Muli Extra Light italic'), - local('Muli-Extra Lightitalic'), - url(/fonts/muli-latin-200italic.woff2) format('woff2'), - url(/fonts/muli-latin-200italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-300normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 300; - src: - local('Muli Light '), - local('Muli-Light'), - url(/fonts/muli-latin-300.woff2) format('woff2'), - url(/fonts/muli-latin-300.woff) format('woff'); /* Modern Browsers */ -} -/* muli-300italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 300; - src: - local('Muli Light italic'), - local('Muli-Lightitalic'), - url(/fonts/muli-latin-300italic.woff2) format('woff2'), - url(/fonts/muli-latin-300italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-400normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 400; - src: - local('Muli Regular '), - local('Muli-Regular'), - url(/fonts/muli-latin-400.woff2) format('woff2'), - url(/fonts/muli-latin-400.woff) format('woff'); /* Modern Browsers */ -} -/* muli-400italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 400; - src: - local('Muli Regular italic'), - local('Muli-Regularitalic'), - url(/fonts/muli-latin-400italic.woff2) format('woff2'), - url(/fonts/muli-latin-400italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-600normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 600; - src: - local('Muli SemiBold '), - local('Muli-SemiBold'), - url(/fonts/muli-latin-600.woff2) format('woff2'), - url(/fonts/muli-latin-600.woff) format('woff'); /* Modern Browsers */ -} -/* muli-600italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 600; - src: - local('Muli SemiBold italic'), - local('Muli-SemiBolditalic'), - url(/fonts/muli-latin-600italic.woff2) format('woff2'), - url(/fonts/muli-latin-600italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-700normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 700; - src: - local('Muli Bold '), - local('Muli-Bold'), - url(/fonts/muli-latin-700.woff2) format('woff2'), - url(/fonts/muli-latin-700.woff) format('woff'); /* Modern Browsers */ -} -/* muli-700italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 700; - src: - local('Muli Bold italic'), - local('Muli-Bolditalic'), - url(/fonts/muli-latin-700italic.woff2) format('woff2'), - url(/fonts/muli-latin-700italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-800normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 800; - src: - local('Muli ExtraBold '), - local('Muli-ExtraBold'), - url(/fonts/muli-latin-800.woff2) format('woff2'), - url(/fonts/muli-latin-800.woff) format('woff'); /* Modern Browsers */ -} -/* muli-800italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 800; - src: - local('Muli ExtraBold italic'), - local('Muli-ExtraBolditalic'), - url(/fonts/muli-latin-800italic.woff2) format('woff2'), - url(/fonts/muli-latin-800italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-900normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 900; - src: - local('Muli Black '), - local('Muli-Black'), - url(/fonts/muli-latin-900.woff2) format('woff2'), - url(/fonts/muli-latin-900.woff) format('woff'); /* Modern Browsers */ -} -/* muli-900italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 900; - src: - local('Muli Black italic'), - local('Muli-Blackitalic'), - url(/fonts/muli-latin-900italic.woff2) format('woff2'), - url(/fonts/muli-latin-900italic.woff) format('woff'); /* Modern Browsers */ -} - - -/*Base Styles*/ -/*! TACHYONS v4.7.0 | http://tachyons.io */ -/* - * NOTE: The Tachyons folder is for backup/reference only. This file references the module - * ________ ______ - * ___ __/_____ _________ /______ ______________________ - * __ / _ __ `/ ___/_ __ \_ / / / __ \_ __ \_ ___/ - * _ / / /_/ // /__ _ / / / /_/ // /_/ / / / /(__ ) - * /_/ \__,_/ \___/ /_/ /_/_\__, / \____//_/ /_//____/ - * /____/ - * - * TABLE OF CONTENTS - * - * 1. External Library Includes - * - Normalize.css | http://normalize.css.github.io - * 2. Tachyons Modules - * 3. Variables - * - Media Queries - * - Colors - * 4. Debugging - * - Debug all - * - Debug children - * - */ -/* External Library Includes */ -/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ -/* Document - ========================================================================== */ -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} -/* Sections - ========================================================================== */ -/** - * Remove the margin in all browsers. - */ -body { - margin: 0; -} -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ -h1 { - font-size: 2em; - margin: 0.67em 0; -} -/* Grouping content - ========================================================================== */ -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -/* Text-level semantics - ========================================================================== */ -/** - * Remove the gray background on active links in IE 10. - */ -a { - background-color: transparent; -} -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; /* 2 */ -} -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ -b, -strong { - font-weight: bolder; -} -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -/** - * Add the correct font size in all browsers. - */ -small { - font-size: 80%; -} -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -/* Embedded content - ========================================================================== */ -/** - * Remove the border on images inside links in IE 10. - */ -img { - border-style: none; -} -/* Forms - ========================================================================== */ -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ -button, -input { /* 1 */ - overflow: visible; -} -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ -button, -select { /* 1 */ - text-transform: none; -} -/** - * Correct the inability to style clickable types in iOS and Safari. - */ -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} -/** - * Remove the inner border and padding in Firefox. - */ -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} -/** - * Restore the focus styles unset by the previous rule. - */ -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} -/** - * Correct the padding in Firefox. - */ -fieldset { - padding: 0.35em 0.75em 0.625em; -} -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ -legend { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ -progress { - vertical-align: baseline; -} -/** - * Remove the default vertical scrollbar in IE 10+. - */ -textarea { - overflow: auto; -} -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ -[type="checkbox"], -[type="radio"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} -/* Interactive - ========================================================================== */ -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ -details { - display: block; -} -/* - * Add the correct display in all browsers. - */ -summary { - display: list-item; -} -/* Misc - ========================================================================== */ -/** - * Add the correct display in IE 10+. - */ -template { - display: none; -} -/** - * Add the correct display in IE 10. - */ -[hidden] { - display: none; -} -/* Modules */ -/* - - BOX SIZING - -*/ -html, -body, -div, -article, -aside, -section, -main, -nav, -footer, -header, -form, -fieldset, -legend, -pre, -code, -a, -h1,h2,h3,h4,h5,h6, -p, -ul, -ol, -li, -dl, -dt, -dd, -blockquote, -figcaption, -figure, -textarea, -table, -td, -th, -tr, -input[type="email"], -input[type="number"], -input[type="password"], -input[type="tel"], -input[type="text"], -input[type="url"], -.border-box { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -/*@import 'tachyons/src/_aspect-ratios';*/ -/* - - IMAGES - Docs: http://tachyons.io/docs/elements/images/ - -*/ -/* Responsive images! */ -img { max-width: 100%; } -/* - - BACKGROUND SIZE - Docs: http://tachyons.io/docs/themes/background-size/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* - Often used in combination with background image set as an inline style - on an html element. -*/ -.cover { background-size: cover!important; } -.contain { background-size: contain!important; } -@media screen and (min-width: 30em) { - .cover-ns { background-size: cover!important; } - .contain-ns { background-size: contain!important; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .cover-m { background-size: cover!important; } - .contain-m { background-size: contain!important; } -} -@media screen and (min-width: 60em) { - .cover-l { background-size: cover!important; } - .contain-l { background-size: contain!important; } -} -/* - - BACKGROUND POSITION - - Base: - bg = background - - Modifiers: - -center = center center - -top = top center - -right = center right - -bottom = bottom center - -left = center left - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.bg-center { - background-repeat: no-repeat; - background-position: center center; -} -.bg-top { - background-repeat: no-repeat; - background-position: top center; -} -.bg-right { - background-repeat: no-repeat; - background-position: center right; -} -.bg-bottom { - background-repeat: no-repeat; - background-position: bottom center; -} -.bg-left { - background-repeat: no-repeat; - background-position: center left; -} -@media screen and (min-width: 30em) { - .bg-center-ns { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-ns { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-ns { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-ns { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-ns { - background-repeat: no-repeat; - background-position: center left; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .bg-center-m { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-m { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-m { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-m { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-m { - background-repeat: no-repeat; - background-position: center left; - } -} -@media screen and (min-width: 60em) { - .bg-center-l { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-l { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-l { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-l { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-l { - background-repeat: no-repeat; - background-position: center left; - } -} -/*@import 'tachyons/src/_outlines';*/ -/* - - BORDERS - Docs: http://tachyons.io/docs/themes/borders/ - - Base: - b = border - - Modifiers: - a = all - t = top - r = right - b = bottom - l = left - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ba { border-style: solid; border-width: 1px; } -.bt { border-top-style: solid; border-top-width: 1px; } -.br { border-right-style: solid; border-right-width: 1px; } -.bb { border-bottom-style: solid; border-bottom-width: 1px; } -.bl { border-left-style: solid; border-left-width: 1px; } -.bn { border-style: none; border-width: 0; } -@media screen and (min-width: 30em) { - .ba-ns { border-style: solid; border-width: 1px; } - .bt-ns { border-top-style: solid; border-top-width: 1px; } - .br-ns { border-right-style: solid; border-right-width: 1px; } - .bb-ns { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-ns { border-left-style: solid; border-left-width: 1px; } - .bn-ns { border-style: none; border-width: 0; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ba-m { border-style: solid; border-width: 1px; } - .bt-m { border-top-style: solid; border-top-width: 1px; } - .br-m { border-right-style: solid; border-right-width: 1px; } - .bb-m { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-m { border-left-style: solid; border-left-width: 1px; } - .bn-m { border-style: none; border-width: 0; } -} -@media screen and (min-width: 60em) { - .ba-l { border-style: solid; border-width: 1px; } - .bt-l { border-top-style: solid; border-top-width: 1px; } - .br-l { border-right-style: solid; border-right-width: 1px; } - .bb-l { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-l { border-left-style: solid; border-left-width: 1px; } - .bn-l { border-style: none; border-width: 0; } -} -/* - - BORDER COLORS - Docs: http://tachyons.io/docs/themes/borders/ - - Border colors can be used to extend the base - border classes ba,bt,bb,br,bl found in the _borders.css file. - - The base border class by default will set the color of the border - to that of the current text color. These classes are for the cases - where you desire for the text and border colors to be different. - - Base: - b = border - - Modifiers: - --color-name = each color variable name is also a border color name - -*/ -.b--black { border-color: #000; } -.b--near-black { border-color: #111; } -.b--dark-gray { border-color: #333; } -.b--mid-gray { border-color: #555; } -.b--gray { border-color: #777; } -.b--silver { border-color: #999; } -.b--light-silver { border-color: #aaa; } -.b--moon-gray { border-color: #ccc; } -.b--light-gray { border-color: #eee; } -.b--near-white { border-color: #f4f4f4; } -.b--white { border-color: #fff; } -.b--white-90 { border-color: rgba(255, 255, 255, .9); } -.b--white-80 { border-color: rgba(255, 255, 255, .8); } -.b--white-70 { border-color: rgba(255, 255, 255, .7); } -.b--white-60 { border-color: rgba(255, 255, 255, .6); } -.b--white-50 { border-color: rgba(255, 255, 255, .5); } -.b--white-40 { border-color: rgba(255, 255, 255, .4); } -.b--white-30 { border-color: rgba(255, 255, 255, .3); } -.b--white-20 { border-color: rgba(255, 255, 255, .2); } -.b--white-10 { border-color: rgba(255, 255, 255, .1); } -.b--white-05 { border-color: rgba(255, 255, 255, .05); } -.b--white-025 { border-color: rgba(255, 255, 255, .025); } -.b--white-0125 { border-color: rgba(255, 255, 255, .0125); } -.b--black-90 { border-color: rgba(0, 0, 0, .9); } -.b--black-80 { border-color: rgba(0, 0, 0, .8); } -.b--black-70 { border-color: rgba(0, 0, 0, .7); } -.b--black-60 { border-color: rgba(0, 0, 0, .6); } -.b--black-50 { border-color: rgba(0, 0, 0, .5); } -.b--black-40 { border-color: rgba(0, 0, 0, .4); } -.b--black-30 { border-color: rgba(0, 0, 0, .3); } -.b--black-20 { border-color: rgba(0, 0, 0, .2); } -.b--black-10 { border-color: rgba(0, 0, 0, .1); } -.b--black-05 { border-color: rgba(0, 0, 0, .05); } -.b--black-025 { border-color: rgba(0, 0, 0, .025); } -.b--black-0125 { border-color: rgba(0, 0, 0, .0125); } -.b--dark-red { border-color: #e7040f; } -.b--red { border-color: #ff4136; } -.b--light-red { border-color: #ff725c; } -.b--orange { border-color: #ff6300; } -.b--gold { border-color: #ffb700; } -.b--yellow { border-color: #ffd700; } -.b--light-yellow { border-color: #fbf1a9; } -.b--purple { border-color: #5e2ca5; } -.b--light-purple { border-color: #a463f2; } -.b--dark-pink { border-color: #d5008f; } -.b--hot-pink { border-color: #ff41b4; } -.b--pink { border-color: #ff80cc; } -.b--light-pink { border-color: #ffa3d7; } -.b--dark-green { border-color: #137752; } -.b--green { border-color: #19a974; } -.b--light-green { border-color: #9eebcf; } -.b--navy { border-color: #001b44; } -.b--dark-blue { border-color: #00449e; } -.b--blue { border-color: #0594CB; } -.b--light-blue { border-color: #96ccff; } -.b--lightest-blue { border-color: #cdecff; } -.b--washed-blue { border-color: #f6fffe; } -.b--washed-green { border-color: #e8fdf5; } -.b--washed-yellow { border-color: #fffceb; } -.b--washed-red { border-color: #ffdfdf; } -.b--transparent { border-color: transparent; } -.b--inherit { border-color: inherit; } -/* - - BORDER RADIUS - Docs: http://tachyons.io/docs/themes/border-radius/ - - Base: - br = border-radius - - Modifiers: - 0 = 0/none - 1 = 1st step in scale - 2 = 2nd step in scale - 3 = 3rd step in scale - 4 = 4th step in scale - - Literal values: - -100 = 100% - -pill = 9999px - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.br0 { border-radius: 0; } -.br1 { border-radius: .125rem; } -.br2 { border-radius: .25rem; } -.br3 { border-radius: .5rem; } -.br4 { border-radius: 1rem; } -.br-100 { border-radius: 100%; } -.br-pill { border-radius: 9999px; } -.br--bottom { - border-top-left-radius: 0; - border-top-right-radius: 0; - } -.br--top { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } -.br--right { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -.br--left { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -@media screen and (min-width: 30em) { - .br0-ns { border-radius: 0; } - .br1-ns { border-radius: .125rem; } - .br2-ns { border-radius: .25rem; } - .br3-ns { border-radius: .5rem; } - .br4-ns { border-radius: 1rem; } - .br-100-ns { border-radius: 100%; } - .br-pill-ns { border-radius: 9999px; } - .br--bottom-ns { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-ns { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-ns { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-ns { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .br0-m { border-radius: 0; } - .br1-m { border-radius: .125rem; } - .br2-m { border-radius: .25rem; } - .br3-m { border-radius: .5rem; } - .br4-m { border-radius: 1rem; } - .br-100-m { border-radius: 100%; } - .br-pill-m { border-radius: 9999px; } - .br--bottom-m { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-m { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-m { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-m { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -@media screen and (min-width: 60em) { - .br0-l { border-radius: 0; } - .br1-l { border-radius: .125rem; } - .br2-l { border-radius: .25rem; } - .br3-l { border-radius: .5rem; } - .br4-l { border-radius: 1rem; } - .br-100-l { border-radius: 100%; } - .br-pill-l { border-radius: 9999px; } - .br--bottom-l { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-l { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-l { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-l { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -/* - - BORDER STYLES - Docs: http://tachyons.io/docs/themes/borders/ - - Depends on base border module in _borders.css - - Base: - b = border-style - - Modifiers: - --none = none - --dotted = dotted - --dashed = dashed - --solid = solid - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.b--dotted { border-style: dotted; } -.b--dashed { border-style: dashed; } -.b--solid { border-style: solid; } -.b--none { border-style: none; } -@media screen and (min-width: 30em) { - .b--dotted-ns { border-style: dotted; } - .b--dashed-ns { border-style: dashed; } - .b--solid-ns { border-style: solid; } - .b--none-ns { border-style: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .b--dotted-m { border-style: dotted; } - .b--dashed-m { border-style: dashed; } - .b--solid-m { border-style: solid; } - .b--none-m { border-style: none; } -} -@media screen and (min-width: 60em) { - .b--dotted-l { border-style: dotted; } - .b--dashed-l { border-style: dashed; } - .b--solid-l { border-style: solid; } - .b--none-l { border-style: none; } -} -/* - - BORDER WIDTHS - Docs: http://tachyons.io/docs/themes/borders/ - - Base: - bw = border-width - - Modifiers: - 0 = 0 width border - 1 = 1st step in border-width scale - 2 = 2nd step in border-width scale - 3 = 3rd step in border-width scale - 4 = 4th step in border-width scale - 5 = 5th step in border-width scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.bw0 { border-width: 0; } -.bw1 { border-width: .125rem; } -.bw2 { border-width: .25rem; } -.bw3 { border-width: .5rem; } -.bw4 { border-width: 1rem; } -.bw5 { border-width: 2rem; } -/* Resets */ -.bt-0 { border-top-width: 0; } -.br-0 { border-right-width: 0; } -.bb-0 { border-bottom-width: 0; } -.bl-0 { border-left-width: 0; } -@media screen and (min-width: 30em) { - .bw0-ns { border-width: 0; } - .bw1-ns { border-width: .125rem; } - .bw2-ns { border-width: .25rem; } - .bw3-ns { border-width: .5rem; } - .bw4-ns { border-width: 1rem; } - .bw5-ns { border-width: 2rem; } - .bt-0-ns { border-top-width: 0; } - .br-0-ns { border-right-width: 0; } - .bb-0-ns { border-bottom-width: 0; } - .bl-0-ns { border-left-width: 0; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .bw0-m { border-width: 0; } - .bw1-m { border-width: .125rem; } - .bw2-m { border-width: .25rem; } - .bw3-m { border-width: .5rem; } - .bw4-m { border-width: 1rem; } - .bw5-m { border-width: 2rem; } - .bt-0-m { border-top-width: 0; } - .br-0-m { border-right-width: 0; } - .bb-0-m { border-bottom-width: 0; } - .bl-0-m { border-left-width: 0; } -} -@media screen and (min-width: 60em) { - .bw0-l { border-width: 0; } - .bw1-l { border-width: .125rem; } - .bw2-l { border-width: .25rem; } - .bw3-l { border-width: .5rem; } - .bw4-l { border-width: 1rem; } - .bw5-l { border-width: 2rem; } - .bt-0-l { border-top-width: 0; } - .br-0-l { border-right-width: 0; } - .bb-0-l { border-bottom-width: 0; } - .bl-0-l { border-left-width: 0; } -} -/* - - BOX-SHADOW - Docs: http://tachyons.io/docs/themes/box-shadow/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.shadow-1 { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } -.shadow-2 { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } -.shadow-3 { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } -.shadow-4 { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } -.shadow-5 { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -@media screen and (min-width: 30em) { - .shadow-1-ns { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-ns { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-ns { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-ns { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-ns { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .shadow-1-m { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-m { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-m { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-m { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-m { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -@media screen and (min-width: 60em) { - .shadow-1-l { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-l { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-l { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-l { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-l { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -/*@import 'tachyons/src/_code';*/ -/* - - COORDINATES - Docs: http://tachyons.io/docs/layout/position/ - - Use in combination with the position module. - - Base: - top - bottom - right - left - - Modifiers: - -0 = literal value 0 - -1 = literal value 1 - -2 = literal value 2 - --1 = literal value -1 - --2 = literal value -2 - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.top-0 { top: 0; } -.right-0 { right: 0; } -.bottom-0 { bottom: 0; } -.left-0 { left: 0; } -.top-1 { top: 1rem; } -.right-1 { right: 1rem; } -.bottom-1 { bottom: 1rem; } -.left-1 { left: 1rem; } -.top-2 { top: 2rem; } -.right-2 { right: 2rem; } -.bottom-2 { bottom: 2rem; } -.left-2 { left: 2rem; } -.top--1 { top: -1rem; } -.right--1 { right: -1rem; } -.bottom--1 { bottom: -1rem; } -.left--1 { left: -1rem; } -.top--2 { top: -2rem; } -.right--2 { right: -2rem; } -.bottom--2 { bottom: -2rem; } -.left--2 { left: -2rem; } -.absolute--fill { - top: 0; - right: 0; - bottom: 0; - left: 0; -} -@media screen and (min-width: 30em) { - .top-0-ns { top: 0; } - .left-0-ns { left: 0; } - .right-0-ns { right: 0; } - .bottom-0-ns { bottom: 0; } - .top-1-ns { top: 1rem; } - .left-1-ns { left: 1rem; } - .right-1-ns { right: 1rem; } - .bottom-1-ns { bottom: 1rem; } - .top-2-ns { top: 2rem; } - .left-2-ns { left: 2rem; } - .right-2-ns { right: 2rem; } - .bottom-2-ns { bottom: 2rem; } - .top--1-ns { top: -1rem; } - .right--1-ns { right: -1rem; } - .bottom--1-ns { bottom: -1rem; } - .left--1-ns { left: -1rem; } - .top--2-ns { top: -2rem; } - .right--2-ns { right: -2rem; } - .bottom--2-ns { bottom: -2rem; } - .left--2-ns { left: -2rem; } - .absolute--fill-ns { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .top-0-m { top: 0; } - .left-0-m { left: 0; } - .right-0-m { right: 0; } - .bottom-0-m { bottom: 0; } - .top-1-m { top: 1rem; } - .left-1-m { left: 1rem; } - .right-1-m { right: 1rem; } - .bottom-1-m { bottom: 1rem; } - .top-2-m { top: 2rem; } - .left-2-m { left: 2rem; } - .right-2-m { right: 2rem; } - .bottom-2-m { bottom: 2rem; } - .top--1-m { top: -1rem; } - .right--1-m { right: -1rem; } - .bottom--1-m { bottom: -1rem; } - .left--1-m { left: -1rem; } - .top--2-m { top: -2rem; } - .right--2-m { right: -2rem; } - .bottom--2-m { bottom: -2rem; } - .left--2-m { left: -2rem; } - .absolute--fill-m { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -@media screen and (min-width: 60em) { - .top-0-l { top: 0; } - .left-0-l { left: 0; } - .right-0-l { right: 0; } - .bottom-0-l { bottom: 0; } - .top-1-l { top: 1rem; } - .left-1-l { left: 1rem; } - .right-1-l { right: 1rem; } - .bottom-1-l { bottom: 1rem; } - .top-2-l { top: 2rem; } - .left-2-l { left: 2rem; } - .right-2-l { right: 2rem; } - .bottom-2-l { bottom: 2rem; } - .top--1-l { top: -1rem; } - .right--1-l { right: -1rem; } - .bottom--1-l { bottom: -1rem; } - .left--1-l { left: -1rem; } - .top--2-l { top: -2rem; } - .right--2-l { right: -2rem; } - .bottom--2-l { bottom: -2rem; } - .left--2-l { left: -2rem; } - .absolute--fill-l { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -/* - - CLEARFIX - http://tachyons.io/docs/layout/clearfix/ - -*/ -/* Nicolas Gallaghers Clearfix solution - Ref: http://nicolasgallagher.com/micro-clearfix-hack/ */ -.cf:before, -.cf:after { content: " "; display: table; } -.cf:after { clear: both; } -.cf { *zoom: 1; } -.cl { clear: left; } -.cr { clear: right; } -.cb { clear: both; } -.cn { clear: none; } -@media screen and (min-width: 30em) { - .cl-ns { clear: left; } - .cr-ns { clear: right; } - .cb-ns { clear: both; } - .cn-ns { clear: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .cl-m { clear: left; } - .cr-m { clear: right; } - .cb-m { clear: both; } - .cn-m { clear: none; } -} -@media screen and (min-width: 60em) { - .cl-l { clear: left; } - .cr-l { clear: right; } - .cb-l { clear: both; } - .cn-l { clear: none; } -} -/* - - DISPLAY - Docs: http://tachyons.io/docs/layout/display - - Base: - d = display - - Modifiers: - n = none - b = block - ib = inline-block - it = inline-table - t = table - tc = table-cell - t-row = table-row - t-columm = table-column - t-column-group = table-column-group - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.dn { display: none; } -.di { display: inline; } -.db { display: block; } -.dib { display: inline-block; } -.dit { display: inline-table; } -.dt { display: table; } -.dtc { display: table-cell; } -.dt-row { display: table-row; } -.dt-row-group { display: table-row-group; } -.dt-column { display: table-column; } -.dt-column-group { display: table-column-group; } -/* - This will set table to full width and then - all cells will be equal width -*/ -.dt--fixed { - table-layout: fixed; - width: 100%; -} -@media screen and (min-width: 30em) { - .dn-ns { display: none; } - .di-ns { display: inline; } - .db-ns { display: block; } - .dib-ns { display: inline-block; } - .dit-ns { display: inline-table; } - .dt-ns { display: table; } - .dtc-ns { display: table-cell; } - .dt-row-ns { display: table-row; } - .dt-row-group-ns { display: table-row-group; } - .dt-column-ns { display: table-column; } - .dt-column-group-ns { display: table-column-group; } - - .dt--fixed-ns { - table-layout: fixed; - width: 100%; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .dn-m { display: none; } - .di-m { display: inline; } - .db-m { display: block; } - .dib-m { display: inline-block; } - .dit-m { display: inline-table; } - .dt-m { display: table; } - .dtc-m { display: table-cell; } - .dt-row-m { display: table-row; } - .dt-row-group-m { display: table-row-group; } - .dt-column-m { display: table-column; } - .dt-column-group-m { display: table-column-group; } - - .dt--fixed-m { - table-layout: fixed; - width: 100%; - } -} -@media screen and (min-width: 60em) { - .dn-l { display: none; } - .di-l { display: inline; } - .db-l { display: block; } - .dib-l { display: inline-block; } - .dit-l { display: inline-table; } - .dt-l { display: table; } - .dtc-l { display: table-cell; } - .dt-row-l { display: table-row; } - .dt-row-group-l { display: table-row-group; } - .dt-column-l { display: table-column; } - .dt-column-group-l { display: table-column-group; } - - .dt--fixed-l { - table-layout: fixed; - width: 100%; - } -} -/* - - FLEXBOX - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.flex { display: -webkit-box; display: -ms-flexbox; display: flex; } -.inline-flex { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } -/* 1. Fix for Chrome 44 bug. - * https://code.google.com/p/chromium/issues/detail?id=506893 */ -.flex-auto { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ -} -.flex-none { -webkit-box-flex: 0; -ms-flex: none; flex: none; } -.flex-column { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } -.flex-row { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } -.flex-wrap { -ms-flex-wrap: wrap; flex-wrap: wrap; } -.flex-nowrap { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } -.flex-wrap-reverse { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } -.flex-column-reverse { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } -.flex-row-reverse { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } -.items-start { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } -.items-end { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } -.items-center { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } -.items-baseline { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } -.items-stretch { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } -.self-start { -ms-flex-item-align: start; align-self: flex-start; } -.self-end { -ms-flex-item-align: end; align-self: flex-end; } -.self-center { -ms-flex-item-align: center; align-self: center; } -.self-baseline { -ms-flex-item-align: baseline; align-self: baseline; } -.self-stretch { -ms-flex-item-align: stretch; align-self: stretch; } -.justify-start { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } -.justify-end { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } -.justify-center { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } -.justify-between { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } -.justify-around { -ms-flex-pack: distribute; justify-content: space-around; } -.content-start { -ms-flex-line-pack: start; align-content: flex-start; } -.content-end { -ms-flex-line-pack: end; align-content: flex-end; } -.content-center { -ms-flex-line-pack: center; align-content: center; } -.content-between { -ms-flex-line-pack: justify; align-content: space-between; } -.content-around { -ms-flex-line-pack: distribute; align-content: space-around; } -.content-stretch { -ms-flex-line-pack: stretch; align-content: stretch; } -.order-0 { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } -.order-1 { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } -.order-2 { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } -.order-3 { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } -.order-4 { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } -.order-5 { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } -.order-6 { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } -.order-7 { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } -.order-8 { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } -.order-last { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } -.flex-grow-0 { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } -.flex-grow-1 { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } -.flex-shrink-0 { -ms-flex-negative: 0; flex-shrink: 0; } -.flex-shrink-1 { -ms-flex-negative: 1; flex-shrink: 1; } -@media screen and (min-width: 30em) { - .flex-ns { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-ns { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-ns { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-ns { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-ns { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-ns { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-ns { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-ns { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-ns { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-ns { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-ns { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - .items-start-ns { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-ns { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-ns { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-ns { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-ns { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-ns { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-ns { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-ns { -ms-flex-item-align: center; align-self: center; } - .self-baseline-ns { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-ns { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-ns { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-ns { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-ns { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-ns { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-ns { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-ns { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-ns { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-ns { -ms-flex-line-pack: center; align-content: center; } - .content-between-ns { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-ns { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-ns { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-ns { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-ns { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-ns { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-ns { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-ns { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-ns { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-ns { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-ns { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-ns { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-ns { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-ns { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-ns { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-ns { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-ns { -ms-flex-negative: 1; flex-shrink: 1; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .flex-m { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-m { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-m { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-m { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-m { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-m { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-m { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-m { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-m { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-m { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-m { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - .items-start-m { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-m { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-m { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-m { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-m { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-m { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-m { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-m { -ms-flex-item-align: center; align-self: center; } - .self-baseline-m { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-m { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-m { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-m { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-m { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-m { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-m { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-m { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-m { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-m { -ms-flex-line-pack: center; align-content: center; } - .content-between-m { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-m { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-m { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-m { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-m { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-m { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-m { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-m { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-m { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-m { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-m { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-m { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-m { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-m { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-m { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-m { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-m { -ms-flex-negative: 1; flex-shrink: 1; } -} -@media screen and (min-width: 60em) { - .flex-l { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-l { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-l { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-l { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-l { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-l { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-l { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-l { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-l { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-l { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-l { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - - .items-start-l { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-l { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-l { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-l { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-l { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-l { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-l { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-l { -ms-flex-item-align: center; align-self: center; } - .self-baseline-l { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-l { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-l { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-l { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-l { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-l { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-l { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-l { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-l { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-l { -ms-flex-line-pack: center; align-content: center; } - .content-between-l { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-l { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-l { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-l { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-l { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-l { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-l { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-l { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-l { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-l { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-l { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-l { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-l { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-l { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-l { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-l { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-l { -ms-flex-negative: 1; flex-shrink: 1; } -} -/* - - FLOATS - http://tachyons.io/docs/layout/floats/ - - 1. Floated elements are automatically rendered as block level elements. - Setting floats to display inline will fix the double margin bug in - ie6. You know... just in case. - - 2. Don't forget to clearfix your floats with .cf - - Base: - f = float - - Modifiers: - l = left - r = right - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.fl { float: left; _display: inline; } -.fr { float: right; _display: inline; } -.fn { float: none; } -@media screen and (min-width: 30em) { - .fl-ns { float: left; _display: inline; } - .fr-ns { float: right; _display: inline; } - .fn-ns { float: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .fl-m { float: left; _display: inline; } - .fr-m { float: right; _display: inline; } - .fn-m { float: none; } -} -@media screen and (min-width: 60em) { - .fl-l { float: left; _display: inline; } - .fr-l { float: right; _display: inline; } - .fn-l { float: none; } -} -/*@import 'tachyons/src/_font-family';*/ -/* - - FONT STYLE - Docs: http://tachyons.io/docs/typography/font-style/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.i { font-style: italic; } -.fs-normal { font-style: normal; } -@media screen and (min-width: 30em) { - .i-ns { font-style: italic; } - .fs-normal-ns { font-style: normal; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .i-m { font-style: italic; } - .fs-normal-m { font-style: normal; } -} -@media screen and (min-width: 60em) { - .i-l { font-style: italic; } - .fs-normal-l { font-style: normal; } -} -/* - - FONT WEIGHT - Docs: http://tachyons.io/docs/typography/font-weight/ - - Base - fw = font-weight - - Modifiers: - 1 = literal value 100 - 2 = literal value 200 - 3 = literal value 300 - 4 = literal value 400 - 5 = literal value 500 - 6 = literal value 600 - 7 = literal value 700 - 8 = literal value 800 - 9 = literal value 900 - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.normal { font-weight: normal; } -.b { font-weight: bold; } -.fw1 { font-weight: 100; } -.fw2 { font-weight: 200; } -.fw3 { font-weight: 300; } -.fw4 { font-weight: 400; } -.fw5 { font-weight: 500; } -.fw6 { font-weight: 600; } -.fw7 { font-weight: 700; } -.fw8 { font-weight: 800; } -.fw9 { font-weight: 900; } -@media screen and (min-width: 30em) { - .normal-ns { font-weight: normal; } - .b-ns { font-weight: bold; } - .fw1-ns { font-weight: 100; } - .fw2-ns { font-weight: 200; } - .fw3-ns { font-weight: 300; } - .fw4-ns { font-weight: 400; } - .fw5-ns { font-weight: 500; } - .fw6-ns { font-weight: 600; } - .fw7-ns { font-weight: 700; } - .fw8-ns { font-weight: 800; } - .fw9-ns { font-weight: 900; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .normal-m { font-weight: normal; } - .b-m { font-weight: bold; } - .fw1-m { font-weight: 100; } - .fw2-m { font-weight: 200; } - .fw3-m { font-weight: 300; } - .fw4-m { font-weight: 400; } - .fw5-m { font-weight: 500; } - .fw6-m { font-weight: 600; } - .fw7-m { font-weight: 700; } - .fw8-m { font-weight: 800; } - .fw9-m { font-weight: 900; } -} -@media screen and (min-width: 60em) { - .normal-l { font-weight: normal; } - .b-l { font-weight: bold; } - .fw1-l { font-weight: 100; } - .fw2-l { font-weight: 200; } - .fw3-l { font-weight: 300; } - .fw4-l { font-weight: 400; } - .fw5-l { font-weight: 500; } - .fw6-l { font-weight: 600; } - .fw7-l { font-weight: 700; } - .fw8-l { font-weight: 800; } - .fw9-l { font-weight: 900; } -} -/* - - FORMS - -*/ -.input-reset { - -webkit-appearance: none; - -moz-appearance: none; -} -.button-reset::-moz-focus-inner, -.input-reset::-moz-focus-inner { - border: 0; - padding: 0; -} -/* - - HEIGHTS - Docs: http://tachyons.io/docs/layout/heights/ - - Base: - h = height - min-h = min-height - min-vh = min-height vertical screen height - vh = vertical screen height - - Modifiers - 1 = 1st step in height scale - 2 = 2nd step in height scale - 3 = 3rd step in height scale - 4 = 4th step in height scale - 5 = 5th step in height scale - - -25 = literal value 25% - -50 = literal value 50% - -75 = literal value 75% - -100 = literal value 100% - - -auto = string value of auto - -inherit = string value of inherit - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Height Scale */ -.h1 { height: 1rem; } -.h2 { height: 2rem; } -.h3 { height: 4rem; } -.h4 { height: 8rem; } -.h5 { height: 16rem; } -/* Height Percentages - Based off of height of parent */ -.h-25 { height: 25%; } -.h-50 { height: 50%; } -.h-75 { height: 75%; } -.h-100 { height: 100%; } -.min-h-100 { min-height: 100%; } -/* Screen Height Percentage */ -.vh-25 { height: 25vh; } -.vh-50 { height: 50vh; } -.vh-75 { height: 75vh; } -.vh-100 { height: 100vh; } -.min-vh-100 { min-height: 100vh; } -/* String Properties */ -.h-auto { height: auto; } -.h-inherit { height: inherit; } -@media screen and (min-width: 30em) { - .h1-ns { height: 1rem; } - .h2-ns { height: 2rem; } - .h3-ns { height: 4rem; } - .h4-ns { height: 8rem; } - .h5-ns { height: 16rem; } - .h-25-ns { height: 25%; } - .h-50-ns { height: 50%; } - .h-75-ns { height: 75%; } - .h-100-ns { height: 100%; } - .min-h-100-ns { min-height: 100%; } - .vh-25-ns { height: 25vh; } - .vh-50-ns { height: 50vh; } - .vh-75-ns { height: 75vh; } - .vh-100-ns { height: 100vh; } - .min-vh-100-ns { min-height: 100vh; } - .h-auto-ns { height: auto; } - .h-inherit-ns { height: inherit; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .h1-m { height: 1rem; } - .h2-m { height: 2rem; } - .h3-m { height: 4rem; } - .h4-m { height: 8rem; } - .h5-m { height: 16rem; } - .h-25-m { height: 25%; } - .h-50-m { height: 50%; } - .h-75-m { height: 75%; } - .h-100-m { height: 100%; } - .min-h-100-m { min-height: 100%; } - .vh-25-m { height: 25vh; } - .vh-50-m { height: 50vh; } - .vh-75-m { height: 75vh; } - .vh-100-m { height: 100vh; } - .min-vh-100-m { min-height: 100vh; } - .h-auto-m { height: auto; } - .h-inherit-m { height: inherit; } -} -@media screen and (min-width: 60em) { - .h1-l { height: 1rem; } - .h2-l { height: 2rem; } - .h3-l { height: 4rem; } - .h4-l { height: 8rem; } - .h5-l { height: 16rem; } - .h-25-l { height: 25%; } - .h-50-l { height: 50%; } - .h-75-l { height: 75%; } - .h-100-l { height: 100%; } - .min-h-100-l { min-height: 100%; } - .vh-25-l { height: 25vh; } - .vh-50-l { height: 50vh; } - .vh-75-l { height: 75vh; } - .vh-100-l { height: 100vh; } - .min-vh-100-l { min-height: 100vh; } - .h-auto-l { height: auto; } - .h-inherit-l { height: inherit; } -} -/* - - LETTER SPACING - Docs: http://tachyons.io/docs/typography/tracking/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.tracked { letter-spacing: .1em; } -.tracked-tight { letter-spacing: -.05em; } -.tracked-mega { letter-spacing: .25em; } -@media screen and (min-width: 30em) { - .tracked-ns { letter-spacing: .1em; } - .tracked-tight-ns { letter-spacing: -.05em; } - .tracked-mega-ns { letter-spacing: .25em; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .tracked-m { letter-spacing: .1em; } - .tracked-tight-m { letter-spacing: -.05em; } - .tracked-mega-m { letter-spacing: .25em; } -} -@media screen and (min-width: 60em) { - .tracked-l { letter-spacing: .1em; } - .tracked-tight-l { letter-spacing: -.05em; } - .tracked-mega-l { letter-spacing: .25em; } -} -/* - - LINE HEIGHT / LEADING - Docs: http://tachyons.io/docs/typography/line-height - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.lh-solid { line-height: 1; } -.lh-title { line-height: 1.25; } -.lh-copy { line-height: 1.5; } -@media screen and (min-width: 30em) { - .lh-solid-ns { line-height: 1; } - .lh-title-ns { line-height: 1.25; } - .lh-copy-ns { line-height: 1.5; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .lh-solid-m { line-height: 1; } - .lh-title-m { line-height: 1.25; } - .lh-copy-m { line-height: 1.5; } -} -@media screen and (min-width: 60em) { - .lh-solid-l { line-height: 1; } - .lh-title-l { line-height: 1.25; } - .lh-copy-l { line-height: 1.5; } -} -/* - - LINKS - Docs: http://tachyons.io/docs/elements/links/ - -*/ -.link { - text-decoration: none; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:link, -.link:visited { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:hover { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:active { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:focus { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; - outline: 1px dotted currentColor; -} -/* - - LISTS - http://tachyons.io/docs/elements/lists/ - -*/ -.list { list-style-type: none; } -/* - - MAX WIDTHS - Docs: http://tachyons.io/docs/layout/max-widths/ - - Base: - mw = max-width - - Modifiers - 1 = 1st step in width scale - 2 = 2nd step in width scale - 3 = 3rd step in width scale - 4 = 4th step in width scale - 5 = 5th step in width scale - 6 = 6st step in width scale - 7 = 7nd step in width scale - 8 = 8rd step in width scale - 9 = 9th step in width scale - - -100 = literal value 100% - - -none = string value none - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Max Width Percentages */ -.mw-100 { max-width: 100%; } -/* Max Width Scale */ -.mw1 { max-width: 1rem; } -.mw2 { max-width: 2rem; } -.mw3 { max-width: 4rem; } -.mw4 { max-width: 8rem; } -.mw5 { max-width: 16rem; } -.mw6 { max-width: 32rem; } -.mw7 { max-width: 48rem; } -.mw8 { max-width: 64rem; } -.mw9 { max-width: 96rem; } -/* Max Width String Properties */ -.mw-none { max-width: none; } -@media screen and (min-width: 30em) { - .mw-100-ns { max-width: 100%; } - - .mw1-ns { max-width: 1rem; } - .mw2-ns { max-width: 2rem; } - .mw3-ns { max-width: 4rem; } - .mw4-ns { max-width: 8rem; } - .mw5-ns { max-width: 16rem; } - .mw6-ns { max-width: 32rem; } - .mw7-ns { max-width: 48rem; } - .mw8-ns { max-width: 64rem; } - .mw9-ns { max-width: 96rem; } - - .mw-none-ns { max-width: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .mw-100-m { max-width: 100%; } - - .mw1-m { max-width: 1rem; } - .mw2-m { max-width: 2rem; } - .mw3-m { max-width: 4rem; } - .mw4-m { max-width: 8rem; } - .mw5-m { max-width: 16rem; } - .mw6-m { max-width: 32rem; } - .mw7-m { max-width: 48rem; } - .mw8-m { max-width: 64rem; } - .mw9-m { max-width: 96rem; } - - .mw-none-m { max-width: none; } -} -@media screen and (min-width: 60em) { - .mw-100-l { max-width: 100%; } - - .mw1-l { max-width: 1rem; } - .mw2-l { max-width: 2rem; } - .mw3-l { max-width: 4rem; } - .mw4-l { max-width: 8rem; } - .mw5-l { max-width: 16rem; } - .mw6-l { max-width: 32rem; } - .mw7-l { max-width: 48rem; } - .mw8-l { max-width: 64rem; } - .mw9-l { max-width: 96rem; } - - .mw-none-l { max-width: none; } -} -/* - - WIDTHS - Docs: http://tachyons.io/docs/layout/widths/ - - Base: - w = width - - Modifiers - 1 = 1st step in width scale - 2 = 2nd step in width scale - 3 = 3rd step in width scale - 4 = 4th step in width scale - 5 = 5th step in width scale - - -10 = literal value 10% - -20 = literal value 20% - -25 = literal value 25% - -30 = literal value 30% - -33 = literal value 33% - -34 = literal value 34% - -40 = literal value 40% - -50 = literal value 50% - -60 = literal value 60% - -70 = literal value 70% - -75 = literal value 75% - -80 = literal value 80% - -90 = literal value 90% - -100 = literal value 100% - - -third = 100% / 3 (Not supported in opera mini or IE8) - -two-thirds = 100% / 1.5 (Not supported in opera mini or IE8) - -auto = string value auto - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Width Scale */ -.w1 { width: 1rem; } -.w2 { width: 2rem; } -.w3 { width: 4rem; } -.w4 { width: 8rem; } -.w5 { width: 16rem; } -.w-10 { width: 10%; } -.w-20 { width: 20%; } -.w-25 { width: 25%; } -.w-30 { width: 30%; } -.w-33 { width: 33%; } -.w-34 { width: 34%; } -.w-40 { width: 40%; } -.w-50 { width: 50%; } -.w-60 { width: 60%; } -.w-70 { width: 70%; } -.w-75 { width: 75%; } -.w-80 { width: 80%; } -.w-90 { width: 90%; } -.w-100 { width: 100%; } -.w-third { width: 33.33333%; } -.w-two-thirds { width: 66.66667%; } -.w-auto { width: auto; } -@media screen and (min-width: 30em) { - .w1-ns { width: 1rem; } - .w2-ns { width: 2rem; } - .w3-ns { width: 4rem; } - .w4-ns { width: 8rem; } - .w5-ns { width: 16rem; } - .w-10-ns { width: 10%; } - .w-20-ns { width: 20%; } - .w-25-ns { width: 25%; } - .w-30-ns { width: 30%; } - .w-33-ns { width: 33%; } - .w-34-ns { width: 34%; } - .w-40-ns { width: 40%; } - .w-50-ns { width: 50%; } - .w-60-ns { width: 60%; } - .w-70-ns { width: 70%; } - .w-75-ns { width: 75%; } - .w-80-ns { width: 80%; } - .w-90-ns { width: 90%; } - .w-100-ns { width: 100%; } - .w-third-ns { width: 33.33333%; } - .w-two-thirds-ns { width: 66.66667%; } - .w-auto-ns { width: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .w1-m { width: 1rem; } - .w2-m { width: 2rem; } - .w3-m { width: 4rem; } - .w4-m { width: 8rem; } - .w5-m { width: 16rem; } - .w-10-m { width: 10%; } - .w-20-m { width: 20%; } - .w-25-m { width: 25%; } - .w-30-m { width: 30%; } - .w-33-m { width: 33%; } - .w-34-m { width: 34%; } - .w-40-m { width: 40%; } - .w-50-m { width: 50%; } - .w-60-m { width: 60%; } - .w-70-m { width: 70%; } - .w-75-m { width: 75%; } - .w-80-m { width: 80%; } - .w-90-m { width: 90%; } - .w-100-m { width: 100%; } - .w-third-m { width: 33.33333%; } - .w-two-thirds-m { width: 66.66667%; } - .w-auto-m { width: auto; } -} -@media screen and (min-width: 60em) { - .w1-l { width: 1rem; } - .w2-l { width: 2rem; } - .w3-l { width: 4rem; } - .w4-l { width: 8rem; } - .w5-l { width: 16rem; } - .w-10-l { width: 10%; } - .w-20-l { width: 20%; } - .w-25-l { width: 25%; } - .w-30-l { width: 30%; } - .w-33-l { width: 33%; } - .w-34-l { width: 34%; } - .w-40-l { width: 40%; } - .w-50-l { width: 50%; } - .w-60-l { width: 60%; } - .w-70-l { width: 70%; } - .w-75-l { width: 75%; } - .w-80-l { width: 80%; } - .w-90-l { width: 90%; } - .w-100-l { width: 100%; } - .w-third-l { width: 33.33333%; } - .w-two-thirds-l { width: 66.66667%; } - .w-auto-l { width: auto; } -} -/* - - OVERFLOW - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.overflow-visible { overflow: visible; } -.overflow-hidden { overflow: hidden; } -.overflow-scroll { overflow: scroll; } -.overflow-auto { overflow: auto; } -.overflow-x-visible { overflow-x: visible; } -.overflow-x-hidden { overflow-x: hidden; } -.overflow-x-scroll { overflow-x: scroll; } -.overflow-x-auto { overflow-x: auto; } -.overflow-y-visible { overflow-y: visible; } -.overflow-y-hidden { overflow-y: hidden; } -.overflow-y-scroll { overflow-y: scroll; } -.overflow-y-auto { overflow-y: auto; } -@media screen and (min-width: 30em) { - .overflow-visible-ns { overflow: visible; } - .overflow-hidden-ns { overflow: hidden; } - .overflow-scroll-ns { overflow: scroll; } - .overflow-auto-ns { overflow: auto; } - .overflow-x-visible-ns { overflow-x: visible; } - .overflow-x-hidden-ns { overflow-x: hidden; } - .overflow-x-scroll-ns { overflow-x: scroll; } - .overflow-x-auto-ns { overflow-x: auto; } - - .overflow-y-visible-ns { overflow-y: visible; } - .overflow-y-hidden-ns { overflow-y: hidden; } - .overflow-y-scroll-ns { overflow-y: scroll; } - .overflow-y-auto-ns { overflow-y: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .overflow-visible-m { overflow: visible; } - .overflow-hidden-m { overflow: hidden; } - .overflow-scroll-m { overflow: scroll; } - .overflow-auto-m { overflow: auto; } - - .overflow-x-visible-m { overflow-x: visible; } - .overflow-x-hidden-m { overflow-x: hidden; } - .overflow-x-scroll-m { overflow-x: scroll; } - .overflow-x-auto-m { overflow-x: auto; } - - .overflow-y-visible-m { overflow-y: visible; } - .overflow-y-hidden-m { overflow-y: hidden; } - .overflow-y-scroll-m { overflow-y: scroll; } - .overflow-y-auto-m { overflow-y: auto; } -} -@media screen and (min-width: 60em) { - .overflow-visible-l { overflow: visible; } - .overflow-hidden-l { overflow: hidden; } - .overflow-scroll-l { overflow: scroll; } - .overflow-auto-l { overflow: auto; } - - .overflow-x-visible-l { overflow-x: visible; } - .overflow-x-hidden-l { overflow-x: hidden; } - .overflow-x-scroll-l { overflow-x: scroll; } - .overflow-x-auto-l { overflow-x: auto; } - - .overflow-y-visible-l { overflow-y: visible; } - .overflow-y-hidden-l { overflow-y: hidden; } - .overflow-y-scroll-l { overflow-y: scroll; } - .overflow-y-auto-l { overflow-y: auto; } -} -/* - - POSITIONING - Docs: http://tachyons.io/docs/layout/position/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.static { position: static; } -.relative { position: relative; } -.absolute { position: absolute; } -.fixed { position: fixed; } -@media screen and (min-width: 30em) { - .static-ns { position: static; } - .relative-ns { position: relative; } - .absolute-ns { position: absolute; } - .fixed-ns { position: fixed; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .static-m { position: static; } - .relative-m { position: relative; } - .absolute-m { position: absolute; } - .fixed-m { position: fixed; } -} -@media screen and (min-width: 60em) { - .static-l { position: static; } - .relative-l { position: relative; } - .absolute-l { position: absolute; } - .fixed-l { position: fixed; } -} -/* - - OPACITY - Docs: http://tachyons.io/docs/themes/opacity/ - -*/ -.o-100 { opacity: 1; } -.o-90 { opacity: .9; } -.o-80 { opacity: .8; } -.o-70 { opacity: .7; } -.o-60 { opacity: .6; } -.o-50 { opacity: .5; } -.o-40 { opacity: .4; } -.o-30 { opacity: .3; } -.o-20 { opacity: .2; } -.o-10 { opacity: .1; } -.o-05 { opacity: .05; } -.o-025 { opacity: .025; } -.o-0 { opacity: 0; } -/*@import 'tachyons/src/_rotations';*/ -/* - - SKINS - Docs: http://tachyons.io/docs/themes/skins/ - - Classes for setting foreground and background colors on elements. - If you haven't declared a border color, but set border on an element, it will - be set to the current text color. - -*/ -/* Text colors */ -.black-90 { color: rgba(0, 0, 0, .9); } -.black-80 { color: rgba(0, 0, 0, .8); } -.black-70 { color: rgba(0, 0, 0, .7); } -.black-60 { color: rgba(0, 0, 0, .6); } -.black-50 { color: rgba(0, 0, 0, .5); } -.black-40 { color: rgba(0, 0, 0, .4); } -.black-30 { color: rgba(0, 0, 0, .3); } -.black-20 { color: rgba(0, 0, 0, .2); } -.black-10 { color: rgba(0, 0, 0, .1); } -.black-05 { color: rgba(0, 0, 0, .05); } -.white-90 { color: rgba(255, 255, 255, .9); } -.white-80 { color: rgba(255, 255, 255, .8); } -.white-70 { color: rgba(255, 255, 255, .7); } -.white-60 { color: rgba(255, 255, 255, .6); } -.white-50 { color: rgba(255, 255, 255, .5); } -.white-40 { color: rgba(255, 255, 255, .4); } -.white-30 { color: rgba(255, 255, 255, .3); } -.white-20 { color: rgba(255, 255, 255, .2); } -.white-10 { color: rgba(255, 255, 255, .1); } -.black { color: #000; } -.near-black { color: #111; } -.dark-gray { color: #333; } -.mid-gray { color: #555; } -.gray { color: #777; } -.silver { color: #999; } -.light-silver { color: #aaa; } -.moon-gray { color: #ccc; } -.light-gray { color: #eee; } -.near-white { color: #f4f4f4; } -.white { color: #fff; } -.dark-red { color: #e7040f; } -.red { color: #ff4136; } -.light-red { color: #ff725c; } -.orange { color: #ff6300; } -.gold { color: #ffb700; } -.yellow { color: #ffd700; } -.light-yellow { color: #fbf1a9; } -.purple { color: #5e2ca5; } -.light-purple { color: #a463f2; } -.dark-pink { color: #d5008f; } -.hot-pink { color: #ff41b4; } -.pink { color: #ff80cc; } -.light-pink { color: #ffa3d7; } -.dark-green { color: #137752; } -.green { color: #19a974; } -.light-green { color: #9eebcf; } -.navy { color: #001b44; } -.dark-blue { color: #00449e; } -.blue { color: #0594CB; } -.light-blue { color: #96ccff; } -.lightest-blue { color: #cdecff; } -.washed-blue { color: #f6fffe; } -.washed-green { color: #e8fdf5; } -.washed-yellow { color: #fffceb; } -.washed-red { color: #ffdfdf; } -.color-inherit { color: inherit; } -.bg-black-90 { background-color: rgba(0, 0, 0, .9); } -.bg-black-80 { background-color: rgba(0, 0, 0, .8); } -.bg-black-70 { background-color: rgba(0, 0, 0, .7); } -.bg-black-60 { background-color: rgba(0, 0, 0, .6); } -.bg-black-50 { background-color: rgba(0, 0, 0, .5); } -.bg-black-40 { background-color: rgba(0, 0, 0, .4); } -.bg-black-30 { background-color: rgba(0, 0, 0, .3); } -.bg-black-20 { background-color: rgba(0, 0, 0, .2); } -.bg-black-10 { background-color: rgba(0, 0, 0, .1); } -.bg-black-05 { background-color: rgba(0, 0, 0, .05); } -.bg-white-90 { background-color: rgba(255, 255, 255, .9); } -.bg-white-80 { background-color: rgba(255, 255, 255, .8); } -.bg-white-70 { background-color: rgba(255, 255, 255, .7); } -.bg-white-60 { background-color: rgba(255, 255, 255, .6); } -.bg-white-50 { background-color: rgba(255, 255, 255, .5); } -.bg-white-40 { background-color: rgba(255, 255, 255, .4); } -.bg-white-30 { background-color: rgba(255, 255, 255, .3); } -.bg-white-20 { background-color: rgba(255, 255, 255, .2); } -.bg-white-10 { background-color: rgba(255, 255, 255, .1); } -/* Background colors */ -.bg-black { background-color: #000; } -.bg-near-black { background-color: #111; } -.bg-dark-gray { background-color: #333; } -.bg-mid-gray { background-color: #555; } -.bg-gray { background-color: #777; } -.bg-silver { background-color: #999; } -.bg-light-silver { background-color: #aaa; } -.bg-moon-gray { background-color: #ccc; } -.bg-light-gray { background-color: #eee; } -.bg-near-white { background-color: #f4f4f4; } -.bg-white { background-color: #fff; } -.bg-transparent { background-color: transparent; } -.bg-dark-red { background-color: #e7040f; } -.bg-red { background-color: #ff4136; } -.bg-light-red { background-color: #ff725c; } -.bg-orange { background-color: #ff6300; } -.bg-gold { background-color: #ffb700; } -.bg-yellow { background-color: #ffd700; } -.bg-light-yellow { background-color: #fbf1a9; } -.bg-purple { background-color: #5e2ca5; } -.bg-light-purple { background-color: #a463f2; } -.bg-dark-pink { background-color: #d5008f; } -.bg-hot-pink { background-color: #ff41b4; } -.bg-pink { background-color: #ff80cc; } -.bg-light-pink { background-color: #ffa3d7; } -.bg-dark-green { background-color: #137752; } -.bg-green { background-color: #19a974; } -.bg-light-green { background-color: #9eebcf; } -.bg-navy { background-color: #001b44; } -.bg-dark-blue { background-color: #00449e; } -.bg-blue { background-color: #0594CB; } -.bg-light-blue { background-color: #96ccff; } -.bg-lightest-blue { background-color: #cdecff; } -.bg-washed-blue { background-color: #f6fffe; } -.bg-washed-green { background-color: #e8fdf5; } -.bg-washed-yellow { background-color: #fffceb; } -.bg-washed-red { background-color: #ffdfdf; } -.bg-inherit { background-color: inherit; } -/* - - SKINS:PSEUDO - - Customize the color of an element when - it is focused or hovered over. - - */ -.hover-black:hover, -.hover-black:focus { color: #000; } -.hover-near-black:hover, -.hover-near-black:focus { color: #111; } -.hover-dark-gray:hover, -.hover-dark-gray:focus { color: #333; } -.hover-mid-gray:hover, -.hover-mid-gray:focus { color: #555; } -.hover-gray:hover, -.hover-gray:focus { color: #777; } -.hover-silver:hover, -.hover-silver:focus { color: #999; } -.hover-light-silver:hover, -.hover-light-silver:focus { color: #aaa; } -.hover-moon-gray:hover, -.hover-moon-gray:focus { color: #ccc; } -.hover-light-gray:hover, -.hover-light-gray:focus { color: #eee; } -.hover-near-white:hover, -.hover-near-white:focus { color: #f4f4f4; } -.hover-white:hover, -.hover-white:focus { color: #fff; } -.hover-black-90:hover, -.hover-black-90:focus { color: rgba(0, 0, 0, .9); } -.hover-black-80:hover, -.hover-black-80:focus { color: rgba(0, 0, 0, .8); } -.hover-black-70:hover, -.hover-black-70:focus { color: rgba(0, 0, 0, .7); } -.hover-black-60:hover, -.hover-black-60:focus { color: rgba(0, 0, 0, .6); } -.hover-black-50:hover, -.hover-black-50:focus { color: rgba(0, 0, 0, .5); } -.hover-black-40:hover, -.hover-black-40:focus { color: rgba(0, 0, 0, .4); } -.hover-black-30:hover, -.hover-black-30:focus { color: rgba(0, 0, 0, .3); } -.hover-black-20:hover, -.hover-black-20:focus { color: rgba(0, 0, 0, .2); } -.hover-black-10:hover, -.hover-black-10:focus { color: rgba(0, 0, 0, .1); } -.hover-white-90:hover, -.hover-white-90:focus { color: rgba(255, 255, 255, .9); } -.hover-white-80:hover, -.hover-white-80:focus { color: rgba(255, 255, 255, .8); } -.hover-white-70:hover, -.hover-white-70:focus { color: rgba(255, 255, 255, .7); } -.hover-white-60:hover, -.hover-white-60:focus { color: rgba(255, 255, 255, .6); } -.hover-white-50:hover, -.hover-white-50:focus { color: rgba(255, 255, 255, .5); } -.hover-white-40:hover, -.hover-white-40:focus { color: rgba(255, 255, 255, .4); } -.hover-white-30:hover, -.hover-white-30:focus { color: rgba(255, 255, 255, .3); } -.hover-white-20:hover, -.hover-white-20:focus { color: rgba(255, 255, 255, .2); } -.hover-white-10:hover, -.hover-white-10:focus { color: rgba(255, 255, 255, .1); } -.hover-inherit:hover, -.hover-inherit:focus { color: inherit; } -.hover-bg-black:hover, -.hover-bg-black:focus { background-color: #000; } -.hover-bg-near-black:hover, -.hover-bg-near-black:focus { background-color: #111; } -.hover-bg-dark-gray:hover, -.hover-bg-dark-gray:focus { background-color: #333; } -.hover-bg-mid-gray:hover, -.hover-bg-mid-gray:focus { background-color: #555; } -.hover-bg-gray:hover, -.hover-bg-gray:focus { background-color: #777; } -.hover-bg-silver:hover, -.hover-bg-silver:focus { background-color: #999; } -.hover-bg-light-silver:hover, -.hover-bg-light-silver:focus { background-color: #aaa; } -.hover-bg-moon-gray:hover, -.hover-bg-moon-gray:focus { background-color: #ccc; } -.hover-bg-light-gray:hover, -.hover-bg-light-gray:focus { background-color: #eee; } -.hover-bg-near-white:hover, -.hover-bg-near-white:focus { background-color: #f4f4f4; } -.hover-bg-white:hover, -.hover-bg-white:focus { background-color: #fff; } -.hover-bg-transparent:hover, -.hover-bg-transparent:focus { background-color: transparent; } -.hover-bg-black-90:hover, -.hover-bg-black-90:focus { background-color: rgba(0, 0, 0, .9); } -.hover-bg-black-80:hover, -.hover-bg-black-80:focus { background-color: rgba(0, 0, 0, .8); } -.hover-bg-black-70:hover, -.hover-bg-black-70:focus { background-color: rgba(0, 0, 0, .7); } -.hover-bg-black-60:hover, -.hover-bg-black-60:focus { background-color: rgba(0, 0, 0, .6); } -.hover-bg-black-50:hover, -.hover-bg-black-50:focus { background-color: rgba(0, 0, 0, .5); } -.hover-bg-black-40:hover, -.hover-bg-black-40:focus { background-color: rgba(0, 0, 0, .4); } -.hover-bg-black-30:hover, -.hover-bg-black-30:focus { background-color: rgba(0, 0, 0, .3); } -.hover-bg-black-20:hover, -.hover-bg-black-20:focus { background-color: rgba(0, 0, 0, .2); } -.hover-bg-black-10:hover, -.hover-bg-black-10:focus { background-color: rgba(0, 0, 0, .1); } -.hover-bg-white-90:hover, -.hover-bg-white-90:focus { background-color: rgba(255, 255, 255, .9); } -.hover-bg-white-80:hover, -.hover-bg-white-80:focus { background-color: rgba(255, 255, 255, .8); } -.hover-bg-white-70:hover, -.hover-bg-white-70:focus { background-color: rgba(255, 255, 255, .7); } -.hover-bg-white-60:hover, -.hover-bg-white-60:focus { background-color: rgba(255, 255, 255, .6); } -.hover-bg-white-50:hover, -.hover-bg-white-50:focus { background-color: rgba(255, 255, 255, .5); } -.hover-bg-white-40:hover, -.hover-bg-white-40:focus { background-color: rgba(255, 255, 255, .4); } -.hover-bg-white-30:hover, -.hover-bg-white-30:focus { background-color: rgba(255, 255, 255, .3); } -.hover-bg-white-20:hover, -.hover-bg-white-20:focus { background-color: rgba(255, 255, 255, .2); } -.hover-bg-white-10:hover, -.hover-bg-white-10:focus { background-color: rgba(255, 255, 255, .1); } -.hover-dark-red:hover, -.hover-dark-red:focus { color: #e7040f; } -.hover-red:hover, -.hover-red:focus { color: #ff4136; } -.hover-light-red:hover, -.hover-light-red:focus { color: #ff725c; } -.hover-orange:hover, -.hover-orange:focus { color: #ff6300; } -.hover-gold:hover, -.hover-gold:focus { color: #ffb700; } -.hover-yellow:hover, -.hover-yellow:focus { color: #ffd700; } -.hover-light-yellow:hover, -.hover-light-yellow:focus { color: #fbf1a9; } -.hover-purple:hover, -.hover-purple:focus { color: #5e2ca5; } -.hover-light-purple:hover, -.hover-light-purple:focus { color: #a463f2; } -.hover-dark-pink:hover, -.hover-dark-pink:focus { color: #d5008f; } -.hover-hot-pink:hover, -.hover-hot-pink:focus { color: #ff41b4; } -.hover-pink:hover, -.hover-pink:focus { color: #ff80cc; } -.hover-light-pink:hover, -.hover-light-pink:focus { color: #ffa3d7; } -.hover-dark-green:hover, -.hover-dark-green:focus { color: #137752; } -.hover-green:hover, -.hover-green:focus { color: #19a974; } -.hover-light-green:hover, -.hover-light-green:focus { color: #9eebcf; } -.hover-navy:hover, -.hover-navy:focus { color: #001b44; } -.hover-dark-blue:hover, -.hover-dark-blue:focus { color: #00449e; } -.hover-blue:hover, -.hover-blue:focus { color: #0594CB; } -.hover-light-blue:hover, -.hover-light-blue:focus { color: #96ccff; } -.hover-lightest-blue:hover, -.hover-lightest-blue:focus { color: #cdecff; } -.hover-washed-blue:hover, -.hover-washed-blue:focus { color: #f6fffe; } -.hover-washed-green:hover, -.hover-washed-green:focus { color: #e8fdf5; } -.hover-washed-yellow:hover, -.hover-washed-yellow:focus { color: #fffceb; } -.hover-washed-red:hover, -.hover-washed-red:focus { color: #ffdfdf; } -.hover-bg-dark-red:hover, -.hover-bg-dark-red:focus { background-color: #e7040f; } -.hover-bg-red:hover, -.hover-bg-red:focus { background-color: #ff4136; } -.hover-bg-light-red:hover, -.hover-bg-light-red:focus { background-color: #ff725c; } -.hover-bg-orange:hover, -.hover-bg-orange:focus { background-color: #ff6300; } -.hover-bg-gold:hover, -.hover-bg-gold:focus { background-color: #ffb700; } -.hover-bg-yellow:hover, -.hover-bg-yellow:focus { background-color: #ffd700; } -.hover-bg-light-yellow:hover, -.hover-bg-light-yellow:focus { background-color: #fbf1a9; } -.hover-bg-purple:hover, -.hover-bg-purple:focus { background-color: #5e2ca5; } -.hover-bg-light-purple:hover, -.hover-bg-light-purple:focus { background-color: #a463f2; } -.hover-bg-dark-pink:hover, -.hover-bg-dark-pink:focus { background-color: #d5008f; } -.hover-bg-hot-pink:hover, -.hover-bg-hot-pink:focus { background-color: #ff41b4; } -.hover-bg-pink:hover, -.hover-bg-pink:focus { background-color: #ff80cc; } -.hover-bg-light-pink:hover, -.hover-bg-light-pink:focus { background-color: #ffa3d7; } -.hover-bg-dark-green:hover, -.hover-bg-dark-green:focus { background-color: #137752; } -.hover-bg-green:hover, -.hover-bg-green:focus { background-color: #19a974; } -.hover-bg-light-green:hover, -.hover-bg-light-green:focus { background-color: #9eebcf; } -.hover-bg-navy:hover, -.hover-bg-navy:focus { background-color: #001b44; } -.hover-bg-dark-blue:hover, -.hover-bg-dark-blue:focus { background-color: #00449e; } -.hover-bg-blue:hover, -.hover-bg-blue:focus { background-color: #0594CB; } -.hover-bg-light-blue:hover, -.hover-bg-light-blue:focus { background-color: #96ccff; } -.hover-bg-lightest-blue:hover, -.hover-bg-lightest-blue:focus { background-color: #cdecff; } -.hover-bg-washed-blue:hover, -.hover-bg-washed-blue:focus { background-color: #f6fffe; } -.hover-bg-washed-green:hover, -.hover-bg-washed-green:focus { background-color: #e8fdf5; } -.hover-bg-washed-yellow:hover, -.hover-bg-washed-yellow:focus { background-color: #fffceb; } -.hover-bg-washed-red:hover, -.hover-bg-washed-red:focus { background-color: #ffdfdf; } -.hover-bg-inherit:hover, -.hover-bg-inherit:focus { background-color: inherit; } -/* Variables */ -/* - SPACING - Docs: http://tachyons.io/docs/layout/spacing/ - - An eight step powers of two scale ranging from 0 to 16rem. - - Base: - p = padding - m = margin - - Modifiers: - a = all - h = horizontal - v = vertical - t = top - r = right - b = bottom - l = left - - 0 = none - 1 = 1st step in spacing scale - 2 = 2nd step in spacing scale - 3 = 3rd step in spacing scale - 4 = 4th step in spacing scale - 5 = 5th step in spacing scale - 6 = 6th step in spacing scale - 7 = 7th step in spacing scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.pa0 { padding: 0; } -.pa1 { padding: .25rem; } -.pa2 { padding: .5rem; } -.pa3 { padding: 1rem; } -.pa4 { padding: 2rem; } -.pa5 { padding: 4rem; } -.pa6 { padding: 8rem; } -.pa7 { padding: 16rem; } -.pl0 { padding-left: 0; } -.pl1 { padding-left: .25rem; } -.pl2 { padding-left: .5rem; } -.pl3 { padding-left: 1rem; } -.pl4 { padding-left: 2rem; } -.pl5 { padding-left: 4rem; } -.pl6 { padding-left: 8rem; } -.pl7 { padding-left: 16rem; } -.pr0 { padding-right: 0; } -.pr1 { padding-right: .25rem; } -.pr2 { padding-right: .5rem; } -.pr3 { padding-right: 1rem; } -.pr4 { padding-right: 2rem; } -.pr5 { padding-right: 4rem; } -.pr6 { padding-right: 8rem; } -.pr7 { padding-right: 16rem; } -.pb0 { padding-bottom: 0; } -.pb1 { padding-bottom: .25rem; } -.pb2 { padding-bottom: .5rem; } -.pb3 { padding-bottom: 1rem; } -.pb4 { padding-bottom: 2rem; } -.pb5 { padding-bottom: 4rem; } -.pb6 { padding-bottom: 8rem; } -.pb7 { padding-bottom: 16rem; } -.pt0 { padding-top: 0; } -.pt1 { padding-top: .25rem; } -.pt2 { padding-top: .5rem; } -.pt3 { padding-top: 1rem; } -.pt4 { padding-top: 2rem; } -.pt5 { padding-top: 4rem; } -.pt6 { padding-top: 8rem; } -.pt7 { padding-top: 16rem; } -.pv0 { - padding-top: 0; - padding-bottom: 0; -} -.pv1 { - padding-top: .25rem; - padding-bottom: .25rem; -} -.pv2 { - padding-top: .5rem; - padding-bottom: .5rem; -} -.pv3 { - padding-top: 1rem; - padding-bottom: 1rem; -} -.pv4 { - padding-top: 2rem; - padding-bottom: 2rem; -} -.pv5 { - padding-top: 4rem; - padding-bottom: 4rem; -} -.pv6 { - padding-top: 8rem; - padding-bottom: 8rem; -} -.pv7 { - padding-top: 16rem; - padding-bottom: 16rem; -} -.ph0 { - padding-left: 0; - padding-right: 0; -} -.ph1 { - padding-left: .25rem; - padding-right: .25rem; -} -.ph2 { - padding-left: .5rem; - padding-right: .5rem; -} -.ph3 { - padding-left: 1rem; - padding-right: 1rem; -} -.ph4 { - padding-left: 2rem; - padding-right: 2rem; -} -.ph5 { - padding-left: 4rem; - padding-right: 4rem; -} -.ph6 { - padding-left: 8rem; - padding-right: 8rem; -} -.ph7 { - padding-left: 16rem; - padding-right: 16rem; -} -.ma0 { margin: 0; } -.ma1 { margin: .25rem; } -.ma2 { margin: .5rem; } -.ma3 { margin: 1rem; } -.ma4 { margin: 2rem; } -.ma5 { margin: 4rem; } -.ma6 { margin: 8rem; } -.ma7 { margin: 16rem; } -.ml0 { margin-left: 0; } -.ml1 { margin-left: .25rem; } -.ml2 { margin-left: .5rem; } -.ml3 { margin-left: 1rem; } -.ml4 { margin-left: 2rem; } -.ml5 { margin-left: 4rem; } -.ml6 { margin-left: 8rem; } -.ml7 { margin-left: 16rem; } -.mr0 { margin-right: 0; } -.mr1 { margin-right: .25rem; } -.mr2 { margin-right: .5rem; } -.mr3 { margin-right: 1rem; } -.mr4 { margin-right: 2rem; } -.mr5 { margin-right: 4rem; } -.mr6 { margin-right: 8rem; } -.mr7 { margin-right: 16rem; } -.mb0 { margin-bottom: 0; } -.mb1 { margin-bottom: .25rem; } -.mb2 { margin-bottom: .5rem; } -.mb3 { margin-bottom: 1rem; } -.mb4 { margin-bottom: 2rem; } -.mb5 { margin-bottom: 4rem; } -.mb6 { margin-bottom: 8rem; } -.mb7 { margin-bottom: 16rem; } -.mt0 { margin-top: 0; } -.mt1 { margin-top: .25rem; } -.mt2 { margin-top: .5rem; } -.mt3 { margin-top: 1rem; } -.mt4 { margin-top: 2rem; } -.mt5 { margin-top: 4rem; } -.mt6 { margin-top: 8rem; } -.mt7 { margin-top: 16rem; } -.mv0 { - margin-top: 0; - margin-bottom: 0; -} -.mv1 { - margin-top: .25rem; - margin-bottom: .25rem; -} -.mv2 { - margin-top: .5rem; - margin-bottom: .5rem; -} -.mv3 { - margin-top: 1rem; - margin-bottom: 1rem; -} -.mv4 { - margin-top: 2rem; - margin-bottom: 2rem; -} -.mv5 { - margin-top: 4rem; - margin-bottom: 4rem; -} -.mv6 { - margin-top: 8rem; - margin-bottom: 8rem; -} -.mv7 { - margin-top: 16rem; - margin-bottom: 16rem; -} -.mh0 { - margin-left: 0; - margin-right: 0; -} -.mh1 { - margin-left: .25rem; - margin-right: .25rem; -} -.mh2 { - margin-left: .5rem; - margin-right: .5rem; -} -.mh3 { - margin-left: 1rem; - margin-right: 1rem; -} -.mh4 { - margin-left: 2rem; - margin-right: 2rem; -} -.mh5 { - margin-left: 4rem; - margin-right: 4rem; -} -.mh6 { - margin-left: 8rem; - margin-right: 8rem; -} -.mh7 { - margin-left: 16rem; - margin-right: 16rem; -} -@media screen and (min-width: 30em) { - .pa0-ns { padding: 0; } - .pa1-ns { padding: .25rem; } - .pa2-ns { padding: .5rem; } - .pa3-ns { padding: 1rem; } - .pa4-ns { padding: 2rem; } - .pa5-ns { padding: 4rem; } - .pa6-ns { padding: 8rem; } - .pa7-ns { padding: 16rem; } - - .pl0-ns { padding-left: 0; } - .pl1-ns { padding-left: .25rem; } - .pl2-ns { padding-left: .5rem; } - .pl3-ns { padding-left: 1rem; } - .pl4-ns { padding-left: 2rem; } - .pl5-ns { padding-left: 4rem; } - .pl6-ns { padding-left: 8rem; } - .pl7-ns { padding-left: 16rem; } - - .pr0-ns { padding-right: 0; } - .pr1-ns { padding-right: .25rem; } - .pr2-ns { padding-right: .5rem; } - .pr3-ns { padding-right: 1rem; } - .pr4-ns { padding-right: 2rem; } - .pr5-ns { padding-right: 4rem; } - .pr6-ns { padding-right: 8rem; } - .pr7-ns { padding-right: 16rem; } - - .pb0-ns { padding-bottom: 0; } - .pb1-ns { padding-bottom: .25rem; } - .pb2-ns { padding-bottom: .5rem; } - .pb3-ns { padding-bottom: 1rem; } - .pb4-ns { padding-bottom: 2rem; } - .pb5-ns { padding-bottom: 4rem; } - .pb6-ns { padding-bottom: 8rem; } - .pb7-ns { padding-bottom: 16rem; } - - .pt0-ns { padding-top: 0; } - .pt1-ns { padding-top: .25rem; } - .pt2-ns { padding-top: .5rem; } - .pt3-ns { padding-top: 1rem; } - .pt4-ns { padding-top: 2rem; } - .pt5-ns { padding-top: 4rem; } - .pt6-ns { padding-top: 8rem; } - .pt7-ns { padding-top: 16rem; } - - .pv0-ns { - padding-top: 0; - padding-bottom: 0; - } - .pv1-ns { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-ns { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-ns { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-ns { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-ns { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-ns { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-ns { - padding-top: 16rem; - padding-bottom: 16rem; - } - .ph0-ns { - padding-left: 0; - padding-right: 0; - } - .ph1-ns { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-ns { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-ns { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-ns { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-ns { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-ns { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-ns { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-ns { margin: 0; } - .ma1-ns { margin: .25rem; } - .ma2-ns { margin: .5rem; } - .ma3-ns { margin: 1rem; } - .ma4-ns { margin: 2rem; } - .ma5-ns { margin: 4rem; } - .ma6-ns { margin: 8rem; } - .ma7-ns { margin: 16rem; } - - .ml0-ns { margin-left: 0; } - .ml1-ns { margin-left: .25rem; } - .ml2-ns { margin-left: .5rem; } - .ml3-ns { margin-left: 1rem; } - .ml4-ns { margin-left: 2rem; } - .ml5-ns { margin-left: 4rem; } - .ml6-ns { margin-left: 8rem; } - .ml7-ns { margin-left: 16rem; } - - .mr0-ns { margin-right: 0; } - .mr1-ns { margin-right: .25rem; } - .mr2-ns { margin-right: .5rem; } - .mr3-ns { margin-right: 1rem; } - .mr4-ns { margin-right: 2rem; } - .mr5-ns { margin-right: 4rem; } - .mr6-ns { margin-right: 8rem; } - .mr7-ns { margin-right: 16rem; } - - .mb0-ns { margin-bottom: 0; } - .mb1-ns { margin-bottom: .25rem; } - .mb2-ns { margin-bottom: .5rem; } - .mb3-ns { margin-bottom: 1rem; } - .mb4-ns { margin-bottom: 2rem; } - .mb5-ns { margin-bottom: 4rem; } - .mb6-ns { margin-bottom: 8rem; } - .mb7-ns { margin-bottom: 16rem; } - - .mt0-ns { margin-top: 0; } - .mt1-ns { margin-top: .25rem; } - .mt2-ns { margin-top: .5rem; } - .mt3-ns { margin-top: 1rem; } - .mt4-ns { margin-top: 2rem; } - .mt5-ns { margin-top: 4rem; } - .mt6-ns { margin-top: 8rem; } - .mt7-ns { margin-top: 16rem; } - - .mv0-ns { - margin-top: 0; - margin-bottom: 0; - } - .mv1-ns { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-ns { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-ns { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-ns { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-ns { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-ns { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-ns { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-ns { - margin-left: 0; - margin-right: 0; - } - .mh1-ns { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-ns { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-ns { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-ns { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-ns { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-ns { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-ns { - margin-left: 16rem; - margin-right: 16rem; - } - -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .pa0-m { padding: 0; } - .pa1-m { padding: .25rem; } - .pa2-m { padding: .5rem; } - .pa3-m { padding: 1rem; } - .pa4-m { padding: 2rem; } - .pa5-m { padding: 4rem; } - .pa6-m { padding: 8rem; } - .pa7-m { padding: 16rem; } - - .pl0-m { padding-left: 0; } - .pl1-m { padding-left: .25rem; } - .pl2-m { padding-left: .5rem; } - .pl3-m { padding-left: 1rem; } - .pl4-m { padding-left: 2rem; } - .pl5-m { padding-left: 4rem; } - .pl6-m { padding-left: 8rem; } - .pl7-m { padding-left: 16rem; } - - .pr0-m { padding-right: 0; } - .pr1-m { padding-right: .25rem; } - .pr2-m { padding-right: .5rem; } - .pr3-m { padding-right: 1rem; } - .pr4-m { padding-right: 2rem; } - .pr5-m { padding-right: 4rem; } - .pr6-m { padding-right: 8rem; } - .pr7-m { padding-right: 16rem; } - - .pb0-m { padding-bottom: 0; } - .pb1-m { padding-bottom: .25rem; } - .pb2-m { padding-bottom: .5rem; } - .pb3-m { padding-bottom: 1rem; } - .pb4-m { padding-bottom: 2rem; } - .pb5-m { padding-bottom: 4rem; } - .pb6-m { padding-bottom: 8rem; } - .pb7-m { padding-bottom: 16rem; } - - .pt0-m { padding-top: 0; } - .pt1-m { padding-top: .25rem; } - .pt2-m { padding-top: .5rem; } - .pt3-m { padding-top: 1rem; } - .pt4-m { padding-top: 2rem; } - .pt5-m { padding-top: 4rem; } - .pt6-m { padding-top: 8rem; } - .pt7-m { padding-top: 16rem; } - - .pv0-m { - padding-top: 0; - padding-bottom: 0; - } - .pv1-m { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-m { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-m { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-m { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-m { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-m { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-m { - padding-top: 16rem; - padding-bottom: 16rem; - } - - .ph0-m { - padding-left: 0; - padding-right: 0; - } - .ph1-m { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-m { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-m { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-m { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-m { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-m { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-m { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-m { margin: 0; } - .ma1-m { margin: .25rem; } - .ma2-m { margin: .5rem; } - .ma3-m { margin: 1rem; } - .ma4-m { margin: 2rem; } - .ma5-m { margin: 4rem; } - .ma6-m { margin: 8rem; } - .ma7-m { margin: 16rem; } - - .ml0-m { margin-left: 0; } - .ml1-m { margin-left: .25rem; } - .ml2-m { margin-left: .5rem; } - .ml3-m { margin-left: 1rem; } - .ml4-m { margin-left: 2rem; } - .ml5-m { margin-left: 4rem; } - .ml6-m { margin-left: 8rem; } - .ml7-m { margin-left: 16rem; } - - .mr0-m { margin-right: 0; } - .mr1-m { margin-right: .25rem; } - .mr2-m { margin-right: .5rem; } - .mr3-m { margin-right: 1rem; } - .mr4-m { margin-right: 2rem; } - .mr5-m { margin-right: 4rem; } - .mr6-m { margin-right: 8rem; } - .mr7-m { margin-right: 16rem; } - - .mb0-m { margin-bottom: 0; } - .mb1-m { margin-bottom: .25rem; } - .mb2-m { margin-bottom: .5rem; } - .mb3-m { margin-bottom: 1rem; } - .mb4-m { margin-bottom: 2rem; } - .mb5-m { margin-bottom: 4rem; } - .mb6-m { margin-bottom: 8rem; } - .mb7-m { margin-bottom: 16rem; } - - .mt0-m { margin-top: 0; } - .mt1-m { margin-top: .25rem; } - .mt2-m { margin-top: .5rem; } - .mt3-m { margin-top: 1rem; } - .mt4-m { margin-top: 2rem; } - .mt5-m { margin-top: 4rem; } - .mt6-m { margin-top: 8rem; } - .mt7-m { margin-top: 16rem; } - - .mv0-m { - margin-top: 0; - margin-bottom: 0; - } - .mv1-m { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-m { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-m { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-m { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-m { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-m { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-m { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-m { - margin-left: 0; - margin-right: 0; - } - .mh1-m { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-m { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-m { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-m { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-m { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-m { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-m { - margin-left: 16rem; - margin-right: 16rem; - } - -} -@media screen and (min-width: 60em) { - .pa0-l { padding: 0; } - .pa1-l { padding: .25rem; } - .pa2-l { padding: .5rem; } - .pa3-l { padding: 1rem; } - .pa4-l { padding: 2rem; } - .pa5-l { padding: 4rem; } - .pa6-l { padding: 8rem; } - .pa7-l { padding: 16rem; } - - .pl0-l { padding-left: 0; } - .pl1-l { padding-left: .25rem; } - .pl2-l { padding-left: .5rem; } - .pl3-l { padding-left: 1rem; } - .pl4-l { padding-left: 2rem; } - .pl5-l { padding-left: 4rem; } - .pl6-l { padding-left: 8rem; } - .pl7-l { padding-left: 16rem; } - - .pr0-l { padding-right: 0; } - .pr1-l { padding-right: .25rem; } - .pr2-l { padding-right: .5rem; } - .pr3-l { padding-right: 1rem; } - .pr4-l { padding-right: 2rem; } - .pr5-l { padding-right: 4rem; } - .pr6-l { padding-right: 8rem; } - .pr7-l { padding-right: 16rem; } - - .pb0-l { padding-bottom: 0; } - .pb1-l { padding-bottom: .25rem; } - .pb2-l { padding-bottom: .5rem; } - .pb3-l { padding-bottom: 1rem; } - .pb4-l { padding-bottom: 2rem; } - .pb5-l { padding-bottom: 4rem; } - .pb6-l { padding-bottom: 8rem; } - .pb7-l { padding-bottom: 16rem; } - - .pt0-l { padding-top: 0; } - .pt1-l { padding-top: .25rem; } - .pt2-l { padding-top: .5rem; } - .pt3-l { padding-top: 1rem; } - .pt4-l { padding-top: 2rem; } - .pt5-l { padding-top: 4rem; } - .pt6-l { padding-top: 8rem; } - .pt7-l { padding-top: 16rem; } - - .pv0-l { - padding-top: 0; - padding-bottom: 0; - } - .pv1-l { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-l { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-l { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-l { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-l { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-l { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-l { - padding-top: 16rem; - padding-bottom: 16rem; - } - - .ph0-l { - padding-left: 0; - padding-right: 0; - } - .ph1-l { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-l { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-l { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-l { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-l { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-l { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-l { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-l { margin: 0; } - .ma1-l { margin: .25rem; } - .ma2-l { margin: .5rem; } - .ma3-l { margin: 1rem; } - .ma4-l { margin: 2rem; } - .ma5-l { margin: 4rem; } - .ma6-l { margin: 8rem; } - .ma7-l { margin: 16rem; } - - .ml0-l { margin-left: 0; } - .ml1-l { margin-left: .25rem; } - .ml2-l { margin-left: .5rem; } - .ml3-l { margin-left: 1rem; } - .ml4-l { margin-left: 2rem; } - .ml5-l { margin-left: 4rem; } - .ml6-l { margin-left: 8rem; } - .ml7-l { margin-left: 16rem; } - - .mr0-l { margin-right: 0; } - .mr1-l { margin-right: .25rem; } - .mr2-l { margin-right: .5rem; } - .mr3-l { margin-right: 1rem; } - .mr4-l { margin-right: 2rem; } - .mr5-l { margin-right: 4rem; } - .mr6-l { margin-right: 8rem; } - .mr7-l { margin-right: 16rem; } - - .mb0-l { margin-bottom: 0; } - .mb1-l { margin-bottom: .25rem; } - .mb2-l { margin-bottom: .5rem; } - .mb3-l { margin-bottom: 1rem; } - .mb4-l { margin-bottom: 2rem; } - .mb5-l { margin-bottom: 4rem; } - .mb6-l { margin-bottom: 8rem; } - .mb7-l { margin-bottom: 16rem; } - - .mt0-l { margin-top: 0; } - .mt1-l { margin-top: .25rem; } - .mt2-l { margin-top: .5rem; } - .mt3-l { margin-top: 1rem; } - .mt4-l { margin-top: 2rem; } - .mt5-l { margin-top: 4rem; } - .mt6-l { margin-top: 8rem; } - .mt7-l { margin-top: 16rem; } - - .mv0-l { - margin-top: 0; - margin-bottom: 0; - } - .mv1-l { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-l { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-l { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-l { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-l { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-l { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-l { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-l { - margin-left: 0; - margin-right: 0; - } - .mh1-l { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-l { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-l { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-l { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-l { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-l { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-l { - margin-left: 16rem; - margin-right: 16rem; - } -} -/* - NEGATIVE MARGINS - - Base: - n = negative - - Modifiers: - a = all - t = top - r = right - b = bottom - l = left - - 1 = 1st step in spacing scale - 2 = 2nd step in spacing scale - 3 = 3rd step in spacing scale - 4 = 4th step in spacing scale - 5 = 5th step in spacing scale - 6 = 6th step in spacing scale - 7 = 7th step in spacing scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.na1 { margin: -0.25rem; } -.na2 { margin: -0.5rem; } -.na3 { margin: -1rem; } -.na4 { margin: -2rem; } -.na5 { margin: -4rem; } -.na6 { margin: -8rem; } -.na7 { margin: -16rem; } -.nl1 { margin-left: -0.25rem; } -.nl2 { margin-left: -0.5rem; } -.nl3 { margin-left: -1rem; } -.nl4 { margin-left: -2rem; } -.nl5 { margin-left: -4rem; } -.nl6 { margin-left: -8rem; } -.nl7 { margin-left: -16rem; } -.nr1 { margin-right: -0.25rem; } -.nr2 { margin-right: -0.5rem; } -.nr3 { margin-right: -1rem; } -.nr4 { margin-right: -2rem; } -.nr5 { margin-right: -4rem; } -.nr6 { margin-right: -8rem; } -.nr7 { margin-right: -16rem; } -.nb1 { margin-bottom: -0.25rem; } -.nb2 { margin-bottom: -0.5rem; } -.nb3 { margin-bottom: -1rem; } -.nb4 { margin-bottom: -2rem; } -.nb5 { margin-bottom: -4rem; } -.nb6 { margin-bottom: -8rem; } -.nb7 { margin-bottom: -16rem; } -.nt1 { margin-top: -0.25rem; } -.nt2 { margin-top: -0.5rem; } -.nt3 { margin-top: -1rem; } -.nt4 { margin-top: -2rem; } -.nt5 { margin-top: -4rem; } -.nt6 { margin-top: -8rem; } -.nt7 { margin-top: -16rem; } -@media screen and (min-width: 30em) { - - .na1-ns { margin: -0.25rem; } - .na2-ns { margin: -0.5rem; } - .na3-ns { margin: -1rem; } - .na4-ns { margin: -2rem; } - .na5-ns { margin: -4rem; } - .na6-ns { margin: -8rem; } - .na7-ns { margin: -16rem; } - - .nl1-ns { margin-left: -0.25rem; } - .nl2-ns { margin-left: -0.5rem; } - .nl3-ns { margin-left: -1rem; } - .nl4-ns { margin-left: -2rem; } - .nl5-ns { margin-left: -4rem; } - .nl6-ns { margin-left: -8rem; } - .nl7-ns { margin-left: -16rem; } - - .nr1-ns { margin-right: -0.25rem; } - .nr2-ns { margin-right: -0.5rem; } - .nr3-ns { margin-right: -1rem; } - .nr4-ns { margin-right: -2rem; } - .nr5-ns { margin-right: -4rem; } - .nr6-ns { margin-right: -8rem; } - .nr7-ns { margin-right: -16rem; } - - .nb1-ns { margin-bottom: -0.25rem; } - .nb2-ns { margin-bottom: -0.5rem; } - .nb3-ns { margin-bottom: -1rem; } - .nb4-ns { margin-bottom: -2rem; } - .nb5-ns { margin-bottom: -4rem; } - .nb6-ns { margin-bottom: -8rem; } - .nb7-ns { margin-bottom: -16rem; } - - .nt1-ns { margin-top: -0.25rem; } - .nt2-ns { margin-top: -0.5rem; } - .nt3-ns { margin-top: -1rem; } - .nt4-ns { margin-top: -2rem; } - .nt5-ns { margin-top: -4rem; } - .nt6-ns { margin-top: -8rem; } - .nt7-ns { margin-top: -16rem; } - -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .na1-m { margin: -0.25rem; } - .na2-m { margin: -0.5rem; } - .na3-m { margin: -1rem; } - .na4-m { margin: -2rem; } - .na5-m { margin: -4rem; } - .na6-m { margin: -8rem; } - .na7-m { margin: -16rem; } - - .nl1-m { margin-left: -0.25rem; } - .nl2-m { margin-left: -0.5rem; } - .nl3-m { margin-left: -1rem; } - .nl4-m { margin-left: -2rem; } - .nl5-m { margin-left: -4rem; } - .nl6-m { margin-left: -8rem; } - .nl7-m { margin-left: -16rem; } - - .nr1-m { margin-right: -0.25rem; } - .nr2-m { margin-right: -0.5rem; } - .nr3-m { margin-right: -1rem; } - .nr4-m { margin-right: -2rem; } - .nr5-m { margin-right: -4rem; } - .nr6-m { margin-right: -8rem; } - .nr7-m { margin-right: -16rem; } - - .nb1-m { margin-bottom: -0.25rem; } - .nb2-m { margin-bottom: -0.5rem; } - .nb3-m { margin-bottom: -1rem; } - .nb4-m { margin-bottom: -2rem; } - .nb5-m { margin-bottom: -4rem; } - .nb6-m { margin-bottom: -8rem; } - .nb7-m { margin-bottom: -16rem; } - - .nt1-m { margin-top: -0.25rem; } - .nt2-m { margin-top: -0.5rem; } - .nt3-m { margin-top: -1rem; } - .nt4-m { margin-top: -2rem; } - .nt5-m { margin-top: -4rem; } - .nt6-m { margin-top: -8rem; } - .nt7-m { margin-top: -16rem; } - -} -@media screen and (min-width: 60em) { - .na1-l { margin: -0.25rem; } - .na2-l { margin: -0.5rem; } - .na3-l { margin: -1rem; } - .na4-l { margin: -2rem; } - .na5-l { margin: -4rem; } - .na6-l { margin: -8rem; } - .na7-l { margin: -16rem; } - - .nl1-l { margin-left: -0.25rem; } - .nl2-l { margin-left: -0.5rem; } - .nl3-l { margin-left: -1rem; } - .nl4-l { margin-left: -2rem; } - .nl5-l { margin-left: -4rem; } - .nl6-l { margin-left: -8rem; } - .nl7-l { margin-left: -16rem; } - - .nr1-l { margin-right: -0.25rem; } - .nr2-l { margin-right: -0.5rem; } - .nr3-l { margin-right: -1rem; } - .nr4-l { margin-right: -2rem; } - .nr5-l { margin-right: -4rem; } - .nr6-l { margin-right: -8rem; } - .nr7-l { margin-right: -16rem; } - - .nb1-l { margin-bottom: -0.25rem; } - .nb2-l { margin-bottom: -0.5rem; } - .nb3-l { margin-bottom: -1rem; } - .nb4-l { margin-bottom: -2rem; } - .nb5-l { margin-bottom: -4rem; } - .nb6-l { margin-bottom: -8rem; } - .nb7-l { margin-bottom: -16rem; } - - .nt1-l { margin-top: -0.25rem; } - .nt2-l { margin-top: -0.5rem; } - .nt3-l { margin-top: -1rem; } - .nt4-l { margin-top: -2rem; } - .nt5-l { margin-top: -4rem; } - .nt6-l { margin-top: -8rem; } - .nt7-l { margin-top: -16rem; } -} -/* - - TABLES - Docs: http://tachyons.io/docs/elements/tables/ - -*/ -.collapse { - border-collapse: collapse; - border-spacing: 0; -} -.striped--light-silver:nth-child(odd) { - background-color: #aaa; -} -.striped--moon-gray:nth-child(odd) { - background-color: #ccc; -} -.striped--light-gray:nth-child(odd) { - background-color: #eee; -} -.striped--near-white:nth-child(odd) { - background-color: #f4f4f4; -} -.stripe-light:nth-child(odd) { - background-color: rgba(255, 255, 255, .1); -} -.stripe-dark:nth-child(odd) { - background-color: rgba(0, 0, 0, .1); -} -/* - - TEXT DECORATION - Docs: http://tachyons.io/docs/typography/text-decoration/ - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.strike { text-decoration: line-through; } -.underline { text-decoration: underline; } -.no-underline { text-decoration: none; } -@media screen and (min-width: 30em) { - .strike-ns { text-decoration: line-through; } - .underline-ns { text-decoration: underline; } - .no-underline-ns { text-decoration: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .strike-m { text-decoration: line-through; } - .underline-m { text-decoration: underline; } - .no-underline-m { text-decoration: none; } -} -@media screen and (min-width: 60em) { - .strike-l { text-decoration: line-through; } - .underline-l { text-decoration: underline; } - .no-underline-l { text-decoration: none; } -} -/* - - TEXT ALIGN - Docs: http://tachyons.io/docs/typography/text-align/ - - Base - t = text-align - - Modifiers - l = left - r = right - c = center - j = justify - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.tl { text-align: left; } -.tr { text-align: right; } -.tc { text-align: center; } -.tj { text-align: justify; } -@media screen and (min-width: 30em) { - .tl-ns { text-align: left; } - .tr-ns { text-align: right; } - .tc-ns { text-align: center; } - .tj-ns { text-align: justify; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .tl-m { text-align: left; } - .tr-m { text-align: right; } - .tc-m { text-align: center; } - .tj-m { text-align: justify; } -} -@media screen and (min-width: 60em) { - .tl-l { text-align: left; } - .tr-l { text-align: right; } - .tc-l { text-align: center; } - .tj-l { text-align: justify; } -} -/* - - TEXT TRANSFORM - Docs: http://tachyons.io/docs/typography/text-transform/ - - Base: - tt = text-transform - - Modifiers - c = capitalize - l = lowercase - u = uppercase - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ttc { text-transform: capitalize; } -.ttl { text-transform: lowercase; } -.ttu { text-transform: uppercase; } -.ttn { text-transform: none; } -@media screen and (min-width: 30em) { - .ttc-ns { text-transform: capitalize; } - .ttl-ns { text-transform: lowercase; } - .ttu-ns { text-transform: uppercase; } - .ttn-ns { text-transform: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ttc-m { text-transform: capitalize; } - .ttl-m { text-transform: lowercase; } - .ttu-m { text-transform: uppercase; } - .ttn-m { text-transform: none; } -} -@media screen and (min-width: 60em) { - .ttc-l { text-transform: capitalize; } - .ttl-l { text-transform: lowercase; } - .ttu-l { text-transform: uppercase; } - .ttn-l { text-transform: none; } -} -/* - - TYPE SCALE - Docs: http://tachyons.io/docs/typography/scale/ - - Base: - f = font-size - - Modifiers - 1 = 1st step in size scale - 2 = 2nd step in size scale - 3 = 3rd step in size scale - 4 = 4th step in size scale - 5 = 5th step in size scale - 6 = 6th step in size scale - 7 = 7th step in size scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large -*/ -/* - * For Hero/Marketing Titles - * - * These generally are too large for mobile - * so be careful using them on smaller screens. - * */ -.f-6, -.f-headline { - font-size: 6rem; -} -.f-5, -.f-subheadline { - font-size: 5rem; -} -/* Type Scale */ -.f1 { font-size: 3rem; } -.f2 { font-size: 2.25rem; } -.f3 { font-size: 1.5rem; } -.f4 { font-size: 1.25rem; } -.f5 { font-size: 1rem; } -.f6 { font-size: .875rem; } -.f7 { font-size: .75rem; } -/* Small and hard to read for many people so use with extreme caution */ -@media screen and (min-width: 30em){ - .f-6-ns, - .f-headline-ns { font-size: 6rem; } - .f-5-ns, - .f-subheadline-ns { font-size: 5rem; } - .f1-ns { font-size: 3rem; } - .f2-ns { font-size: 2.25rem; } - .f3-ns { font-size: 1.5rem; } - .f4-ns { font-size: 1.25rem; } - .f5-ns { font-size: 1rem; } - .f6-ns { font-size: .875rem; } - .f7-ns { font-size: .75rem; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .f-6-m, - .f-headline-m { font-size: 6rem; } - .f-5-m, - .f-subheadline-m { font-size: 5rem; } - .f1-m { font-size: 3rem; } - .f2-m { font-size: 2.25rem; } - .f3-m { font-size: 1.5rem; } - .f4-m { font-size: 1.25rem; } - .f5-m { font-size: 1rem; } - .f6-m { font-size: .875rem; } - .f7-m { font-size: .75rem; } -} -@media screen and (min-width: 60em) { - .f-6-l, - .f-headline-l { - font-size: 6rem; - } - .f-5-l, - .f-subheadline-l { - font-size: 5rem; - } - .f1-l { font-size: 3rem; } - .f2-l { font-size: 2.25rem; } - .f3-l { font-size: 1.5rem; } - .f4-l { font-size: 1.25rem; } - .f5-l { font-size: 1rem; } - .f6-l { font-size: .875rem; } - .f7-l { font-size: .75rem; } -} -/* - - TYPOGRAPHY - http://tachyons.io/docs/typography/measure/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Measure is limited to ~66 characters */ -.measure { - max-width: 30em; -} -/* Measure is limited to ~80 characters */ -.measure-wide { - max-width: 34em; -} -/* Measure is limited to ~45 characters */ -.measure-narrow { - max-width: 20em; -} -/* Book paragraph style - paragraphs are indented with no vertical spacing. */ -.indent { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; -} -.small-caps { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; -} -/* Combine this class with a width to truncate text (or just leave as is to truncate at width of containing element. */ -.truncate { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -@media screen and (min-width: 30em) { - .measure-ns { - max-width: 30em; - } - .measure-wide-ns { - max-width: 34em; - } - .measure-narrow-ns { - max-width: 20em; - } - .indent-ns { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-ns { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-ns { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .measure-m { - max-width: 30em; - } - .measure-wide-m { - max-width: 34em; - } - .measure-narrow-m { - max-width: 20em; - } - .indent-m { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-m { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-m { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -@media screen and (min-width: 60em) { - .measure-l { - max-width: 30em; - } - .measure-wide-l { - max-width: 34em; - } - .measure-narrow-l { - max-width: 20em; - } - .indent-l { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-l { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-l { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -/* - - UTILITIES - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Equivalent to .overflow-y-scroll */ -.overflow-container { - overflow-y: scroll; -} -.center { - margin-right: auto; - margin-left: auto; -} -.mr-auto { margin-right: auto; } -.ml-auto { margin-left: auto; } -@media screen and (min-width: 30em){ - .center-ns { - margin-right: auto; - margin-left: auto; - } - .mr-auto-ns { margin-right: auto; } - .ml-auto-ns { margin-left: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em){ - .center-m { - margin-right: auto; - margin-left: auto; - } - .mr-auto-m { margin-right: auto; } - .ml-auto-m { margin-left: auto; } -} -@media screen and (min-width: 60em){ - .center-l { - margin-right: auto; - margin-left: auto; - } - .mr-auto-l { margin-right: auto; } - .ml-auto-l { margin-left: auto; } -} -/* - - VISIBILITY - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* - Text that is hidden but accessible - Ref: http://snook.ca/archives/html_and_css/hiding-content-for-accessibility -*/ -.clip { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); -} -@media screen and (min-width: 30em) { - .clip-ns { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .clip-m { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -@media screen and (min-width: 60em) { - .clip-l { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -/* - - WHITE SPACE - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ws-normal { white-space: normal; } -.nowrap { white-space: nowrap; } -.pre { white-space: pre; } -@media screen and (min-width: 30em) { - .ws-normal-ns { white-space: normal; } - .nowrap-ns { white-space: nowrap; } - .pre-ns { white-space: pre; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ws-normal-m { white-space: normal; } - .nowrap-m { white-space: nowrap; } - .pre-m { white-space: pre; } -} -@media screen and (min-width: 60em) { - .ws-normal-l { white-space: normal; } - .nowrap-l { white-space: nowrap; } - .pre-l { white-space: pre; } -} -/* - - VERTICAL ALIGN - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.v-base { vertical-align: baseline; } -.v-mid { vertical-align: middle; } -.v-top { vertical-align: top; } -.v-btm { vertical-align: bottom; } -@media screen and (min-width: 30em) { - .v-base-ns { vertical-align: baseline; } - .v-mid-ns { vertical-align: middle; } - .v-top-ns { vertical-align: top; } - .v-btm-ns { vertical-align: bottom; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .v-base-m { vertical-align: baseline; } - .v-mid-m { vertical-align: middle; } - .v-top-m { vertical-align: top; } - .v-btm-m { vertical-align: bottom; } -} -@media screen and (min-width: 60em) { - .v-base-l { vertical-align: baseline; } - .v-mid-l { vertical-align: middle; } - .v-top-l { vertical-align: top; } - .v-btm-l { vertical-align: bottom; } -} -/* - - HOVER EFFECTS - Docs: http://tachyons.io/docs/themes/hovers/ - - - Dim - - Glow - - Hide Child - - Underline text - - Grow - - Pointer - - Shadow - -*/ -/* - - Dim element on hover by adding the dim class. - -*/ -.dim { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.dim:hover, -.dim:focus { - opacity: .5; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.dim:active { - opacity: .8; -webkit-transition: opacity .15s ease-out; transition: opacity .15s ease-out; -} -/* - - Animate opacity to 100% on hover by adding the glow class. - -*/ -.glow { - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.glow:hover, -.glow:focus { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -/* - - Hide child & reveal on hover: - - Put the hide-child class on a parent element and any nested element with the - child class will be hidden and displayed on hover or focus. - -
-
Hidden until hover or focus
-
Hidden until hover or focus
-
Hidden until hover or focus
-
Hidden until hover or focus
-
-*/ -.hide-child .child { - opacity: 0; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.hide-child:hover .child, -.hide-child:focus .child, -.hide-child:active .child { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.underline-hover:hover, -.underline-hover:focus { - text-decoration: underline; -} -/* Can combine this with overflow-hidden to make background images grow on hover - * even if you are using background-size: cover */ -.grow { - -moz-osx-font-smoothing: grayscale; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-transition: -webkit-transform 0.25s ease-out; - transition: -webkit-transform 0.25s ease-out; - transition: transform 0.25s ease-out; - transition: transform 0.25s ease-out, -webkit-transform 0.25s ease-out; -} -.grow:hover, -.grow:focus { - -webkit-transform: scale(1.05); - transform: scale(1.05); -} -.grow:active { - -webkit-transform: scale(.90); - transform: scale(.90); -} -.grow-large { - -moz-osx-font-smoothing: grayscale; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-transition: -webkit-transform .25s ease-in-out; - transition: -webkit-transform .25s ease-in-out; - transition: transform .25s ease-in-out; - transition: transform .25s ease-in-out, -webkit-transform .25s ease-in-out; -} -.grow-large:hover, -.grow-large:focus { - -webkit-transform: scale(1.2); - transform: scale(1.2); -} -.grow-large:active { - -webkit-transform: scale(.95); - transform: scale(.95); -} -/* Add pointer on hover */ -.pointer:hover { - cursor: pointer; -} -/* - Add shadow on hover. - - Performant box-shadow animation pattern from - http://tobiasahlin.com/blog/how-to-animate-box-shadow/ -*/ -.shadow-hover { - cursor: pointer; - position: relative; - -webkit-transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); - transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); -} -.shadow-hover::after { - content: ''; - -webkit-box-shadow: 0px 0px 16px 2px rgba(0, 0, 0, .2); - box-shadow: 0px 0px 16px 2px rgba(0, 0, 0, .2); - border-radius: inherit; - opacity: 0; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - -webkit-transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); - transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); -} -.shadow-hover:hover::after, -.shadow-hover:focus::after { - opacity: 1; -} -/* Combine with classes in skins and skins-pseudo for - * many different transition possibilities. */ -.bg-animate, -.bg-animate:hover, -.bg-animate:focus { - -webkit-transition: background-color .15s ease-in-out; - transition: background-color .15s ease-in-out; -} -/* - - Z-INDEX - - Base - z = z-index - - Modifiers - -0 = literal value 0 - -1 = literal value 1 - -2 = literal value 2 - -3 = literal value 3 - -4 = literal value 4 - -5 = literal value 5 - -999 = literal value 999 - -9999 = literal value 9999 - - -max = largest accepted z-index value as integer - - -inherit = string value inherit - -initial = string value initial - -unset = string value unset - - MDN: https://developer.mozilla.org/en/docs/Web/CSS/z-index - Spec: http://www.w3.org/TR/CSS2/zindex.html - Articles: - https://philipwalton.com/articles/what-no-one-told-you-about-z-index/ - - Tips on extending: - There might be a time worth using negative z-index values. - Or if you are using tachyons with another project, you might need to - adjust these values to suit your needs. - -*/ -.z-0 { z-index: 0; } -.z-1 { z-index: 1; } -.z-2 { z-index: 2; } -.z-3 { z-index: 3; } -.z-4 { z-index: 4; } -.z-5 { z-index: 5; } -.z-999 { z-index: 999; } -.z-9999 { z-index: 9999; } -.z-max { - z-index: 2147483647; -} -.z-inherit { z-index: inherit; } -.z-initial { z-index: auto; z-index: initial; } -.z-unset { z-index: unset; } -/* - - NESTED - Tachyons module for styling nested elements - that are generated by a cms. - -*/ -.nested-copy-line-height p, -.nested-copy-line-height ul, -.nested-copy-line-height ol { - line-height: 1.5; -} -.nested-headline-line-height h1, -.nested-headline-line-height h2, -.nested-headline-line-height h3, -.nested-headline-line-height h4, -.nested-headline-line-height h5, -.nested-headline-line-height h6 { - line-height: 1.25; -} -.nested-list-reset ul, -.nested-list-reset ol { - padding-left: 0; - margin-left: 0; - list-style-type: none; -} -.nested-copy-indent p+p { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; -} -.nested-copy-separator p+p { - margin-top: 1.5em; -} -.nested-img img { - width: 100%; - max-width: 100%; - display: block; -} -.nested-links a { - color: #0594CB; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.nested-links a:hover, -.nested-links a:focus { - color: #96ccff; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -/*@import 'tachyons/src/_styles';*/ -/* Variables */ -/* Importing here will allow you to override any variables in the modules */ -/* - - Tachyons - COLOR VARIABLES - - Grayscale - - Solids - - Transparencies - Colors - -*/ -/* - - CUSTOM MEDIA QUERIES - - Media query values can be changed to fit your own content. - There are no magic bullets when it comes to media query width values. - They should be declared in em units - and they should be set to meet - the needs of your content. You can also add additional media queries, - or remove some of the existing ones. - - These media queries can be referenced like so: - - @media (--breakpoint-not-small) { - .medium-and-larger-specific-style { - background-color: red; - } - } - - @media (--breakpoint-medium) { - .medium-screen-specific-style { - background-color: red; - } - } - - @media (--breakpoint-large) { - .large-and-larger-screen-specific-style { - background-color: red; - } - } - -*/ -/* Media Queries */ -/* Debugging */ -/*@import 'tachyons/src/_debug-children'; -@import 'tachyons/src/_debug-grid';*/ -/* Uncomment out the line below to help debug layout issues */ -/* @import 'tachyons/src/_debug'; */ -/* purgecss start ignore */ -.header-link:after { - position: relative; - left: 0.5em; - opacity: 0; - font-size: 0.8em; - -moz-transition: opacity 0.2s ease-in-out 0.1s; - -ms-transition: opacity 0.2s ease-in-out 0.1s; -} -h2:hover .header-link, -h3:hover .header-link, -h4:hover .header-link, -h5:hover .header-link, -h6:hover .header-link { - opacity: 1; -} -.animated { - -webkit-animation-duration: .5s; - animation-duration: .5s; - -webkit-animation-fill-mode: forwards; - animation-fill-mode: forwards; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; -} -@-webkit-keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -.fadeIn { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; -} -.animated-delay-1 { - -webkit-animation-delay: 0.5s; - animation-delay: 0.5s; -} -.note, -.warning { - - border-left-width: 4px; - border-left-style: solid; - position: relative; - border-color: #0594CB; - - display: block; -} -.note #exclamation-icon, -.warning #exclamation-icon { - - fill: #0594CB; - position: absolute; - top: 35%; - left: -12px; - /*background-color: white;*/ -} -.admonition-content { - display: block; - margin: 0px; - padding: .125em 1em; - /*margin-left: 1em;*/ - margin-top: 2em; - margin-bottom: 2em; - overflow-x: auto; - /*font-size: .9375em;*/ - background-color: rgba(0, 0, 0, .05); - } -.hide-child-menu .child-menu { - display: none; - } -.hide-child-menu:hover .child-menu, - .hide-child-menu:focus .child-menu, - .hide-child-menu:active .child-menu { - display: block; - } -/*documentation-copy headings exaggerate spacing and size to chunk content */ -.documentation-copy h2 { - margin-top: 3em - } -.documentation-copy h2.minor { - font-size: inherit; - margin-top: inherit; - border-bottom: none; -} -.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;visibility:visible!important} -.searchbox .algolia-autocomplete{display:block;width:100%;height:100%} -.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative} -.searchbox__input{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:background .4s ease,-webkit-box-shadow .4s ease;transition:background .4s ease,-webkit-box-shadow .4s ease;transition:box-shadow .4s ease,background .4s ease;transition:box-shadow .4s ease,background .4s ease,-webkit-box-shadow .4s ease;border:0;border-radius:16px;-webkit-box-shadow:inset 0 0 0 1px #ccc;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none} -.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none} -.searchbox__input:hover{-webkit-box-shadow:inset 0 0 0 1px #b3b3b3;box-shadow:inset 0 0 0 1px #b3b3b3} -.searchbox__input:active,.searchbox__input:focus{outline:0;-webkit-box-shadow:inset 0 0 0 1px #aaa;box-shadow:inset 0 0 0 1px #aaa;background:#fff} -.searchbox__input::-webkit-input-placeholder{color:#aaa} -.searchbox__input:-ms-input-placeholder{color:#aaa} -.searchbox__input::-ms-input-placeholder{color:#aaa} -.searchbox__input::placeholder{color:#aaa} -.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69, 142, 225, 0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0} -.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""} -.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer} -.searchbox__submit:focus{outline:0} -.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96} -.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0, 0, 0, .5)} -.searchbox__reset.hide{display:none} -.searchbox__reset:focus{outline:0} -.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px} -.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s} -@-webkit-keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}} -@keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}} -.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important} -.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px} -.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important} -.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px} -.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;-webkit-box-shadow:0 1px 0 0 rgba(0, 0, 0, .2),0 2px 3px 0 rgba(0, 0, 0, .1);box-shadow:0 1px 0 0 rgba(0, 0, 0, .2),0 2px 3px 0 rgba(0, 0, 0, .1)} -.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69, 142, 225, .05)} -.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px} -.algolia-autocomplete .ds-dropdown-menu *{-webkit-box-sizing:border-box;box-sizing:border-box} -.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden} -.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143, 187, 237, .1);padding:.1em .05em} -.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;-webkit-box-shadow:inset 0 -2px 0 0 rgba(69, 142, 225, .8);box-shadow:inset 0 -2px 0 0 rgba(69, 142, 225, .8);color:inherit} -.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer} -.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px} -.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d} -.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none} -.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700} -.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d} -.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em} -.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none} -.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace} -.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none} -.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block} -@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}} -@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}} -.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;-webkit-box-shadow:none;box-shadow:none} -.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0} -.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} -/* These styles enhance the home page carousel, located here: themes/gohugoioTheme/layouts/partials/home-page-sections/showcase.html */ -.overflow-x-scroll{ - -webkit-overflow-scrolling: touch; -} -.row { - -webkit-transition: 450ms -webkit-transform; - transition: 450ms -webkit-transform; - transition: 450ms transform; - transition: 450ms transform, 450ms -webkit-transform; - font-size: 0; -} -.tile { - -webkit-transition: 450ms all; - transition: 450ms all; -} -.details { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, .9)), to(rgba(0, 0, 0, 0))); - background: linear-gradient(to top, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, 0) 100%); - -webkit-transition: 450ms opacity; - transition: 450ms opacity; -} -.tile:hover .details { - opacity: 1; -} -.row:hover .tile { - opacity: 0.3; -} -.row:hover .tile:hover { - opacity: 1; -} -.chroma .lntable pre { - padding: 0; - margin: 0; - border: 0; -} -.chroma .lntable pre code { - padding: 0; - margin: 0; -} -code { - padding: 2px 3px; - margin: 0; - font-size: 93.75%; - background-color: rgba(27, 31, 35, .05); - border-radius: 3px; -} -pre code { - display: block; - padding: 1.5em 1.5em; - font-size: .875rem; - line-height: 2; - overflow-x: auto; -} -pre { - background-color: #fff; - color: #333; - white-space: pre; - -webkit-hyphens: none; - -ms-hyphens: none; - hyphens: none; - position: relative; - border-width: 1px; - border-color: #ccc; - border-style: solid; -} -/* The Pygments highlighter comes with its own styles. */ -.highlight pre { - background-color: inherit; - color: inherit; - padding: 0.5em; - font-size: .875rem; -} -/*We are adding the copy button content here so we can change it with javascript. See the "Clipboard scripts"*/ -.copy:after { - content: "Copy" -} -.copied:after { - content: "Copied" -} -@media screen and (min-width: 60em) { - .full-width - { - /*width: 100vw; - position: relative; - left: 50%; - right: 50%; - margin-left: -50vw; - margin-right: -50vw;*/ - /*width: 60vw;*/ - /*position: relative; - left: 50%; - right: 50%;*/ - /*margin-left: -30vw;*/ - margin-right: -30vw; - max-width: 100vw; - } -} -.code-block .line-numbers-rows { - background: #2f3a46; - border: none; - bottom: -50px; - color: #98a4b3; - left: -178px; - padding: 50px 0; - top: -50px; - width: 138px -} -.code-block .line-numbers-rows>span:before { - color: inherit; - padding-right: 30px -} -.tab-button{ - margin-bottom:1px; - position: relative; - z-index: 1; - color:#333; - border-color:#ccc; - outline: none; - background-color:white; -} -.tab-pane code{ - background:#f1f2f2; - border-radius:0; -} -.tab-pane .chroma{ - background:none; - padding:0; -} -.tab-button.active{ - border-bottom-color:#f1f2f2; - background-color: #f1f2f2; -} -.tab-content .tab-pane{ - display: none; -} -.tab-content .tab-pane.active{ - display: block; -} -/* Treatment of copy buttons inside a tab module */ -.tab-content .copy, .tab-content .copied{ - display: none; -} -.tab-content .tab-pane.active + .copy, .tab-content .tab-pane.active + .copied{ - display: block; -} -.primary-color {color: #0594CB} -.bg-primary-color {background-color: #0594CB} -.hover-bg-primary-color:hover {background-color: #0594CB} -.primary-color-dark {color: #0A1922} -.bg-primary-color-dark {background-color: #0A1922} -.hover-bg-primary-color-dark:hover {background-color: #0A1922} -.primary-color-light {color: #f9f9f9} -.bg-primary-color-light {background-color: #f9f9f9} -.hover-bg-primary-color-light:hover {background-color: #f9f9f9} -.accent-color {color: #EBB951} -.bg-accent-color {background-color: #EBB951} -.hover-bg-accent-color:hover {background-color: #EBB951} -.accent-color-light {color: #FF4088} -.hover-accent-color-light:hover {color: #FF4088} -.bg-accent-color-light {background-color: #FF4088} -.hover-bg-accent-color-light:hover {background-color: #FF4088} -.accent-color-dark {color: #33ba91} -.bg-accent-color-dark {background-color: #33ba91} -.hover-bg-accent-color-dark:hover {background-color: #33ba91} -.text-color-primary {color: #373737} -.text-on-primary-color {color: #fff} -.text-color-secondary {color: #ccc} -.text-color-disabled {color: #F7f7f7} -.divider-color {color: #f6f6f6} -.warn-color {color: red} -.nested-links a { - color: #0594CB; - text-decoration: none; - -} -.column-count-2 {-webkit-column-count: 1;column-count: 1} -.column-gap-1 {-webkit-column-gap: 0;column-gap: 0} -.break-inside-avoid {-webkit-column-break-inside: auto;break-inside: auto} -@media screen and (min-width: 60em) { - .column-count-3-l {-webkit-column-count: 3;column-count: 3} - .column-count-2-l {-webkit-column-count: 2;column-count: 2} - .column-gap-1-l {-webkit-column-gap: 1;column-gap: 1} - .break-inside-avoid-l {-webkit-column-break-inside: avoid;break-inside: avoid} -} -.prose ul, .prose ol { - margin-bottom: 2em; -} -.prose ul li, .prose ol li { - margin-bottom: .5em; -} -.prose li:hover { - background-color: #eee -} -.prose ::selection { - background: #0594CB; /* WebKit/Blink Browsers */ - color: white; -} -.prose-glossary h3 { - margin-top: 0; - font-size: 1.125rem; -} -.prose-glossary h3:first-of-type { - margin-top: 3em; -} -.prose-glossary h3 ~ p { - margin: 0.5em 0 2em 0; -} -body { - -line-height: 1.45; - -} -p {margin-bottom: 1.3em;} -h1, h2, h3, h4 { -margin: 1.414em 0 0.5em; - -line-height: 1.2; -} -h1 { -margin-top: 0; -font-size: 2.441em; -} -h2 {font-size: 1.953em;} -h3 {font-size: 1.563em;} -h4 {font-size: 1.25em;} -small, .font_small {font-size: 0.8em;} -.prose table { - width: 100%; - margin-bottom: 3em; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; - border: 1px solid #eee - -} -.prose table th { - background-color: #0594CB; - border-bottom: 1px solid #0594CB; - color: white; - font-weight: 400; - text-align: left; - padding: .375em .5em; -} -.prose table td, .prose table tc { - padding: .75em .5em; - text-align: left; - border-right: 1px solid #eee; -} -.prose table tr:nth-child(even) { - background-color: #eee; -} -dl dt { - font-weight: bold; - font-size: 1.125rem; -} -dd { - margin: .5em 0 2em 0; - padding: 0; -} -.f2-fluid { - font-size: 2.25rem; -} -@media screen and (min-width: 60em) { - .f2-fluid { - font-size: 1.25rem; - font-size: calc(0.70833rem + 0.83333vw); - } -} -/* From https://www.cssfontstack.com */ -code, .code, pre code, .highlight pre { - font-family: 'inconsolata',Menlo,Monaco,'Courier New',monospace; -} -.sans-serif { - font-family: 'Muli', Avenir, 'Helvetica Neue', Helvetica, Roboto, Noto, 'Segoe UI', Arial, sans-serif; -} -.serif { - font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif; -} -/* Monospaced Typefaces (for code) */ -.courier { - font-family: 'Courier Next', - courier, - monospace; -} -/* Sans-Serif Typefaces */ -.helvetica { - font-family: 'helvetica neue', helvetica, - sans-serif; -} -.avenir { - font-family: 'avenir next', avenir, - sans-serif; -} -/* Serif Typefaces */ -.athelas { - font-family: athelas, - georgia, - serif; -} -.georgia { - font-family: georgia, - serif; -} -.times { - font-family: times, - serif; -} -.bodoni { - font-family: "Bodoni MT", - serif; -} -.calisto { - font-family: "Calisto MT", - serif; -} -.garamond { - font-family: garamond, - serif; -} -.baskerville { - font-family: baskerville, - serif; -} -/* pagination.html: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/template_embedded.go#L117 */ -.pagination { - margin: 3rem 0; -} -.pagination li { - display: inline-block; - margin-right: .375rem; - font-size: .875rem; - margin-bottom: 2.5em; -} -.pagination li a { - padding: .5rem .625rem; - background-color: white; - color: #333; - border: 1px solid #ddd; - border-radius: 3px; - text-decoration: none; -} -.pagination li.disabled { - display: none; -} -.pagination li.active a:link, -.pagination li.active a:active, -.pagination li.active a:visited { - background-color: #ddd; -} -/* Hides non-meaningful TOC items*/ -#TableOfContents ul li ul li ul li{ - display: none; - } -#TableOfContents ul li { - color: black; - display: block; - margin-bottom: .375em; - line-height: 1.375; -} -#TableOfContents ul li a{ - width: 100%; - padding: .25em .375em; - margin-left: -.375em; - -} -#TableOfContents ul li a:hover { - background-color: #999; - color: white; - -} -.no-js .needs-js { - opacity: 0 -} -.js .needs-js { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.facebook, -.twitter, -.instagram, -.youtube { - fill: #bababa; -} -.facebook:hover { - fill: #3b5998; -} -.twitter { - fill: #55acee; -} -.twitter:hover { - fill: #bababa; -} -.instagram:hover { - fill: #e95950; -} -.youtube:hover { - fill: #bb0000; -} -.mstdn { - display: inline-block; - background-color: #282c37; - color: #d9e1e8; - text-decoration: none; - padding: 4px 10px 4px 30px; - border-radius: 4px; - font-size: 16px; - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"); - background-size: 16px; - background-repeat: no-repeat; - background-position: top 50% left 8px; - -webkit-transition: all 0.5s; - transition: all 0.5s; -} -.mstdn:hover { - background-color: #484c56; -} -.mstdn > span { - color: #9baec8; - font-size: 12px; - padding-left: 3px; -} -.mstdn > span:before { - content: "@"; -} -@media (min-width: 75em) { - - [data-scrolldir="down"] .sticky { - position: fixed; - top:100px; - right:0; - } - - [data-scrolldir="up"] .sticky { - position: fixed; - top:100px; - right:0; - } -} -#right-sidebar { - scrollbar-width: none; /* hide scrollbar: Firefox */ - -ms-overflow-style: none; /* hide scrollbar: Internet Explorer 10+ */ - height: calc(100vh - 9rem); - overflow-y: auto; -} -#right-sidebar::-webkit-scrollbar { /* hide scrollbar: WebKit */ - width: 0; - height: 0; -} -.fill-current { fill: currentColor; } -/* Background */ -.chroma { background-color: #ffffff } -/* Error */ -.chroma .err { color: #a61717; background-color: #e3d2d2 } -/* LineTableTD */ -.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } -/* LineTable */ -.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; } -/* LineHighlight */ -.chroma .hl { display: block; width: 100%;background-color: #ffffcc } -/* LineNumbersTable */ -.chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* LineNumbers */ -.chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* Keyword */ -.chroma .k { font-weight: bold } -/* KeywordConstant */ -.chroma .kc { font-weight: bold } -/* KeywordDeclaration */ -.chroma .kd { font-weight: bold } -/* KeywordNamespace */ -.chroma .kn { font-weight: bold } -/* KeywordPseudo */ -.chroma .kp { font-weight: bold } -/* KeywordReserved */ -.chroma .kr { font-weight: bold } -/* KeywordType */ -.chroma .kt { color: #445588; font-weight: bold } -/* NameAttribute */ -.chroma .na { color: #008080 } -/* NameBuiltin */ -.chroma .nb { color: #999999 } -/* NameClass */ -.chroma .nc { color: #445588; font-weight: bold } -/* NameConstant */ -.chroma .no { color: #008080 } -/* NameEntity */ -.chroma .ni { color: #800080 } -/* NameException */ -.chroma .ne { color: #990000; font-weight: bold } -/* NameFunction */ -.chroma .nf { color: #990000; font-weight: bold } -/* NameNamespace */ -.chroma .nn { color: #555555 } -/* NameTag */ -.chroma .nt { color: #000080 } -/* NameVariable */ -.chroma .nv { color: #008080 } -/* LiteralString */ -.chroma .s { color: #bb8844 } -/* LiteralStringAffix */ -.chroma .sa { color: #bb8844 } -/* LiteralStringBacktick */ -.chroma .sb { color: #bb8844 } -/* LiteralStringChar */ -.chroma .sc { color: #bb8844 } -/* LiteralStringDelimiter */ -.chroma .dl { color: #bb8844 } -/* LiteralStringDoc */ -.chroma .sd { color: #bb8844 } -/* LiteralStringDouble */ -.chroma .s2 { color: #bb8844 } -/* LiteralStringEscape */ -.chroma .se { color: #bb8844 } -/* LiteralStringHeredoc */ -.chroma .sh { color: #bb8844 } -/* LiteralStringInterpol */ -.chroma .si { color: #bb8844 } -/* LiteralStringOther */ -.chroma .sx { color: #bb8844 } -/* LiteralStringRegex */ -.chroma .sr { color: #808000 } -/* LiteralStringSingle */ -.chroma .s1 { color: #bb8844 } -/* LiteralStringSymbol */ -.chroma .ss { color: #bb8844 } -/* LiteralNumber */ -.chroma .m { color: #009999 } -/* LiteralNumberBin */ -.chroma .mb { color: #009999 } -/* LiteralNumberFloat */ -.chroma .mf { color: #009999 } -/* LiteralNumberHex */ -.chroma .mh { color: #009999 } -/* LiteralNumberInteger */ -.chroma .mi { color: #009999 } -/* LiteralNumberIntegerLong */ -.chroma .il { color: #009999 } -/* LiteralNumberOct */ -.chroma .mo { color: #009999 } -/* Operator */ -.chroma .o { font-weight: bold } -/* OperatorWord */ -.chroma .ow { font-weight: bold } -/* Comment */ -.chroma .c { color: #999988; font-style: italic } -/* CommentHashbang */ -.chroma .ch { color: #999988; font-style: italic } -/* CommentMultiline */ -.chroma .cm { color: #999988; font-style: italic } -/* CommentSingle */ -.chroma .c1 { color: #999988; font-style: italic } -/* CommentSpecial */ -.chroma .cs { color: #999999; font-weight: bold; font-style: italic } -/* CommentPreproc */ -.chroma .cp { color: #999999; font-weight: bold } -/* CommentPreprocFile */ -.chroma .cpf { color: #999999; font-weight: bold } -/* GenericDeleted */ -.chroma .gd { color: #000000; background-color: #ffdddd } -/* GenericEmph */ -.chroma .ge { font-style: italic } -/* GenericError */ -.chroma .gr { color: #aa0000 } -/* GenericHeading */ -.chroma .gh { color: #999999 } -/* GenericInserted */ -.chroma .gi { color: #000000; background-color: #ddffdd } -/* GenericOutput */ -.chroma .go { color: #888888 } -/* GenericPrompt */ -.chroma .gp { color: #555555 } -/* GenericStrong */ -.chroma .gs { font-weight: bold } -/* GenericSubheading */ -.chroma .gu { color: #aaaaaa } -/* GenericTraceback */ -.chroma .gt { color: #aa0000 } -/* TextWhitespace */ -.chroma .w { color: #bbbbbb } -@media print { - #page-footer, - body > footer, - body > nav { - display: none; - } -} -/* -Make h6 elements behave like dt elements. Initially implemented to support -linkable glossary entries. - -Yes, it's a hack. That's why it's in the shame file. -*/ -h6 { - margin-top: 0; - margin-bottom: 0; - font-size: 1.125rem; -} -h6:first-of-type { - margin-top: 3em; -} -h6 ~ p { - margin: 0.5em 0 2em 0; -} -/* QR codes */ -img.qrcode { - width: auto; - width: initial; -} -.nested-blockquote blockquote { - border-left: 4px solid #0594CB; - padding-left: 1em; -} -.mw-90 { - max-width:90%; -} -/* purgecss end ignore */ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js b/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js deleted file mode 100644 index a3e1801f8..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js +++ /dev/null @@ -1,17 +0,0 @@ -!function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=1)}([function(t,e,n){!function(e,n){var r=function(t,e,n){"use strict";var r,i;if(function(){var e,n={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in i=t.lazySizesConfig||t.lazysizesConfig||{},n)e in i||(i[e]=n[e])}(),!e||!e.getElementsByClassName)return{init:function(){},cfg:i,noSupport:!0};var o=e.documentElement,s=t.HTMLPictureElement,a=t.addEventListener.bind(t),u=t.setTimeout,c=t.requestAnimationFrame||u,l=t.requestIdleCallback,h=/^picture$/i,f=["load","error","lazyincluded","_lazyloaded"],d={},p=Array.prototype.forEach,g=function(t,e){return d[e]||(d[e]=new RegExp("(\\s|^)"+e+"(\\s|$)")),d[e].test(t.getAttribute("class")||"")&&d[e]},m=function(t,e){g(t,e)||t.setAttribute("class",(t.getAttribute("class")||"").trim()+" "+e)},y=function(t,e){var n;(n=g(t,e))&&t.setAttribute("class",(t.getAttribute("class")||"").replace(n," "))},v=function(t,e,n){var r=n?"addEventListener":"removeEventListener";n&&v(t,e),f.forEach((function(n){t[r](n,e)}))},b=function(t,n,i,o,s){var a=e.createEvent("Event");return i||(i={}),i.instance=r,a.initEvent(n,!o,!s),a.detail=i,t.dispatchEvent(a),a},w=function(e,n){var r;!s&&(r=t.picturefill||i.pf)?(n&&n.src&&!e.getAttribute("srcset")&&e.setAttribute("srcset",n.src),r({reevaluate:!0,elements:[e]})):n&&n.src&&(e.src=n.src)},_=function(t,e){return(getComputedStyle(t,null)||{})[e]},x=function(t,e,n){for(n=n||t.offsetWidth;n0)&&"visible"!=_(i,"overflow")&&(r=i.getBoundingClientRect(),s=z>r.left&&Fr.top-1&&H500&&o.clientWidth>500?500:370:i.expand,r._defEx=d,p=d*i.expFactor,g=i.hFac,U=null,W2&&D>2&&!e.hidden?(W=p,X=0):W=D>1&&X>1&&Q<6?d:0),f!==c&&($=innerWidth+c*g,M=innerHeight+c,l=-1*c,f=c),s=m[n].getBoundingClientRect(),(B=s.bottom)>=l&&(H=s.top)<=M&&(z=s.right)>=l*g&&(F=s.left)<=$&&(B||z||F||H)&&(i.loadHidden||Z(m[n]))&&(R&&Q<3&&!h&&(D<3||X<4)||Y(m[n],c))){if(at(m[n]),u=!0,Q>9)break}else!u&&R&&!a&&Q<4&&X<4&&D>2&&(L[0]||i.preloadAfterLoad)&&(L[0]||!h&&(B||z||F||H||"auto"!=m[n].getAttribute(i.sizesAttr)))&&(a=L[0]||m[n]);a&&!u&&at(a)}},et=function(t){var e,r=0,o=i.throttleDelay,s=i.ricTimeout,a=function(){e=!1,r=n.now(),t()},c=l&&s>49?function(){l(a,{timeout:s}),s!==i.ricTimeout&&(s=i.ricTimeout)}:C((function(){u(a)}),!0);return function(t){var i;(t=!0===t)&&(s=33),e||(e=!0,(i=o-(n.now()-r))<0&&(i=0),t||i<9?c():u(c,i))}}(tt),nt=function(t){var e=t.target;e._lazyCache?delete e._lazyCache:(G(t),m(e,i.loadedClass),y(e,i.loadingClass),v(e,it),b(e,"lazyloaded"))},rt=C(nt),it=function(t){rt({target:t.target})},ot=function(t){var e,n=t.getAttribute(i.srcsetAttr);(e=i.customMedia[t.getAttribute("data-media")||t.getAttribute("media")])&&t.setAttribute("media",e),n&&t.setAttribute("srcset",n)},st=C((function(t,e,n,r,o){var s,a,c,l,f,d;(f=b(t,"lazybeforeunveil",e)).defaultPrevented||(r&&(n?m(t,i.autosizesClass):t.setAttribute("sizes",r)),a=t.getAttribute(i.srcsetAttr),s=t.getAttribute(i.srcAttr),o&&(l=(c=t.parentNode)&&h.test(c.nodeName||"")),d=e.firesLoad||"src"in t&&(a||s||l),f={target:t},m(t,i.loadingClass),d&&(clearTimeout(P),P=u(G,2500),v(t,it,!0)),l&&p.call(c.getElementsByTagName("source"),ot),a?t.setAttribute("srcset",a):s&&!l&&(K.test(t.nodeName)?function(t,e){var n=t.getAttribute("data-load-mode")||i.iframeLoadMode;0==n?t.contentWindow.location.replace(e):1==n&&(t.src=e)}(t,s):t.src=s),o&&(a||l)&&w(t,{src:s})),t._lazyRace&&delete t._lazyRace,y(t,i.lazyClass),S((function(){var e=t.complete&&t.naturalWidth>1;d&&!e||(e&&m(t,i.fastLoadedClass),nt(f),t._lazyCache=!0,u((function(){"_lazyCache"in t&&delete t._lazyCache}),9)),"lazy"==t.loading&&Q--}),!0)})),at=function(t){if(!t._lazyRace){var e,n=V.test(t.nodeName),r=n&&(t.getAttribute(i.sizesAttr)||t.getAttribute("sizes")),o="auto"==r;(!o&&R||!n||!t.getAttribute("src")&&!t.srcset||t.complete||g(t,i.errorClass)||!g(t,i.lazyClass))&&(e=b(t,"lazyunveilread").detail,o&&T.updateElem(t,!0,t.offsetWidth),t._lazyRace=!0,Q++,st(t,e,o,r,n))}},ut=A((function(){i.loadMode=3,et()})),ct=function(){3==i.loadMode&&(i.loadMode=2),ut()},lt=function(){R||(n.now()-q<999?u(lt,999):(R=!0,i.loadMode=3,et(),a("scroll",ct,!0)))},{_:function(){q=n.now(),r.elements=e.getElementsByClassName(i.lazyClass),L=e.getElementsByClassName(i.lazyClass+" "+i.preloadClass),a("scroll",et,!0),a("resize",et,!0),a("pageshow",(function(t){if(t.persisted){var n=e.querySelectorAll("."+i.loadingClass);n.length&&n.forEach&&c((function(){n.forEach((function(t){t.complete&&at(t)}))}))}})),t.MutationObserver?new MutationObserver(et).observe(o,{childList:!0,subtree:!0,attributes:!0}):(o.addEventListener("DOMNodeInserted",et,!0),o.addEventListener("DOMAttrModified",et,!0),setInterval(et,999)),a("hashchange",et,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach((function(t){e.addEventListener(t,et,!0)})),/d$|^c/.test(e.readyState)?lt():(a("load",lt),e.addEventListener("DOMContentLoaded",et),u(lt,2e4)),r.elements.length?(tt(),S._lsFlush()):et()},checkElems:et,unveil:at,_aLSL:ct}),T=(N=C((function(t,e,n,r){var i,o,s;if(t._lazysizesWidth=r,r+="px",t.setAttribute("sizes",r),h.test(e.nodeName||""))for(o=0,s=(i=e.getElementsByTagName("source")).length;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===r(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,a.default)(t,"click",(function(t){return e.onClick(t)}))}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new o.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return l("action",t)}},{key:"defaultTarget",value:function(t){var e=l("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return l("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach((function(t){n=n&&!!document.queryCommandSupported(t)})),n}}]),e}(s.default);function l(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}t.exports=c},function(t,e,n){"use strict";var r,i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,a.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,a.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=u},function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(t),r.removeAllRanges(),r.addRange(i),e=r.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var r=this.e||(this.e={});return(r[t]||(r[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var r=this;function i(){r.off(t,i),e.apply(n,arguments)}return i._=e,this.on(t,i,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),r=0,i=n.length;r";var r=document.createElement("div");r.appendChild(document.createTextNode(e)),n=n||"";var i=document.createElement("div");i.appendChild(document.createTextNode(n));var s=document.createElement("div");return s.appendChild(document.createTextNode(t)),s.innerHTML.replace(RegExp(o(r.innerHTML),"g"),e).replace(RegExp(o(i.innerHTML),"g"),n)}}},function(t,e,n){"use strict";t.exports={element:null}},function(t,e){var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString;t.exports=function(t,e,i){if("[object Function]"!==r.call(e))throw new TypeError("iterator must be a function");var o=t.length;if(o===+o)for(var s=0;s was loaded but did not call our provided callback"),JSONPScriptError:o("JSONPScriptError"," -{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html deleted file mode 100644 index dec9ae48b..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html deleted file mode 100644 index f1fb6d059..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - {{ range . }} - - - - - - {{ end }} - -
LastModLinkGitHub
{{ .Lastmod.Format "2006-01-02" }} - {{ .Title }} - - - {{ with .GitInfo }}{{ .Subject }}{{ else }}Source{{ end }} - -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html deleted file mode 100644 index defcaa055..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html +++ /dev/null @@ -1,9 +0,0 @@ - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html deleted file mode 100644 index a8fc27e21..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ $currentPage := . }} -{{ $menu := .Site.Menus.docs.ByWeight }} -
    - {{ range $menu }}{{ $post := printf "%s" .Post }} -
  • - - {{ .Name }} - -
  • - {{end}} -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html deleted file mode 100644 index 61aa11dde..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html +++ /dev/null @@ -1,23 +0,0 @@ -{{ $currentPage := . }} - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html deleted file mode 100644 index 6ad98923e..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ $currentPage := . }} -{{ $menu := .Site.Menus.global }} - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html deleted file mode 100644 index af3790b16..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html +++ /dev/null @@ -1,37 +0,0 @@ -{{ $currentPage := . }} -{{ $.Scratch.Add "listlinkClasses" "f6 link primary-color-dark hover-white db brand-font ma0 w-100 pv3 ph4" }} - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html deleted file mode 100644 index b04866e52..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- {{ partial "nav-links-docs-mobile.html" . }} -
- -
- - - -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html deleted file mode 100644 index f64111409..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ $currentPage := . }} -
- - - {{ partial "nav-links.html" .}} -
- {{ partial "nav-button-open.html" .}} -
-
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html deleted file mode 100644 index 79b315a44..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html +++ /dev/null @@ -1,24 +0,0 @@ -{{ $images := $.Resources.ByType "image" }} -{{ $featured := $images.GetMatch "*feature*" }} -{{ if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end }} -{{ if not $featured }} - {{ $featured = resources.Get "/opengraph/gohugoio-card-base-1.png" }} - {{ $size := 80 }} - {{ $title := $.LinkTitle }} - {{ if gt (len $title) 20 }} - {{ $size = 70 }} - {{ end }} - - {{ $text := $title }} - {{ $textOptions := dict - "color" "#FFF" - "size" $size - "lineSpacing" 10 - "x" 65 "y" 80 - "font" (resources.Get "/opengraph/mulish-black.ttf") - }} - - {{ $featured = $featured | images.Filter (images.Text $text $textOptions) }} -{{ end }} - -{{ return $featured }} \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html deleted file mode 100644 index 6d195ede6..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - -{{- with $.Params.images -}} -{{- range first 6 . }}{{ end -}} -{{- else -}} -{{- $featured := partial "opengraph/get-featured-image.html" . }} -{{- with $featured -}} - -{{- else -}} -{{- with $.Site.Params.images }}{{ end -}} -{{- end -}} -{{- end -}} - -{{- if .IsPage }} -{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} - -{{ with .PublishDate }}{{ end }} -{{ with .Lastmod }}{{ end }} -{{- end -}} - -{{- with .Params.audio }}{{ end }} -{{- with .Params.locale }}{{ end }} -{{- with .Site.Params.title }}{{ end }} -{{- with .Params.videos }}{{- range . }} - -{{ end }}{{ end }} - -{{- /* If it is part of a series, link to related articles */}} -{{- $permalink := .Permalink }} -{{- $siteSeries := .Site.Taxonomies.series }} -{{ with .Params.series }}{{- range $name := . }} - {{- $series := index $siteSeries ($name | urlize) }} - {{- range $page := first 6 $series.Pages }} - {{- if ne $page.Permalink $permalink }}{{ end }} - {{- end }} -{{ end }}{{ end }} - -{{- /* Facebook Page Admin ID for Domain Insights */}} -{{- with site.Params.social.facebook_admin }}{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html deleted file mode 100644 index 456f87b1c..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html +++ /dev/null @@ -1,22 +0,0 @@ -{{- with $.Params.images -}} - - -{{ else -}} -{{- $featured := partial "opengraph/get-featured-image.html" . }} -{{- with $featured -}} - - -{{- else -}} -{{- with $.Site.Params.images -}} - - -{{ else -}} - -{{- end -}} -{{- end -}} -{{- end }} - - -{{ with site.Params.social.twitter -}} - -{{ end -}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html deleted file mode 100644 index f41e5b791..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html +++ /dev/null @@ -1,3 +0,0 @@ -Improve this page diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html deleted file mode 100644 index dcc96242f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ $currentPage := . }} -{{ $currentURL := .RelPermalink }} -
-
    - -
  • - - News: - -
  • - {{ range $name, $taxonomy := .Site.Taxonomies.categories }} - {{ $link := $name | printf "%s%s" "/categories/" | printf "%s/" }} -
  • - - {{ $name | humanize }} - -
  • - {{ end }} -
-
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html deleted file mode 100644 index e6b644b2f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html +++ /dev/null @@ -1,36 +0,0 @@ -{{ $section_to_display := .section_to_display }} -
- -
-
- {{ partial "nav-links-docs.html" .context }} -
- -
- - -
- {{ $interior_classes := .context.Site.Params.flex_box_interior_classes }} -
- {{ $pages := $section_to_display }} - {{ if in (slice "functions" "methods") $.context.Type }} - {{ $pages = $.context.Pages }} - {{ end }} - {{ range $pages }} - {{ partial "boxes-section-summaries.html" (dict "context" . "classes" $interior_classes "fullcontent" true) }} - {{ end }} -
-
- -
- -
- -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html deleted file mode 100644 index 71a14c0ef..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html +++ /dev/null @@ -1,14 +0,0 @@ -{{ if or .PrevInSection .NextInSection }} -{{/* this div holds these a tags as a unit for flex-box display */}} - -{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html deleted file mode 100644 index af9f4aac1..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ if or .PrevInSection .NextInSection }} -{{/* this div holds these a tags as a unit for flex-box display */}} - -{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html deleted file mode 100644 index cd43dd840..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html +++ /dev/null @@ -1,25 +0,0 @@ -{{if .Prev }} - - {{ partial "svg/ic_chevron_left_black_24px.svg" (dict "size" "30px") }} {{ .Prev.Title }} - -{{end}} - -{{if .Next }} - - {{ .Next.Title }} {{ partial "svg/ic_chevron_right_black_24px.svg" (dict "size" "30px") }} - -{{end}} - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html deleted file mode 100644 index 53995a486..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html +++ /dev/null @@ -1,22 +0,0 @@ -{{- $heading := "See also" }} -{{- $related := site.RegularPages.Related . | first 5 }} - -{{- if in (slice "functions" "methods") .Type }} - {{- $related = slice }} - {{- range .Params.action.related }} - {{- with site.GetPage (lower .) }} - {{- $related = $related | append . }} - {{- else }} - {{- errorf "The 'related' partial was unable to get page %s" . }} - {{- end }} - {{- end }} -{{- end }} - -{{- with $related }} -

{{ $heading }}

- -{{- end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html deleted file mode 100644 index ecdbeb33f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html deleted file mode 100644 index 09c013361..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html +++ /dev/null @@ -1,48 +0,0 @@ -
-
-
- - - -
- Hugo Logo -
- - - - - - {{ with getenv "REPOSITORY_URL" -}} -

Netlify badge

- {{- end }} - -
- -
  - {{ partial "home-page-sections/sponsors.html" (dict "cx" . "gtag" "footer" "classes_section" "pb3 w-100" "classes_copy" "f7 w-90-ns") }} -
- -
- -
  -

The Hugo logos are copyright © Steve Francia 2013–{{ now.Year }}.

-

The Hugo Gopher is based on an original work by Renée French.

-
- - - -
- {{- partial "nav-mobile.html" . -}} -
- -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html deleted file mode 100644 index 54472ba16..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html deleted file mode 100644 index f387d66f3..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html +++ /dev/null @@ -1,38 +0,0 @@ -{{ $currentPage := . }} - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html deleted file mode 100644 index 7dec9de18..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html +++ /dev/null @@ -1,17 +0,0 @@ - -{{ $scripts := resources.Get "output/js/app.js" }} -{{ $isDev := eq hugo.Environment "development" }} -{{ if not $isDev }} -{{ $scripts = $scripts | fingerprint }} -{{ end }} -{{ with $scripts }} - {{ if $isDev }} - - {{ else }} - - {{ end }} - {{ $.Scratch.Set "scripts" . }} -{{end}} - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html deleted file mode 100644 index 8c97ac454..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html deleted file mode 100644 index 243b22ccb..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html +++ /dev/null @@ -1,15 +0,0 @@ -gohugoio -Star diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html deleted file mode 100644 index 0f140cf70..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
- {{ humanize .Section }} -

- - {{ .Title }} - -

- -
-
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg deleted file mode 100644 index da9438414..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg +++ /dev/null @@ -1 +0,0 @@ -Twitter_Logo_Blue diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg deleted file mode 100644 index 6f3c20f76..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg deleted file mode 100644 index e1b170359..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg deleted file mode 100644 index e1b170359..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg deleted file mode 100644 index 2ea15de87..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg deleted file mode 100644 index bc696b90b..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg deleted file mode 100644 index 9f9d71769..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/exclamation.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/exclamation.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg deleted file mode 100644 index 6e6af44a2..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg deleted file mode 100644 index ed2c929b4..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg deleted file mode 100644 index 842be09a1..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg deleted file mode 100644 index 717a35686..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg deleted file mode 100644 index 29bc57ad3..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg deleted file mode 100644 index dabc741e0..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg deleted file mode 100644 index 9c2de7da2..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg deleted file mode 100644 index 9ab114aa3..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html deleted file mode 100644 index 1a6b82159..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg deleted file mode 100644 index 961221f18..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg deleted file mode 100644 index 0f8fbe0d9..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg deleted file mode 100644 index 36d9f1c41..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg deleted file mode 100644 index 05cfb84d1..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg deleted file mode 100644 index bc1e5010c..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg deleted file mode 100644 index 7f6ec255c..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg deleted file mode 100644 index ea72a6f51..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg deleted file mode 100644 index 58d025596..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg deleted file mode 100644 index 3ba28c3f5..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg deleted file mode 100644 index 8ec2eb766..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg deleted file mode 100644 index da37757cf..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg deleted file mode 100644 index 47689a91e..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg deleted file mode 100644 index 5c2ccc2f4..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg deleted file mode 100644 index ae915113b..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg deleted file mode 100644 index b0e2f5b0d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg deleted file mode 100644 index d2ba6d0fc..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg deleted file mode 100644 index ba9400b7f..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg deleted file mode 100644 index f5de52d02..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg deleted file mode 100644 index f1a794565..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg deleted file mode 100644 index d0d9ae938..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Svg Vector Icons : http://www.onlinewebfonts.com/icon - - \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg deleted file mode 100644 index 83b706383..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg deleted file mode 100644 index da3d9cfcf..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg deleted file mode 100644 index 181789b54..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg deleted file mode 100644 index 247ca9062..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg deleted file mode 100644 index 2bdcf5f94..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg deleted file mode 100644 index fe3bf0296..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg deleted file mode 100644 index 59eeb71c2..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html deleted file mode 100644 index 59e3e51a0..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html +++ /dev/null @@ -1,37 +0,0 @@ -{{ $currentPageUrl := .RelPermalink }} -{{ if and .Params.tags .Site.Taxonomies.tags }} - {{ $name := index .Params.tags 0 }} - {{ $name := $name | urlize }} - {{ $tags := index .Site.Taxonomies.tags $name }} - -
- -
-{{end}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt deleted file mode 100644 index 25b9e9a0d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt +++ /dev/null @@ -1,8 +0,0 @@ -User-agent: * -# robotstxt.org - if ENV production variable is false robots will be disallowed. -{{ if eq (getenv "HUGO_ENV") "production" }} - Disallow: admin/ - Disallow: -{{ else }} - Disallow: / -{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html deleted file mode 100644 index 2755b1e2d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - {{ range $ind, $art := $.Site.Data.articles.article }} - - - - - - {{ end }} - -
Title - Author - Date -
{{$art.title | markdownify }}{{ $art.author | markdownify }}{{ $art.date }}
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html deleted file mode 100644 index 2e10c3dee..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html +++ /dev/null @@ -1,6 +0,0 @@ -
- {{ range .Site.Data.docs.chroma.lexers }} -
{{ .Name }}
-
{{ with .Aliases }}{{ delimit . ", " }}{{ end }}
- {{ end }} -
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html deleted file mode 100644 index dd21551cb..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html +++ /dev/null @@ -1,35 +0,0 @@ -{{- /* -Renders syntax highlighted code. - -@param {bool} [copy=false] If true, display a copy to clipboard button. -@param {string} [file] The file name to display above the rendered code. -@param {string} [lang] The code language of the inner content. - -@returns {template.HTML} -*/}} - -{{- /* Get parameters. */}} -{{- $copy := false }} -{{- if in (slice "false" false 0) (.Get "copy") }} - {{- $copy = false }} -{{- else if in (slice "true" true 1) (.Get "copy")}} - {{- $copy = true }} -{{- end }} -{{- $file := or (.Get "file") " " }} -{{- $lang := or (.Get "lang") (path.Ext $file | strings.TrimPrefix ".") "text" }} - -{{- /* Use the go-html-template Chroma lexer for HTML. */}} -{{- if eq $lang "html" }} - {{- $lang = "go-html-template" }} -{{- end }} - -{{- /* Render. */}} -
-
{{ $file | htmlUnescape }}
- {{- if $copy }} - - {{- end }} -
- {{- highlight (trim .Inner "\n\r") $lang }} -
-
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html deleted file mode 100644 index 4e96504ab..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html +++ /dev/null @@ -1,4 +0,0 @@ -

Syntax

-
-  {{- .Inner -}}
-
diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html deleted file mode 100644 index d544cd471..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html +++ /dev/null @@ -1,18 +0,0 @@ -{{ $text := ` -Most of the commands for **Hugo Modules** require a newer version (>= 1.18) of Go installed (see https://golang.org/dl/) and the relevant VCS client (e.g. Git, see https://git-scm.com/downloads/ ). -If you have an "older" site running on Netlify, you may have to set GO_VERSION to 1.19 or newer in your Environment settings. - -For more information about Go Modules, see: - -* https://go.dev/wiki/Modules -* https://blog.golang.org/using-go-modules -` }} - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html deleted file mode 100644 index 606d2219c..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html +++ /dev/null @@ -1,34 +0,0 @@ -{{- /* -Renders a "new in" button indicating the version in which a feature was added. - -When comparing the current version to the specified version, the "new in" -button will be hidden if any of the following conditions is true: - -- The major version difference exceeds the majorVersionDiffThreshold -- The minor version difference exceeds the minorVersionDiffThreshold - -@param {string} version The semantic version string, with or without a leading v. -@returns {template.HTML} - -@example {{< new-in 0.100.0 >}} -*/}} - -{{- /* Set defaults. */}} -{{- $majorVersionDiffThreshold := 0 }} -{{- $minorVersionDiffThreshold := 30 }} -{{- $displayExpirationWarning := true }} - -{{- /* Render. */}} -{{- with $version := .Get 0 | strings.TrimPrefix "v" }} - {{- $majorVersionDiff := sub (index (split hugo.Version ".") 0 | int) (index (split $version ".") 0 | int) }} - {{- $minorVersionDiff := sub (index (split hugo.Version ".") 1 | int) (index (split $version ".") 1 | int) }} - {{- if or (gt $majorVersionDiff $majorVersionDiffThreshold) (gt $minorVersionDiff $minorVersionDiffThreshold) }} - {{- if $displayExpirationWarning }} - {{- warnf "This call to the %q shortcode should be removed: %s. The button is now hidden because the specified version (%s) is older than the display threshold." $.Name $.Position $version }} - {{- end }} - {{- else }} - New in v{{ $version }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a positional parameter (version). See %s" .Name .Position }} -{{- end -}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html deleted file mode 100644 index 99818114e..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html +++ /dev/null @@ -1,7 +0,0 @@ -{{ $_hugo_config := `{ "version": 1 }` }} - - diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html deleted file mode 100644 index de8083443..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html +++ /dev/null @@ -1,29 +0,0 @@ -{{- $highlight := or (.Get "highlight") "" }} - -{{- $markdown := false }} -{{- if in (slice "false" false 0) (.Get "markdown") }} - {{- $markdown = false }} -{{- else if in (slice "true" true 1) (.Get "markdown") }} - {{- $markdown = true }} -{{- end }} - -{{- with .Get "file" }} - {{- if os.FileExists . }} - {{- with os.ReadFile . }} - {{- $content := trim . "\n\r" }} - {{- if $markdown }} - {{- $content | markdownify }} - {{- else if $highlight }} - {{- highlight $content $highlight }} - {{- else }} - {{- $content | safeHTML }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to read %q. See %s" $.Name . $.Position }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a 'file' parameter. See %s" $.Name $.Position }} -{{- end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html deleted file mode 100644 index 50a099267..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html +++ /dev/null @@ -1 +0,0 @@ -{{ if .Inner }}{{ end }} \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html deleted file mode 100644 index bff52ad8d..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html +++ /dev/null @@ -1,46 +0,0 @@ -{{ define "main" }} -
-
-

- {{ .Title }} -

-
- {{ .Content }} -
-
-
- {{ range (.Paginate (.Pages | shuffle ) 20).Pages }} - {{template "showcase_items" .}} - {{ end }} -
- -
The Showcase articles are copyrighted by their respective content authors. Any open source license will be attached.
-
-{{ end }} - - -{{define "showcase_items"}} - -
- {{ $img := (.Resources.ByType "image").GetMatch "*featured*" }} - {{ with $img }} - {{ $big := .Fill "1024x512 top" }} - {{ $small := $big.Resize "512x" }} - - {{end}} -
{{/* the margin aligns to the bottom */}} -

- {{- .Title -}} -

-
-
-
- - -{{end}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html deleted file mode 100644 index 5ae1e07a7..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html +++ /dev/null @@ -1,106 +0,0 @@ -{{ define "title" }} -Showcase: {{ .Title }} -{{ end }} - -{{ define "main" }} -
- - -
- -
- {{template "sc-details" .}} -
- -
- {{template "sc-main-column" .}} -
- - - -
- -
{{/* bottom row */}} - Last Update: {{ .Lastmod.Format "January 2, 2006" }}
- {{ partial "page-edit.html" . }} -
-
The Showcase articles are copyright the content authors. Any open source license will be attached.
-
-{{ end }} - - - -{{define "sc-main-column"}} - {{ $img := (.Resources.ByType "image").GetMatch "*featured*" }} - {{ with $img }} - {{ $big := .Fill "1024x512 top" }} - {{ $small := $big.Resize "512x" }} - {{ $img.Title }} - {{ end }} - - -{{end}} - -{{define "sc-details"}} - -{{end}} - -{{define "sc-navigation"}} - {{$section := where .Site.RegularPages "Section" .Section}} - {{$number_of_entries := $section | len}} - -{{end}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html b/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html deleted file mode 100644 index 8b5f0da85..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html +++ /dev/null @@ -1,55 +0,0 @@ -{{ $pkg := .Params.package}} -{{ $funcs := index site.Data.docs.tpl.funcs $pkg }} - -{{ range $k, $v := $funcs }} - {{ if $v.Description }} - {{ $func := printf "%s.%s" $pkg $k }} - {{ $id := $func | anchorize | safeURL }} -

- - - - - - - {{ $func }} -

- {{ with $v.Description }} -

- {{ . | $.RenderString | safeHTML }} -

- {{ end }} -

- Syntax -

-
- {{ $pkg }}.{{ $k }} - {{ with $v.Args }} - - {{ delimit $v.Args ", "}} - - {{ end }} - -
- {{ if $v.Examples }} -

- Examples -

- {{ end }} - {{ range $v.Examples }} - {{ $input := index . 0 }} - {{ $result := index . 1 }} - {{ $example := printf "%s ---> %s" $input $result }} - - {{ highlight $example "go-html-template" "" }} - {{ end }} - {{ with $v.Aliases }} -

- Aliases -

-

- {{ delimit . ", "}} -

- {{ end }} - {{ end }} -{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/package.json b/_vendor/github.com/gohugoio/gohugoioTheme/package.json deleted file mode 100644 index 14d128910..000000000 --- a/_vendor/github.com/gohugoio/gohugoioTheme/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "gohugo-default-styles", - "version": "1.1.0", - "description": "Default Theme for Hugo Sites", - "main": "index.js", - "repository": "", - "author": "budparr", - "license": "MIT", - "scripts": { - "build": "NODE_ENV=production webpack", - "build-dev": "NODE_ENV=development webpack --progress --watch", - "start": "npm run build-dev" - }, - "devDependencies": { - "clean-webpack-plugin": "^1.0.0", - "clipboard": "^2.0.4", - "css-loader": "^1.0.1", - "docsearch.js": "^2.6.1", - "file-loader": "^2.0.0", - "glob-all": "^3.1.0", - "lazysizes": "^5.3.2", - "mini-css-extract-plugin": "^0.4.4", - "postcss": "^7.0.5", - "postcss-cssnext": "^3.1.0", - "postcss-import": "^12.0.1", - "postcss-loader": "^3.0.0", - "purgecss-webpack-plugin": "^1.3.1", - "scrolldir": "^1.4.0", - "tachyons": "^4.7.0", - "typeface-muli": "0.0.54", - "webpack": "^4.25.1", - "webpack-command": "^0.4.2" - } -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png b/_vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png deleted file mode 100644 index ecf1fc020af4ac75a5875c266315b26b72eebdd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6238 zcmb_gXHXPPw;n(N$x4<*f-lIDa}JV01SF>=vt%T*WRNTwfh8y?AfQBHiIQ^`5Xnni zmMA$%zUzB`-5TFX!^g(l zt1DBWnC70Hthy`!R3#B!Ti(a?8Ld^c)d9et6#zmb0N@fc6uJoj-n;;?Z2c)UEjIgoY_=l zZ%|)Ig0(GgCf0g8WJnG2?wv1#nzj$;1zTSt+QzqOh507+D-i@|-lR1e2wQ&!(08+pS!KwUsldLb^N+Ng{ zmhGBCZWKjAd3&ETB8eQwh#84^6iC82{!<(NM91s<8(w(3(zSiAF=-aE#3?xQ4?`;~ z{a{n+aH4*^^?p#um|QS>9DVWOYpt8bfx*0blj)KfpNb`l>?1BJdD8O4-I+(P#=ls?-nmDmuD;aPtyN;>WW)EJZEkcipwakyZH}Y_R3nE(V#MBDd`w_y?MUd)jI(k|mgQ$3-%@xZ~YY<){5C#Tp4xSoW&`eMlEi#fbxO2- z^!H*C@Ge{{;JJ&vm7}jtXnNnGz|qn=+;_`fH_PCg@OPdFsAK&nwpbHdQkn03mpR19 zpEIAX7NEWAiSYdNV4sS*^N3*QDQiDIm_2-@P9p;5`gWkVL9R-#;+cBj7H;I1 z_H2beZ`FUw(*)iBQZ42~mhcsBxaq(nyhJqEwIpxRGtR?Ck#AXid zAMBn-O1CH=5qj5y?7|c@CkxvVDQSP_-48u&e>>;2Zxp*%*xVlK5Jkm29z(Ga22#{Y zL~Ul9rKssCR=Lc_G2@ewqdU!8Op#(NuT8`~BKuSFp`r4nIjNhc(BahAQSltvaG?7vkh{?yHOI_8` zIdm;MRxLq2BzUv)RN=O{?H$x zfW>sr^S6W?4JzB1>xXSAw8vd?vIL)uZbRO@O&-~(h^)!9imE50brh=^f}UO#r-x6J z$Hitq*>U5^2%_ZPzV-U&Ndc{Gg>m4TKGS?Pd!dKBaPj00O#p`i8QSDwg7kNz>r&zl zsC;%4=~qxuKDaX1;EY~KV~uO#!~;w;!+_sCG2qL-b$lf;zN$p#NUH=kXG0T|+mbhP z!D!*9uBwVKm$=2YZ2DGY(B|4VVjZbK)!6{uD>O^#_{mREqD;#y70KL86;DZ-B)_#O z#t6PCx`b8d3T<|yBD4z}D~j}9TeIZ%fcL(m9&@8UH*lZDx|I|h35^>_#r5yQ-lS2w z#_aPWX3#@&@+>!n&uk?Un1Yu}MEzoSbQd-${jx~-FhqPM zLs}thHSzF1qbMudy-}(@m<#n@OAaM&Ruic9K_s7cGyG>O%ls9 z{tmY?gRU;jG+INI0Sp9j_ zC4*|N)438~KMn-UcSZ97)I=|`bOp040!%T`6!CIGmq1bM{j?A zfIWgB_jkl+VAy^tXr{*LSw%VtXu>SWbx(QZbuhSHP@S+!g;`*fqWKP!0u`l`R>CG+ z2F=vWt)dLgcV~YlOu9^{@)oI?Z<6KPAl&qlS^ys4d8r-5FyN#&{So)G;;wq(?LbHzm^&x!Rsn0 z1L~FxYN8>f!iF?ePo^hW@wv1vWSxM23zMB^ETnpvn}YfJqh{}!GwDK08Fi$}Il7%> z>c`reU6OuC;1s*BxZ~L!*w>Bgyc~nNP++A`StYs}kN$n^=&|pE_H)3MgFEQ0Y4g3G zpd;RI46sP2n5&Y#JnL6}>D>t7L6zxcSwdE2$3uXy~&Ey6Z4hCWuNpu`vGe1rU z8?Z2m)!q9|9D6DEo{Utnts%+vGLEQ0qpHAMV?c}6hY|f3(D|e9SnbpgSZ{BiY`MxP z-fzAf&&l}JUzs};bpB0mT)egO^hl24QC2)z!mC~A_OqTIK$39&QF7)yIT)|&y<&UA z5F##|X*Im*UHj-RZXM{)#pHVt9P4}S(elW_0ToI8VcQ+Ya+KUX92!cl^M$T&5Ebc$ zaciC?#^_uF4!p!!Xv?wMCm({`MApwc;6Gfw0+JseQ_G)^0D$0V7-~ui8;A$T#;UlR zQKaQ_+Igz0?>D5?5Ba0ps}43^qqOrg%zR`?j?2o_?IcXnE!xXQsnu9NzhrNBpWa7> z-M5BAQ!)F{;}pb&N1lD1_$&~LXzGb}c%h_XHMkHYMlnd|Zp3MJD$hOXPdUM7v#EJh?K`CC*q5jBSQ%@y$CQ2YuxcmnA)#rwQ3LMW-Qb>)IGY?63CCFqnV(fR8z6kVnY*RigiP= z4IR^Moiz`~^=niG~C^eD22gEd@I6F2_i z4W9*Q?E=%4&mXJLpgPqgiL%sIqq@L(`scZ~O8X4MK{1@uDpNY)Jm?(`&iZ|(e+asX zG%Y}n?wf5+ERVOPgp)?w`}>j-ClLs$AFoKabmVDzFg_2WBkFq+WqFCPrH!?QMXW^h z%eqDW9}SA-++M|JsF9(mkWC|gM>E#XyXWx=zqJORHIkb&h2(h@&QfMl6N(!TdxpJ3 z-(hT*Mzt3i6h<8bx@Yj8TJKE<9U$mp?{_i$iI508aSK&?gNsSV6$@W9|8?s;Pj5YM zSwee3DI@QXefGyNG2ZsV$IV7S(Yyz*{qOUu_$CnWoXO$~_Yf9|*Xetet_NV12Nh&u z`2VkVvhRsboCr@WSs;I%-d^k9t$oqcm7l;$xN}c>5|*5| zRU13^(R^pA{&#{cB6CDMQT}?9ECSRAwnZo*#9l8|D~AqVSgp*Jxy*Mb`GqID%tw|Y z(1BGlw$DE%$&1(JwurQ$jH4d7B_<&RaK1n7lQf9~YKfl0&?O~o7&ex^ zN;LFHMw$czf9xKrKhUTCIM$N;v)u?q)8z2=8?R9J?6bh^1Rz|$)ZE41@hfJJjCjhd zsHN~QbSKhI{VRnXqBeG0lmuJQTwoKBKMqbJ-iiX1=Kr=S^VZ09tCZ+zJOdWVx-W!J@f5YHo)sBS z_?@O7)7!1#V^{p@e zkA*)AHw9y8tx>uD{^#Zftl0dB$_3g0tJwVJTl7<8As?hom-`mA?<6BW&o0z_9c|H0 zp-woiV#Q2O6`8~X;{!(9*|7qDK|6oIF{u??*Uadq=vEJ?pAdfQ7&3x2JfN!<%%N^1eqacSTxmAK)L*9HAQ+-P0 z55PGD_rmcPUd31d^I6fsg4+We*}S+;(xx_J%EQm`t?eDNKMZm7*Ks`z^YY`*Pq)z-YZ;w znZj|@`gCrT$2fMqiY(8;IKEFw6y{pITx*Qs((u8$jia!)xZ&sJ$v&?MUCiYWQmWqr z*p%rog4QggviV%)IH9!8!JkthFxZ?kEA1y(h8mzrQTUt_wxceV!9z{JRe-K){$wQV zcK+_qp37=Wy+Ceig~^S6CE9+XSzn7Z2i(T%(yVh*$3lc72VzL$--@BIie-6YC4@dGQ!E0h&nG;E8>WRX2 zmimhgy0ZZ*$})Fi}Ody5(!PN!DF#r-Vv)fl48$*_ED#a|uZ zw6=$?`M7H?bIGYphsfuUkuskRfynre2>+`RZ{9qYOph;24Q>lL2xhdQQ4%x2>`{yL z&f}b2E+!PSVw5*?j>4U%O&mBW1?rwk;G5;N%WJQl{8iugP%^m=vL$W>nkd?MW_%;~ zBsWjZk0;ejKqT9ejo{Ir^%xqt=!m;$N=TL-{Nx?_+0Wa|V{bs2y zMrAtr3Ij7~gCHMc2_YvFGY?pMD2^<5?u(w(2UGhy=?wT;N~OAN@S}^Oq5w`FnkZY5 zqI^wtMFzv23^^%>gTN~hNe=+%ZSbpti2FirGA7pJw}oKJF{IJ&;#eaLYla#nB*XODenB`o%^U>^Cw2xN({LT{F}<-rRjD_2IfK2+Fq5767q!O_844v z)M?tk@UW9+x^xmF%u3km&{Fp|BChshB!wvF>Kc^6z8fQSa$<9a#%dQmO_M==bgq!P z?4k@c%YSK4oe1Ct&{HzwV?EcIKtn1Z$s_P$3J{P93@x?1oDrhD8KlSI;d#A?%1g%q z0F-U3zBuM&0XeYlH`|db3|QQQ@MiXZE_mRuc(w4b`XvIAjD~1T3wp)5n0kiFd0Es_ z1Uu?n=Ow(+^|h~8B&i1h!17GpZ3SnWTUqdCv)_mllbn{W3WhG$kR@oJfIMIkp!o{+ zbWAX2<^GT&?a-MXtIZXmw|zy}^=cGSzZfz=SYmUcMW$CbP``bXglV3cQ~0^uKaY~0 zvGRA2?HkAZm8shaXIcXL===0XhartG=4z#`v!E0bzlpmBOm6+H?*QY5BzF zHVr5)qMz;Xe0XifW=Tp1%ng|)ZWO*Wj!~k4%i>$cb4`@6Q;K1@z8NN|wBG1xZ&nfi ztQYoB!%Yo?E&kC;!vQd-O&zk+Cxf#?qoR<^D4Jb$*jyp#Bh6TzNl z#Dy!8Ho!mDJR=V`1LGU{F#T^%JGM3H&cSrACKXMb{E)yw%GIhnLVt|<`ln6c^x1(_ zJ@E#=w+20}gV-Dm`upY4#UP^ZG*v8#4rrD~5Y>a}{TyEeTGYx2Q-FQ@;=?h2h2}BC zAevfp%3N}x8FE-XQf5t-k3Gb^qAa4oFgv_N#c)EurIggGKmq{mMdi5-?E5l515d+@ z&T6nM%ov|LbM`N^@wa+5H9+|uM?$whTncG;)bP z>euS#UO5KJ5uN>?m+29&)&L;B%3WRW_S6f#tco)AGsD}TQyyWK5bkNM?GRxi8nrFY ztqxtYM~P~noHZ^s$ZIox)Ov)7!<0h3vIEGJSQP zGZySO?jhzI4bVuFT_1gJa8f%#a5&tFW>AYj-wciy=}oa$<`|qaOefmq&=I8g_^rHu zp$v0L2=W%9BPRJ?7;Y&$G_G{@9HY7OS_6MvzVh};T{T=6)(W&xckZ(dj+~}GN)Y^e zeoXkKnR%F+8Lxx#Kd|&W91s`dd`^Ol59V~7#Y@r9%f`|RDsJro#WVmvFP{h(FFzMQ zpDwScIIplczZeHEpExfs7_vO@KO9_KZ5(X<{&xpMEJJUMgMq)Up_jHLjM2@*)z-lo z%IM|i24!?`^Rfm2-w&uGB7Ap!Hnu^{p0&&p.parentNode.classList.add("expand")}}catch(t){a=!0,u=t}finally{try{!s&&l.return&&l.return()}finally{if(a)throw u}}}},function(t,e,n){"use strict";n(13)({apiKey:"167e7998590aebda7f9fedcf86bc4a55",indexName:"hugodocs",inputSelector:"#search-input",debug:!0})},function(t,e,n){"use strict";n(14),n(15)},function(t,e,n){"use strict";function r(){for(var t=this.dataset.target.split(" "),e=document.querySelector(".mobilemenu:not(.dn)"),n=document.querySelector(".desktopmenu:not(.dn)"),r=document.querySelector(".desktopmenu:not(.dn)"),i=0;i=0?function(){var t=window.pageYOffset;(t>=i-s||window.innerHeight+t>=document.body.offsetHeight)&&clearInterval(u)}:function(){window.pageYOffset<=(i||0)&&clearInterval(u)};var u=setInterval(a,16)},e=document.querySelectorAll("#TableOfContents ul li a");[].forEach.call(e,function(e){e.addEventListener("click",function(n){n.preventDefault();var r=e.getAttribute("href"),i=document.querySelector(r),o=e.getAttribute("data-speed");i&&t(i,o||500)},!1)})}}()},function(t,e,n){"use strict";function r(t){if(t.target){t.preventDefault();var e=t.currentTarget,n=e.getAttribute("data-toggle-tab")}else var n=t;window.localStorage&&window.localStorage.setItem("configLangPref",n);for(var r=document.querySelectorAll("[data-toggle-tab='"+n+"']"),i=document.querySelectorAll("[data-pane='"+n+"']"),a=0;a0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=s})},{select:5}],8:[function(e,n,r){!function(i,o){if("function"==typeof t&&t.amd)t(["module","./clipboard-action","tiny-emitter","good-listener"],o);else if(void 0!==r)o(n,e("./clipboard-action"),e("tiny-emitter"),e("good-listener"));else{var s={exports:{}};o(s,i.clipboardAction,i.tinyEmitter,i.goodListener),i.clipboard=s.exports}}(this,function(t,e,n,r){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function u(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}var c=i(e),l=i(n),h=i(r),f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},p=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===f(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,h.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new c.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(l.default);t.exports=d})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)})},function(t,e,n){/*! docsearch 2.4.1 | © Algolia | github.com/algolia/docsearch */ -!function(e,n){t.exports=n()}(0,function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=46)}([function(t,e,n){"use strict";function r(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var i=n(1);t.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(){return!!/(msie|trident)/i.test(navigator.userAgent)&&navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]},escapeRegExChars:function(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(t){return"number"==typeof t},toStr:function(t){return void 0===t||null===t?"":t+""},cloneDeep:function(t){var e=this.mixin({},t),n=this;return this.each(e,function(t,r){t&&(n.isArray(t)?e[r]=[].concat(t):n.isObject(t)&&(e[r]=n.cloneDeep(t)))}),e},error:function(t){throw new Error(t)},every:function(t,e){var n=!0;return t?(this.each(t,function(r,i){if(!(n=e.call(null,r,i,t)))return!1}),!!n):n},any:function(t,e){var n=!1;return t?(this.each(t,function(r,i){if(e.call(null,r,i,t))return n=!0,!1}),n):n},getUniqueId:function(){var t=0;return function(){return t++}}(),templatify:function(t){if(this.isFunction(t))return t;var e=i.element(t);return"SCRIPT"===e.prop("tagName")?function(){return e.text()}:function(){return String(t)}},defer:function(t){setTimeout(t,0)},noop:function(){},formatPrefix:function(t,e){return e?"":t+"-"},className:function(t,e,n){return(n?"":".")+t+e},escapeHighlightedString:function(t,e,n){e=e||"";var i=document.createElement("div");i.appendChild(document.createTextNode(e)),n=n||"";var o=document.createElement("div");o.appendChild(document.createTextNode(n));var s=document.createElement("div");return s.appendChild(document.createTextNode(t)),s.innerHTML.replace(RegExp(r(i.innerHTML),"g"),e).replace(RegExp(r(o.innerHTML),"g"),n)}}},function(t,e,n){"use strict";t.exports={element:null}},function(t,e){var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString;t.exports=function(t,e,i){if("[object Function]"!==r.call(e))throw new TypeError("iterator must be a function");var o=t.length;if(o===+o)for(var s=0;s was loaded but did not call our provided callback"),JSONPScriptError:i("JSONPScriptError"," {{< /code >}} @@ -192,9 +195,25 @@ See the [inline delimiters](#inline-delimiters) section for details. To use KaTeX instead of MathJax, replace the partial template from [Step 2] with this: {{< code file=layouts/partials/math.html copy=true >}} - - - + + + + {{ end }} + {{ else }} {{ end }} - {{ else }} - {{ $opts := dict "minified" true }} - {{ with . | babel $opts | fingerprint }} - - {{ end }} {{ end }} {{ end }} ``` @@ -72,20 +75,32 @@ module.exports = { ## Options -config -: (`string`) Path to the Babel configuration file. Hugo will, by default, look for a `babel.config.js` in your project. More information on these configuration files can be found here: [babel configuration](https://babeljs.io/docs/en/configuration). +###### compact -minified -: (`bool`) Save as many bytes as possible when printing +(`bool`) Whether to remove optional newlines and whitespace. Enabled when `minified` is `true`. Default is `false` -noComments -: (`bool`) Write comments to generated output (true by default) +###### config -compact -: (`bool`) Do not include superfluous whitespace characters and line terminators. Defaults to `auto` if not set. +(`string`) Path to the Babel configuration file. Hugo will, by default, look for a `babel.config.js` file in the root of your project. See [details](https://babeljs.io/docs/en/configuration). -verbose -: (`bool`) Log everything +###### minified -sourceMap -: (`string`) Output `inline` or `external` sourcemap from the babel compile. External sourcemaps will be written to the target with the output file name + ".map". Input sourcemaps can be read from js.Build and node modules and combined into the output sourcemaps. +(`bool`) Whether to minify the compiled code. Enables the `compact` option. Default is `false`. + +###### noBabelrc + +(`string`) Whether to ignore `.babelrc` and `.babelignore` files. Default is `false`. + +###### noComments + +(`bool`) Whether to remove comments. Default is `false`. + +###### sourceMap + +(`string`) Whether to generate source maps, one of `external`, `inline`, or `none`. Default is `none`. + + + +###### verbose + +(`bool`) Whether to enable verbose logging. Default is `false` diff --git a/content/en/functions/js/Batch.md b/content/en/functions/js/Batch.md index fbc1c7e61..08913f61f 100644 --- a/content/en/functions/js/Batch.md +++ b/content/en/functions/js/Batch.md @@ -1,7 +1,6 @@ --- title: js.Batch description: Build JavaScript bundle groups with global code splitting and flexible hooks/runners setup. -weight: 50 categories: [] keywords: [] action: @@ -13,6 +12,7 @@ action: - functions/resources/Minify returnType: js.Batcher signatures: ['js.Batch [ID]'] +weight: 20 toc: true --- diff --git a/content/en/functions/js/Build.md b/content/en/functions/js/Build.md index 4829f3b83..9bdc2454a 100644 --- a/content/en/functions/js/Build.md +++ b/content/en/functions/js/Build.md @@ -1,17 +1,18 @@ --- title: js.Build -description: Bundles, transpiles, tree shakes, and minifies JavaScript resources. -weight: 30 +description: Bundle, transpile, tree shake, and minify JavaScript resources. categories: [] keywords: [] action: aliases: [] related: + - functions/js/Batch - functions/js/Babel - functions/resources/Fingerprint - functions/resources/Minify returnType: resource.Resource signatures: ['js.Build [OPTIONS] RESOURCE'] +weight: 10 toc: true --- @@ -27,31 +28,37 @@ The `js.Build` function uses the [evanw/esbuild] package to: ```go-html-template {{ with resources.Get "js/main.js" }} - {{ if hugo.IsDevelopment }} - {{ with . | js.Build }} + {{ $opts := dict + "minify" hugo.IsProduction + "sourceMap" (cond hugo.IsProduction "" "external") + "targetPath" "js/main.js" + }} + {{ with . | js.Build $opts }} + {{ if hugo.IsProduction }} + {{ with . | fingerprint }} + + {{ end }} + {{ else }} {{ end }} - {{ else }} - {{ $opts := dict "minify" true }} - {{ with . | js.Build $opts | fingerprint }} - - {{ end }} {{ end }} {{ end }} ``` ## Options -targetPath -: (`string`) If not set, the source path will be used as the base target path. +###### targetPath + +(`string`) If not set, the source path will be used as the base target path. Note that the target path's extension may change if the target MIME type is different, e.g. when the source is TypeScript. -format -: (`string`) The output format. One of: `iife`, `cjs`, `esm`. Default is `iife`, a self-executing function, suitable for inclusion as a ` - {{ end }} - {{ else }} - {{ $opts := dict "minified" true }} - {{ with . | babel $opts | fingerprint }} - - {{ end }} - {{ end }} -{{ end }} -``` - -## Setup - -Step 1 -: Install [Node.js](https://nodejs.org/en/download) - -Step 2 -: Install the required Node.js packages in the root of your project. - -```sh -npm install --save-dev @babel/core @babel/cli -``` - -Step 3 -: Add the babel executable to Hugo's `security.exec.allow` list in your site configuration: - -{{< code-toggle file=hugo >}} -[security.exec] - allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$', '^babel$'] -{{< /code-toggle >}} - -## Configuration - -We add the main project's `node_modules` to `NODE_PATH` when running Babel and similar tools. There are some known [issues](https://github.com/babel/babel/issues/5618) with Babel in this area, so if you have a `babel.config.js` living in a Hugo Module (and not in the project itself), we recommend using `require` to load the presets/plugins, e.g.: - -```js -module.exports = { - presets: [ - [ - require("@babel/preset-env"), - { - useBuiltIns: "entry", - corejs: 3, - }, - ], - ], -}; -``` - -## Options - -config -: (`string`) Path to the Babel configuration file. Hugo will, by default, look for a `babel.config.js` in your project. More information on these configuration files can be found here: [babel configuration](https://babeljs.io/docs/en/configuration). - -minified -: (`bool`) Save as many bytes as possible when printing - -noComments -: (`bool`) Write comments to generated output (true by default) - -compact -: (`bool`) Do not include superfluous whitespace characters and line terminators. Defaults to `auto` if not set. - -verbose -: (`bool`) Log everything - -sourceMap -: (`string`) Output `inline` or `external` sourcemap from the babel compile. External sourcemaps will be written to the target with the output file name + ".map". Input sourcemaps can be read from js.Build and node modules and combined into the output sourcemaps. diff --git a/content/en/functions/resources/Concat.md b/content/en/functions/resources/Concat.md index 09bde82fe..6e81f3f44 100644 --- a/content/en/functions/resources/Concat.md +++ b/content/en/functions/resources/Concat.md @@ -12,7 +12,7 @@ action: The `resources.Concat` function returns a concatenated slice of resources, caching the result using the target path as its cache key. Each resource must have the same [media type]. -Hugo publishes the resource to the target path when you call its [`Publish`], [`Permalink`], or [`RelPermalink`] method. +Hugo publishes the resource to the target path when you call its [`Publish`], [`Permalink`], or [`RelPermalink`] method. [media type]: https://en.wikipedia.org/wiki/Media_type [`publish`]: /methods/resource/publish/ diff --git a/content/en/functions/resources/GetRemote.md b/content/en/functions/resources/GetRemote.md index 982529077..3b294e5a1 100644 --- a/content/en/functions/resources/GetRemote.md +++ b/content/en/functions/resources/GetRemote.md @@ -18,6 +18,15 @@ action: toc: true --- +{{< new-in 0.141.0 >}} +The `Err` method on the returned resource was removed in v0.141.0. + +Use the [`try`] statement instead, as shown in the [error handling] example below. + +[`try`]: /functions/go-template/try +[error handling]: #error-handling +{{< /new-in >}} + ```go-html-template {{ $url := "https://example.org/images/a.jpg" }} {{ with try (resources.GetRemote $url) }} @@ -35,6 +44,39 @@ toc: true The `resources.GetRemote` function takes an optional map of options. +###### body + +(`string`) The data you want to transmit to the server. + +###### headers + +(`map[string][]string`) The collection of key-value pairs that provide additional information about the request. + +###### key + +(`string`) The cache key. Hugo derives the default value from the URL and options map. See [caching](#caching). + +###### method + +(`string`) The action to perform on the requested resource, typically one of `GET`, `POST`, or `HEAD`. + +###### responseHeaders +{{< new-in 0.143.0 />}} + +(`[]string`) The headers to extract from the server's response, accessible through the resource's [`Data.Headers`] method. Header name matching is case-insensitive. + +[`Data.Headers`]: /methods/resource/data/#headers + +## Options examples + +{{% note %}} +For brevity, the examples below do not include [error handling]. + +[error handling]: #error-handling +{{% /note %}} + +To include a header: + ```go-html-template {{ $url := "https://example.org/api" }} {{ $opts := dict @@ -43,7 +85,7 @@ The `resources.GetRemote` function takes an optional map of options. {{ $resource := resources.GetRemote $url $opts }} ``` -If you need multiple values for the same header key, use a slice: +To specify more than one value for the same header key, use a slice: ```go-html-template {{ $url := "https://example.org/api" }} @@ -53,7 +95,7 @@ If you need multiple values for the same header key, use a slice: {{ $resource := resources.GetRemote $url $opts }} ``` -You can also change the request method and set the request body: +To post data: ```go-html-template {{ $url := "https://example.org/api" }} @@ -65,6 +107,27 @@ You can also change the request method and set the request body: {{ $resource := resources.GetRemote $url $opts }} ``` +To override the default cache key: + +```go-html-template +{{ $url := "https://example.org/images/a.jpg" }} +{{ $opts := dict + "key" (print $url (now.Format "2006-01-02")) +}} +{{ $resource := resources.GetRemote $url $opts }} +``` + +To extract specific headers from the server's response: + +```go-html-template +{{ $url := "https://example.org/images/a.jpg" }} +{{ $opts := dict + "method" "HEAD" + "responseHeaders" (slice "X-Frame-Options" "Server") +}} +{{ $resource := resources.GetRemote $url $opts }} +``` + ## Remote data When retrieving remote data, use the [`transform.Unmarshal`] function to [unmarshal](g) the response. @@ -139,40 +202,6 @@ The [`Data`] method on a resource returned by the `resources.GetRemote` function [`Data`]: /methods/resource/data/ -```go-html-template -{{ $url := "https://example.org/images/a.jpg" }} -{{ with try (resources.GetRemote $url) }} - {{ with .Err }} - {{ errorf "%s" . }} - {{ else with .Value }} - {{ with .Data }} - {{ .ContentLength }} → 42764 - {{ .ContentType }} → image/jpeg - {{ .Status }} → 200 OK - {{ .StatusCode }} → 200 - {{ .TransferEncoding }} → [] - {{ end }} - {{ else }} - {{ errorf "Unable to get remote resource %q" $url }} - {{ end }} -{{ end }} -``` - -ContentLength -: (`int`) The content length in bytes. - -ContentType -: (`string`) The content type. - -Status -: (`string`) The HTTP status text. - -StatusCode -: (`int`) The HTTP status code. - -TransferEncoding -: (`string`) The transfer encoding. - ## Caching Resources returned from `resources.GetRemote` are cached to disk. See [configure file caches] for details. @@ -184,7 +213,8 @@ Override the cache key by setting a `key` in the options map. Use this approach ```go-html-template {{ $url := "https://example.org/images/a.jpg" }} {{ $cacheKey := print $url (now.Format "2006-01-02") }} -{{ $resource := resources.GetRemote $url (dict "key" $cacheKey) }} +{{ $opts := dict "key" $cacheKey }} +{{ $resource := resources.GetRemote $url $opts }} ``` [configure file caches]: /getting-started/configuration/#configure-file-caches diff --git a/content/en/functions/resources/PostCSS.md b/content/en/functions/resources/PostCSS.md index 2389d2ff5..5d3f85a30 100644 --- a/content/en/functions/resources/PostCSS.md +++ b/content/en/functions/resources/PostCSS.md @@ -4,15 +4,10 @@ description: Processes the given resource with PostCSS using any PostCSS plugin. categories: [] keywords: [] action: - related: - - functions/resources/Fingerprint - - functions/resources/Minify - - functions/resources/PostProcess - - functions/css/Sass + related: [] returnType: resource.Resource signatures: ['resources.PostCSS [OPTIONS] RESOURCE'] -toc: true -expiryDate: 2025-06-24 # deprecated 2024-06-24 +expiryDate: 2026-06-24 # deprecated 2024-06-24 in v0.128.0 --- {{% deprecated-in 0.128.0 %}} @@ -20,116 +15,3 @@ Use [`css.PostCSS`] instead. [`css.PostCSS`]: /functions/css/postcss/ {{% /deprecated-in %}} - -```go-html-template -{{ with resources.Get "css/main.css" | postCSS }} - -{{ end }} -``` - -## Setup - -Follow the steps below to transform CSS using any of the available [PostCSS plugins]. - -Step 1 -: Install [Node.js]. - -Step 2 -: Install the required Node.js packages in the root of your project. For example, to add vendor prefixes to your CSS rules: - -```sh -npm i -D postcss postcss-cli autoprefixer -``` - -Step 3 -: Create a PostCSS configuration file in the root of your project. You must name this file `postcss.config.js` or another [supported file name]. For example: - -```js -module.exports = { - plugins: [ - require('autoprefixer') - ] -}; -``` - -{{% note %}} -{{% include "functions/resources/_common/postcss-windows-warning.md" %}} -{{% /note %}} - -Step 4 -: Place your CSS file within the `assets/css` directory. - -Step 5 -: Process the resource with PostCSS: - -```go-html-template -{{ with resources.Get "css/main.css" | postCSS }} - -{{ end }} -``` - -## Options - -The `resources.PostCSS` method takes an optional map of options. - -config -: (`string`) The directory that contains the PostCSS configuration file. Default is the root of the project directory. - -noMap -: (`bool`) Default is `false`. If `true`, disables inline sourcemaps. - -inlineImports -: (`bool`) Default is `false`. Enable inlining of @import statements. It does so recursively, but will only import a file once. URL imports (e.g. `@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');`) and imports with media queries will be ignored. Note that this import routine does not care about the CSS spec, so you can have @import anywhere in the file. Hugo will look for imports relative to the module mount and will respect theme overrides. - -skipInlineImportsNotFound -: (`bool`) Default is `false`. Before Hugo 0.99.0 when `inlineImports` was enabled and we failed to resolve an import, we logged it as a warning. We now fail the build. If you have regular CSS imports in your CSS that you want to preserve, you can either use imports with URL or media queries (Hugo does not try to resolve those) or set `skipInlineImportsNotFound` to true. - -```go-html-template -{{ $opts := dict "config" "config-directory" "noMap" true }} -{{ with resources.Get "css/main.css" | postCSS $opts }} - -{{ end }} -``` - -## No configuration file - -To avoid using a PostCSS configuration file, you can specify a minimal configuration using the options map. - -use -: (`string`) A space-delimited list of PostCSS plugins to use. - -parser -: (`string`) A custom PostCSS parser. - -stringifier -: (`string`) A custom PostCSS stringifier. - -syntax -: (`string`) Custom postcss syntax. - -```go-html-template -{{ $opts := dict "use" "autoprefixer postcss-color-alpha" }} -{{ with resources.Get "css/main.css" | postCSS $opts }} - -{{ end }} -``` - -## Check environment - -The current Hugo environment name (set by `--environment` or in configuration or OS environment) is available in the Node context, which allows constructs like this: - -```js -const autoprefixer = require('autoprefixer'); -const purgecss = require('@fullhuman/postcss-purgecss'); -module.exports = { - plugins: [ - autoprefixer, - process.env.HUGO_ENVIRONMENT !== 'development' ? purgecss : null - ] -} -``` - -[node.js]: https://nodejs.org/en/download -[postcss plugins]: https://www.postcss.parts/ -[supported file name]: https://github.com/postcss/postcss-load-config#usage -[transpile to CSS]: /functions/css/sass/ diff --git a/content/en/functions/resources/PostProcess.md b/content/en/functions/resources/PostProcess.md index 82f02c9e5..b157d5fd9 100644 --- a/content/en/functions/resources/PostProcess.md +++ b/content/en/functions/resources/PostProcess.md @@ -13,33 +13,28 @@ action: toc: true --- -```go-html-template -{{ with resources.Get "css/main.css" }} - {{ if hugo.IsDevelopment }} - - {{ else }} - {{ with . | postCSS | minify | fingerprint | resources.PostProcess }} - - {{ end }} - {{ end }} -{{ end }} -``` +The `resources.PostProcess` function delays resource transformation steps until the build is complete, primarily for tasks like removing unused CSS rules. -Marking a resource with `resources.PostProcess` postpones transformations until the build has finished. +## Example -Call `resources.PostProcess` when one or more of the steps in the transformation chain depends on the result of the build. +In this example, after the build is complete, Hugo will: -A prime use case for this is purging unused CSS rules using the [PurgeCSS] plugin for the PostCSS Node.js package. +1. Purge unused CSS using the [PurgeCSS] plugin for [PostCSS] +2. Add vendor prefixes to CSS rules using the [Autoprefixer] plugin for PostCSS +3. [Minify] the CSS +4. [Fingerprint] the CSS -## CSS Purging - -{{% note %}} -There are several ways to set up CSS purging with PostCSS in Hugo. If you have a simple project, you should consider going the simpler route and drop the use of `resources.PostProcess` and just extract keywords from the templates. See the [Tailwind documentation](https://tailwindcss.com/docs/controlling-file-size/#app) for examples. -{{% /note %}} +[autoprefixer]: https://github.com/postcss/autoprefixer +[fingerprint]: /functions/resources/fingerprint/ +[minify]: /functions/resources/minify/ +[postcss]: /functions/css/postcss/ +[purgecss]: https://purgecss.com/plugins/postcss.html Step 1 : Install [Node.js]. +[node.js]: https://nodejs.org/en/download + Step 2 : Install the required Node.js packages in the root of your project: @@ -48,11 +43,27 @@ npm i -D postcss postcss-cli autoprefixer @fullhuman/postcss-purgecss ``` Step 3 -: Create a PostCSS configuration file in the root of your project. You must name this file `postcss.config.js` or another [supported file name]. For example: +: Enable creation of the `hugo_stats.json` file when building the site. If you are only using this for the production build, consider placing it below [`config/production`]. -```js +[`config/production`]: /getting-started/configuration/#configuration-directory + +{{< code-toggle file=hugo >}} +[build.buildStats] +enable = true +{{< /code-toggle >}} + +See the [configure build] documentation for details and options. + +[configure build]: /getting-started/configuration/#configure-build + +Step 4 +: Create a PostCSS configuration file in the root of your project. + +{{< code file="postcss.config.js" copy=true >}} const autoprefixer = require('autoprefixer'); -const purgecss = require('@fullhuman/postcss-purgecss')({ +const purgeCSSPlugin = require('@fullhuman/postcss-purgecss').default; + +const purgecss = purgeCSSPlugin({ content: ['./hugo_stats.json'], defaultExtractor: content => { const els = JSON.parse(content).htmlElements; @@ -68,26 +79,16 @@ const purgecss = require('@fullhuman/postcss-purgecss')({ module.exports = { plugins: [ + process.env.HUGO_ENVIRONMENT !== 'development' ? purgecss : null, autoprefixer, - process.env.HUGO_ENVIRONMENT !== 'development' ? purgecss : null ] }; -``` +{{< /code >}} {{% note %}} {{% include "functions/resources/_common/postcss-windows-warning.md" %}} {{% /note %}} -Step 4 -: Enable creation of the `hugo_stats.json` file when building the site. If you are only using this for the production build, consider placing it below [`config/production`]. - -{{< code-toggle file=hugo >}} -[build.buildStats] -enable = true -{{< /code-toggle >}} - -See the [configure build] documentation for details and options. - Step 5 : Place your CSS file within the `assets/css` directory. @@ -108,10 +109,10 @@ Step 6 ## Environment variables -Hugo passes these environment variables to PostCSS, which allows you to do something like: +Hugo passes the environment variables below to PostCSS, allowing you to do something like: ```js -process.env.HUGO_ENVIRONMENT === 'production' ? [autoprefixer] : [] +process.env.HUGO_ENVIRONMENT !== 'development' ? purgecss : null, ``` PWD @@ -122,16 +123,16 @@ HUGO_ENVIRONMENT Default is `production` for `hugo` and `development` for `hugo server`. HUGO_PUBLISHDIR -: The absolute path to the publish directory (the `public` directory). Note that the value will always point to a directory on disk even when running `hugo server` in memory mode. If you write to this directory from PostCSS when running the server, you could run the server with one of these flags: +: The absolute path to the publish directory, typically `public`. This value points to a directory on disk, even when rendering to memory with the `--renderToMemory` command line flag. -```sh -hugo server --renderToDisk -hugo server --renderStaticToDisk -``` +HUGO_FILE_X +: Hugo automatically mounts the following files from your project's root directory under `assets/_jsconfig`: -Also, Hugo will add environment variables for all files mounted below `assets/_jsconfig`. A default mount will be set up with files in the project root matching this regexp: `(babel|postcss|tailwind)\.config\.js`. +- `babel.config.js` +- `postcss.config.js` +- `tailwind.config.js` -These will get environment variables named on the form `HUGO_FILE_:filename:` where `:filename:` is all upper case with periods replaced with underscore. This allows you to do something like: +For each file, Hugo creates a corresponding environment variable named `HUGO_FILE_:filename:`, where `:filename:` is the uppercase version of the filename with periods replaced by underscores. This allows you to access these files within your JavaScript, for example: ```js let tailwindConfig = process.env.HUGO_FILE_TAILWIND_CONFIG_JS || './tailwind.config.js'; @@ -150,9 +151,3 @@ You cannot manipulate the values returned from the resource’s methods. For exa {{ $css = $css | css.PostCSS | minify | fingerprint | resources.PostProcess }} {{ $css.RelPermalink | strings.ToUpper }} ``` - -[node.js]: https://nodejs.org/en/download -[supported file name]: https://github.com/postcss/postcss-load-config#usage -[`config/production`]: /getting-started/configuration/#configuration-directory -[configure build]: /getting-started/configuration/#configure-build -[purgecss]: https://github.com/FullHuman/purgecss#readme diff --git a/content/en/functions/resources/ToCSS.md b/content/en/functions/resources/ToCSS.md index b944c72aa..54c34a6b0 100644 --- a/content/en/functions/resources/ToCSS.md +++ b/content/en/functions/resources/ToCSS.md @@ -4,15 +4,10 @@ description: Transpiles Sass to CSS. categories: [] keywords: [] action: - related: - - functions/resources/Fingerprint - - functions/resources/Minify - - functions/css/PostCSS - - functions/resources/PostProcess + related: [] returnType: resource.Resource signatures: ['resources.ToCSS [OPTIONS] RESOURCE'] -toc: true -expiryDate: 2025-06-24 # deprecated 2024-06-24 +expiryDate: 2026-06-24 # deprecated 2024-06-24 in v0.128.0 --- {{% deprecated-in 0.128.0 %}} @@ -20,212 +15,3 @@ Use [`css.Sass`] instead. [`css.Sass`]: /functions/css/sass/ {{% /deprecated-in %}} - -```go-html-template -{{ with resources.Get "sass/main.scss" }} - {{ $opts := dict "transpiler" "libsass" "targetPath" "css/style.css" }} - {{ with . | toCSS $opts }} - {{ if hugo.IsDevelopment }} - - {{ else }} - {{ with . | minify | fingerprint }} - - {{ end }} - {{ 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. - -[scss]: https://sass-lang.com/documentation/syntax#scss -[indented]: https://sass-lang.com/documentation/syntax#the-indented-syntax - -## Options - -transpiler -: (`string`) The transpiler to use, either `libsass` (default) or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass) below. - -targetPath -: (`string`) If not set, the transformed resource's target path will be the original path of the asset file with its extension replaced by `.css`. - -vars -: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/). - -```scss -// LibSass -@import "hugo:vars"; - -// Dart Sass -@use "hugo:vars" as v; -``` - -outputStyle -: (`string`) Output styles available to LibSass include `nested` (default), `expanded`, `compact`, and `compressed`. Output styles available to Dart Sass include `expanded` (default) and `compressed`. - -precision -: (`int`) Precision of floating point math. Not applicable to Dart Sass. - -enableSourceMap -: (`bool`) If `true`, generates a source map. - -sourceMapIncludeSources -: (`bool`) If `true`, embeds sources in the generated source map. Not applicable to LibSass. - -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 }} - -{{ end }} -``` - -## Dart Sass - -The extended version of Hugo includes [LibSass] to transpile Sass to CSS. In 2020, the Sass team deprecated LibSass in favor of [Dart Sass]. - -Use the latest features of the Sass language by installing Dart Sass in your development and production environments. - -### Installation overview - -Dart Sass is compatible with Hugo v0.114.0 and later. - -If you have been using Embedded Dart Sass[^1] with Hugo v0.113.0 and earlier, uninstall Embedded Dart Sass, then install Dart Sass. If you have installed both, Hugo will use Dart Sass. - -If you install Hugo as a [Snap package] there is no need to install Dart Sass. The Hugo Snap package includes Dart Sass. - -[^1]: In 2023, the Sass team deprecated Embedded Dart Sass in favor of Dart Sass. - -### Installing in a development environment - -When you install Dart Sass somewhere in your PATH, Hugo will find it. - -OS|Package manager|Site|Installation -:--|:--|:--|:-- -Linux|Homebrew|[brew.sh]|`brew install sass/sass/sass` -Linux|Snap|[snapcraft.io]|`sudo snap install dart-sass` -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. - -Run `hugo env` to list the active transpilers. - -### Installing in a production environment - -For [CI/CD] 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. - -[^2]: You do not have to do this if (a) you have not modified the assets cache location, and (b) you have not set `useResourceCacheWhen` to `never` in your [site configuration], and (c) you add and commit your `resources` directory to your repository. - -#### GitHub Pages - -To install Dart Sass for your builds on GitHub Pages, add this step to the GitHub Pages workflow file: - -```yaml -- name: Install Dart Sass - 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.141.0 - DART_SASS_VERSION: 1.83.4 - GIT_DEPTH: 0 - GIT_STRATEGY: clone - GIT_SUBMODULE_STRATEGY: recursive - TZ: America/Los_Angeles -image: - name: golang:1.20-buster -pages: - script: - # Install Dart Sass - - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - - tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - - cp -r dart-sass/* /usr/local/bin - - rm -rf dart-sass* - # Install Hugo - - curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb - - apt install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb - - rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb - # Build - - hugo --gc --minify - artifacts: - paths: - - public - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -``` - -#### Netlify - -To install Dart Sass for your builds on Netlify, the `netlify.toml` file should look something like this: - -```toml -[build.environment] -HUGO_VERSION = "0.141.0" -DART_SASS_VERSION = "1.83.4" -NODE_VERSION = "22" -TZ = "America/Los_Angeles" - -[build] -publish = "public" -command = """\ - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \ - 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 && \ - hugo --gc --minify \ - """ -``` - -### Example - -To transpile with Dart Sass, set `transpiler` to `dartsass` in the options map passed to `resources.ToCSS`. For example: - -```go-html-template -{{ with resources.Get "sass/main.scss" }} - {{ $opts := dict "transpiler" "dartsass" "targetPath" "css/style.css" }} - {{ with . | toCSS $opts }} - {{ if hugo.IsDevelopment }} - - {{ else }} - {{ with . | minify | fingerprint }} - - {{ end }} - {{ 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 -[ci/cd]: https://en.wikipedia.org/wiki/CI/CD -[dart sass]: https://sass-lang.com/dart-sass -[libsass]: https://sass-lang.com/libsass -[prebuilt binaries]: https://github.com/sass/dart-sass/releases/latest -[scoop.sh]: https://scoop.sh/#/apps?q=sass -[site configuration]: /getting-started/configuration/#configure-build -[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 diff --git a/content/en/functions/strings/Diff/index.md b/content/en/functions/strings/Diff/index.md index da172bbeb..281b76763 100644 --- a/content/en/functions/strings/Diff/index.md +++ b/content/en/functions/strings/Diff/index.md @@ -9,7 +9,7 @@ action: signatures: [strings.Diff OLDNAME OLD NEWNAME NEW] --- -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} Use `strings.Diff` to compare two strings and render a highlighted diff: diff --git a/content/en/functions/strings/TrimSpace.md b/content/en/functions/strings/TrimSpace.md index eef4e8121..7cb293674 100644 --- a/content/en/functions/strings/TrimSpace.md +++ b/content/en/functions/strings/TrimSpace.md @@ -15,7 +15,7 @@ action: signatures: [strings.TrimSpace INPUT] --- -{{< new-in 0.136.3 >}} +{{< new-in 0.136.3 />}} Whitespace characters include `\t`, `\n`, `\v`, `\f`, `\r`, and characters in the [Unicode Space Separator] category. diff --git a/content/en/functions/templates/Defer.md b/content/en/functions/templates/Defer.md index 1d538db4a..c43ef43bf 100644 --- a/content/en/functions/templates/Defer.md +++ b/content/en/functions/templates/Defer.md @@ -12,7 +12,7 @@ action: aliases: [/functions/templates.defer] --- -{{< new-in "0.128.0" >}} +{{< new-in 0.128.0 />}} 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](/getting-started/configuration/#configure-build) to determine which classes and other HTML identifiers are being used in the final output: diff --git a/content/en/functions/transform/ToMath.md b/content/en/functions/transform/ToMath.md index 62d1360ce..10cd256c5 100644 --- a/content/en/functions/transform/ToMath.md +++ b/content/en/functions/transform/ToMath.md @@ -13,7 +13,7 @@ aliases: [/functions/tomath] toc: true --- -{{< new-in "0.132.0" >}} +{{< new-in 0.132.0 />}} Hugo uses an embedded instance of the [KaTeX] display engine to render mathematical markup to HTML. You do not need to install the KaTeX display engine. diff --git a/content/en/functions/transform/Unmarshal.md b/content/en/functions/transform/Unmarshal.md index 959f20223..e50fc0ee0 100644 --- a/content/en/functions/transform/Unmarshal.md +++ b/content/en/functions/transform/Unmarshal.md @@ -132,7 +132,7 @@ delimiter comment : (`string`) The comment character used in the CSV. If set, lines beginning with the comment character without preceding whitespace are ignored. -lazyQuotes {{< new-in 0.122.0 >}} +lazyQuotes {{< new-in 0.122.0 />}} : (`bool`) If true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field. Default is `false`. ```go-html-template diff --git a/content/en/functions/transform/XMLEscape.md b/content/en/functions/transform/XMLEscape.md index d0aafc4bd..291f0f622 100644 --- a/content/en/functions/transform/XMLEscape.md +++ b/content/en/functions/transform/XMLEscape.md @@ -10,7 +10,7 @@ action: signatures: [transform.XMLEscape INPUT] --- -{{< new-in 0.121.0 >}} +{{< new-in 0.121.0 />}} The `transform.XMLEscape` function removes [disallowed characters] as defined in the XML specification, then escapes the result by replacing the following characters with [HTML entities]: diff --git a/content/en/getting-started/_index.md b/content/en/getting-started/_index.md index 1648f0224..ca180d999 100644 --- a/content/en/getting-started/_index.md +++ b/content/en/getting-started/_index.md @@ -1,7 +1,7 @@ --- title: Getting started -linkTitle: In this section -description: Quick start and guides for installing Hugo on your preferred operating system. + +description: How to get started with Hugo. categories: [] keywords: [] menu: diff --git a/content/en/getting-started/configuration-build.md b/content/en/getting-started/configuration-build.md index 637bfeb22..791d4bb99 100644 --- a/content/en/getting-started/configuration-build.md +++ b/content/en/getting-started/configuration-build.md @@ -16,21 +16,21 @@ The `build` configuration section contains global build-related configuration op {{< code-toggle config=build />}} -#### buildStats +###### buildStats See [Configure buildStats](#configure-build-stats). -#### cachebusters +###### cachebusters See [Configure Cache Busters](#configure-cache-busters). -#### noJSConfigInAssets +###### noJSConfigInAssets (`bool`) If `true`, turns off writing a `jsconfig.json` into your `assets` directory with mapping of imports from running [js.Build](/hugo-pipes/js). This file is intended to help with intellisense/navigation inside code editors such as [VS Code](https://code.visualstudio.com/). Note that if you do not use `js.Build`, no file will be written. -#### useResourceCacheWhen +###### useResourceCacheWhen -(`string`) When to use the cached resources in `/resources/_gen` for PostCSS and ToCSS. Valid values are `never`, `always` and `fallback`. The last value means that the cache will be tried if PostCSS/extended version is not available. +(`string`) When to use the resource file cache, one of `never`, `fallback`, or `always`. Applicable when transpiling Sass to CSS. Default is `fallback`. ## Configure cache busters @@ -54,7 +54,7 @@ The `build.cachebusters` configuration option was added to support development u target = "$1" {{< /code-toggle >}} -When `buildStats` {{< new-in 0.115.1 >}} is enabled, Hugo writes a `hugo_stats.json` file on each build with HTML classes etc. that's used in the rendered output. Changes to this file will trigger a rebuild of the `styles.css` file. You also need to add `hugo_stats.json` to Hugo's server watcher. See [Hugo Starter Tailwind Basic](https://github.com/bep/hugo-starter-tailwind-basic) for a running example. +When `buildStats` {{< new-in 0.115.1 />}} is enabled, Hugo writes a `hugo_stats.json` file on each build with HTML classes etc. that's used in the rendered output. Changes to this file will trigger a rebuild of the `styles.css` file. You also need to add `hugo_stats.json` to Hugo's server watcher. See [Hugo Starter Tailwind Basic](https://github.com/bep/hugo-starter-tailwind-basic) for a running example. source : A regexp matching file(s) relative to one of the virtual component directories in Hugo, typically `assets/...`. @@ -66,11 +66,11 @@ target {{< code-toggle config=build.buildStats />}} -{{< new-in 0.115.1 >}} +{{< new-in 0.115.1 />}} If `enable` is set to `true`, creates a `hugo_stats.json` file in the root of your project. This file contains arrays of the `class` attributes, `id` attributes, and tags of every HTML element within your published site. Use this file as data source when [removing unused CSS] from your site. This process is also known as pruning, purging, or tree shaking. -[removing unused CSS]: /hugo-pipes/postprocess/#css-purging-with-postcss +[removing unused CSS]: /functions/resources/postprocess/ Exclude `class` attributes, `id` attributes, or tags from `hugo_stats.json` with the `disableClasses`, `disableIDs`, and `disableTags` keys. diff --git a/content/en/getting-started/configuration-markup.md b/content/en/getting-started/configuration-markup.md index f01d0dddd..ef885e44b 100644 --- a/content/en/getting-started/configuration-markup.md +++ b/content/en/getting-started/configuration-markup.md @@ -86,7 +86,7 @@ typographer|[Goldmark Extensions: Typographer]|:heavy_check_mark: #### Extras -{{< new-in 0.126.0 >}} +{{< new-in 0.126.0 />}} Enable [deleted text], [inserted text], [mark text], [subscript], and [superscript] elements in Markdown. @@ -124,7 +124,7 @@ enable = true #### Passthrough -{{< new-in 0.122.0 >}} +{{< new-in 0.122.0 />}} Enable the passthrough extension to include mathematical equations and expressions in Markdown using LaTeX markup. See [mathematics in Markdown] for details. @@ -153,7 +153,7 @@ Most of the Goldmark settings above are self-explanatory, but some require expla ###### duplicateResourceFiles -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} (`bool`) If `true`, shared page resources on multilingual single-host sites will be duplicated for each language. See [multilingual page resources] for details. Default is `false`. @@ -194,7 +194,7 @@ This is also the strategy used by the [anchorize](/functions/urls/anchorize) tem ###### renderHooks.image.enableDefault -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} (`bool`) If `true`, enables Hugo's [embedded image render hook]. Default is `false`. @@ -208,7 +208,7 @@ The embedded image render hook is automatically enabled for multilingual single- ###### renderHooks.link.enableDefault -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} (`bool`) If `true`, enables Hugo's [embedded link render hook]. Default is `false`. diff --git a/content/en/getting-started/configuration.md b/content/en/getting-started/configuration.md index 448689295..23376f68f 100644 --- a/content/en/getting-started/configuration.md +++ b/content/en/getting-started/configuration.md @@ -228,13 +228,13 @@ See [Configure File Caches](#configure-file-caches). ###### canonifyURLs -(`bool`) See [details](/content-management/urls/#canonical-urls) before enabling this feature. Default is `false`. +(`bool`) See [details](/content-management/urls/#canonical-urls) before enabling this feature. Default is `false`. ###### capitalizeListTitles -{{< new-in 0.123.3 >}} +{{< new-in 0.123.3 />}} -(`bool`) Whether to capitalize automatic list titles. Applicable to section, taxonomy, and term pages. Default is `true`. You can change the capitalization style in your site configuration to one of `ap`, `chicago`, `go`, `firstupper`, or `none`. See [details]. +(`bool`) Whether to capitalize automatic list titles. Applicable to section, taxonomy, and term pages. Default is `true`. You can change the capitalization style in your site configuration to one of `ap`, `chicago`, `go`, `firstupper`, or `none`. See [details]. [details]: /getting-started/configuration/#configure-title-case @@ -281,7 +281,7 @@ To remain consistent and prevent unexpected behavior, do not mix these strategie ###### disableDefaultLanguageRedirect -{{< new-in 0.140.0 >}} +{{< new-in 0.140.0 />}} (`bool`) Disables generation of redirect to the default language when DefaultContentLanguageInSubdir is `true`. Default is `false`. @@ -291,7 +291,7 @@ To remain consistent and prevent unexpected behavior, do not mix these strategie ###### disableKinds -(`string slice`) Disable rendering of the specified page [kinds](g), any of `404`, `home`, `page`, `robotstxt`, `rss`, `section`, `sitemap`, `taxonomy`, or `term`. +(`[]string`) Disable rendering of the specified page [kinds](g), any of `404`, `home`, `page`, `robotstxt`, `rss`, `section`, `sitemap`, `taxonomy`, or `term`. ###### disableLanguages @@ -337,7 +337,7 @@ See [Front matter Configuration](#configure-front-matter). (`bool`) Ignore the cache directory. Default is `false`. ###### ignoreLogs -(`string slice`) A slice of message identifiers corresponding to warnings and errors you wish to suppress. See [`erroridf`] and [`warnidf`]. +(`[]string`) A slice of message identifiers corresponding to warnings and errors you wish to suppress. See [`erroridf`] and [`warnidf`]. [`erroridf`]: /functions/fmt/erroridf/ [`warnidf`]: /functions/fmt/warnidf/ @@ -460,7 +460,7 @@ See [Related Content](/content-management/related/#configure-related-content). ###### relativeURLs -(`bool`) See [details](/content-management/urls/#relative-urls) before enabling this feature. Default is `false`. +(`bool`) See [details](/content-management/urls/#relative-urls) before enabling this feature. Default is `false`. ###### removePathAccents @@ -472,9 +472,9 @@ content/post/hügó.md → https://example.org/post/hugo/ ###### renderSegments -{{< new-in 0.124.0 >}} +{{< new-in 0.124.0 />}} -(`string slice`) A list of segments to render. If not set, everything will be rendered. This is more commonly set in a CLI flag, e.g. `hugo --renderSegments segment1,segment2`. The segment names must match the names in the [segments](#configure-segments) configuration. +(`[]string`) A list of segments to render. If not set, everything will be rendered. This is more commonly set in a CLI flag, e.g. `hugo --renderSegments segment1,segment2`. The segment names must match the names in the [segments](#configure-segments) configuration. ###### sectionPagesMenu @@ -541,7 +541,7 @@ See [module configuration](/hugo-modules/configuration/#module-configuration-imp ###### uglyURLs -(`bool` or `map`) Whether to generate uglyURLs. Default is `false`. See [details](/content-management/urls/#appearance). +(`bool` or `map`) Whether to generate uglyURLs. Default is `false`. See [details](/content-management/urls/#appearance). ###### watch @@ -563,7 +563,7 @@ enableemoji: true ## Configure page -{{< new-in 0.133.0 >}} +{{< new-in 0.133.0 />}} These methods on a `Page` object navigate to the next or previous page within a page collection, relative to the current page: @@ -734,7 +734,7 @@ HUGO_ENVIRONMENT HUGO_FILE_LOG_FORMAT : (`string`) A format string for the file path, line number, and column number displayed when reporting errors, or when calling the `Position` method from a shortcode or Markdown render hook. Valid tokens are `:file`, `:line`, and `:col`. Default is `:file::line::col`. -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} HUGO_MEMORYLIMIT : (`int`) The maximum amount of system memory, in gigabytes, that Hugo can use while rendering your site. Default is 25% of total system memory. @@ -876,7 +876,7 @@ This can be set using the `cacheDir` config option or via the OS environment var If this is not set, Hugo will use, in order of preference: 1. If running on Netlify: `/opt/build/cache/hugo_cache/`. This means that if you run your builds on Netlify, all caches configured with `:cacheDir` will be saved and restored on the next build. For other CI vendors, please read their documentation. For an CircleCI example, see [this configuration](https://github.com/bep/hugo-sass-test/blob/6c3960a8f4b90e8938228688bc49bdcdd6b2d99e/.circleci/config.yml). -1. In a `hugo_cache` directory below the OS user cache directory as defined by Go's [os.UserCacheDir](https://pkg.go.dev/os#UserCacheDir). On Unix systems, this is `$XDG_CACHE_HOME` as specified by [basedir-spec-latest](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) if non-empty, else `$HOME/.cache`. On MacOS, this is `$HOME/Library/Caches`. On Windows, this is`%LocalAppData%`. On Plan 9, this is `$home/lib/cache`. {{< new-in 0.116.0 >}} +1. In a `hugo_cache` directory below the OS user cache directory as defined by Go's [os.UserCacheDir](https://pkg.go.dev/os#UserCacheDir). On Unix systems, this is `$XDG_CACHE_HOME` as specified by [basedir-spec-latest](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) if non-empty, else `$HOME/.cache`. On MacOS, this is `$HOME/Library/Caches`. On Windows, this is`%LocalAppData%`. On Plan 9, this is `$home/lib/cache`. {{< new-in 0.116.0 />}} 1. In a `hugo_cache_$USER` directory below the OS temp dir. If you want to know the current value of `cacheDir`, you can run `hugo config`, e.g: `hugo config | grep cachedir`. @@ -890,7 +890,7 @@ If you want to know the current value of `cacheDir`, you can run `hugo config`, ## Configure HTTP cache -{{< new-in 0.127.0 >}} +{{< new-in 0.127.0 />}} Note that this configuration is currently only relevant when using the [resources.GetRemote] function. @@ -936,7 +936,7 @@ polling ## Configure segments -{{< new-in 0.124.0 >}} +{{< new-in 0.124.0 />}} {{% note %}} The `segments` configuration is currently only used to configure partitioned rendering. diff --git a/content/en/getting-started/external-learning-resources/index.md b/content/en/getting-started/external-learning-resources/index.md index 46c66a737..80805d4a6 100644 --- a/content/en/getting-started/external-learning-resources/index.md +++ b/content/en/getting-started/external-learning-resources/index.md @@ -1,5 +1,6 @@ --- title: External learning resources +linkTitle: External resources description: Use these third-party resources to learn Hugo. categories: [getting started] keywords: [books, tutorials, learning, usage] diff --git a/content/en/getting-started/glossary/action.md b/content/en/getting-started/glossary/action.md deleted file mode 100644 index b38df744f..000000000 --- a/content/en/getting-started/glossary/action.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: action ---- - -See [template action](g). diff --git a/content/en/getting-started/glossary/archetype.md b/content/en/getting-started/glossary/archetype.md deleted file mode 100644 index ae427409e..000000000 --- a/content/en/getting-started/glossary/archetype.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: archetype ---- - -An archetype is a template for new content. See [details](/content-management/archetypes/). diff --git a/content/en/getting-started/glossary/argument.md b/content/en/getting-started/glossary/argument.md deleted file mode 100644 index 223f866a4..000000000 --- a/content/en/getting-started/glossary/argument.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: argument ---- - -A [scalar](g), [array](g), [slice](g), [map](g), or [object](g) passed to a [function](g), [method](g), or [shortcode](g). diff --git a/content/en/getting-started/glossary/array.md b/content/en/getting-started/glossary/array.md deleted file mode 100644 index bcd1718bb..000000000 --- a/content/en/getting-started/glossary/array.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: array ---- - -A numbered sequence of [elements](g). Unlike Go's [slice](g) data type, an array has a fixed length. Elements within an array can be [scalars](g), slices, [maps](g), pages, or other arrays. See the [Go documentation](https://go.dev/ref/spec#Array_types) for details. diff --git a/content/en/getting-started/glossary/boolean.md b/content/en/getting-started/glossary/boolean.md deleted file mode 100644 index d9b655396..000000000 --- a/content/en/getting-started/glossary/boolean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: boolean ---- - -A data type with two possible values, either `true` or `false`. diff --git a/content/en/getting-started/glossary/branch-bundle.md b/content/en/getting-started/glossary/branch-bundle.md deleted file mode 100644 index 1e1e85e4d..000000000 --- a/content/en/getting-started/glossary/branch-bundle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: branch bundle ---- - -A directory that contains an `_index.md` file and zero or more [resources](g). Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top level directories with or without `_index.md` files are also branch bundles. This includes the home page. See [details](/content-management/page-bundles/). diff --git a/content/en/getting-started/glossary/build.md b/content/en/getting-started/glossary/build.md deleted file mode 100644 index b5eed6659..000000000 --- a/content/en/getting-started/glossary/build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: build ---- - -To generate a static site that includes HTML files and assets such as images, CSS, and JavaScript. The build process includes rendering and resource transformations. diff --git a/content/en/getting-started/glossary/cache.md b/content/en/getting-started/glossary/cache.md deleted file mode 100644 index 139ce9033..000000000 --- a/content/en/getting-started/glossary/cache.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: cache ---- - -A software component that stores data so that future requests for the same data are faster. diff --git a/content/en/getting-started/glossary/chain.md b/content/en/getting-started/glossary/chain.md deleted file mode 100644 index b54c09315..000000000 --- a/content/en/getting-started/glossary/chain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: chain ---- - -Within a template, to connect one or more [identifiers](g) with a dot. An identifier can represent a method, object, or field. For example, `.Site.Params.author.name` or `.Date.UTC.Hour`. diff --git a/content/en/getting-started/glossary/cjk.md b/content/en/getting-started/glossary/cjk.md deleted file mode 100644 index 1f9e14ed2..000000000 --- a/content/en/getting-started/glossary/cjk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: CJK ---- - -A collective term for the Chinese, Japanese, and Korean languages. See [details](https://en.wikipedia.org/wiki/CJK_characters). diff --git a/content/en/getting-started/glossary/cli.md b/content/en/getting-started/glossary/cli.md deleted file mode 100644 index 11602f7e3..000000000 --- a/content/en/getting-started/glossary/cli.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: CLI ---- - -Command line interface. diff --git a/content/en/getting-started/glossary/collection.md b/content/en/getting-started/glossary/collection.md deleted file mode 100644 index 8ff962447..000000000 --- a/content/en/getting-started/glossary/collection.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: collection ---- - -An [array](g), [slice](g), or [map](g). diff --git a/content/en/getting-started/glossary/content-adapter.md b/content/en/getting-started/glossary/content-adapter.md deleted file mode 100644 index a54a36f22..000000000 --- a/content/en/getting-started/glossary/content-adapter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: content adapter ---- - -A template that dynamically creates pages when building a site. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML. See [details](/content-management/content-adapters/). diff --git a/content/en/getting-started/glossary/content-format.md b/content/en/getting-started/glossary/content-format.md deleted file mode 100644 index 5ed855b90..000000000 --- a/content/en/getting-started/glossary/content-format.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: content format ---- - -A markup language for creating content. Typically Markdown, but may also be HTML, AsciiDoc, Org, Pandoc, or reStructuredText. See [details](/content-management/formats/). diff --git a/content/en/getting-started/glossary/content-type.md b/content/en/getting-started/glossary/content-type.md deleted file mode 100644 index 9e8a720bf..000000000 --- a/content/en/getting-started/glossary/content-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: content type ---- - -A classification of content inferred from the top-level directory name or the `type` set in [front matter](g). Pages in the root of the `content` directory, including the home page, are of type "page". Accessed via `.Page.Type` in [templates](g). See [details](/content-management/types/) diff --git a/content/en/getting-started/glossary/content-view.md b/content/en/getting-started/glossary/content-view.md deleted file mode 100644 index a89c50b6e..000000000 --- a/content/en/getting-started/glossary/content-view.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: content view ---- - -A template called with the `.Page.Render` method. See [details](/templates/content-view/). diff --git a/content/en/getting-started/glossary/context.md b/content/en/getting-started/glossary/context.md deleted file mode 100644 index 25faf478a..000000000 --- a/content/en/getting-started/glossary/context.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: context ---- - -Represented by a dot "." within a [template action](g), context is the current location in a data structure. For example, while iterating over a [collection](g) of pages, the context within each iteration is the page's data structure. The context received by each template depends on template type and/or how it was called. See [details](/templates/introduction/#context). diff --git a/content/en/getting-started/glossary/default-sort-order.md b/content/en/getting-started/glossary/default-sort-order.md deleted file mode 100644 index c0f55fbf6..000000000 --- a/content/en/getting-started/glossary/default-sort-order.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: default sort order ---- - -The default sort order for page collections. Hugo sorts by [weight](g), then by date (descending), then by link title, and then by file path. diff --git a/content/en/getting-started/glossary/element.md b/content/en/getting-started/glossary/element.md deleted file mode 100644 index 5c33c55ad..000000000 --- a/content/en/getting-started/glossary/element.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: element ---- - -A member of a slice or array. diff --git a/content/en/getting-started/glossary/field.md b/content/en/getting-started/glossary/field.md deleted file mode 100644 index f49e65c79..000000000 --- a/content/en/getting-started/glossary/field.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: field ---- - -A predefined key-value pair in front matter such as `date` or `title`. See also [parameter](g). diff --git a/content/en/getting-started/glossary/flag.md b/content/en/getting-started/glossary/flag.md deleted file mode 100644 index df9fd21fb..000000000 --- a/content/en/getting-started/glossary/flag.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: flag ---- - -An option passed to a command-line program, beginning with one or two hyphens. See [details](/commands/hugo/). diff --git a/content/en/getting-started/glossary/floating-point.md b/content/en/getting-started/glossary/floating-point.md deleted file mode 100644 index cdf5a2294..000000000 --- a/content/en/getting-started/glossary/floating-point.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: floating point ---- - -A numeric data type with a fractional component. For example, `3.14159`. diff --git a/content/en/getting-started/glossary/fragment.md b/content/en/getting-started/glossary/fragment.md deleted file mode 100644 index b1e3fd7eb..000000000 --- a/content/en/getting-started/glossary/fragment.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: fragment ---- - -The final segment of a URL, beginning with a hash (`#`) mark, that references an `id` attribute of an HTML element on the page. diff --git a/content/en/getting-started/glossary/front-matter.md b/content/en/getting-started/glossary/front-matter.md deleted file mode 100644 index d66e400b4..000000000 --- a/content/en/getting-started/glossary/front-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: front matter ---- - -Metadata at the beginning of each content page, separated from the content by format-specific delimiters. See [details](/content-management/front-matter/). diff --git a/content/en/getting-started/glossary/function.md b/content/en/getting-started/glossary/function.md deleted file mode 100644 index d07f4ae6b..000000000 --- a/content/en/getting-started/glossary/function.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: function ---- - -Used within a [template action](g), a function takes one or more [arguments](g) and returns a value. Unlike [methods](g), functions are not associated with an [object](g). See [details](/functions/). diff --git a/content/en/getting-started/glossary/global-resource.md b/content/en/getting-started/glossary/global-resource.md deleted file mode 100644 index e51e68363..000000000 --- a/content/en/getting-started/glossary/global-resource.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: global resource ---- - -A file within the `assets` directory, or within any directory [mounted](/hugo-modules/configuration/#module-configuration-mounts) to the `assets` directory. Capture one or more global resources using the [`resources.Get`], [`resources.GetMatch`], [`resources.Match`], or [`resources.ByType`] functions. - -[`resources.Get`]: /functions/resources/get/ -[`resources.GetMatch`]: /functions/resources/getmatch/ -[`resources.Match`]: /functions/resources/match/ -[`resources.ByType`]: /functions/resources/byType/ diff --git a/content/en/getting-started/glossary/headless-bundle.md b/content/en/getting-started/glossary/headless-bundle.md deleted file mode 100644 index 60da816af..000000000 --- a/content/en/getting-started/glossary/headless-bundle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: headless bundle ---- - -An unpublished leaf or branch bundle whose content and resources you can include in other pages. See [build options](/content-management/build-options/). diff --git a/content/en/getting-started/glossary/identifier.md b/content/en/getting-started/glossary/identifier.md deleted file mode 100644 index eeb17dbf5..000000000 --- a/content/en/getting-started/glossary/identifier.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: identifier ---- - -A string that represents a variable, method, object, or field. It must conform to Go's [language specification](https://go.dev/ref/spec#Identifiers), beginning with a letter or underscore, followed by zero or more letters, digits, or underscores. diff --git a/content/en/getting-started/glossary/integer.md b/content/en/getting-started/glossary/integer.md deleted file mode 100644 index aa61f248f..000000000 --- a/content/en/getting-started/glossary/integer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: integer ---- - -A numeric data type without a fractional component. For example, `42`. diff --git a/content/en/getting-started/glossary/internationalization.md b/content/en/getting-started/glossary/internationalization.md deleted file mode 100644 index aa65f8ffb..000000000 --- a/content/en/getting-started/glossary/internationalization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: internationalization ---- - -Software design and development efforts that enable [localization](g). See the [W3C definition](https://www.w3.org/International/questions/qa-i18n). Abbreviated i18n. diff --git a/content/en/getting-started/glossary/interpreted-string-literal.md b/content/en/getting-started/glossary/interpreted-string-literal.md deleted file mode 100644 index 97bd8ae67..000000000 --- a/content/en/getting-started/glossary/interpreted-string-literal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: interpreted string literal ---- - -Interpreted string literals are character sequences between double quotes, as in "foo". Within the quotes, any character may appear except a newline and an unescaped double quote. The text between the quotes forms the value of the literal, with backslash escapes interpreted. See [details](https://go.dev/ref/spec#String_literals). diff --git a/content/en/getting-started/glossary/interval.md b/content/en/getting-started/glossary/interval.md deleted file mode 100644 index ad19a402e..000000000 --- a/content/en/getting-started/glossary/interval.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: interval ---- - -An [interval](https://en.wikipedia.org/wiki/Interval_(mathematics)) is a range of numbers between two endpoints: closed, open, or half-open. - -- A _closed_ interval, denoted by brackets, includes its endpoints. For example, [0, 1] is the interval where `0 <= x <= 1`. - -- An _open_ interval, denoted by parentheses, excludes its endpoints. For example, (0, 1) is the interval where `0 < x < 1`. - -- A _half-open_ interval includes only one of its endpoints. For example, (0, 1] is the _left-open_ interval where `0 < x <= 1`, while [0, 1) is the _right-open_ interval where `0 <= x < 1`. diff --git a/content/en/getting-started/glossary/kind.md b/content/en/getting-started/glossary/kind.md deleted file mode 100644 index 1434e55c5..000000000 --- a/content/en/getting-started/glossary/kind.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: kind ---- - -See [page kind](g). diff --git a/content/en/getting-started/glossary/leaf-bundle.md b/content/en/getting-started/glossary/leaf-bundle.md deleted file mode 100644 index 88d934cf5..000000000 --- a/content/en/getting-started/glossary/leaf-bundle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: leaf bundle ---- - -A directory that contains an index.md file and zero or more [resources](g). Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. See [details](/content-management/page-bundles/). diff --git a/content/en/getting-started/glossary/lexer.md b/content/en/getting-started/glossary/lexer.md deleted file mode 100644 index 93d2ed6c5..000000000 --- a/content/en/getting-started/glossary/lexer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: lexer ---- - -A software component that identifies keywords, identifiers, operators, numbers, and other basic building blocks of a programming language within the input text. diff --git a/content/en/getting-started/glossary/list-page.md b/content/en/getting-started/glossary/list-page.md deleted file mode 100644 index f17e0ac69..000000000 --- a/content/en/getting-started/glossary/list-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: list page ---- - -Any [page kind](g) that receives a page [collection](g) in [context](g). This includes the home page, [section pages](g), [taxonomy pages](g), and [term pages](g). diff --git a/content/en/getting-started/glossary/list-template.md b/content/en/getting-started/glossary/list-template.md deleted file mode 100644 index e3283ff71..000000000 --- a/content/en/getting-started/glossary/list-template.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: list template ---- - -Any template that renders a [list page](g). This includes [home](/templates/types/#home), [section](/templates/types/#section), [taxonomy](/templates/types/#taxonomy), and [term](/templates/types/#term) templates. diff --git a/content/en/getting-started/glossary/localization.md b/content/en/getting-started/glossary/localization.md deleted file mode 100644 index d03dedd9e..000000000 --- a/content/en/getting-started/glossary/localization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: localization ---- - -Adaptation of a site to meet language and regional requirements. This includes translations, language-specific media, date and currency formats, etc. See [details](/content-management/multilingual/) and the [W3C definition](https://www.w3.org/International/questions/qa-i18n). Abbreviated l10n. diff --git a/content/en/getting-started/glossary/logical-path.md b/content/en/getting-started/glossary/logical-path.md deleted file mode 100644 index 3a2c96f18..000000000 --- a/content/en/getting-started/glossary/logical-path.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: logical path ---- - -{{< new-in 0.123.0 >}} - -A page or page resource identifier derived from the file path, excluding its extension and language identifier. This value is neither a file path nor a URL. Starting with a file path relative to the `content` directory, Hugo determines the logical path by stripping the file extension and language identifier, converting to lower case, then replacing spaces with hyphens. See [examples](/methods/page/path/#examples). diff --git a/content/en/getting-started/glossary/map.md b/content/en/getting-started/glossary/map.md deleted file mode 100644 index aa80357d9..000000000 --- a/content/en/getting-started/glossary/map.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: map ---- - -An unordered group of elements, each indexed by a unique key. See the [Go documentation](https://go.dev/ref/spec#Map_types) for details. diff --git a/content/en/getting-started/glossary/markdown-attribute.md b/content/en/getting-started/glossary/markdown-attribute.md deleted file mode 100644 index ab9a984f5..000000000 --- a/content/en/getting-started/glossary/markdown-attribute.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Markdown attribute ---- - -A list of attributes, containing one or more key-value pairs, separated by spaces or commas, and wrapped by braces. Apply Markdown attributes to images and block-level elements including blockquotes, fenced code blocks, headings, horizontal rules, lists, paragraphs, and tables. See [details](/getting-started/configuration-markup/#goldmark). diff --git a/content/en/getting-started/glossary/marshal.md b/content/en/getting-started/glossary/marshal.md deleted file mode 100644 index 73c59e8b7..000000000 --- a/content/en/getting-started/glossary/marshal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: marshal ---- - -To transform a data structure into a serialized object. For example, transforming a [map](g) into a JSON string. See [unmarshal](g). diff --git a/content/en/getting-started/glossary/method.md b/content/en/getting-started/glossary/method.md deleted file mode 100644 index 960488b4e..000000000 --- a/content/en/getting-started/glossary/method.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: method ---- - -Used within a [template action](g) and associated with an [object](g), a method takes zero or more [arguments](g) and either returns a value or performs an action. For example, `.IsHome` is a method on the `.Page` object which returns `true` if the current page is the home page. See also [function](g). diff --git a/content/en/getting-started/glossary/module.md b/content/en/getting-started/glossary/module.md deleted file mode 100644 index 13a3945ab..000000000 --- a/content/en/getting-started/glossary/module.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: module ---- - -Like a [theme](g), a module is a packaged combination of [archetypes](g), assets, content, data, [templates](g), translation tables, static files, or configuration settings. A module may serve as the basis for a new site, or to augment an existing site. See [details](/hugo-modules/). diff --git a/content/en/getting-started/glossary/node.md b/content/en/getting-started/glossary/node.md deleted file mode 100644 index 2cd1e7b1f..000000000 --- a/content/en/getting-started/glossary/node.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: node ---- - -A class of [page kinds](g) including `home`, `section`, `taxonomy`, and `term`. diff --git a/content/en/getting-started/glossary/object.md b/content/en/getting-started/glossary/object.md deleted file mode 100644 index ea19987c7..000000000 --- a/content/en/getting-started/glossary/object.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: object ---- - -A data structure with or without associated [methods](g). diff --git a/content/en/getting-started/glossary/ordered-taxonomy.md b/content/en/getting-started/glossary/ordered-taxonomy.md deleted file mode 100644 index 7df5ebfe1..000000000 --- a/content/en/getting-started/glossary/ordered-taxonomy.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: ordered taxonomy ---- - -Created by invoking the [`Alphabetical`] or [`ByCount`] method on a [`Taxonomy`](g) object, which is a [map](g), an _ordered taxonomy_ is a [slice](g), where each element is an object that contains the [term](g) and a slice of its [weighted pages](g). - -[`Alphabetical`]: /methods/taxonomy/alphabetical/ -[`ByCount`]: /methods/taxonomy/bycount/ diff --git a/content/en/getting-started/glossary/output-format.md b/content/en/getting-started/glossary/output-format.md deleted file mode 100644 index b4bab9ce7..000000000 --- a/content/en/getting-started/glossary/output-format.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: output format ---- - -Hugo generates one or more files per page when building a site. For example, when rendering home, [section](g), [taxonomy](g), and [term](g) pages, Hugo generates an HTML file and an RSS file. Both HTML and RSS are built-in _output formats_. Create multiple output formats, and control generation based on [page kind](g), or by enabling one or more output formats for one or more pages. See [details]. - -[details]: /templates/output-formats/ diff --git a/content/en/getting-started/glossary/page-bundle.md b/content/en/getting-started/glossary/page-bundle.md deleted file mode 100644 index a82a3d257..000000000 --- a/content/en/getting-started/glossary/page-bundle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: page bundle ---- - -A directory that encapsulates both content and associated [resources](g). There are two types of page bundles: [leaf bundles](g) and [branch bundles](g). See [details](/content-management/page-bundles/). diff --git a/content/en/getting-started/glossary/page-collection.md b/content/en/getting-started/glossary/page-collection.md deleted file mode 100644 index 13f9eb990..000000000 --- a/content/en/getting-started/glossary/page-collection.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: page collection ---- - -A slice of `Page` objects. diff --git a/content/en/getting-started/glossary/page-kind.md b/content/en/getting-started/glossary/page-kind.md deleted file mode 100644 index a71dbd6b2..000000000 --- a/content/en/getting-started/glossary/page-kind.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: page kind ---- - -A classification of pages, one of `home`, `page`, `section`, `taxonomy`, or `term`. See [details](/methods/page/kind/). - -Note that there are also `RSS`, `sitemap`, `robotsTXT`, and `404` page kinds, but these are only available during the rendering of each of these respective page's kind and therefore *not* available in any of the `Pages` collections. diff --git a/content/en/getting-started/glossary/page-resource.md b/content/en/getting-started/glossary/page-resource.md deleted file mode 100644 index 324f09470..000000000 --- a/content/en/getting-started/glossary/page-resource.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: page resource ---- - -A file within a [page bundle](g). Capture one or more page resources using any of the [`Resources`] methods on a `Page` object. - -[`Resources`]: /methods/page/resources/#methods diff --git a/content/en/getting-started/glossary/pager.md b/content/en/getting-started/glossary/pager.md deleted file mode 100644 index 206f81204..000000000 --- a/content/en/getting-started/glossary/pager.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: pager ---- - -Created during [pagination](g), a pager contains a subset of a list page and navigation links to other pagers. diff --git a/content/en/getting-started/glossary/paginate.md b/content/en/getting-started/glossary/paginate.md deleted file mode 100644 index 1a0b54f1e..000000000 --- a/content/en/getting-started/glossary/paginate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: paginate ---- - -To split a list page into two or more subsets. diff --git a/content/en/getting-started/glossary/pagination.md b/content/en/getting-started/glossary/pagination.md deleted file mode 100644 index a41f88162..000000000 --- a/content/en/getting-started/glossary/pagination.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: pagination ---- - -The process of [paginating](g) a list page. See [details](/templates/pagination/). diff --git a/content/en/getting-started/glossary/paginator.md b/content/en/getting-started/glossary/paginator.md deleted file mode 100644 index 0358a5ab7..000000000 --- a/content/en/getting-started/glossary/paginator.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: paginator ---- - -A collection of [pagers](g). diff --git a/content/en/getting-started/glossary/parameter.md b/content/en/getting-started/glossary/parameter.md deleted file mode 100644 index 24adcd60e..000000000 --- a/content/en/getting-started/glossary/parameter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: parameter ---- - -Typically, a user-defined key-value pair at the site or page level, but may also refer to a configuration setting or an [argument](g). See also [field](g). diff --git a/content/en/getting-started/glossary/partial.md b/content/en/getting-started/glossary/partial.md deleted file mode 100644 index e4405d9d9..000000000 --- a/content/en/getting-started/glossary/partial.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: partial ---- - -A [template](g) called from any other template including [shortcodes](g), [render hooks](g), and other partials. A partial either renders something or returns something. A partial can also call itself, for example, to [walk](g) a data structure. diff --git a/content/en/getting-started/glossary/permalink.md b/content/en/getting-started/glossary/permalink.md deleted file mode 100644 index ac3601b26..000000000 --- a/content/en/getting-started/glossary/permalink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: permalink ---- - -The absolute URL of a published resource or a rendered page, including scheme and host. diff --git a/content/en/getting-started/glossary/pipeline.md b/content/en/getting-started/glossary/pipeline.md deleted file mode 100644 index 17ca8344c..000000000 --- a/content/en/getting-started/glossary/pipeline.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: pipeline ---- - -Within a [template action](g), a pipeline is a possibly chained sequence of values, [function](g) calls, or [method](g) calls. Functions and methods in the pipeline may take multiple [arguments](g). - -A pipeline may be *chained* by separating a sequence of commands with pipeline characters "|". In a chained pipeline, the result of each command is passed as the last argument to the following command. The output of the final command in the pipeline is the value of the pipeline. See the [Go documentation](https://pkg.go.dev/text/template#hdr-Pipelines) for details. diff --git a/content/en/getting-started/glossary/raw-string-literal.md b/content/en/getting-started/glossary/raw-string-literal.md deleted file mode 100644 index add8a0cae..000000000 --- a/content/en/getting-started/glossary/raw-string-literal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: raw string literal ---- - -Raw string literals are character sequences between backticks, as in \`bar\`. Within the backticks, any character may appear except a backtick. Backslashes have no special meaning and the string may contain newlines. Carriage return characters (`\r`) inside raw string literals are discarded from the raw string value. See [details](https://go.dev/ref/spec#String_literals). diff --git a/content/en/getting-started/glossary/regular-page.md b/content/en/getting-started/glossary/regular-page.md deleted file mode 100644 index 265a9cbc0..000000000 --- a/content/en/getting-started/glossary/regular-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: regular page ---- - -Content with the "page" [page kind](g). See also [section page](g). diff --git a/content/en/getting-started/glossary/relative-permalink.md b/content/en/getting-started/glossary/relative-permalink.md deleted file mode 100644 index 73d676f1b..000000000 --- a/content/en/getting-started/glossary/relative-permalink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: relative permalink ---- - -The host-relative URL of a published resource or a rendered page. diff --git a/content/en/getting-started/glossary/remote-resource.md b/content/en/getting-started/glossary/remote-resource.md deleted file mode 100644 index d0d00eae5..000000000 --- a/content/en/getting-started/glossary/remote-resource.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: remote resource ---- - -A file on a remote server, accessible via HTTP or HTTPS with the [`resources.GetRemote`](/functions/resources/getremote) function. diff --git a/content/en/getting-started/glossary/render-hook.md b/content/en/getting-started/glossary/render-hook.md deleted file mode 100644 index bed571972..000000000 --- a/content/en/getting-started/glossary/render-hook.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: render hook ---- - -A [template](g) that overrides standard Markdown rendering. See [details](/render-hooks). diff --git a/content/en/getting-started/glossary/resource-type.md b/content/en/getting-started/glossary/resource-type.md deleted file mode 100644 index d2543dd04..000000000 --- a/content/en/getting-started/glossary/resource-type.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: resource type ---- - -The main type of a resource's [media type]. Content files such as Markdown, HTML, AsciiDoc, Pandoc, reStructuredText, and Emacs Org Mode have resource type `page`. Other resource types include `image`, `video`, etc. Retrieve the resource type using the [`ResourceType`] method on a `Resource` object. - -[media type]: /methods/resource/mediatype/ -[`ResourceType`]: /methods/resource/resourcetype/ diff --git a/content/en/getting-started/glossary/resource.md b/content/en/getting-started/glossary/resource.md deleted file mode 100644 index 6de626a4e..000000000 --- a/content/en/getting-started/glossary/resource.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: resource ---- - -Any file consumed by the build process to augment or generate content, structure, behavior, or presentation. For example: images, videos, content snippets, CSS, Sass, JavaScript, and data. - -Hugo supports three types of resources: [global resources](g), [page resources](g), and [remote resources](g). diff --git a/content/en/getting-started/glossary/scalar.md b/content/en/getting-started/glossary/scalar.md deleted file mode 100644 index d1e3cd26c..000000000 --- a/content/en/getting-started/glossary/scalar.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: scalar ---- - -A _scalar_ is a single value, one of [string](g), [integer](g), [floating point](g), or [boolean](g). diff --git a/content/en/getting-started/glossary/scratch-pad.md b/content/en/getting-started/glossary/scratch-pad.md deleted file mode 100644 index fe1fc65a9..000000000 --- a/content/en/getting-started/glossary/scratch-pad.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: scratch pad ---- - -Conceptually, a [map](g) with [methods](g) to set, get, update, and delete values. Attach the data structure to a `Page` or `Site` object using the [`Store`] method, or create a locally scoped scratch pad using the [`newScratch`] function. - -[`Store`]: /methods/page/store/ -[`newScratch`]: /functions/collections/newscratch/ diff --git a/content/en/getting-started/glossary/section-page.md b/content/en/getting-started/glossary/section-page.md deleted file mode 100644 index 060527015..000000000 --- a/content/en/getting-started/glossary/section-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: section page ---- - -Content with the "section" [page kind](g). Typically a listing of [regular pages](g) and/or other section pages within the current [section](g). diff --git a/content/en/getting-started/glossary/section.md b/content/en/getting-started/glossary/section.md deleted file mode 100644 index 4e615b112..000000000 --- a/content/en/getting-started/glossary/section.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: section ---- - -A section is a top-level content directory, or any content directory with an `_index.md` file. A content directory with an `_index.md` file is also known as a [branch bundle](g). Section templates receive one or more page [collections](g) in [context](g). See [details](/content-management/sections/). diff --git a/content/en/getting-started/glossary/shortcode.md b/content/en/getting-started/glossary/shortcode.md deleted file mode 100644 index 3bd6f1979..000000000 --- a/content/en/getting-started/glossary/shortcode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: shortcode ---- - -A [template](g) called from within Markdown, taking zero or more [arguments](g). See [details](/content-management/shortcodes/). diff --git a/content/en/getting-started/glossary/slice.md b/content/en/getting-started/glossary/slice.md deleted file mode 100644 index fa6bc3cc0..000000000 --- a/content/en/getting-started/glossary/slice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: slice ---- - -A numbered sequence of elements. Unlike Go's [array](g) data type, slices are dynamically sized. [Elements](g) within a slice can be [scalars](g), [arrays](g), [maps](g), pages, or other slices. See the [Go documentation](https://go.dev/ref/spec#Slice_types) for details. diff --git a/content/en/getting-started/glossary/string.md b/content/en/getting-started/glossary/string.md deleted file mode 100644 index daa3a4d9d..000000000 --- a/content/en/getting-started/glossary/string.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: string ---- - -A sequence of bytes. For example, `"What is 6 times 7?"`. diff --git a/content/en/getting-started/glossary/taxonomic-weight.md b/content/en/getting-started/glossary/taxonomic-weight.md deleted file mode 100644 index 7dcb8080d..000000000 --- a/content/en/getting-started/glossary/taxonomic-weight.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: taxonomic weight ---- - -Defined in front matter and unique to each taxonomy, this [weight](g) determines the sort order of page collections contained within a [`Taxonomy`](g) object. See [details](/content-management/taxonomies/#order-taxonomies). diff --git a/content/en/getting-started/glossary/taxonomy-object.md b/content/en/getting-started/glossary/taxonomy-object.md deleted file mode 100644 index 31315b988..000000000 --- a/content/en/getting-started/glossary/taxonomy-object.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: taxonomy object ---- - -A [map](g) of [terms](g) and the [weighted pages](g) associated with each term. diff --git a/content/en/getting-started/glossary/taxonomy-page.md b/content/en/getting-started/glossary/taxonomy-page.md deleted file mode 100644 index bf1b4aafb..000000000 --- a/content/en/getting-started/glossary/taxonomy-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: taxonomy page ---- - -Content with the "taxonomy" [page kind](g). Typically a listing of [terms](g) within a given [taxonomy](g). diff --git a/content/en/getting-started/glossary/taxonomy.md b/content/en/getting-started/glossary/taxonomy.md deleted file mode 100644 index f7d38351d..000000000 --- a/content/en/getting-started/glossary/taxonomy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: taxonomy ---- - -A group of related [terms](g) used to classify content. For example, a "colors" taxonomy might include the terms "red", "green", and "blue". See [details](/content-management/taxonomies/). diff --git a/content/en/getting-started/glossary/template-action.md b/content/en/getting-started/glossary/template-action.md deleted file mode 100644 index ac5fad85e..000000000 --- a/content/en/getting-started/glossary/template-action.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: template action ---- - -A data evaluation or control structure within a [template](g), delimited by "{{" and "}}". See the [Go documentation](https://pkg.go.dev/text/template#hdr-Actions) for details. diff --git a/content/en/getting-started/glossary/template.md b/content/en/getting-started/glossary/template.md deleted file mode 100644 index b8b046092..000000000 --- a/content/en/getting-started/glossary/template.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: template ---- - -A file with [template actions](g), located within the `layouts` directory of a project, theme, or module. See [details](/templates/). diff --git a/content/en/getting-started/glossary/term-page.md b/content/en/getting-started/glossary/term-page.md deleted file mode 100644 index ed9bfec0d..000000000 --- a/content/en/getting-started/glossary/term-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: term page ---- - -Content with the "term" [page kind](g). Typically a listing of [regular pages](g) and [section pages](g) with a given [term](g). diff --git a/content/en/getting-started/glossary/term.md b/content/en/getting-started/glossary/term.md deleted file mode 100644 index d04f58b80..000000000 --- a/content/en/getting-started/glossary/term.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: term ---- - -A member of a [taxonomy](g), used to classify content. See [details](/content-management/taxonomies/). diff --git a/content/en/getting-started/glossary/theme.md b/content/en/getting-started/glossary/theme.md deleted file mode 100644 index e5c6ee951..000000000 --- a/content/en/getting-started/glossary/theme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: theme ---- - -A packaged combination of [archetypes](g), assets, content, data, [templates](g), translation tables, static files, or configuration settings. A theme may serve as the basis for a new site, or to augment an existing site. See also [module](g). diff --git a/content/en/getting-started/glossary/token.md b/content/en/getting-started/glossary/token.md deleted file mode 100644 index 0a5ac22f6..000000000 --- a/content/en/getting-started/glossary/token.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: token ---- - -An identifier within a format string, beginning with a colon and replaced with a value when rendered. For example, use tokens in format strings for both [permalinks](/content-management/urls/#permalinks) and [dates](/functions/time/format/#localization). diff --git a/content/en/getting-started/glossary/unmarshal.md b/content/en/getting-started/glossary/unmarshal.md deleted file mode 100644 index 15db995f0..000000000 --- a/content/en/getting-started/glossary/unmarshal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: unmarshal ---- - -To transform a serialized object into a data structure. For example, transforming a JSON file into a [map](g) that you can access within a template. See [marshal](g). diff --git a/content/en/getting-started/glossary/variable.md b/content/en/getting-started/glossary/variable.md deleted file mode 100644 index 5a80f4e0a..000000000 --- a/content/en/getting-started/glossary/variable.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: variable ---- - -A user-defined [identifier](g) prepended with a `$` symbol, representing a value of any data type, initialized or assigned within a [template action](g). For example, `$foo` and `$bar` are variables. diff --git a/content/en/getting-started/glossary/walk.md b/content/en/getting-started/glossary/walk.md deleted file mode 100644 index 811495187..000000000 --- a/content/en/getting-started/glossary/walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: walk ---- - -To recursively traverse a nested data structure. For example, rendering a multilevel menu. diff --git a/content/en/getting-started/glossary/weight.md b/content/en/getting-started/glossary/weight.md deleted file mode 100644 index 5c04e6ef7..000000000 --- a/content/en/getting-started/glossary/weight.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: weight ---- - -Used to position an element within a collection sorted by weight. Assign weights using non-zero integers. Lighter items float to the top, while heavier items sink to the bottom. Unweighted or zero-weighted elements are placed at the end of the collection. Weights are typically assigned to pages, menu entries, languages, and output formats. diff --git a/content/en/getting-started/glossary/weighted-page.md b/content/en/getting-started/glossary/weighted-page.md deleted file mode 100644 index 88ec816da..000000000 --- a/content/en/getting-started/glossary/weighted-page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: weighted page ---- - -Contained within a [`Taxonomy`](g) object, a weighted page is a [map](g) with two elements: a `Page` object, and its [taxonomic weight](g) as defined in front matter. Access the elements using the `Page` and `Weight` keys. diff --git a/content/en/getting-started/usage.md b/content/en/getting-started/usage.md index 2e67ecb7e..3380b0ff2 100644 --- a/content/en/getting-started/usage.md +++ b/content/en/getting-started/usage.md @@ -65,7 +65,7 @@ Hugo allows you to set `draft`, `date`, `publishDate`, and `expiryDate` in the [ - The `publishDate` is in the future - The `expiryDate` is in the past -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} {{% note %}} Hugo publishes descendants of draft, future, and expired [node](g) pages. To prevent publication of these descendants, use the [`cascade`] front matter field to cascade [build options] to the descendant pages. diff --git a/content/en/hosting-and-deployment/_index.md b/content/en/hosting-and-deployment/_index.md index b6f54d3fa..a99ffd69c 100644 --- a/content/en/hosting-and-deployment/_index.md +++ b/content/en/hosting-and-deployment/_index.md @@ -1,7 +1,7 @@ --- title: Hosting and deployment -linkTitle: In this section -description: Site builds, automated deployments, and popular hosting solutions. + +description: Services and tools to host and deploy your site. categories: [] keywords: [] menu: @@ -9,7 +9,7 @@ menu: identifier: hosting-and-deployment-in-this-section parent: hosting-and-deployment weight: 1 -weight: 1 +weight: 10 --- Because Hugo renders *static* websites, you can host your new Hugo website virtually anywhere. The following represent only a few of the more popular hosting and automated deployment solutions used by the Hugo community. diff --git a/content/en/hosting-and-deployment/hosting-on-github/index.md b/content/en/hosting-and-deployment/hosting-on-github/index.md index 6c62fb29d..81d6a4c87 100644 --- a/content/en/hosting-and-deployment/hosting-on-github/index.md +++ b/content/en/hosting-and-deployment/hosting-on-github/index.md @@ -188,7 +188,7 @@ The example workflow above includes this step, which typically takes 10‑15 You may remove this step if your site, themes, and modules do not transpile Sass to CSS using the [Dart Sass] transpiler. -[Dart Sass]: /hugo-pipes/transpile-sass-to-css/#dart-sass +[Dart Sass]: /functions/css/sass/#dart-sass ## Other resources diff --git a/content/en/hosting-and-deployment/hosting-on-keycdn.md b/content/en/hosting-and-deployment/hosting-on-keycdn/index.md similarity index 90% rename from content/en/hosting-and-deployment/hosting-on-keycdn.md rename to content/en/hosting-and-deployment/hosting-on-keycdn/index.md index e81129981..3073c7172 100644 --- a/content/en/hosting-and-deployment/hosting-on-keycdn.md +++ b/content/en/hosting-and-deployment/hosting-on-keycdn/index.md @@ -20,7 +20,7 @@ menu: The first step will be to log in to your KeyCDN account and create a new zone. Name this whatever you like and select the [Pull Zone](https://www.keycdn.com/support/create-a-pull-zone/) option. As for the origin URL, your site will be running on [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/getting_started_part_one.html) with a URL of `https://youruser.gitlab.io/reponame/`. Use this as the Origin URL. -![Screenshot of KeyCDN's pull zone creation page](/images/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png) +![Screenshot of KeyCDN's pull zone creation page](keycdn-pull-zone.png) While the origin location doesn’t exist yet, you will need to use your new Zone URL address (or [Zone Alias](https://www.keycdn.com/support/create-a-zone-alias/)) in the `.gitlab-ci.yml` file that will be uploaded to your GitLab project. @@ -64,17 +64,17 @@ Using this integration method, you will have to specify the Zone ID and your [K The Secret Variable for your Zone ID should look similar to: -![Screenshot of setting the Zone ID secret variable](/images/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png) +![Screenshot of setting the Zone ID secret variable](secret-zone-id.png) While the Secret Variable for your API Key will look similar to: -![Screenshot of setting the API Key secret variable](/images/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png) +![Screenshot of setting the API Key secret variable](secret-api-key.png) The Zone ID and API key are used to purge your zone – it’s not strictly needed but otherwise, the CDN might deliver older versions of your assets for quite a while. ## Push your changes to GitLab -Now it’s time to push the newly created repository to GitLab: +Now it's time to push the newly created repository to GitLab: ```sh git remote add origin git@gitlab.com:youruser/ci-example.git diff --git a/static/images/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png b/content/en/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png similarity index 100% rename from static/images/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png rename to content/en/hosting-and-deployment/hosting-on-keycdn/keycdn-pull-zone.png diff --git a/static/images/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png b/content/en/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png similarity index 100% rename from static/images/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png rename to content/en/hosting-and-deployment/hosting-on-keycdn/secret-api-key.png diff --git a/static/images/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png b/content/en/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png similarity index 100% rename from static/images/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png rename to content/en/hosting-and-deployment/hosting-on-keycdn/secret-zone-id.png diff --git a/content/en/hugo-modules/_index.md b/content/en/hugo-modules/_index.md index 87ce33a6c..b6f38924f 100644 --- a/content/en/hugo-modules/_index.md +++ b/content/en/hugo-modules/_index.md @@ -1,7 +1,7 @@ --- title: Hugo Modules -linkTitle: In this section -description: How to use Hugo Modules. + +description: Use Hugo Modules to manage the content, presentation, and behavior of your site. categories: [] keywords: [] menu: diff --git a/content/en/hugo-modules/configuration.md b/content/en/hugo-modules/configuration.md index c6d8c3900..0bc1b47d8 100644 --- a/content/en/hugo-modules/configuration.md +++ b/content/en/hugo-modules/configuration.md @@ -75,7 +75,7 @@ max : (`string`) The maximum Hugo version supported, e.g. `0.55.0` extended -: (`bool`) Whether the extended version of Hugo is required. +: (`bool`) Whether the extended edition of Hugo is required, satisfied by installing either the extended or extended/deploy edition. ## Module configuration: imports @@ -154,21 +154,21 @@ target : (`string`) Where it should be mounted into Hugo's virtual filesystem. It must start with one of Hugo's component directories: `static`, `content`, `layouts`, `data`, `assets`, `i18n`, or `archetypes`. E.g. `content/blog`. disableWatch -{{< new-in 0.128.0 >}} +{{< new-in 0.128.0 />}} : (`bool`) Whether to disable watching in watch mode for this mount. Default is `false`. lang : (`string`) The language code, e.g. "en". Only relevant for `content` mounts, and `static` mounts when in multihost mode. includeFiles -: (`string` or `string slice`) One or more [glob](https://github.com/gobwas/glob) patterns matching files or directories to include. If `excludeFiles` is not set, the files matching `includeFiles` will be the files mounted. +: (`string` or `[]string`) One or more [glob](https://github.com/gobwas/glob) patterns matching files or directories to include. If `excludeFiles` is not set, the files matching `includeFiles` will be the files mounted. The glob patterns are matched to the file names starting from the `source` root, they should have Unix styled slashes even on Windows, `/` matches the mount root and `**` can be used as a super-asterisk to match recursively down all directories, e.g `/posts/**.jpg`. The search is case-insensitive. excludeFiles -: (`string` or `string slice`) One or more glob patterns matching files to exclude. +: (`string` or `[]string`) One or more glob patterns matching files to exclude. ### Example diff --git a/content/en/hugo-modules/use-modules.md b/content/en/hugo-modules/use-modules.md index 6af96c992..f29d4b198 100644 --- a/content/en/hugo-modules/use-modules.md +++ b/content/en/hugo-modules/use-modules.md @@ -148,7 +148,7 @@ use . use ../gohugoioTheme ``` -Using the `use` directive, list all the modules you want to work on, pointing to its relative location. As in the example above, it's recommended to always include the main project (the ".") in the list. +Using the `use` directive, list all the modules you want to work on, pointing to its relative location. As in the example above, it's recommended to always include the main project (the `.`) in the list. With that you can start the Hugo server with that workspace enabled: diff --git a/content/en/hugo-pipes/_index.md b/content/en/hugo-pipes/_index.md index 6e4190b87..5e2d1d416 100755 --- a/content/en/hugo-pipes/_index.md +++ b/content/en/hugo-pipes/_index.md @@ -1,6 +1,6 @@ --- title: Hugo Pipes -linkTitle: In this section +description: Use asset pipelines to transform and optimize images, stylesheets, and JavaScript. categories: [] keywords: [] menu: diff --git a/content/en/hugo-pipes/bundling.md b/content/en/hugo-pipes/bundling.md index 7fc9fc9df..1e0908737 100755 --- a/content/en/hugo-pipes/bundling.md +++ b/content/en/hugo-pipes/bundling.md @@ -9,18 +9,6 @@ menu: parent: hugo-pipes weight: 90 weight: 90 -action: - aliases: [] - returnType: resource.Resource - signatures: ['resources.Concat TARGETPATH [RESOURCE...]'] --- -## Usage - -Asset files of the same MIME type can be bundled into one resource using `resources.Concat` which takes two arguments, the target path for the created resource bundle and a slice of resource objects to be concatenated. - -```go-html-template -{{ $plugins := resources.Get "js/plugins.js" }} -{{ $global := resources.Get "js/global.js" }} -{{ $js := slice $plugins $global | resources.Concat "js/bundle.js" }} -``` +See the [`resources.Concat`](/functions/resources/concat/) function. diff --git a/content/en/hugo-pipes/fingerprint.md b/content/en/hugo-pipes/fingerprint.md index 68e41acd1..78fb18081 100755 --- a/content/en/hugo-pipes/fingerprint.md +++ b/content/en/hugo-pipes/fingerprint.md @@ -1,7 +1,7 @@ --- title: Fingerprint linkTitle: Fingerprinting and SRI hashing -description: Process a given resource, adding a hash string of the resource's content. +description: Cryptographically hash the content of the given resource. categories: [asset management] keywords: [] menu: @@ -9,22 +9,6 @@ menu: parent: hugo-pipes weight: 100 weight: 100 -action: - aliases: [fingerprint] - returnType: resource.Resource - signatures: ['resources.Fingerprint [ALGORITHM] RESOURCE'] --- -## Usage - -Fingerprinting and [SRI](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) can be applied to any asset file using `resources.Fingerprint` which takes two arguments, the resource object and an optional [hash algorithm](https://en.wikipedia.org/wiki/Secure_Hash_Algorithms). - -The default hash algorithm is `sha256`. Other available algorithms are `sha384` and (as of Hugo `0.55`) `sha512` and `md5`. - -Any so processed asset will bear a `.Data.Integrity` property containing an integrity string, which is made up of the name of the hash algorithm, one hyphen and the base64-encoded hash sum. - -```go-html-template -{{ $js := resources.Get "js/global.js" }} -{{ $secureJS := $js | resources.Fingerprint "sha512" }} - -``` +See the [`resources.Fingerprint`](/functions/resources/fingerprint/) function. diff --git a/content/en/hugo-pipes/minification.md b/content/en/hugo-pipes/minification.md index a5d4724f9..3943d87d0 100755 --- a/content/en/hugo-pipes/minification.md +++ b/content/en/hugo-pipes/minification.md @@ -1,7 +1,7 @@ --- title: Minify linkTitle: Asset minification -description: Minifies a given resource. +description: Minify a given resource. categories: [asset management] keywords: [] menu: @@ -9,19 +9,6 @@ menu: parent: hugo-pipes weight: 80 weight: 80 -action: - aliases: [minify] - returnType: resource.Resource - signatures: [resources.Minify RESOURCE] --- -## Usage - -Any CSS, JS, JSON, HTML, SVG, or XML resource can be minified using `resources.Minify` which takes for argument the resource object. - -```go-html-template -{{ $css := resources.Get "css/main.css" }} -{{ $style := $css | resources.Minify }} -``` - -Note that you can also minify the final HTML output to `/public` by running `hugo --minify`. +See the [`resources.Minify`](/functions/resources/minify/) function. diff --git a/content/en/hugo-pipes/postcss.md b/content/en/hugo-pipes/postcss.md index 8ca983f7e..72a091c8f 100755 --- a/content/en/hugo-pipes/postcss.md +++ b/content/en/hugo-pipes/postcss.md @@ -1,6 +1,6 @@ --- title: PostCSS -description: Process CSS files with PostCSS, using any of the available plugins. +description: Process the given resource with PostCSS using any PostCSS plugin. categories: [asset management] keywords: [] menu: @@ -8,124 +8,6 @@ menu: parent: hugo-pipes weight: 40 weight: 40 -toc: true -action: - aliases: [postCSS] - returnType: resource.Resource - signatures: ['css.PostCSS [OPTIONS] RESOURCE'] --- -## Setup - -Follow the steps below to transform CSS using any of the [available PostCSS plugins](https://www.postcss.parts/). - -Step 1 -: Install [Node.js](https://nodejs.org/en/download). - -Step 2 -: Install the required Node.js packages in the root of your project. For example, to add vendor prefixes to CSS rules: - -```sh -npm i -D postcss postcss-cli autoprefixer -``` - -Step 3 -: Create a PostCSS configuration file in the root of your project. You must name this file `postcss.config.js` or one of the other [supported file names]. For example: - -[supported file names]: https://github.com/postcss/postcss-load-config#usage - -{{< code file=postcss.config.js >}} -module.exports = { - plugins: [ - require('autoprefixer') - ] -}; -{{< /code >}} - -{{% note %}} -If you are a Windows user, and the path to your project contains a space, you must place the PostCSS configuration within the package.json file. See [this example](https://github.com/postcss/postcss-load-config#packagejson) and issue [#7333](https://github.com/gohugoio/hugo/issues/7333). -{{% /note %}} - -Step 4 -: Place your CSS file within the `assets` directory. - -Step 5 -: Capture the CSS file as a resource and pipe it through `css.PostCSS` (alias `postCSS`): - -{{< code file=layouts/partials/css.html >}} -{{ with resources.Get "css/main.css" | postCSS }} - -{{ end }} -{{< /code >}} - -If starting with a Sass file within the `assets` directory: - -{{< code file=layouts/partials/css.html >}} -{{ with resources.Get "sass/main.scss" | toCSS | postCSS }} - -{{ end }} -{{< /code >}} - -## Options - -The `css.PostCSS` method takes an optional map of options. - -config -: (`string`) The directory that contains the PostCSS configuration file. Default is the root of the project directory. - -noMap -: (`bool`) Default is `false`. If `true`, disables inline sourcemaps. - -inlineImports -: (`bool`) Default is `false`. Enable inlining of @import statements. It does so recursively, but will only import a file once. -URL imports (e.g. `@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');`) and imports with media queries will be ignored. -Note that this import routine does not care about the CSS spec, so you can have @import anywhere in the file. -Hugo will look for imports relative to the module mount and will respect theme overrides. - -skipInlineImportsNotFound -: (`bool`) Default is `false`. If you have regular CSS imports in your CSS that you want to preserve, you can either use imports with URL or media queries (Hugo does not try to resolve those) or set `skipInlineImportsNotFound` to true. - -{{< code file=layouts/partials/css.html >}} -{{ $opts := dict "config" "config-directory" "noMap" true }} -{{ with resources.Get "css/main.css" | postCSS $opts }} - -{{ end }} -{{< /code >}} - -## No configuration file - -To avoid using a PostCSS configuration file, you can specify a minimal configuration using the options map. - -use -: (`string`) A space-delimited list of PostCSS plugins to use. - -parser -: (`string`) A custom PostCSS parser. - -stringifier -: (`string`) A custom PostCSS stringifier. - -syntax -: (`string`) Custom postcss syntax. - -{{< code file=layouts/partials/css.html >}} -{{ $opts := dict "use" "autoprefixer postcss-color-alpha" }} -{{ with resources.Get "css/main.css" | postCSS $opts }} - -{{ end }} -{{< /code >}} - -## Check Hugo environment - -The current Hugo environment name (set by `--environment` or in configuration or OS environment) is available in the Node context, which allows constructs like this: - -{{< code file=postcss.config.js >}} -module.exports = { - plugins: [ - require('autoprefixer'), - ...process.env.HUGO_ENVIRONMENT === 'production' - ? [purgecss] - : [] - ] -} -{{< /code >}} +See the [`css.PostCSS`](/functions/css/postcss/) function. diff --git a/content/en/hugo-pipes/postprocess.md b/content/en/hugo-pipes/postprocess.md index 5a0a5797e..e916dfc4d 100755 --- a/content/en/hugo-pipes/postprocess.md +++ b/content/en/hugo-pipes/postprocess.md @@ -1,6 +1,6 @@ --- title: PostProcess -description: Allows delaying of resource transformations to after the build. +description: Process the given resource after the build. categories: [asset management] keywords: [] menu: @@ -8,97 +8,6 @@ menu: parent: hugo-pipes weight: 50 weight: 50 -action: - aliases: [] - returnType: postpub.PostPublishedResource - signatures: [resources.PostProcess RESOURCE] --- -## Usage - -Marking a resource with `resources.PostProcess` delays any transformations to after the build, typically because one or more of the steps in the transformation chain depends on the result of the build (e.g. files in `public`). - -A prime use case for this is [CSS purging with PostCSS](#css-purging-with-postcss). - -There are currently two limitations to this: - -1. This only works in `*.html` templates (i.e. templates that produces HTML files). -1. You cannot manipulate the values returned from the resource's methods. E.g. the `upper` in this example will not work as expected: - - ```go-html-template - {{ $css := resources.Get "css/main.css" }} - {{ $css = $css | css.PostCSS | minify | fingerprint | resources.PostProcess }} - {{ $css.RelPermalink | upper }} - ``` - -## CSS purging with PostCSS - -{{% note %}} -There are several ways to set up CSS purging with PostCSS in Hugo. If you have a simple project, you should consider going the simpler route and drop the use of `resources.PostProcess` and just extract keywords from the templates. See the [Tailwind documentation](https://tailwindcss.com/docs/controlling-file-size/#app) for some examples. -{{% /note %}} - -The below configuration will write a `hugo_stats.json` file to the project root as part of the build. If you're only using this for the production build, you should consider placing it below [`config/production`](/getting-started/configuration/#configuration-directory). - -{{< code-toggle file=hugo >}} -[build.buildStats] - enable = true -{{< /code-toggle >}} - -See the [configure build] documentation for details and options. - -[configure build]: /getting-started/configuration/#configure-build - -`postcss.config.js` - -```js -const purgecss = require('@fullhuman/postcss-purgecss')({ - content: [ './hugo_stats.json' ], - defaultExtractor: (content) => { - let els = JSON.parse(content).htmlElements; - return els.tags.concat(els.classes, els.ids); - } -}); - -module.exports = { - plugins: [ - ...(process.env.HUGO_ENVIRONMENT === 'production' ? [ purgecss ] : []) - ] - }; -``` - -Note that in the example above, the "CSS purge step" will only be applied to the production build. This means that you need to do something like this in your head template to build and include your CSS: - -```go-html-template -{{ $css := resources.Get "css/main.css" }} -{{ $css = $css | css.PostCSS }} -{{ if hugo.IsProduction }} -{{ $css = $css | minify | fingerprint | resources.PostProcess }} -{{ end }} - -``` - -## Hugo environment variables available in PostCSS - -These are the environment variables Hugo passes down to PostCSS (and Babel), which allows you do do `process.env.HUGO_ENVIRONMENT === 'production' ? [autoprefixer] : []` and similar: - -PWD -: The absolute path to the project working directory. - -HUGO_ENVIRONMENT -: The value e.g. set with `hugo -e production` (defaults to `production` for `hugo` and `development` for `hugo server`). - -HUGO_PUBLISHDIR -: The absolute path to the publish directory (the `public` directory). Note that the value will always point to a directory on disk even when running `hugo server` in memory mode. If you write to this directory from PostCSS when running the server, you could run the server with one of these flags: - -```sh -hugo server --renderToDisk -hugo server --renderStaticToDisk -``` - -Also, Hugo will add environment variables for all files mounted below `assets/_jsconfig`. A default mount will be set up with files in the project root matching this regexp: `(babel|postcss|tailwind)\.config\.js`. - -These will get environment variables named on the form `HUGO_FILE_:filename:` where `:filename:` is all upper case with periods replaced with underscore. This allows you to do this and similar: - -```js -let tailwindConfig = process.env.HUGO_FILE_TAILWIND_CONFIG_JS || './tailwind.config.js'; -``` +See the [`resources.PostProcess`](/functions/resources/postprocess/) function. diff --git a/content/en/hugo-pipes/resource-from-string.md b/content/en/hugo-pipes/resource-from-string.md index 53bcfff74..9c943591c 100755 --- a/content/en/hugo-pipes/resource-from-string.md +++ b/content/en/hugo-pipes/resource-from-string.md @@ -1,7 +1,7 @@ --- title: FromString linkTitle: Resource from string -description: Creates a resource from a string. +description: Create a resource from a string. categories: [asset management] keywords: [] menu: @@ -9,26 +9,6 @@ menu: parent: hugo-pipes weight: 110 weight: 110 -action: - aliases: [] - returnType: resource.Resource - signatures: [resources.FromString TARGETPATH STRING] --- -## Usage - -It is possible to create a resource directly from the template using `resources.FromString` which takes two arguments, the target path for the created resource and the given content string. - -The result is cached using the target path as the cache key. - -The following example creates a resource file containing localized variables for every project's languages. - -```go-html-template -{{ $string := (printf "var rootURL = '%s'; var apiURL = '%s';" (absURL "/") (.Param "API_URL")) }} -{{ $targetPath := "js/vars.js" }} -{{ $vars := $string | resources.FromString $targetPath }} -{{ $global := resources.Get "js/global.js" | resources.Minify }} - - - -``` +See the [`resources.FromString`](/functions/resources/fromstring/) function. diff --git a/content/en/hugo-pipes/resource-from-template.md b/content/en/hugo-pipes/resource-from-template.md index 0c81f0c00..76303822d 100755 --- a/content/en/hugo-pipes/resource-from-template.md +++ b/content/en/hugo-pipes/resource-from-template.md @@ -1,7 +1,7 @@ --- title: ExecuteAsTemplate linkTitle: Resource from template -description: Creates a resource from a template +description: Create a resource from a Go template, parsed and executed with the given context. categories: [asset management] keywords: [] menu: @@ -9,30 +9,6 @@ menu: parent: hugo-pipes weight: 120 weight: 120 -action: - aliases: [] - returnType: resource.Resource - signatures: [resources.ExecuteAsTemplate TARGETPATH CONTEXT RESOURCE] --- -## Usage - -In order to use Hugo Pipes function on an asset file containing Go Template magic the function `resources.ExecuteAsTemplate` must be used. - -The function takes three arguments: the target path for the created resource, the template context, and the resource object. The target path is used to cache the result. - -```go-html-template -// assets/sass/template.scss -$backgroundColor: {{ .Param "backgroundColor" }}; -$textColor: {{ .Param "textColor" }}; -body{ - background-color:$backgroundColor; - color: $textColor; -} -// [...] -``` - -```go-html-template -{{ $sassTemplate := resources.Get "sass/template.scss" }} -{{ $style := $sassTemplate | resources.ExecuteAsTemplate "main.scss" . | css.Sass }} -``` +See the [`resources.ExecuteAsTemplate`](/functions/resources/executeastemplate/) function. diff --git a/content/en/hugo-pipes/transpile-sass-to-css.md b/content/en/hugo-pipes/transpile-sass-to-css.md index a7f790c8a..918687b17 100644 --- a/content/en/hugo-pipes/transpile-sass-to-css.md +++ b/content/en/hugo-pipes/transpile-sass-to-css.md @@ -7,208 +7,9 @@ keywords: [] menu: docs: parent: hugo-pipes - returnType: resource.Resource weight: 30 weight: 30 -action: - aliases: [toCSS] - returnType: resource.Resource - signatures: ['css.Sass [OPTIONS] RESOURCE'] -toc: true aliases: [/hugo-pipes/transform-to-css/] --- -## Usage - -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. - -```go-html-template -{{ $opts := dict "transpiler" "libsass" "targetPath" "css/style.css" }} -{{ with resources.Get "sass/main.scss" | toCSS $opts | minify | fingerprint }} - -{{ end }} -``` - -Sass has two forms of syntax: [SCSS] and [indented]. Hugo supports both. - -[scss]: https://sass-lang.com/documentation/syntax#scss -[indented]: https://sass-lang.com/documentation/syntax#the-indented-syntax - -## Options - -transpiler -: (`string`) The transpiler to use, either `libsass` (default) or `dartsass`. Hugo's extended and extended/deploy editions include the LibSass transpiler. To use the Dart Sass transpiler, see the [installation instructions](#dart-sass) below. - -targetPath -: (`string`) If not set, the transformed resource's target path will be the original path of the asset file with its extension replaced by `.css`. - -vars -: (`map`) A map of key-value pairs that will be available in the `hugo:vars` namespace. Useful for [initializing Sass variables from Hugo templates](https://discourse.gohugo.io/t/42053/). - -```scss -// LibSass -@import "hugo:vars"; - -// Dart Sass -@use "hugo:vars" as v; -``` - -outputStyle -: (`string`) Output styles available to LibSass include `nested` (default), `expanded`, `compact`, and `compressed`. Output styles available to Dart Sass include `expanded` (default) and `compressed`. - -precision -: (`int`) Precision of floating point math. Not applicable to Dart Sass. - -enableSourceMap -: (`bool`) If `true`, generates a source map. - -sourceMapIncludeSources -: (`bool`) If `true`, embeds sources in the generated source map. Not applicable to LibSass. - -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 }} - -{{ end }} -``` - -## Dart Sass - -The extended version of Hugo includes [LibSass] to transpile Sass to CSS. In 2020, the Sass team deprecated LibSass in favor of [Dart Sass]. - -Use the latest features of the Sass language by installing Dart Sass in your development and production environments. - -### Installation overview - -Dart Sass is compatible with Hugo v0.114.0 and later. - -If you have been using Embedded Dart Sass[^1] with Hugo v0.113.0 and earlier, uninstall Embedded Dart Sass, then install Dart Sass. If you have installed both, Hugo will use Dart Sass. - -If you install Hugo as a [Snap package] there is no need to install Dart Sass. The Hugo Snap package includes Dart Sass. - -[^1]: In 2023, the Sass team deprecated Embedded Dart Sass in favor of Dart Sass. - -### Installing in a development environment - -When you install Dart Sass somewhere in your PATH, Hugo will find it. - -OS|Package manager|Site|Installation -:--|:--|:--|:-- -Linux|Homebrew|[brew.sh]|`brew install sass/sass/sass` -Linux|Snap|[snapcraft.io]|`sudo snap install dart-sass` -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. - -Run `hugo env` to list the active transpilers. - -### Installing in a production environment - -For [CI/CD] 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. - -[^2]: You do not have to do this if (a) you have not modified the assets cache location, and (b) you have not set `useResourceCacheWhen` to `never` in your [site configuration], and (c) you add and commit your `resources` directory to your repository. - -#### GitHub Pages - -To install Dart Sass for your builds on GitHub Pages, add this step to the GitHub Pages workflow file: - -```yaml -- name: Install Dart Sass - 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.141.0 - DART_SASS_VERSION: 1.83.4 - GIT_DEPTH: 0 - GIT_STRATEGY: clone - GIT_SUBMODULE_STRATEGY: recursive - TZ: America/Los_Angeles -image: - name: golang:1.20-buster -pages: - script: - # Install Dart Sass - - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - - tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - - cp -r dart-sass/* /usr/local/bin - - rm -rf dart-sass* - # Install Hugo - - curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb - - apt install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb - - rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb - # Build - - hugo --gc --minify - artifacts: - paths: - - public - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -``` - -#### Netlify - -To install Dart Sass for your builds on Netlify, the `netlify.toml` file should look something like this: - -```toml -[build.environment] -HUGO_VERSION = "0.141.0" -DART_SASS_VERSION = "1.83.4" -NODE_VERSION = "22" -TZ = "America/Los_Angeles" - -[build] -publish = "public" -command = """\ - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \ - 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 && \ - hugo --gc --minify \ - """ -``` - -### Example - -To transpile with Dart Sass, set `transpiler` to `dartsass` in the options map passed to `css.Sass`. For example: - -```go-html-template -{{ $opts := dict "transpiler" "dartsass" "targetPath" "css/style.css" }} -{{ with resources.Get "sass/main.scss" | toCSS $opts | minify | fingerprint }} - -{{ 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 -[ci/cd]: https://en.wikipedia.org/wiki/CI/CD -[dart sass]: https://sass-lang.com/dart-sass -[libsass]: https://sass-lang.com/libsass -[prebuilt binaries]: https://github.com/sass/dart-sass/releases/latest -[scoop.sh]: https://scoop.sh/#/apps?q=sass -[site configuration]: /getting-started/configuration/#configure-build -[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 +See the [`css.Sass`](/functions/css/sass) function. diff --git a/content/en/installation/_common/01-editions.md b/content/en/installation/_common/01-editions.md index b6fc23c74..11e7e9080 100644 --- a/content/en/installation/_common/01-editions.md +++ b/content/en/installation/_common/01-editions.md @@ -8,9 +8,9 @@ Feature|extended edition|extended/deploy edition :--|:-:|:-: Encode to the WebP format when [processing images]. You can decode WebP images with any edition.|:heavy_check_mark:|:heavy_check_mark: [Transpile Sass to CSS] using the embedded LibSass transpiler. You can use the [Dart Sass] transpiler with any edition.|:heavy_check_mark:|:heavy_check_mark: -Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details].|:x:|:heavy_check_mark: +Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details].|:x:|:heavy_check_mark: -[dart sass]: /hugo-pipes/transpile-sass-to-css/#dart-sass +[dart sass]: /functions/css/sass/#dart-sass [processing images]: /content-management/image-processing/ -[transpile sass to css]: /hugo-pipes/transpile-sass-to-css/ +[transpile sass to css]: /functions/css/sass/ [details]: /hosting-and-deployment/hugo-deploy/ diff --git a/content/en/installation/_common/02-prerequisites.md b/content/en/installation/_common/02-prerequisites.md index 65ec2a12f..f27d9d56b 100644 --- a/content/en/installation/_common/02-prerequisites.md +++ b/content/en/installation/_common/02-prerequisites.md @@ -29,7 +29,7 @@ Please refer to the relevant documentation for installation instructions: [cloudcannon]: https://cloudcannon.com/ [cloudflare pages]: https://pages.cloudflare.com/ -[dart sass install]: /hugo-pipes/transpile-sass-to-css/#dart-sass +[dart sass install]: /functions/css/sass/#dart-sass [dart sass]: https://sass-lang.com/dart-sass [git install]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git [git]: https://git-scm.com/ diff --git a/content/en/installation/_index.md b/content/en/installation/_index.md index 7e445a07d..69b3e197e 100644 --- a/content/en/installation/_index.md +++ b/content/en/installation/_index.md @@ -1,6 +1,6 @@ --- title: Installation -linkTitle: In this section + description: Install Hugo on macOS, Linux, Windows, BSD, and on any machine that can run the Go compiler tool chain. aliases: [/getting-started/installing/] categories: [] diff --git a/content/en/installation/windows.md b/content/en/installation/windows.md index e04268cd2..e66878d70 100644 --- a/content/en/installation/windows.md +++ b/content/en/installation/windows.md @@ -55,6 +55,12 @@ scoop install hugo-extended winget install Hugo.Hugo.Extended ``` +To uninstall the extended edition of Hugo: + +```sh +winget uninstall --name "Hugo (Extended)" +``` + [Winget]: https://learn.microsoft.com/en-us/windows/package-manager/ {{% include "installation/_common/04-build-from-source.md" %}} diff --git a/content/en/maintenance/_index.md b/content/en/maintenance/_index.md deleted file mode 100644 index aaee3987d..000000000 --- a/content/en/maintenance/_index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Maintenance -description: Some lists useful for the maintenance of the Hugo docs site. -categories: [] -keywords: [] -menu: - docs: - weight: 200 -toc: true ---- diff --git a/content/en/methods/_index.md b/content/en/methods/_index.md index bab637ddb..e45f2fef7 100644 --- a/content/en/methods/_index.md +++ b/content/en/methods/_index.md @@ -1,7 +1,7 @@ --- title: Methods -linkTitle: In this section -description: A list of Hugo template methods including examples. + +description: Use these methods within your templates. categories: [] keywords: [] menu: diff --git a/content/en/methods/page/Ancestors.md b/content/en/methods/page/Ancestors.md index da5cf2ca3..4fb00fc06 100644 --- a/content/en/methods/page/Ancestors.md +++ b/content/en/methods/page/Ancestors.md @@ -16,7 +16,7 @@ action: signatures: [PAGE.Ancestors] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} With this content structure: diff --git a/content/en/methods/page/ContentWithoutSummary.md b/content/en/methods/page/ContentWithoutSummary.md index 44d660cef..527f5d962 100644 --- a/content/en/methods/page/ContentWithoutSummary.md +++ b/content/en/methods/page/ContentWithoutSummary.md @@ -15,7 +15,7 @@ action: signatures: [PAGE.ContentWithoutSummary] --- -{{< new-in 0.134.0 >}} +{{< new-in 0.134.0 />}} Applicable when using manual or automatic [content summaries], the `ContentWithoutSummary` method on a `Page` object renders Markdown and shortcodes to HTML, excluding the content summary from the result. diff --git a/content/en/methods/page/CurrentSection.md b/content/en/methods/page/CurrentSection.md index 17ec652c1..1684ee483 100644 --- a/content/en/methods/page/CurrentSection.md +++ b/content/en/methods/page/CurrentSection.md @@ -16,7 +16,7 @@ action: signatures: [PAGE.CurrentSection] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} {{% note %}} The current section of a [section page](g), [taxonomy page](g), [term page](g), or the home page, is itself. diff --git a/content/en/methods/page/File.md b/content/en/methods/page/File.md index a5b30ddb0..359c1ad2e 100644 --- a/content/en/methods/page/File.md +++ b/content/en/methods/page/File.md @@ -12,7 +12,7 @@ toc: true By default, not all pages are backed by a file, including top level [section pages](g), [taxonomy pages](g), and [term pages](g). By definition, you cannot retrieve file information when the file does not exist. -To back one of the pages above with a file, create an `_index.md` file in the corresponding directory. For example: +To back one of the pages above with a file, create an `_index.md` file in the corresponding directory. For example: ```text content/ @@ -84,7 +84,7 @@ The path separators (slash or backslash) in `Path`, `Dir`, and `Filename` depend ###### IsContentAdapter -{{< new-in 0.126.0 >}} +{{< new-in 0.126.0 />}} (`bool`) Reports whether the file is a [content adapter]. diff --git a/content/en/methods/page/FirstSection.md b/content/en/methods/page/FirstSection.md index b3ae4c04a..29fbdc841 100644 --- a/content/en/methods/page/FirstSection.md +++ b/content/en/methods/page/FirstSection.md @@ -16,7 +16,7 @@ action: signatures: [PAGE.FirstSection] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} {{% note %}} When called on the home page, the `FirstSection` method returns the `Page` object of the home page itself. diff --git a/content/en/methods/page/InSection.md b/content/en/methods/page/InSection.md index 75ceb2239..904f6ce75 100644 --- a/content/en/methods/page/InSection.md +++ b/content/en/methods/page/InSection.md @@ -19,7 +19,7 @@ toc: true The `InSection` method on a `Page` object reports whether the given page is in the given section. Note that the method returns `true` when comparing a page to a sibling. -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} With this content structure: diff --git a/content/en/methods/page/IsAncestor.md b/content/en/methods/page/IsAncestor.md index 17764fbe7..2613a2875 100644 --- a/content/en/methods/page/IsAncestor.md +++ b/content/en/methods/page/IsAncestor.md @@ -17,7 +17,7 @@ action: toc: true --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} With this content structure: diff --git a/content/en/methods/page/IsDescendant.md b/content/en/methods/page/IsDescendant.md index f2fc7e339..4ef7c6598 100644 --- a/content/en/methods/page/IsDescendant.md +++ b/content/en/methods/page/IsDescendant.md @@ -16,7 +16,7 @@ action: signatures: [PAGE1.IsDescendant PAGE2] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} With this content structure: diff --git a/content/en/methods/page/Pages.md b/content/en/methods/page/Pages.md index c376bc3b5..8efcde287 100644 --- a/content/en/methods/page/Pages.md +++ b/content/en/methods/page/Pages.md @@ -70,7 +70,7 @@ When rendering lesson-2, the `Pages` method returns: lessons/lesson-2/resources/task-list.md lessons/lesson-2/resources/worksheet.md -In the last example, the collection includes pages in the resources subdirectory. That directory is not a [section](g)---it does not contain an `_index.md` file. Its contents are part of the lesson-2 section. +In the last example, the collection includes pages in the resources subdirectory. That directory is not a [section](g)---it does not contain an `_index.md` file. Its contents are part of the lesson-2 section. {{% note %}} When used with a `Site` object, the `Pages` method recursively returns all pages within the site. See [details]. diff --git a/content/en/methods/page/Parent.md b/content/en/methods/page/Parent.md index db01eb589..4c182f8e1 100644 --- a/content/en/methods/page/Parent.md +++ b/content/en/methods/page/Parent.md @@ -16,7 +16,7 @@ action: signatures: [PAGE.Parent] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} {{% note %}} The parent section of a regular page is the [current section]. diff --git a/content/en/methods/page/Path.md b/content/en/methods/page/Path.md index 657410112..83ad01b94 100644 --- a/content/en/methods/page/Path.md +++ b/content/en/methods/page/Path.md @@ -12,7 +12,7 @@ action: toc: true --- -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} The `Path` method on a `Page` object returns the [logical path](g) of the given page, regardless of whether the page is backed by a file. diff --git a/content/en/methods/page/RegularPages.md b/content/en/methods/page/RegularPages.md index 29a66b48f..08198fca4 100644 --- a/content/en/methods/page/RegularPages.md +++ b/content/en/methods/page/RegularPages.md @@ -67,7 +67,7 @@ When rendering lesson-2, the `RegularPages` method returns: lessons/lesson-2/resources/task-list.md lessons/lesson-2/resources/worksheet.md -In the last example, the collection includes pages in the resources subdirectory. That directory is not a [section](g)---it does not contain an _index.md file. Its contents are part of the lesson-2 section. +In the last example, the collection includes pages in the resources subdirectory. That directory is not a [section](g)---it does not contain an `_index.md` file. Its contents are part of the lesson-2 section. {{% note %}} When used with the `Site` object, the `RegularPages` method recursively returns all regular pages within the site. See [details]. diff --git a/content/en/methods/page/RenderShortcodes.md b/content/en/methods/page/RenderShortcodes.md index 319edea69..3346f0ba1 100644 --- a/content/en/methods/page/RenderShortcodes.md +++ b/content/en/methods/page/RenderShortcodes.md @@ -17,7 +17,7 @@ action: toc: true --- -{{< new-in 0.117.0 >}} +{{< new-in 0.117.0 />}} Use this method in shortcode templates to compose a page from multiple content files, while preserving a global context for footnotes and the table of contents. diff --git a/content/en/methods/page/Resources.md b/content/en/methods/page/Resources.md index 2495ca8df..6c79f58ee 100644 --- a/content/en/methods/page/Resources.md +++ b/content/en/methods/page/Resources.md @@ -71,7 +71,7 @@ When working with global resources instead of page resources, use the [`resource ###### Mount -{{< new-in "0.140.0" >}} +{{< new-in 0.140.0 />}} (`ResourceGetter`) Mounts the given resources from the two arguments base (`string`) to the given target path (`string`) and returns an object that implements [Get](#get). Note that leading slashes in target marks an absolute path. Relative target paths allows you to mount resources relative to another set, e.g. a [Page bundle](/content-management/page-bundles/): diff --git a/content/en/methods/page/Scratch.md b/content/en/methods/page/Scratch.md index 13cd3dff8..436005a94 100644 --- a/content/en/methods/page/Scratch.md +++ b/content/en/methods/page/Scratch.md @@ -1,17 +1,13 @@ --- title: Scratch -description: Returns a "scratch pad" on the given page to store and manipulate data. +description: Returns a "scratch pad" to store and manipulate data, scoped to the current page. categories: [] keywords: [] action: - related: - - methods/page/Store - - functions/collections/NewScratch + related: [] returnType: maps.Scratch signatures: [PAGE.Scratch] -toc: true -aliases: [/extras/scratch/,/doc/scratch/,/functions/scratch] -expiryDate: 2025-11-18 # deprecated 2024-11-18 +expiryDate: 2026-11-18 # deprecated 2024-11-18 (soft) --- {{% deprecated-in 0.138.0 %}} @@ -23,30 +19,3 @@ Beginning with v0.138.0 the `PAGE.Scratch` method is aliased to `PAGE.Store`. [`PAGE.Store`]: /methods/page/store/ {{% /deprecated-in %}} - -The `Scratch` method on a `Page` object creates a [scratch pad](g) to store and manipulate data. To create a scratch pad that is not reset on server rebuilds, use the [`Store`] method instead. - -To create a locally scoped scratch pad that is not attached to a `Page` object, use the [`newScratch`] function. - -[`Store`]: /methods/page/store/ -[`newScratch`]: /functions/collections/newscratch/ - -{{% include "methods/page/_common/scratch-methods.md" %}} - -## Determinate values - -The `Scratch` method is often used to set scratch pad values within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are indeterminate until Hugo renders the page content. - -If you need to access a scratch pad value from a parent template, and the parent template has not yet rendered the page content, you can trigger content rendering by assigning the returned value to a [noop](g) variable: - -```go-html-template -{{ $noop := .Content }} -{{ .Store.Get "mykey" }} -``` - -You can also trigger content rendering with the `ContentWithoutSummary`, `FuzzyWordCount`, `Len`, `Plain`, `PlainWords`, `ReadingTime`, `Summary`, `Truncated`, and `WordCount` methods. For example: - -```go-html-template -{{ $noop := .WordCount }} -{{ .Store.Get "mykey" }} -``` diff --git a/content/en/methods/page/Sections.md b/content/en/methods/page/Sections.md index 4cce1a4fb..921e4eb4e 100644 --- a/content/en/methods/page/Sections.md +++ b/content/en/methods/page/Sections.md @@ -16,7 +16,7 @@ action: signatures: [PAGE.Sections] --- -{{% include "methods/page/_common/definition-of-section.md" %}} +{{% glossary-term section %}} With this content structure: diff --git a/content/en/methods/page/Sitemap.md b/content/en/methods/page/Sitemap.md index 3277520fb..065c07c35 100644 --- a/content/en/methods/page/Sitemap.md +++ b/content/en/methods/page/Sitemap.md @@ -15,13 +15,13 @@ Access to the `Sitemap` method on a `Page` object is restricted to [sitemap temp ## Methods changefreq -: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#changefreqdef). +: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#changefreqdef). ```go-html-template {{ .Sitemap.ChangeFreq }} ``` -disable {{< new-in 0.125.0 >}} +disable {{< new-in 0.125.0 />}} : (`bool`) Whether to disable page inclusion. Default is `false`. Set to `true` in front matter to exclude the page. ```go-html-template @@ -29,7 +29,7 @@ disable {{< new-in 0.125.0 >}} ``` priority -: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#priority). +: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#priority). ```go-html-template {{ .Sitemap.Priority }} diff --git a/content/en/methods/page/Store.md b/content/en/methods/page/Store.md index af0f78642..769a554f1 100644 --- a/content/en/methods/page/Store.md +++ b/content/en/methods/page/Store.md @@ -1,108 +1,25 @@ --- title: Store -linktitle: PAGE.Store -description: Returns a persistent "scratch pad" on the given page to store and manipulate data. +description: Returns a "scratch pad" to store and manipulate data, scoped to the current page. categories: [] keywords: [] action: related: - - methods/page/scratch - - methods/site/store - - functions/hugo/store - - functions/collections/NewScratch + - methods/site/Store + - methods/shortcode/Store + - functions/hugo/Store + - functions/collections/NewScratch returnType: maps.Scratch signatures: [PAGE.Store] toc: true -aliases: [/functions/store] +aliases: [/functions/store/,/extras/scratch/,/doc/scratch/,/functions/scratch] --- -The `Store` method on a `Page` object creates a persistent [scratch pad](g) to store and manipulate data. To create a locally scoped scratch pad that is not attached to a `Page` object, use the [`newScratch`] function. +Use the `Store` method on a `Page` object to create a [scratch pad](g) to store and manipulate data, scoped to the current page. To create a scratch pad with a different [scope](g), refer to the [scope](#scope) section below. -[`Scratch`]: /methods/page/scratch/ -[`newScratch`]: /functions/collections/newscratch/ +{{% include "_common/store-methods.md" %}} -## Methods - -###### Set - -Sets the value of a given key. - -```go-html-template -{{ .Store.Set "greeting" "Hello" }} -``` - -###### Get - -Gets the value of a given key. - -```go-html-template -{{ .Store.Set "greeting" "Hello" }} -{{ .Store.Get "greeting" }} → Hello -``` - -###### Add - -Adds a given value to existing value(s) of the given key. - -For single values, `Add` accepts values that support Go's `+` operator. If the first `Add` for a key is an array or slice, the following adds will be appended to that list. - -```go-html-template -{{ .Store.Set "greeting" "Hello" }} -{{ .Store.Add "greeting" "Welcome" }} -{{ .Store.Get "greeting" }} → HelloWelcome -``` - -```go-html-template -{{ .Store.Set "total" 3 }} -{{ .Store.Add "total" 7 }} -{{ .Store.Get "total" }} → 10 -``` - -```go-html-template -{{ .Store.Set "greetings" (slice "Hello") }} -{{ .Store.Add "greetings" (slice "Welcome" "Cheers") }} -{{ .Store.Get "greetings" }} → [Hello Welcome Cheers] -``` - -###### SetInMap - -Takes a `key`, `mapKey` and `value` and adds a map of `mapKey` and `value` to the given `key`. - -```go-html-template -{{ .Store.SetInMap "greetings" "english" "Hello" }} -{{ .Store.SetInMap "greetings" "french" "Bonjour" }} -{{ .Store.Get "greetings" }} → map[english:Hello french:Bonjour] -``` - -###### DeleteInMap - -Takes a `key` and `mapKey` and removes the map of `mapKey` from the given `key`. - -```go-html-template -{{ .Store.SetInMap "greetings" "english" "Hello" }} -{{ .Store.SetInMap "greetings" "french" "Bonjour" }} -{{ .Store.DeleteInMap "greetings" "english" }} -{{ .Store.Get "greetings" }} → map[french:Bonjour] -``` - -###### GetSortedMapValues - -Returns an array of values from `key` sorted by `mapKey`. - -```go-html-template -{{ .Store.SetInMap "greetings" "english" "Hello" }} -{{ .Store.SetInMap "greetings" "french" "Bonjour" }} -{{ .Store.GetSortedMapValues "greetings" }} → [Hello Bonjour] -``` - -###### Delete - -Removes the given key. - -```go-html-template -{{ .Store.Set "greeting" "Hello" }} -{{ .Store.Delete "greeting" }} -``` +{{% include "_common/scratch-pad-scope.md" %}} ## Determinate values diff --git a/content/en/methods/page/Title.md b/content/en/methods/page/Title.md index 5c2c98d6b..08fc9f9eb 100644 --- a/content/en/methods/page/Title.md +++ b/content/en/methods/page/Title.md @@ -33,7 +33,7 @@ You can change the capitalization style in your site configuration to one of `ap titleCaseStyle = "firstupper" {{< /code-toggle >}} - See [details]. + See [details]. [`capitalizeListTitles`]: /getting-started/configuration/#capitalizelisttitles [`pluralizeListTitles`]: /getting-started/configuration/#pluralizelisttitles diff --git a/content/en/methods/page/Type.md b/content/en/methods/page/Type.md index 565f6397c..2a2a5c731 100644 --- a/content/en/methods/page/Type.md +++ b/content/en/methods/page/Type.md @@ -7,7 +7,6 @@ action: related: - methods/page/Kind - methods/page/Layout - - methods/page/Type returnType: string signatures: [PAGE.Type] --- diff --git a/content/en/methods/page/_common/definition-of-section.md b/content/en/methods/page/_common/definition-of-section.md deleted file mode 100644 index 4a2e36ff3..000000000 --- a/content/en/methods/page/_common/definition-of-section.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -_comment: Do not remove front matter. ---- - -A _section_ is a top-level content directory, or any content directory with an `_index.md` file. diff --git a/content/en/methods/page/_common/scratch-methods.md b/content/en/methods/page/_common/scratch-methods.md deleted file mode 100644 index 92a97cdd5..000000000 --- a/content/en/methods/page/_common/scratch-methods.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -_comment: Do not remove front matter. ---- - -## Methods - -###### Set - -Sets the value of a given key. - -```go-html-template -{{ .Scratch.Set "greeting" "Hello" }} -``` - -###### Get - -Gets the value of a given key. - -```go-html-template -{{ .Scratch.Set "greeting" "Hello" }} -{{ .Scratch.Get "greeting" }} → Hello -``` - -###### Add - -Adds a given value to existing value(s) of the given key. - -For single values, `Add` accepts values that support Go's `+` operator. If the first `Add` for a key is an array or slice, the following adds will be appended to that list. - -```go-html-template -{{ .Scratch.Set "greeting" "Hello" }} -{{ .Scratch.Add "greeting" "Welcome" }} -{{ .Scratch.Get "greeting" }} → HelloWelcome -``` - -```go-html-template -{{ .Scratch.Set "total" 3 }} -{{ .Scratch.Add "total" 7 }} -{{ .Scratch.Get "total" }} → 10 -``` - -```go-html-template -{{ .Scratch.Set "greetings" (slice "Hello") }} -{{ .Scratch.Add "greetings" (slice "Welcome" "Cheers") }} -{{ .Scratch.Get "greetings" }} → [Hello Welcome Cheers] -``` - -###### SetInMap - -Takes a `key`, `mapKey` and `value` and adds a map of `mapKey` and `value` to the given `key`. - -```go-html-template -{{ .Scratch.SetInMap "greetings" "english" "Hello" }} -{{ .Scratch.SetInMap "greetings" "french" "Bonjour" }} -{{ .Scratch.Get "greetings" }} → map[english:Hello french:Bonjour] -``` - -###### DeleteInMap - -Takes a `key` and `mapKey` and removes the map of `mapKey` from the given `key`. - -```go-html-template -{{ .Scratch.SetInMap "greetings" "english" "Hello" }} -{{ .Scratch.SetInMap "greetings" "french" "Bonjour" }} -{{ .Scratch.DeleteInMap "greetings" "english" }} -{{ .Scratch.Get "greetings" }} → map[french:Bonjour] -``` - -###### GetSortedMapValues - -Returns an array of values from `key` sorted by `mapKey`. - -```go-html-template -{{ .Scratch.SetInMap "greetings" "english" "Hello" }} -{{ .Scratch.SetInMap "greetings" "french" "Bonjour" }} -{{ .Scratch.GetSortedMapValues "greetings" }} → [Hello Bonjour] -``` - -###### Delete - -Removes the given key. - -```go-html-template -{{ .Scratch.Set "greeting" "Hello" }} -{{ .Scratch.Delete "greeting" }} -``` diff --git a/content/en/methods/pager/PageSize.md b/content/en/methods/pager/PageSize.md index 148ad5907..d69c31207 100644 --- a/content/en/methods/pager/PageSize.md +++ b/content/en/methods/pager/PageSize.md @@ -4,11 +4,10 @@ description: Returns the number of pages per pager. categories: [] keywords: [] action: - related: - - methods/page/Paginate + related: [] returnType: int signatures: [PAGER.PageSize] -expiryDate: 2025-06-09 # deprecated 2024-06-09 +expiryDate: 2026-06-09 # deprecated 2024-06-09 in v0.128.0 --- {{% deprecated-in 0.128.0 %}} @@ -16,21 +15,3 @@ Use [`PAGER.PagerSize`] instead. [`PAGER.PagerSize`]: /methods/pager/pagersize/ {{% /deprecated-in %}} - -The number of pages per pager is determined by the optional second argument passed to the [`Paginate`] method, falling back to the `pagerSize` as defined in your [site configuration]. - -[`Paginate`]: /methods/page/paginate/ -[site configuration]: /templates/pagination/#configuration - -```go-html-template -{{ $pages := where site.RegularPages "Type" "posts" }} -{{ $paginator := .Paginate $pages }} - -{{ range $paginator.Pages }} -

{{ .LinkTitle }}

-{{ end }} - -{{ with $paginator }} - {{ .PageSize }} -{{ end }} -``` diff --git a/content/en/methods/pager/PagerSize.md b/content/en/methods/pager/PagerSize.md index 623548398..5f5f7d3a8 100644 --- a/content/en/methods/pager/PagerSize.md +++ b/content/en/methods/pager/PagerSize.md @@ -10,7 +10,7 @@ action: signatures: [PAGER.PagerSize] --- -{{< new-in 0.128.0 >}} +{{< new-in 0.128.0 />}} The number of pages per pager is determined by the optional second argument passed to the [`Paginate`] method, falling back to the `pagerSize` as defined in your [site configuration]. diff --git a/content/en/methods/resource/Colors.md b/content/en/methods/resource/Colors.md index 73fe1e7ae..08f63ae3f 100644 --- a/content/en/methods/resource/Colors.md +++ b/content/en/methods/resource/Colors.md @@ -20,11 +20,11 @@ The `Resources.Colors` method returns a slice of the most dominant colors in an Each color is an object with the following methods: ColorHex -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`string`) Returns the [hexadecimal color] value, prefixed with a hash sign. Luminance -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`float64`) Returns the [relative luminance] of the color in the sRGB colorspace in the range [0, 1]. A value of `0` represents the darkest black, while a value of `1` represents the lightest white. {{% note %}} diff --git a/content/en/methods/resource/Data.md b/content/en/methods/resource/Data.md index f62de34d2..b6744d1be 100644 --- a/content/en/methods/resource/Data.md +++ b/content/en/methods/resource/Data.md @@ -17,6 +17,7 @@ The `Data` method on a resource returned by the [`resources.GetRemote`] function ```go-html-template {{ $url := "https://example.org/images/a.jpg" }} +{{ $opts := dict "responseHeaders" (slice "Server") }} {{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} @@ -24,6 +25,7 @@ The `Data` method on a resource returned by the [`resources.GetRemote`] function {{ with .Data }} {{ .ContentLength }} → 42764 {{ .ContentType }} → image/jpeg + {{ .Headers }} → map[Server:[Netlify]] {{ .Status }} → 200 OK {{ .StatusCode }} → 200 {{ .TransferEncoding }} → [] @@ -34,19 +36,30 @@ The `Data` method on a resource returned by the [`resources.GetRemote`] function {{ end }} ``` -ContentLength -: (`int`) The content length in bytes. +###### ContentLength -ContentType -: (`string`) The content type. +(`int`) The content length in bytes. -Status -: (`string`) The HTTP status text. +###### ContentType -StatusCode -: (`int`) The HTTP status code. +(`string`) The content type. -TransferEncoding -: (`string`) The transfer encoding. +###### Headers + +(`map[string][]string`) A map of response headers matching those requested in the [`responseHeaders`] option passed to the `resources.GetRemote` function. The header name matching is case-insensitive. In most cases there will be one value per header key. + +[`responseHeaders`]: /functions/resources/getremote/#responseheaders + +###### Status + +(`string`) The HTTP status text. + +###### StatusCode + +(`int`) The HTTP status code. + +###### TransferEncoding + +(`string`) The transfer encoding. [`resources.GetRemote`]: /functions/resources/getremote/ diff --git a/content/en/methods/resource/Err.md b/content/en/methods/resource/Err.md index 776127caf..34ba6f9bc 100644 --- a/content/en/methods/resource/Err.md +++ b/content/en/methods/resource/Err.md @@ -9,7 +9,7 @@ action: - methods/resource/Data returnType: resource.resourceError signatures: [RESOURCE.Err] -expiryDate: 2026-01-16 # deprecated 2025-01-16 +expiryDate: 2027-01-16 # deprecated 2025-01-16 in v0.141.0 --- {{% deprecated-in 0.141.0 %}} diff --git a/content/en/methods/shortcode/Scratch.md b/content/en/methods/shortcode/Scratch.md index b044a66c8..6dd882e24 100644 --- a/content/en/methods/shortcode/Scratch.md +++ b/content/en/methods/shortcode/Scratch.md @@ -1,14 +1,13 @@ --- title: Scratch -description: Returns a "scratch pad" scoped to the shortcode to store and manipulate data. +description: Returns a "scratch pad" to store and manipulate data, scoped to the current shortcode. categories: [] keywords: [] action: - related: - - functions/collections/NewScratch + related: [] returnType: maps.Scratch signatures: [SHORTCODE.Scratch] -expiryDate: 2025-11-18 # deprecated 2024-11-18 +expiryDate: 2026-11-18 # deprecated 2024-11-18 (soft) --- {{% deprecated-in 0.139.0 %}} @@ -20,14 +19,3 @@ Beginning with v0.139.0 the `SHORTCODE.Scratch` method is aliased to `SHORTCODE. [`SHORTCODE.Store`]: /methods/shortcode/store/ {{% /deprecated-in %}} - -The `Scratch` method within a shortcode creates a [scratch pad](g) to store and manipulate data. The scratch pad is scoped to the shortcode. - -{{% note %}} -With the introduction of the [`newScratch`] function, and the ability to [assign values to template variables] after initialization, the `Scratch` method within a shortcode is obsolete. - -[assign values to template variables]: https://go.dev/doc/go1.11#text/template -[`newScratch`]: /functions/collections/newscratch/ -{{% /note %}} - -{{% include "methods/page/_common/scratch-methods.md" %}} diff --git a/content/en/methods/shortcode/Store.md b/content/en/methods/shortcode/Store.md index 2e6c467ce..8d9a8596b 100644 --- a/content/en/methods/shortcode/Store.md +++ b/content/en/methods/shortcode/Store.md @@ -1,21 +1,22 @@ --- title: Store -description: Returns a "Store pad" scoped to the shortcode to store and manipulate data. +description: Returns a "scratch pad" to store and manipulate data, scoped to the current shortcode. categories: [] keywords: [] action: related: - - functions/collections/NewScratch - methods/page/Store - methods/site/Store - functions/hugo/Store - returnType: maps.Store + - functions/collections/NewScratch + returnType: maps.Scratch signatures: [SHORTCODE.Store] +toc: true --- -{{< new-in 0.139.0 >}} +{{< new-in 0.139.0 />}} -The `Store` method within a shortcode creates a [scratch pad](g) to store and manipulate data. The scratch pad is scoped to the shortcode. +Use the `Store` method to create a [scratch pad](g) to store and manipulate data, scoped to the current shortcode. To create a scratch pad with a different [scope](g), refer to the [scope](#scope) section below. {{% note %}} With the introduction of the [`newScratch`] function, and the ability to [assign values to template variables] after initialization, the `Store` method within a shortcode is mostly obsolete. @@ -24,4 +25,6 @@ With the introduction of the [`newScratch`] function, and the ability to [assign [`newScratch`]: /functions/collections/newScratch/ {{% /note %}} -{{% include "methods/page/_common/scratch-methods.md" %}} +{{% include "_common/store-methods.md" %}} + +{{% include "_common/scratch-pad-scope.md" %}} diff --git a/content/en/methods/shortcode/_index.md b/content/en/methods/shortcode/_index.md index d26366844..1c99adba7 100644 --- a/content/en/methods/shortcode/_index.md +++ b/content/en/methods/shortcode/_index.md @@ -7,6 +7,7 @@ keywords: [] menu: docs: parent: methods +aliases: [/variables/shortcodes] --- Use these methods in your shortcode templates. diff --git a/content/en/methods/site/DisqusShortname.md b/content/en/methods/site/DisqusShortname.md index 0e900ac4e..f6fedf4e9 100644 --- a/content/en/methods/site/DisqusShortname.md +++ b/content/en/methods/site/DisqusShortname.md @@ -7,7 +7,7 @@ action: related: [] returnType: string signatures: [SITE.DisqusShortname] -expiryDate: 2024-10-30 # deprecated 2023-10-30 +expiryDate: 2025-10-30 # deprecated 2023-10-30 in v0.120.0 --- {{% deprecated-in 0.120.0 %}} diff --git a/content/en/methods/site/GoogleAnalytics.md b/content/en/methods/site/GoogleAnalytics.md index c58974452..f87e1787e 100644 --- a/content/en/methods/site/GoogleAnalytics.md +++ b/content/en/methods/site/GoogleAnalytics.md @@ -7,7 +7,7 @@ action: related: [] returnType: string signatures: [SITE.GoogleAnalytics] -expiryDate: 2024-10-30 # deprecated 2023-10-30 +expiryDate: 2025-10-30 # deprecated 2023-10-30 in v0.120.0 --- {{% deprecated-in 0.120.0 %}} diff --git a/content/en/methods/site/IsDevelopment.md b/content/en/methods/site/IsDevelopment.md index 6f443316b..51783efb9 100644 --- a/content/en/methods/site/IsDevelopment.md +++ b/content/en/methods/site/IsDevelopment.md @@ -7,7 +7,7 @@ action: related: [] returnType: bool signatures: [SITE.IsDevelopment] -expiryDate: 2024-10-30 # deprecated 2023-10-30 +expiryDate: 2025-10-30 # deprecated 2023-10-30 in v0.120.0 --- {{% deprecated-in 0.120.0 %}} @@ -15,7 +15,3 @@ Use [`hugo.IsDevelopment`] instead. [`hugo.IsDevelopment`]: /functions/hugo/isdevelopment/ {{% /deprecated-in %}} - -```go-html-template -{{ .Site.IsDevelopment }} → true/false -``` diff --git a/content/en/methods/site/IsMultiLingual.md b/content/en/methods/site/IsMultiLingual.md index 7ec938812..10968f14d 100644 --- a/content/en/methods/site/IsMultiLingual.md +++ b/content/en/methods/site/IsMultiLingual.md @@ -7,7 +7,7 @@ action: related: [] returnType: bool signatures: [SITE.IsMultiLingual] -expiryDate: 2025-03-16 # deprecated 2024-03-16 +expiryDate: 2026-03-16 # deprecated 2024-03-16 in 0.124.0 --- {{% deprecated-in 0.124.0 %}} @@ -15,27 +15,3 @@ Use [`hugo.IsMultilingual`] instead. [`hugo.IsMultilingual`]: /functions/hugo/ismultilingual/ {{% /deprecated-in %}} - -Site configuration: - -{{< code-toggle file=hugo >}} -defaultContentLanguage = 'de' -defaultContentLanguageInSubdir = true -[languages] - [languages.de] - languageCode = 'de-DE' - languageName = 'Deutsch' - title = 'Projekt Dokumentation' - weight = 1 - [languages.en] - languageCode = 'en-US' - languageName = 'English' - title = 'Project Documentation' - weight = 2 -{{< /code-toggle >}} - -Template: - -```go-html-template -{{ .Site.IsMultiLingual }} → true -``` diff --git a/content/en/methods/site/IsServer.md b/content/en/methods/site/IsServer.md index a688c553a..8fb5b7bf6 100644 --- a/content/en/methods/site/IsServer.md +++ b/content/en/methods/site/IsServer.md @@ -7,7 +7,7 @@ action: related: [] returnType: bool signatures: [SITE.IsServer] -expiryDate: 2024-10-30 # deprecated 2023-10-30 +expiryDate: 2025-10-30 # deprecated 2023-10-30 in v0.120.0 --- {{% deprecated-in 0.120.0 %}} @@ -15,7 +15,3 @@ Use [`hugo.IsServer`] instead. [`hugo.IsServer`]: /functions/hugo/isserver/ {{% /deprecated-in %}} - -```go-html-template -{{ .Site.IsServer }} → true/false -``` diff --git a/content/en/methods/site/LastChange.md b/content/en/methods/site/LastChange.md index 65925c0eb..f6017df04 100644 --- a/content/en/methods/site/LastChange.md +++ b/content/en/methods/site/LastChange.md @@ -7,7 +7,7 @@ action: related: [] returnType: time.Time signatures: [SITE.LastChange] -expiryDate: 2025-02-19 # deprecated 2024-02-19 +expiryDate: 2026-02-19 # deprecated 2024-02-19 in v0.123.0 --- {{% deprecated-in 0.123.0 %}} @@ -15,14 +15,3 @@ Use [`.Site.Lastmod`] instead. [`.Site.Lastmod`]: /methods/site/lastmod/ {{% /deprecated-in %}} - -The `LastChange` method on a `Site` object returns a [`time.Time`] value. Use this with time [functions] and [methods]. For example: - -```go-html-template -{{ .Site.LastChange | time.Format ":date_long" }} → January 31, 2024 - -``` - -[`time.Time`]: https://pkg.go.dev/time#Time -[functions]: /functions/time/ -[methods]: /methods/time/ diff --git a/content/en/methods/site/Lastmod.md b/content/en/methods/site/Lastmod.md index 081481956..f1663db49 100644 --- a/content/en/methods/site/Lastmod.md +++ b/content/en/methods/site/Lastmod.md @@ -9,7 +9,7 @@ action: signatures: [SITE.Lastmod] --- -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} The `Lastmod` method on a `Site` object returns a [`time.Time`] value. Use this with time [functions] and [methods]. For example: diff --git a/content/en/methods/site/Store.md b/content/en/methods/site/Store.md index 8a6c5b86e..dcc3a0bed 100644 --- a/content/en/methods/site/Store.md +++ b/content/en/methods/site/Store.md @@ -1,7 +1,6 @@ --- title: Store -linktitle: site.Store -description: Returns a persistent "scratch pad" on the given site to store and manipulate data. +description: Returns a "scratch pad" to store and manipulate data, scoped to the current site. categories: [] keywords: [] action: @@ -14,12 +13,9 @@ action: toc: true --- -{{< new-in 0.139.0 >}} +{{< new-in 0.139.0 />}} -The `Store` method on a `Site` object creates a persistent [scratch pad](g) to store and manipulate data. To create a locally scoped scratch pad that is not attached to a `Site` object, use the [`newScratch`] function. - -[`Scratch`]: /methods/site/scratch/ -[`newScratch`]: /functions/collections/newscratch/ +Use the `Store` method on a `Site` object to create a [scratch pad](g) to store and manipulate data, scoped to the current site. To create a scratch pad with a different [scope](g), refer to the [scope](#scope) section below. ## Methods @@ -104,6 +100,8 @@ Removes the given key. {{ site.Store.Delete "greeting" }} ``` +{{% include "_common/scratch-pad-scope.md" %}} + ## Determinate values The `Store` method is often used to set scratch pad values within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are indeterminate until Hugo renders the page content. diff --git a/content/en/methods/taxonomy/Page.md b/content/en/methods/taxonomy/Page.md index e148ac5c7..039719b93 100644 --- a/content/en/methods/taxonomy/Page.md +++ b/content/en/methods/taxonomy/Page.md @@ -9,7 +9,7 @@ action: signatures: [TAXONOMY.Page] --- -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} This `TAXONOMY` method returns nil if the taxonomy has no terms, so you must code defensively: diff --git a/content/en/myshowcase/index.md b/content/en/myshowcase/index.md index 969095d12..abb4ad6b2 100644 --- a/content/en/myshowcase/index.md +++ b/content/en/myshowcase/index.md @@ -2,6 +2,7 @@ title: Myshowcase date: 2021-01-14 +draft: true description: "A short description of this page." diff --git a/content/en/news/_index.md b/content/en/news/_index.md index a1959bd1d..c170a69a4 100644 --- a/content/en/news/_index.md +++ b/content/en/news/_index.md @@ -1,7 +1,9 @@ --- title: News +description: Stay up-to-date with the latest news and announcements. outputs: - html - rss aliases: [/release-notes/] +weight: 10 --- diff --git a/content/en/quick-reference/_index.md b/content/en/quick-reference/_index.md index b5f434e2d..f2673ba37 100644 --- a/content/en/quick-reference/_index.md +++ b/content/en/quick-reference/_index.md @@ -1,7 +1,7 @@ --- title: Quick reference guides -linkTitle: In this section -description: Quick reference guides to Hugo's features, functions, and methods. +linktitle: Quick reference +description: Use these quick reference guides for quick access to key information. categories: [] keywords: [] menu: @@ -13,4 +13,4 @@ weight: 10 showSectionMenu: false --- -Quick reference guides to Hugo's features, functions, and methods. +{{% param description %}} diff --git a/content/en/getting-started/glossary/_index.md b/content/en/quick-reference/glossary/_index.md similarity index 58% rename from content/en/getting-started/glossary/_index.md rename to content/en/quick-reference/glossary/_index.md index 001d80fe0..1b5111e8e 100644 --- a/content/en/getting-started/glossary/_index.md +++ b/content/en/quick-reference/glossary/_index.md @@ -1,13 +1,15 @@ --- -title: Glossary of terms +title: Glossary description: Terms commonly used throughout the documentation. -categories: [getting started] +categories: [quick-reference] keywords: [glossary] +hide_in_this_section: true menu: docs: - parent: getting-started - weight: 80 -weight: 80 + parent: quick-reference + weight: 40 +aliases: [/getting-started/glossary/] +weight: 40 layout: single build: render: always diff --git a/content/en/quick-reference/glossary/action.md b/content/en/quick-reference/glossary/action.md new file mode 100644 index 000000000..ced877327 --- /dev/null +++ b/content/en/quick-reference/glossary/action.md @@ -0,0 +1,5 @@ +--- +title: action +--- + +See [_template action_](g). diff --git a/content/en/quick-reference/glossary/archetype.md b/content/en/quick-reference/glossary/archetype.md new file mode 100644 index 000000000..231089c56 --- /dev/null +++ b/content/en/quick-reference/glossary/archetype.md @@ -0,0 +1,6 @@ +--- +title: archetype +details: /content-management/archetypes +--- + +An _archetype_ is a template for new content. diff --git a/content/en/quick-reference/glossary/argument.md b/content/en/quick-reference/glossary/argument.md new file mode 100644 index 000000000..912951d2b --- /dev/null +++ b/content/en/quick-reference/glossary/argument.md @@ -0,0 +1,5 @@ +--- +title: argument +--- + +An _argument_ is a [_scalar_](g), [_array_](g), [_slice_](g), [_map_](g), or [_object_](g) passed to a [_function_](g), [_method_](g), or [_shortcode_](g). diff --git a/content/en/quick-reference/glossary/array.md b/content/en/quick-reference/glossary/array.md new file mode 100644 index 000000000..0df45f212 --- /dev/null +++ b/content/en/quick-reference/glossary/array.md @@ -0,0 +1,6 @@ +--- +title: array +reference: https://go.dev/ref/spec#Array_types +--- + +An _array_ is a numbered sequence of [_elements_](g). Unlike Go's [_slice_](g) data type, an array has a fixed length. Elements within an array can be [_scalars_](g), slices, [_maps_](g), pages, or other arrays. diff --git a/content/en/quick-reference/glossary/asset-pipeline.md b/content/en/quick-reference/glossary/asset-pipeline.md new file mode 100644 index 000000000..5f3264a6e --- /dev/null +++ b/content/en/quick-reference/glossary/asset-pipeline.md @@ -0,0 +1,5 @@ +--- +title: asset pipeline +--- + +An _asset pipeline_ is a system that automates and optimizes the handling of static assets like images, stylesheets, and JavaScript files. diff --git a/content/en/getting-started/glossary/bool.md b/content/en/quick-reference/glossary/bool.md similarity index 51% rename from content/en/getting-started/glossary/bool.md rename to content/en/quick-reference/glossary/bool.md index 7fd7628ca..a4f33b5b9 100644 --- a/content/en/getting-started/glossary/bool.md +++ b/content/en/quick-reference/glossary/bool.md @@ -2,4 +2,4 @@ title: bool --- -See [boolean](g). +See [_boolean_](g). diff --git a/content/en/quick-reference/glossary/boolean.md b/content/en/quick-reference/glossary/boolean.md new file mode 100644 index 000000000..e727c40b0 --- /dev/null +++ b/content/en/quick-reference/glossary/boolean.md @@ -0,0 +1,5 @@ +--- +title: boolean +--- + +A _boolean_ is a data type with two possible values, either `true` or `false`. diff --git a/content/en/quick-reference/glossary/branch-bundle.md b/content/en/quick-reference/glossary/branch-bundle.md new file mode 100644 index 000000000..d5688ba0b --- /dev/null +++ b/content/en/quick-reference/glossary/branch-bundle.md @@ -0,0 +1,6 @@ +--- +title: branch bundle +reference: /content-management/page-bundles +--- + +A _branch bundle_ is a top-level content directory or any content directory containing an `_index.md` file. Analogous to a physical branch, a branch bundle may have descendants including [_leaf bundles_](g) and other branch bundles. A branch bundle may also contain [_page resources_](g) such as images. diff --git a/content/en/quick-reference/glossary/build.md b/content/en/quick-reference/glossary/build.md new file mode 100644 index 000000000..79b7ec74a --- /dev/null +++ b/content/en/quick-reference/glossary/build.md @@ -0,0 +1,5 @@ +--- +title: build +--- + +To _build_ a site is to generate HTML files and assets such as images, CSS files, and JavaScript files. The build process includes rendering and resource transformations. diff --git a/content/en/getting-started/glossary/bundle.md b/content/en/quick-reference/glossary/bundle.md similarity index 100% rename from content/en/getting-started/glossary/bundle.md rename to content/en/quick-reference/glossary/bundle.md diff --git a/content/en/quick-reference/glossary/cache.md b/content/en/quick-reference/glossary/cache.md new file mode 100644 index 000000000..a86068e4a --- /dev/null +++ b/content/en/quick-reference/glossary/cache.md @@ -0,0 +1,5 @@ +--- +title: cache +--- + +A _cache_ is a software component that stores data so that future requests for the same data are faster. diff --git a/content/en/quick-reference/glossary/chain.md b/content/en/quick-reference/glossary/chain.md new file mode 100644 index 000000000..bbbc14f49 --- /dev/null +++ b/content/en/quick-reference/glossary/chain.md @@ -0,0 +1,5 @@ +--- +title: chain +--- + +To _chain_ is to connect one or more [_identifiers_](g) with a dot. An identifier can represent a [_method_](g), [_object_](g), or [_field_](g). For example, `.Site.Params.author.name` or `.Date.UTC.Hour`. diff --git a/content/en/quick-reference/glossary/cjk.md b/content/en/quick-reference/glossary/cjk.md new file mode 100644 index 000000000..05a294d44 --- /dev/null +++ b/content/en/quick-reference/glossary/cjk.md @@ -0,0 +1,5 @@ +--- +title: CJK +--- + +_CJK_ is a collective term for the Chinese, Japanese, and Korean languages. diff --git a/content/en/quick-reference/glossary/cli.md b/content/en/quick-reference/glossary/cli.md new file mode 100644 index 000000000..61269dc8e --- /dev/null +++ b/content/en/quick-reference/glossary/cli.md @@ -0,0 +1,5 @@ +--- +title: CLI +--- + +_CLI_ is an abbreviation of Command Line Interface. diff --git a/content/en/quick-reference/glossary/collection.md b/content/en/quick-reference/glossary/collection.md new file mode 100644 index 000000000..30e1ef805 --- /dev/null +++ b/content/en/quick-reference/glossary/collection.md @@ -0,0 +1,5 @@ +--- +title: collection +--- + +A _collection_ is an [_array_](g), [_slice_](g), or [_map_](g). diff --git a/content/en/quick-reference/glossary/content-adapter.md b/content/en/quick-reference/glossary/content-adapter.md new file mode 100644 index 000000000..974e61dca --- /dev/null +++ b/content/en/quick-reference/glossary/content-adapter.md @@ -0,0 +1,6 @@ +--- +title: content adapter +reference: /content-management/content-adapters +--- + +A _content adapter_ is a template that dynamically creates pages when building a site. For example, use a content adapter to create pages from a remote data source such as JSON, TOML, YAML, or XML. diff --git a/content/en/quick-reference/glossary/content-format.md b/content/en/quick-reference/glossary/content-format.md new file mode 100644 index 000000000..ea459deb7 --- /dev/null +++ b/content/en/quick-reference/glossary/content-format.md @@ -0,0 +1,6 @@ +--- +title: content format +reference: /content-management/formats +--- + +A _content format_ is a markup language for creating content. Typically Markdown, but may also be HTML, AsciiDoc, Org, Pandoc, or reStructuredText. diff --git a/content/en/quick-reference/glossary/content-type.md b/content/en/quick-reference/glossary/content-type.md new file mode 100644 index 000000000..b7ebc5be4 --- /dev/null +++ b/content/en/quick-reference/glossary/content-type.md @@ -0,0 +1,6 @@ +--- +title: content type +reference: /content-management/types +--- + +A _content type_ is a classification of content inferred from the top-level directory name or the `type` set in [front matter](g). Pages in the root of the `content` directory, including the home page, are of type "page". Accessed via `.Page.Type` in [_templates_](g). diff --git a/content/en/quick-reference/glossary/content-view.md b/content/en/quick-reference/glossary/content-view.md new file mode 100644 index 000000000..009981cb5 --- /dev/null +++ b/content/en/quick-reference/glossary/content-view.md @@ -0,0 +1,6 @@ +--- +title: content view +reference: /templates/content-view +--- + +A _content view_ is a template called with the [`Render`](/methods/page/render/) method on a `Page` object. diff --git a/content/en/quick-reference/glossary/context.md b/content/en/quick-reference/glossary/context.md new file mode 100644 index 000000000..75afc709d --- /dev/null +++ b/content/en/quick-reference/glossary/context.md @@ -0,0 +1,6 @@ +--- +title: context +reference: /templates/introduction/#context +--- + +Represented by a dot (`.`) within a [_template action_](g), _context_ is the current location in a data structure. For example, while iterating over a [_collection_](g) of pages, the context within each iteration is the page's data structure. The context received by each template depends on template type and/or how it was called. diff --git a/content/en/quick-reference/glossary/default-sort-order.md b/content/en/quick-reference/glossary/default-sort-order.md new file mode 100644 index 000000000..2b5588d56 --- /dev/null +++ b/content/en/quick-reference/glossary/default-sort-order.md @@ -0,0 +1,5 @@ +--- +title: default sort order +--- + +The _default sort order_ is the default order in which Hugo sorts page collections: by [_weight_](g), then by date (descending), then by link title, and then by file path. diff --git a/content/en/quick-reference/glossary/duration.md b/content/en/quick-reference/glossary/duration.md new file mode 100644 index 000000000..21fd3c832 --- /dev/null +++ b/content/en/quick-reference/glossary/duration.md @@ -0,0 +1,5 @@ +--- +title: duration +--- + +A _duration_ is a data type that represent a length of time, expressed using units such as seconds (represented by `s`), minutes (represented by `m`), and hours (represented by `h`). For example, `42s` means 42 seconds, `6m7s` means 6 minutes and 7 seconds, and `6h7m42s` means 6 hours, 7 minutes, and 42 seconds. diff --git a/content/en/quick-reference/glossary/element.md b/content/en/quick-reference/glossary/element.md new file mode 100644 index 000000000..39f5df656 --- /dev/null +++ b/content/en/quick-reference/glossary/element.md @@ -0,0 +1,5 @@ +--- +title: element +--- + +An _element_ is a member of a [_slice_](g) or [_array_](g). diff --git a/content/en/quick-reference/glossary/embedded-template.md b/content/en/quick-reference/glossary/embedded-template.md new file mode 100644 index 000000000..3a0871690 --- /dev/null +++ b/content/en/quick-reference/glossary/embedded-template.md @@ -0,0 +1,5 @@ +--- +title: embedded template +--- + +An _embedded template_ is a built-in component within the Hugo application. This includes features like [_partials_](g), [_shortcodes_](g), and [_render hooks_](g) that provide pre-defined structures or functionalities for creating website content. diff --git a/content/en/getting-started/glossary/environment.md b/content/en/quick-reference/glossary/environment.md similarity index 70% rename from content/en/getting-started/glossary/environment.md rename to content/en/quick-reference/glossary/environment.md index 9479605cd..6605c2263 100644 --- a/content/en/getting-started/glossary/environment.md +++ b/content/en/quick-reference/glossary/environment.md @@ -2,7 +2,7 @@ title: environment --- -Typically one of `development`, `staging`, or `production`, each environment may exhibit different behavior depending on configuration and template logic. For example, in a production environment you might minify and fingerprint CSS, but that probably doesn't make sense in a development environment. +Typically one of `development`, `staging`, or `production`, each _environment_ may exhibit different behavior depending on configuration and template logic. For example, in a production environment you might minify and fingerprint CSS, but that probably doesn't make sense in a development environment. When running the built-in development server with the `hugo server` command, the environment is set to `development`. When building your site with the `hugo` command, the environment is set to `production`. To override the environment value, use the `--environment` command line flag or the `HUGO_ENVIRONMENT` environment variable. diff --git a/content/en/quick-reference/glossary/field.md b/content/en/quick-reference/glossary/field.md new file mode 100644 index 000000000..a32eb3a6b --- /dev/null +++ b/content/en/quick-reference/glossary/field.md @@ -0,0 +1,5 @@ +--- +title: field +--- + +A _field_ is a predefined key-value pair in front matter such as `date` or `title`. diff --git a/content/en/quick-reference/glossary/flag.md b/content/en/quick-reference/glossary/flag.md new file mode 100644 index 000000000..e7b6c5746 --- /dev/null +++ b/content/en/quick-reference/glossary/flag.md @@ -0,0 +1,6 @@ +--- +title: flag +reference: /commands/hugo +--- + +A _flag_ is an option passed to a command-line program, beginning with one or two hyphens. diff --git a/content/en/getting-started/glossary/float.md b/content/en/quick-reference/glossary/float.md similarity index 100% rename from content/en/getting-started/glossary/float.md rename to content/en/quick-reference/glossary/float.md diff --git a/content/en/quick-reference/glossary/floating-point.md b/content/en/quick-reference/glossary/floating-point.md new file mode 100644 index 000000000..38ba9f012 --- /dev/null +++ b/content/en/quick-reference/glossary/floating-point.md @@ -0,0 +1,5 @@ +--- +title: floating point +--- + +The term _floating point_ refers to a numeric data type with a fractional component. For example, `3.14159`. diff --git a/content/en/quick-reference/glossary/fragment.md b/content/en/quick-reference/glossary/fragment.md new file mode 100644 index 000000000..57ef1b4ef --- /dev/null +++ b/content/en/quick-reference/glossary/fragment.md @@ -0,0 +1,5 @@ +--- +title: fragment +--- + +A _fragment_ is the final segment of a URL, beginning with a hash (`#`) mark, that references an `id` attribute of an HTML element on the page. diff --git a/content/en/quick-reference/glossary/front-matter.md b/content/en/quick-reference/glossary/front-matter.md new file mode 100644 index 000000000..5a3cd3040 --- /dev/null +++ b/content/en/quick-reference/glossary/front-matter.md @@ -0,0 +1,6 @@ +--- +title: front matter +reference: /content-management/front-matter +--- + +The term _front matter_ refers to the metadata at the beginning of each content page, separated from the content by format-specific delimiters. diff --git a/content/en/quick-reference/glossary/function.md b/content/en/quick-reference/glossary/function.md new file mode 100644 index 000000000..a7da52cd2 --- /dev/null +++ b/content/en/quick-reference/glossary/function.md @@ -0,0 +1,6 @@ +--- +title: function +reference: /functions +--- + +Used within a [_template action_](g), a _function_ takes one or more [_arguments_](g) and returns a value. Unlike [_methods_](g), functions are not associated with an [_object_](g). diff --git a/content/en/quick-reference/glossary/glob.md b/content/en/quick-reference/glossary/glob.md new file mode 100644 index 000000000..5243d1ef9 --- /dev/null +++ b/content/en/quick-reference/glossary/glob.md @@ -0,0 +1,6 @@ +--- +title: glob +reference: https://github.com/gobwas/glob?tab=readme-ov-file#example +--- + +A _glob_ is a pattern used to match filenames and paths. It's a shorthand for specifying a set of files, making it easier to work with multiple files at once. diff --git a/content/en/quick-reference/glossary/global-resource.md b/content/en/quick-reference/glossary/global-resource.md new file mode 100644 index 000000000..a4df65f67 --- /dev/null +++ b/content/en/quick-reference/glossary/global-resource.md @@ -0,0 +1,5 @@ +--- +title: global resource +--- + +A _global resource_ is file within the `assets` directory, or within any directory mounted to the `assets` directory. diff --git a/content/en/quick-reference/glossary/headless-bundle.md b/content/en/quick-reference/glossary/headless-bundle.md new file mode 100644 index 000000000..ac7bf79c8 --- /dev/null +++ b/content/en/quick-reference/glossary/headless-bundle.md @@ -0,0 +1,6 @@ +--- +title: headless bundle +reference: /content-management/build-options/ +--- + +A _headless bundle_ is an unpublished [_leaf bundle_](g) or an unpublished [_branch bundle_](g) whose content and resources you can include in other pages. diff --git a/content/en/quick-reference/glossary/i18n.md b/content/en/quick-reference/glossary/i18n.md new file mode 100644 index 000000000..168828aa8 --- /dev/null +++ b/content/en/quick-reference/glossary/i18n.md @@ -0,0 +1,5 @@ +--- +title: i18n +--- + +See [_internationalization_](g). diff --git a/content/en/quick-reference/glossary/identifier.md b/content/en/quick-reference/glossary/identifier.md new file mode 100644 index 000000000..f53472fa7 --- /dev/null +++ b/content/en/quick-reference/glossary/identifier.md @@ -0,0 +1,5 @@ +--- +title: identifier +--- + +An _identifier_ is a string that represents a variable, method, object, or field. It must conform to Go's [language specification](https://go.dev/ref/spec#Identifiers), beginning with a letter or underscore, followed by zero or more letters, digits, or underscores. diff --git a/content/en/getting-started/glossary/int.md b/content/en/quick-reference/glossary/int.md similarity index 50% rename from content/en/getting-started/glossary/int.md rename to content/en/quick-reference/glossary/int.md index 1077ee5e0..0896f1ce7 100644 --- a/content/en/getting-started/glossary/int.md +++ b/content/en/quick-reference/glossary/int.md @@ -2,4 +2,4 @@ title: int --- -See [integer](g). +See [_integer_](g). diff --git a/content/en/quick-reference/glossary/integer.md b/content/en/quick-reference/glossary/integer.md new file mode 100644 index 000000000..0af821300 --- /dev/null +++ b/content/en/quick-reference/glossary/integer.md @@ -0,0 +1,5 @@ +--- +title: integer +--- + +An _integer_ is a numeric data type without a fractional component. For example, `42`. diff --git a/content/en/quick-reference/glossary/internationalization.md b/content/en/quick-reference/glossary/internationalization.md new file mode 100644 index 000000000..4e8b0199c --- /dev/null +++ b/content/en/quick-reference/glossary/internationalization.md @@ -0,0 +1,5 @@ +--- +title: internationalization +--- + +The term _internationalization_ refers to software design and development efforts that enable [_localization_](g). diff --git a/content/en/quick-reference/glossary/interpreted-string-literal.md b/content/en/quick-reference/glossary/interpreted-string-literal.md new file mode 100644 index 000000000..a5d2cd326 --- /dev/null +++ b/content/en/quick-reference/glossary/interpreted-string-literal.md @@ -0,0 +1,6 @@ +--- +title: interpreted string literal +reference: https://go.dev/ref/spec#String_literals +--- + +An _interpreted string literal_ is a character sequences between double quotes, as in "foo". Within the quotes, any character may appear except a newline and an unescaped double quote. The text between the quotes forms the value of the literal, with backslash escapes interpreted. diff --git a/content/en/quick-reference/glossary/interval.md b/content/en/quick-reference/glossary/interval.md new file mode 100644 index 000000000..f1ce7bc93 --- /dev/null +++ b/content/en/quick-reference/glossary/interval.md @@ -0,0 +1,11 @@ +--- +title: interval +--- + +An [_interval_](https://en.wikipedia.org/wiki/Interval_(mathematics)) is a range of numbers between two endpoints: closed, open, or half-open. + +- A _closed interval_, denoted by brackets, includes its endpoints. For example, [0, 1] is the interval where `0 <= x <= 1`. + +- An _open interval_, denoted by parentheses, excludes its endpoints. For example, (0, 1) is the interval where `0 < x < 1`. + +- A _half-open interval_ includes only one of its endpoints. For example, (0, 1] is the _left-open_ interval where `0 < x <= 1`, while [0, 1) is the _right-open_ interval where `0 <= x < 1`. diff --git a/content/en/quick-reference/glossary/kind.md b/content/en/quick-reference/glossary/kind.md new file mode 100644 index 000000000..a214dfbf1 --- /dev/null +++ b/content/en/quick-reference/glossary/kind.md @@ -0,0 +1,5 @@ +--- +title: kind +--- + +See [_page kind_](g). diff --git a/content/en/quick-reference/glossary/l10n.md b/content/en/quick-reference/glossary/l10n.md new file mode 100644 index 000000000..013d0ab55 --- /dev/null +++ b/content/en/quick-reference/glossary/l10n.md @@ -0,0 +1,5 @@ +--- +title: l10n +--- + +See [_localization_](g). diff --git a/content/en/getting-started/glossary/layout.md b/content/en/quick-reference/glossary/layout.md similarity index 52% rename from content/en/getting-started/glossary/layout.md rename to content/en/quick-reference/glossary/layout.md index 706e9439d..a4e6b33f1 100644 --- a/content/en/getting-started/glossary/layout.md +++ b/content/en/quick-reference/glossary/layout.md @@ -2,4 +2,4 @@ title: layout --- -See [template](g). +See [_template_](g). diff --git a/content/en/quick-reference/glossary/leaf-bundle.md b/content/en/quick-reference/glossary/leaf-bundle.md new file mode 100644 index 000000000..aa41384ee --- /dev/null +++ b/content/en/quick-reference/glossary/leaf-bundle.md @@ -0,0 +1,6 @@ +--- +title: leaf bundle +reference: /content-management/page-bundles/ +--- + +A _leaf bundle_ is a directory that contains an `index.md` file and zero or more [_resources_](g). Analogous to a physical leaf, a leaf bundle is at the end of a [_branch bundle_](g). It has no descendants. diff --git a/content/en/quick-reference/glossary/lexer.md b/content/en/quick-reference/glossary/lexer.md new file mode 100644 index 000000000..a46223148 --- /dev/null +++ b/content/en/quick-reference/glossary/lexer.md @@ -0,0 +1,5 @@ +--- +title: lexer +--- + +A _lexer_ is a software component that identifies keywords, identifiers, operators, numbers, and other basic building blocks of a programming language within the input text. diff --git a/content/en/quick-reference/glossary/list-page.md b/content/en/quick-reference/glossary/list-page.md new file mode 100644 index 000000000..b58ee4a35 --- /dev/null +++ b/content/en/quick-reference/glossary/list-page.md @@ -0,0 +1,5 @@ +--- +title: list page +--- + +A list page is any [_page kind_](g) that receives a page [_collection_](g) in [_context_](g). This includes the home page, [section pages](g), [taxonomy pages](g), and [term pages](g). diff --git a/content/en/quick-reference/glossary/list-template.md b/content/en/quick-reference/glossary/list-template.md new file mode 100644 index 000000000..9a12a275f --- /dev/null +++ b/content/en/quick-reference/glossary/list-template.md @@ -0,0 +1,5 @@ +--- +title: list template +--- + +A _list template_ is any [_template_](g) that renders a [_list page_](g). This includes home, [_section_](g), [_taxonomy_](g), and [_term_](g) templates. diff --git a/content/en/quick-reference/glossary/localization.md b/content/en/quick-reference/glossary/localization.md new file mode 100644 index 000000000..01f450635 --- /dev/null +++ b/content/en/quick-reference/glossary/localization.md @@ -0,0 +1,6 @@ +--- +title: localization +reference: /content-management/multilingual/ +--- + +The term _localization_ refers to the process of adapting a site to meet language and regional requirements. This includes translations, date formats, number formats, currency formats, and collation order. diff --git a/content/en/quick-reference/glossary/logical-path.md b/content/en/quick-reference/glossary/logical-path.md new file mode 100644 index 000000000..aa5ce9374 --- /dev/null +++ b/content/en/quick-reference/glossary/logical-path.md @@ -0,0 +1,8 @@ +--- +title: logical path +reference: /methods/page/path/#examples +--- + +{{< new-in 0.123.0 />}} + +A _logical path_ is a page or page resource identifier derived from the file path, excluding its extension and language identifier. This value is neither a file path nor a URL. Starting with a file path relative to the `content` directory, Hugo determines the logical path by stripping the file extension and language identifier, converting to lower case, then replacing spaces with hyphens. diff --git a/content/en/quick-reference/glossary/map.md b/content/en/quick-reference/glossary/map.md new file mode 100644 index 000000000..b4605d20b --- /dev/null +++ b/content/en/quick-reference/glossary/map.md @@ -0,0 +1,6 @@ +--- +title: map +reference: https://go.dev/ref/spec#Map_types +--- + +A _map_ is an unordered group of elements, each indexed by a unique key. diff --git a/content/en/quick-reference/glossary/markdown-attribute.md b/content/en/quick-reference/glossary/markdown-attribute.md new file mode 100644 index 000000000..ccbcefd66 --- /dev/null +++ b/content/en/quick-reference/glossary/markdown-attribute.md @@ -0,0 +1,6 @@ +--- +title: Markdown attribute +reference: /content-management/markdown-attributes/ +--- + +A _Markdown attribute_ is a key-value pair attached to a Markdown element. These attributes are commonly used to add HTML attributes, like `class` and `id`, to the element when it's rendered into HTML. They provide a way to extend the basic Markdown syntax and add more semantic meaning or styling hooks to your content. diff --git a/content/en/quick-reference/glossary/marshal.md b/content/en/quick-reference/glossary/marshal.md new file mode 100644 index 000000000..3bb1623f5 --- /dev/null +++ b/content/en/quick-reference/glossary/marshal.md @@ -0,0 +1,6 @@ +--- +title: marshal +reference: /functions/transform/remarshal/ +--- + +To _marshal_ is to transform a data structure into a serialized object. For example, transforming a [_map_](g) into a JSON string. diff --git a/content/en/quick-reference/glossary/method.md b/content/en/quick-reference/glossary/method.md new file mode 100644 index 000000000..b7618bbf9 --- /dev/null +++ b/content/en/quick-reference/glossary/method.md @@ -0,0 +1,5 @@ +--- +title: method +--- + +Used within a [_template action_](g) and associated with an [_object_](g), a _method_ takes zero or more [_arguments_](g) and either returns a value or performs an action. For example, `.IsHome` is a method on the `.Page` object which returns `true` if the current page is the home page. See also [_function_](g). diff --git a/content/en/quick-reference/glossary/module.md b/content/en/quick-reference/glossary/module.md new file mode 100644 index 000000000..90c8ff80b --- /dev/null +++ b/content/en/quick-reference/glossary/module.md @@ -0,0 +1,6 @@ +--- +title: module +reference: /hugo-modules/ +--- + +A _module_ is a packaged combination of [_archetypes_](g), assets, content, data, [_templates_](g), translation tables, static files, or configuration settings. A module may serve as the basis for a new site, or to augment an existing site. diff --git a/content/en/quick-reference/glossary/node.md b/content/en/quick-reference/glossary/node.md new file mode 100644 index 000000000..b722e07c8 --- /dev/null +++ b/content/en/quick-reference/glossary/node.md @@ -0,0 +1,5 @@ +--- +title: node +--- + +A _node_ is a class of [_page kinds_](g) including `home`, `section`, `taxonomy`, and `term`. diff --git a/content/en/getting-started/glossary/noop.md b/content/en/quick-reference/glossary/noop.md similarity index 100% rename from content/en/getting-started/glossary/noop.md rename to content/en/quick-reference/glossary/noop.md diff --git a/content/en/quick-reference/glossary/object.md b/content/en/quick-reference/glossary/object.md new file mode 100644 index 000000000..216609d0d --- /dev/null +++ b/content/en/quick-reference/glossary/object.md @@ -0,0 +1,5 @@ +--- +title: object +--- + +An _object_ is a data structure with or without associated [_methods_](g). diff --git a/content/en/quick-reference/glossary/ordered-taxonomy.md b/content/en/quick-reference/glossary/ordered-taxonomy.md new file mode 100644 index 000000000..7e0e2763d --- /dev/null +++ b/content/en/quick-reference/glossary/ordered-taxonomy.md @@ -0,0 +1,8 @@ +--- +title: ordered taxonomy +--- + +Created by invoking the [`Alphabetical`] or [`ByCount`] method on a [`Taxonomy`](g) object, which is a [_map_](g), an _ordered taxonomy_ is a [_slice_](g), where each element is an object that contains the [_term_](g) and a slice of its [weighted pages](g). + +[`Alphabetical`]: /methods/taxonomy/alphabetical/ +[`ByCount`]: /methods/taxonomy/bycount/ diff --git a/content/en/quick-reference/glossary/output-format.md b/content/en/quick-reference/glossary/output-format.md new file mode 100644 index 000000000..9004dc223 --- /dev/null +++ b/content/en/quick-reference/glossary/output-format.md @@ -0,0 +1,6 @@ +--- +title: output format +reference: /templates/output-formats/ +--- + +An _output format_ is a collection of settings that defines how Hugo renders a file when building a site. For example, `html`, `rss`, and `json` are built-in output formats. You can create multiple output formats and control their generation based on [page kind](g), or by enabling one or more output formats for specific pages. diff --git a/content/en/quick-reference/glossary/page-bundle.md b/content/en/quick-reference/glossary/page-bundle.md new file mode 100644 index 000000000..af76da2aa --- /dev/null +++ b/content/en/quick-reference/glossary/page-bundle.md @@ -0,0 +1,6 @@ +--- +title: page bundle +reference: /content-management/page-bundles/ +--- + +A _page bundle_ is a directory that encapsulates both content and associated [_resources_](g). There are two types of page bundles: [_leaf bundles_](g) and [_branch bundles_](g). diff --git a/content/en/quick-reference/glossary/page-collection.md b/content/en/quick-reference/glossary/page-collection.md new file mode 100644 index 000000000..f078ecf27 --- /dev/null +++ b/content/en/quick-reference/glossary/page-collection.md @@ -0,0 +1,5 @@ +--- +title: page collection +--- + +A _page collection_ is a slice of `Page` objects. diff --git a/content/en/quick-reference/glossary/page-kind.md b/content/en/quick-reference/glossary/page-kind.md new file mode 100644 index 000000000..ee47fe010 --- /dev/null +++ b/content/en/quick-reference/glossary/page-kind.md @@ -0,0 +1,6 @@ +--- +title: page kind +reference: /methods/page/kind/ +--- + +A _page kind_ is a classification of pages, one of `home`, `page`, `section`, `taxonomy`, or `term`. diff --git a/content/en/quick-reference/glossary/page-resource.md b/content/en/quick-reference/glossary/page-resource.md new file mode 100644 index 000000000..dab119eb4 --- /dev/null +++ b/content/en/quick-reference/glossary/page-resource.md @@ -0,0 +1,5 @@ +--- +title: page resource +--- + +A _page resource_ is a file within a [_page bundle_](g). diff --git a/content/en/quick-reference/glossary/pager.md b/content/en/quick-reference/glossary/pager.md new file mode 100644 index 000000000..f58874e4a --- /dev/null +++ b/content/en/quick-reference/glossary/pager.md @@ -0,0 +1,5 @@ +--- +title: pager +--- + +Created during [_pagination_](g), a _pager_ contains a subset of a list page and navigation links to other pagers. diff --git a/content/en/quick-reference/glossary/paginate.md b/content/en/quick-reference/glossary/paginate.md new file mode 100644 index 000000000..d2467e098 --- /dev/null +++ b/content/en/quick-reference/glossary/paginate.md @@ -0,0 +1,5 @@ +--- +title: paginate +--- + +To _paginate_ is to split a list page into two or more subsets. diff --git a/content/en/quick-reference/glossary/pagination.md b/content/en/quick-reference/glossary/pagination.md new file mode 100644 index 000000000..136341dbf --- /dev/null +++ b/content/en/quick-reference/glossary/pagination.md @@ -0,0 +1,6 @@ +--- +title: pagination +reference: /templates/pagination +--- + +The term _pagination_ refers to the process of [_paginating_](g) a list page. diff --git a/content/en/quick-reference/glossary/paginator.md b/content/en/quick-reference/glossary/paginator.md new file mode 100644 index 000000000..4d197348d --- /dev/null +++ b/content/en/quick-reference/glossary/paginator.md @@ -0,0 +1,5 @@ +--- +title: paginator +--- + +A _paginator_ is a collection of [_pagers_](g). diff --git a/content/en/quick-reference/glossary/parameter.md b/content/en/quick-reference/glossary/parameter.md new file mode 100644 index 000000000..f1a45ea34 --- /dev/null +++ b/content/en/quick-reference/glossary/parameter.md @@ -0,0 +1,5 @@ +--- +title: parameter +--- + +A _parameter_ is typically a user-defined key-value pair at the site or page level, but may also refer to a configuration setting or an [_argument_](g). diff --git a/content/en/quick-reference/glossary/partial.md b/content/en/quick-reference/glossary/partial.md new file mode 100644 index 000000000..a5dd5e51a --- /dev/null +++ b/content/en/quick-reference/glossary/partial.md @@ -0,0 +1,5 @@ +--- +title: partial +--- + +A _partial_ is a [_template_](g) called from any other template including [_shortcodes_](g), [render hooks](g), and other partials. A partial either renders something or returns something. A partial can also call itself, for example, to [_walk_](g) a data structure. diff --git a/content/en/quick-reference/glossary/permalink.md b/content/en/quick-reference/glossary/permalink.md new file mode 100644 index 000000000..45651de83 --- /dev/null +++ b/content/en/quick-reference/glossary/permalink.md @@ -0,0 +1,5 @@ +--- +title: permalink +--- + +A _permalink_ is the absolute URL of a published resource or a rendered page, including scheme and host. diff --git a/content/en/getting-started/glossary/pipe.md b/content/en/quick-reference/glossary/pipe.md similarity index 50% rename from content/en/getting-started/glossary/pipe.md rename to content/en/quick-reference/glossary/pipe.md index 4daab1999..f587eeccf 100644 --- a/content/en/getting-started/glossary/pipe.md +++ b/content/en/quick-reference/glossary/pipe.md @@ -2,4 +2,4 @@ title: pipe --- -See [pipeline](g). +See [_pipeline_](g). diff --git a/content/en/quick-reference/glossary/pipeline.md b/content/en/quick-reference/glossary/pipeline.md new file mode 100644 index 000000000..2bb3fa3b7 --- /dev/null +++ b/content/en/quick-reference/glossary/pipeline.md @@ -0,0 +1,7 @@ +--- +title: pipeline +--- + +Within a [_template action_](g), a _pipeline_ is a possibly chained sequence of values, [_function_](g) calls, or [_method_](g) calls. Functions and methods in the pipeline may take multiple [_arguments_](g). + +A pipeline may be *chained* by separating a sequence of commands with pipeline characters (`|`). In a chained pipeline, the result of each command is passed as the last argument to the following command. The output of the final command in the pipeline is the value of the pipeline. diff --git a/content/en/quick-reference/glossary/pretty-url.md b/content/en/quick-reference/glossary/pretty-url.md new file mode 100644 index 000000000..b15ef9f0f --- /dev/null +++ b/content/en/quick-reference/glossary/pretty-url.md @@ -0,0 +1,5 @@ +--- +title: pretty URL +--- + +A _pretty URL_ is a URL that does not include a file extension. diff --git a/content/en/getting-started/glossary/publish.md b/content/en/quick-reference/glossary/publish.md similarity index 57% rename from content/en/getting-started/glossary/publish.md rename to content/en/quick-reference/glossary/publish.md index 1a7a491af..743e87a3e 100644 --- a/content/en/getting-started/glossary/publish.md +++ b/content/en/quick-reference/glossary/publish.md @@ -2,4 +2,4 @@ title: publish --- -See [build](g). +See [_build_](g). diff --git a/content/en/quick-reference/glossary/raw-string-literal.md b/content/en/quick-reference/glossary/raw-string-literal.md new file mode 100644 index 000000000..7465c6837 --- /dev/null +++ b/content/en/quick-reference/glossary/raw-string-literal.md @@ -0,0 +1,6 @@ +--- +title: raw string literal +reference: https://go.dev/ref/spec#String_literals +--- + +A _raw string literal_ is a character sequences between backticks, as in \`bar\`. Within the backticks, any character may appear except a backtick. Backslashes have no special meaning and the string may contain newlines. Carriage return characters (`\r`) inside raw string literals are discarded from the raw string value. diff --git a/content/en/quick-reference/glossary/regular-page.md b/content/en/quick-reference/glossary/regular-page.md new file mode 100644 index 000000000..084408e57 --- /dev/null +++ b/content/en/quick-reference/glossary/regular-page.md @@ -0,0 +1,5 @@ +--- +title: regular page +--- + +A _regular page_ is a page with the "page" [_page kind_](g). See also [_section page_](g). diff --git a/content/en/quick-reference/glossary/relative-permalink.md b/content/en/quick-reference/glossary/relative-permalink.md new file mode 100644 index 000000000..a272133a3 --- /dev/null +++ b/content/en/quick-reference/glossary/relative-permalink.md @@ -0,0 +1,5 @@ +--- +title: relative permalink +--- + +A _relative permalink_ is the host-relative URL of a published resource or a rendered page. diff --git a/content/en/quick-reference/glossary/remote-resource.md b/content/en/quick-reference/glossary/remote-resource.md new file mode 100644 index 000000000..163749763 --- /dev/null +++ b/content/en/quick-reference/glossary/remote-resource.md @@ -0,0 +1,5 @@ +--- +title: remote resource +--- + +A _remote resource_ is a file on a remote server, accessible via HTTP or HTTPS. diff --git a/content/en/quick-reference/glossary/render-hook.md b/content/en/quick-reference/glossary/render-hook.md new file mode 100644 index 000000000..2cdb98e05 --- /dev/null +++ b/content/en/quick-reference/glossary/render-hook.md @@ -0,0 +1,6 @@ +--- +title: render hook +reference: /render-hooks +--- + +A _render hook_ is a [_template_](g) that overrides standard Markdown rendering. diff --git a/content/en/quick-reference/glossary/resource-type.md b/content/en/quick-reference/glossary/resource-type.md new file mode 100644 index 000000000..64d9c27ab --- /dev/null +++ b/content/en/quick-reference/glossary/resource-type.md @@ -0,0 +1,8 @@ +--- +title: resource type +--- + +A _resource type_ is the main type of a resource's [media type]. Content files such as Markdown, HTML, AsciiDoc, Pandoc, reStructuredText, and Emacs Org Mode have resource type `page`. Other resource types include `image`, `video`, etc. Retrieve the resource type using the [`ResourceType`] method on a `Resource` object. + +[media type]: /methods/resource/mediatype/ +[`ResourceType`]: /methods/resource/resourcetype/ diff --git a/content/en/quick-reference/glossary/resource.md b/content/en/quick-reference/glossary/resource.md new file mode 100644 index 000000000..bd3a9d10d --- /dev/null +++ b/content/en/quick-reference/glossary/resource.md @@ -0,0 +1,7 @@ +--- +title: resource +--- + +A _resource_ is any file consumed by the build process to augment or generate content, structure, behavior, or presentation. For example: images, videos, content snippets, CSS, Sass, JavaScript, and data. + +Hugo supports three types of resources: [_global resources_](g), [_page resources_](g), and [_remote resources_](g). diff --git a/content/en/quick-reference/glossary/scalar.md b/content/en/quick-reference/glossary/scalar.md new file mode 100644 index 000000000..63ae27d3f --- /dev/null +++ b/content/en/quick-reference/glossary/scalar.md @@ -0,0 +1,5 @@ +--- +title: scalar +--- + +A _scalar_ is a single value, one of [_string_](g), [_integer_](g), [floating point](g), or [_boolean_](g). diff --git a/content/en/quick-reference/glossary/scope.md b/content/en/quick-reference/glossary/scope.md new file mode 100644 index 000000000..5a13312a6 --- /dev/null +++ b/content/en/quick-reference/glossary/scope.md @@ -0,0 +1,5 @@ +--- +title: scope +--- + +The term _scope_ refers to the specific region of code where a [_variable_](g) or [_object_](g) is accessible. For example, a variable initialized in one [template](g) is not available within another. diff --git a/content/en/quick-reference/glossary/scratch-pad.md b/content/en/quick-reference/glossary/scratch-pad.md new file mode 100644 index 000000000..f94d4fd5b --- /dev/null +++ b/content/en/quick-reference/glossary/scratch-pad.md @@ -0,0 +1,8 @@ +--- +title: scratch pad +--- + +Conceptually, a _scratch pad_ is a [_map_](g) with [_methods_](g) to set, get, update, and delete values. Attach the data structure to a `Page` or `Site` object using the [`Store`] method, or create a locally scoped scratch pad using the [`newScratch`] function. + +[`Store`]: /methods/page/store/ +[`newScratch`]: /functions/collections/newscratch/ diff --git a/content/en/quick-reference/glossary/section-page.md b/content/en/quick-reference/glossary/section-page.md new file mode 100644 index 000000000..fd84ed5f2 --- /dev/null +++ b/content/en/quick-reference/glossary/section-page.md @@ -0,0 +1,5 @@ +--- +title: section page +--- + +A _section page_ is a page with the "section" [_page kind_](g). Typically a listing of [_regular pages_](g) and/or other section pages within the current [_section_](g). diff --git a/content/en/quick-reference/glossary/section.md b/content/en/quick-reference/glossary/section.md new file mode 100644 index 000000000..45d1203e0 --- /dev/null +++ b/content/en/quick-reference/glossary/section.md @@ -0,0 +1,5 @@ +--- +title: section +--- + +A _section_ is a top-level content directory or any content directory containing an `_index.md` file. diff --git a/content/en/quick-reference/glossary/segment.md b/content/en/quick-reference/glossary/segment.md new file mode 100644 index 000000000..5851a8825 --- /dev/null +++ b/content/en/quick-reference/glossary/segment.md @@ -0,0 +1,5 @@ +--- +title: segment +--- + +A _segment_ is a subset of a site, filtered by [_logical path_](g), language, [_page kind_](g), or [_output format_](g). diff --git a/content/en/quick-reference/glossary/shortcode.md b/content/en/quick-reference/glossary/shortcode.md new file mode 100644 index 000000000..bfc2f6c51 --- /dev/null +++ b/content/en/quick-reference/glossary/shortcode.md @@ -0,0 +1,6 @@ +--- +title: shortcode +reference: /content-management/shortcodes +--- + +A _shortcode_ is a [_template_](g) invoked within markup, accepting any number of [_arguments_](g). They can be used with any [content format](g) to insert elements such as videos, images, and social media embeds into your content. diff --git a/content/en/quick-reference/glossary/slice.md b/content/en/quick-reference/glossary/slice.md new file mode 100644 index 000000000..5d83bed6b --- /dev/null +++ b/content/en/quick-reference/glossary/slice.md @@ -0,0 +1,6 @@ +--- +title: slice +reference: https://go.dev/ref/spec#Slice_types +--- + +A _slice_ is a numbered sequence of elements. Unlike Go's [_array_](g) data type, slices are dynamically sized. [_Elements_](g) within a slice can be [_scalars_](g), [_arrays_](g), [_maps_](g), pages, or other slices. diff --git a/content/en/quick-reference/glossary/string.md b/content/en/quick-reference/glossary/string.md new file mode 100644 index 000000000..54ed4c1d1 --- /dev/null +++ b/content/en/quick-reference/glossary/string.md @@ -0,0 +1,5 @@ +--- +title: string +--- + +A _string_ is a sequence of bytes. For example, `"What is 6 times 7?"`. diff --git a/content/en/quick-reference/glossary/taxonomic-weight.md b/content/en/quick-reference/glossary/taxonomic-weight.md new file mode 100644 index 000000000..682bc056c --- /dev/null +++ b/content/en/quick-reference/glossary/taxonomic-weight.md @@ -0,0 +1,6 @@ +--- +title: taxonomic weight +reference: content-management/taxonomies/#order-taxonomies +--- + +Defined in front matter and unique to each taxonomy, a _taxonomic weight_ is a [_weight_](g) that determines the sort order of page collections contained within a [`Taxonomy`](g) object. diff --git a/content/en/quick-reference/glossary/taxonomy-object.md b/content/en/quick-reference/glossary/taxonomy-object.md new file mode 100644 index 000000000..590490377 --- /dev/null +++ b/content/en/quick-reference/glossary/taxonomy-object.md @@ -0,0 +1,5 @@ +--- +title: taxonomy object +--- + +A _taxonomy object_ is a [_map_](g) of [_terms_](g) and the [weighted pages](g) associated with each term. diff --git a/content/en/quick-reference/glossary/taxonomy-page.md b/content/en/quick-reference/glossary/taxonomy-page.md new file mode 100644 index 000000000..6b62f0912 --- /dev/null +++ b/content/en/quick-reference/glossary/taxonomy-page.md @@ -0,0 +1,5 @@ +--- +title: taxonomy page +--- + +A _taxonomy page_ is a page with the "taxonomy" [_page kind_](g). Typically a listing of [_terms_](g) within a given [_taxonomy_](g). diff --git a/content/en/quick-reference/glossary/taxonomy.md b/content/en/quick-reference/glossary/taxonomy.md new file mode 100644 index 000000000..ce35a634c --- /dev/null +++ b/content/en/quick-reference/glossary/taxonomy.md @@ -0,0 +1,5 @@ +--- +title: taxonomy +reference: /content-management/taxonomies +--- +A _taxonomy_ is a group of related [_terms_](g) used to classify content. For example, a "colors" taxonomy might include the terms "red", "green", and "blue". diff --git a/content/en/quick-reference/glossary/template-action.md b/content/en/quick-reference/glossary/template-action.md new file mode 100644 index 000000000..d9a6c635c --- /dev/null +++ b/content/en/quick-reference/glossary/template-action.md @@ -0,0 +1,6 @@ +--- +title: template action +reference: https://pkg.go.dev/text/template#hdr-Actions +--- + +A data evaluation or control structure within a [_template_](g), delimited by "{{" and "}}". diff --git a/content/en/quick-reference/glossary/template.md b/content/en/quick-reference/glossary/template.md new file mode 100644 index 000000000..d442c0a22 --- /dev/null +++ b/content/en/quick-reference/glossary/template.md @@ -0,0 +1,6 @@ +--- +title: template +reference: /templates +--- + +A _template_ is a file with [_template actions_](g), located within the `layouts` directory of a project, theme, or module. diff --git a/content/en/quick-reference/glossary/term-page.md b/content/en/quick-reference/glossary/term-page.md new file mode 100644 index 000000000..78eabc11f --- /dev/null +++ b/content/en/quick-reference/glossary/term-page.md @@ -0,0 +1,5 @@ +--- +title: term page +--- + +A _term page_ is a page with the "term" [_page kind_](g). Typically a listing of [_regular pages_](g) and [_section pages_](g) with a given [_term_](g). diff --git a/content/en/quick-reference/glossary/term.md b/content/en/quick-reference/glossary/term.md new file mode 100644 index 000000000..cafd92585 --- /dev/null +++ b/content/en/quick-reference/glossary/term.md @@ -0,0 +1,6 @@ +--- +title: term +reference: /content-management/taxonomies +--- + +A _term_ is a member of a [_taxonomy_](g), used to classify content. diff --git a/content/en/quick-reference/glossary/theme.md b/content/en/quick-reference/glossary/theme.md new file mode 100644 index 000000000..84b9c684a --- /dev/null +++ b/content/en/quick-reference/glossary/theme.md @@ -0,0 +1,5 @@ +--- +title: theme +--- + +A _theme_ is a packaged combination of [_archetypes_](g), assets, content, data, [_templates_](g), translation tables, static files, or configuration settings. A theme may serve as the basis for a new site, or to augment an existing site. diff --git a/content/en/quick-reference/glossary/token.md b/content/en/quick-reference/glossary/token.md new file mode 100644 index 000000000..f8c12d570 --- /dev/null +++ b/content/en/quick-reference/glossary/token.md @@ -0,0 +1,5 @@ +--- +title: token +--- + +A _token_ is an identifier within a format string, beginning with a colon and replaced with a value when rendered. For example, use tokens in format strings for both [permalinks](/content-management/urls/#permalinks) and [dates](/functions/time/format/#localization). diff --git a/content/en/getting-started/glossary/type.md b/content/en/quick-reference/glossary/type.md similarity index 100% rename from content/en/getting-started/glossary/type.md rename to content/en/quick-reference/glossary/type.md diff --git a/content/en/quick-reference/glossary/ugly-url.md b/content/en/quick-reference/glossary/ugly-url.md new file mode 100644 index 000000000..4083f9378 --- /dev/null +++ b/content/en/quick-reference/glossary/ugly-url.md @@ -0,0 +1,5 @@ +--- +title: ugly URL +--- + +An _ugly URL_ is a URL that includes a file extension. diff --git a/content/en/quick-reference/glossary/unmarshal.md b/content/en/quick-reference/glossary/unmarshal.md new file mode 100644 index 000000000..0ed548a21 --- /dev/null +++ b/content/en/quick-reference/glossary/unmarshal.md @@ -0,0 +1,6 @@ +--- +title: unmarshal +reference: /functions/transform/unmarshal/ +--- + +To _unmarshal_ is to transform a serialized object into a data structure. For example, transforming a JSON file into a [_map_](g) that you can access within a template. diff --git a/content/en/quick-reference/glossary/variable.md b/content/en/quick-reference/glossary/variable.md new file mode 100644 index 000000000..f8139a41f --- /dev/null +++ b/content/en/quick-reference/glossary/variable.md @@ -0,0 +1,5 @@ +--- +title: variable +--- + +A _variable_ is a user-defined [_identifier_](g) prepended with a `$` symbol, representing a value of any data type, initialized or assigned within a [_template action_](g). For example, `$foo` and `$bar` are variables. diff --git a/content/en/quick-reference/glossary/walk.md b/content/en/quick-reference/glossary/walk.md new file mode 100644 index 000000000..09f9ecb20 --- /dev/null +++ b/content/en/quick-reference/glossary/walk.md @@ -0,0 +1,5 @@ +--- +title: walk +--- + +To _walk_ is to recursively traverse a nested data structure. For example, rendering a multilevel menu. diff --git a/content/en/quick-reference/glossary/weight.md b/content/en/quick-reference/glossary/weight.md new file mode 100644 index 000000000..deb9e6dc0 --- /dev/null +++ b/content/en/quick-reference/glossary/weight.md @@ -0,0 +1,5 @@ +--- +title: weight +--- + +A _weight_ is a numeric value used to position an element within a sorted [collection](g). Assign weights using non-zero integers. Lighter items float to the top, while heavier items sink to the bottom. Unweighted or zero-weighted elements are placed at the end of the collection. Weights are typically assigned to pages, menu entries, languages, and output formats. diff --git a/content/en/quick-reference/glossary/weighted-page.md b/content/en/quick-reference/glossary/weighted-page.md new file mode 100644 index 000000000..e6cf8d6db --- /dev/null +++ b/content/en/quick-reference/glossary/weighted-page.md @@ -0,0 +1,5 @@ +--- +title: weighted page +--- + +Contained within a [_taxonomy object_](g), a _weighted page_ is a [_map_](g) with two [_elements_](g): a `Page` object, and its [_taxonomic weight_](g) as defined in front matter. Access the elements using the `Page` and `Weight` keys. diff --git a/content/en/getting-started/glossary/zero-time.md b/content/en/quick-reference/glossary/zero-time.md similarity index 100% rename from content/en/getting-started/glossary/zero-time.md rename to content/en/quick-reference/glossary/zero-time.md diff --git a/content/en/quick-reference/methods.md b/content/en/quick-reference/methods.md index abd1db709..5b4797f3a 100644 --- a/content/en/quick-reference/methods.md +++ b/content/en/quick-reference/methods.md @@ -6,8 +6,8 @@ keywords: [] menu: docs: parent: quick-reference - weight: 40 -weight: 40 + weight: 50 +weight: 50 toc: true --- diff --git a/content/en/quick-reference/page-collections.md b/content/en/quick-reference/page-collections.md index c86929b5b..836af3ea2 100644 --- a/content/en/quick-reference/page-collections.md +++ b/content/en/quick-reference/page-collections.md @@ -6,8 +6,8 @@ keywords: [] menu: docs: parent: quick-reference - weight: 50 -weight: 50 + weight: 60 +weight: 60 toc: true --- diff --git a/content/en/render-hooks/_common/pageinner.md b/content/en/render-hooks/_common/pageinner.md index 92bd63c56..6f2622880 100644 --- a/content/en/render-hooks/_common/pageinner.md +++ b/content/en/render-hooks/_common/pageinner.md @@ -4,7 +4,7 @@ _comment: Do not remove front matter. ## PageInner details -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} The primary use case for `PageInner` is to resolve links and [page resources](g) relative to an included `Page`. For example, create an "include" shortcode to compose a page from multiple content files, while preserving a global context for footnotes and the table of contents: diff --git a/content/en/render-hooks/_index.md b/content/en/render-hooks/_index.md index 8cf72c4e8..668808f44 100644 --- a/content/en/render-hooks/_index.md +++ b/content/en/render-hooks/_index.md @@ -1,6 +1,6 @@ --- title: Render hooks -linkTitle: In this section + description: Create render hooks to override the rendering of Markdown to HTML. categories: [] keywords: [] diff --git a/content/en/render-hooks/blockquotes.md b/content/en/render-hooks/blockquotes.md index b0fee7239..bdeb06af9 100755 --- a/content/en/render-hooks/blockquotes.md +++ b/content/en/render-hooks/blockquotes.md @@ -12,7 +12,7 @@ weight: 30 toc: true --- -{{< new-in 0.132.0 >}} +{{< new-in 0.132.0 />}} ## Context @@ -24,13 +24,13 @@ Blockquote render hook templates receive the following [context](g): ###### AlertTitle -{{< new-in 0.134.0 >}} +{{< new-in 0.134.0 />}} (`template.HTML`) Applicable when [`Type`](#type) is `alert`, this is the alert title. See the [alerts](#alerts) section below. ###### AlertSign -{{< new-in 0.134.0 >}} +{{< new-in 0.134.0 />}} (`string`) Applicable when [`Type`](#type) is `alert`, this is the alert sign. Typically used to indicate whether an alert is graphically foldable, this is one of `+`, `-`, or an empty string. See the [alerts](#alerts) section below. diff --git a/content/en/render-hooks/code-blocks.md b/content/en/render-hooks/code-blocks.md index 5022fd3ae..7999dbeb8 100755 --- a/content/en/render-hooks/code-blocks.md +++ b/content/en/render-hooks/code-blocks.md @@ -78,7 +78,7 @@ Code block render hook templates receive the following [context](g): ###### PageInner -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} (`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details). diff --git a/content/en/render-hooks/headings.md b/content/en/render-hooks/headings.md index 2b32797cc..7c9d6b6fe 100755 --- a/content/en/render-hooks/headings.md +++ b/content/en/render-hooks/headings.md @@ -41,7 +41,7 @@ title = true ###### PageInner -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} (`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details). diff --git a/content/en/render-hooks/images.md b/content/en/render-hooks/images.md index 900df3c2f..63f294081 100755 --- a/content/en/render-hooks/images.md +++ b/content/en/render-hooks/images.md @@ -59,7 +59,7 @@ block = true ###### PageInner -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} (`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details). @@ -122,7 +122,7 @@ wrapStandAloneImageWithinParagraph = false ## Default -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} Hugo includes an [embedded image render hook] to resolve Markdown image destinations. Disabled by default, you can enable it in your site configuration: diff --git a/content/en/render-hooks/links.md b/content/en/render-hooks/links.md index 7700a540e..89044c662 100755 --- a/content/en/render-hooks/links.md +++ b/content/en/render-hooks/links.md @@ -38,7 +38,7 @@ Link render hook templates receive the following context: ###### PageInner -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} (`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details). @@ -90,7 +90,7 @@ To include a `rel` attribute set to `external` for external links: ## Default -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} Hugo includes an [embedded link render hook] to resolve Markdown link destinations. Disabled by default, you can enable it in your site configuration: diff --git a/content/en/render-hooks/passthrough.md b/content/en/render-hooks/passthrough.md index 95bc54d88..798225061 100755 --- a/content/en/render-hooks/passthrough.md +++ b/content/en/render-hooks/passthrough.md @@ -12,7 +12,7 @@ weight: 80 toc: true --- -{{< new-in 0.132.0 >}} +{{< new-in 0.132.0 />}} ## Overview diff --git a/content/en/render-hooks/tables.md b/content/en/render-hooks/tables.md index 4252d6de0..d131664fc 100755 --- a/content/en/render-hooks/tables.md +++ b/content/en/render-hooks/tables.md @@ -12,7 +12,7 @@ weight: 90 toc: true --- -{{< new-in 0.134.0 >}} +{{< new-in 0.134.0 />}} ## Context diff --git a/content/en/shortcodes/_index.md b/content/en/shortcodes/_index.md index e525ef5ef..2d9a8fdd6 100644 --- a/content/en/shortcodes/_index.md +++ b/content/en/shortcodes/_index.md @@ -1,6 +1,6 @@ --- title: Shortcodes -linkTitle: In this section + description: Insert elements such as videos, images, and social media embeds into your content using Hugo's embedded shortcodes. categories: [] keywords: [] diff --git a/content/en/shortcodes/comment.md b/content/en/shortcodes/comment.md index 208277bf5..1600c09d3 100755 --- a/content/en/shortcodes/comment.md +++ b/content/en/shortcodes/comment.md @@ -9,7 +9,7 @@ menu: parent: shortcodes weight: weight: -expiryDate: 2025-01-22 # with v0.142.0 and later use HTML comments instead +expiryDate: 2025-01-22 # deprecated 2025-02-01 in v0.143.0 and immediately removed from the documentation --- {{% note %}} @@ -18,7 +18,7 @@ To override Hugo's embedded `comment` shortcode, copy the [source code] to a fil [source code]: {{% eturl comment %}} {{% /note %}} -{{< new-in "0.137.1" >}} +{{< new-in 0.137.1 />}} Use the `comment` shortcode to include comments in your content. Hugo will ignore the text within these comments when rendering your site. diff --git a/content/en/shortcodes/details.md b/content/en/shortcodes/details.md index f6e5ae691..f30f81d75 100755 --- a/content/en/shortcodes/details.md +++ b/content/en/shortcodes/details.md @@ -11,7 +11,7 @@ weight: toc: true --- -{{< new-in 0.140.0 >}} +{{< new-in 0.140.0 />}} {{% note %}} To override Hugo's embedded `details` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory. diff --git a/content/en/shortcodes/gist.md b/content/en/shortcodes/gist.md index dc85f282c..1c740bccb 100755 --- a/content/en/shortcodes/gist.md +++ b/content/en/shortcodes/gist.md @@ -8,13 +8,19 @@ menu: parent: shortcodes weight: weight: +expiryDate: 2027-02-01 # deprecated 2025-02-01 in v0.143.0 --- -{{% note %}} -To override Hugo's embedded `gist` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory. +{{% deprecated-in 0.143.0 %}} +The `gist` shortcode was deprecated in version 0.143.0 and will be removed in a future release. To continue embedding GitHub Gists in your content, you'll need to create a custom shortcode: -[source code]: {{% eturl gist %}} -{{% /note %}} +1. Create a new file: Create a file named `gist.html` within the `layouts/shortcodes` directory. +2. Copy the source code: Paste the [original source code] of the gist shortcode into the newly created `gist.html` file. + +This will allow you to maintain the functionality of embedding GitHub Gists in your content after the deprecation of the original shortcode. + +[original source code]: {{% eturl gist %}} +{{% /deprecated-in %}} To display a GitHub gist with this URL: @@ -28,14 +34,8 @@ Include this in your Markdown: {{}} ``` -This will display all files in the gist alphabetically by file name. - -{{< gist jmooring 23932424365401ffa5e9d9810102a477 >}} - To display a specific file within the gist: ```text {{}} ``` - -{{< gist jmooring 23932424365401ffa5e9d9810102a477 list.html >}} diff --git a/content/en/shortcodes/param.md b/content/en/shortcodes/param.md index 70866d4ba..bff659ec1 100755 --- a/content/en/shortcodes/param.md +++ b/content/en/shortcodes/param.md @@ -27,7 +27,7 @@ params: size: medium --- -We found a {{}} shirt. +We found a {{%/* param "color" */%}} shirt. {{< /code >}} Hugo renders this to: @@ -39,5 +39,5 @@ Hugo renders this to: Access nested values by [chaining](g) the [identifiers](g): ```text -{{}} +{{%/* param my.nested.param */%}} ``` diff --git a/content/en/shortcodes/qr.md b/content/en/shortcodes/qr.md index 15e06cbc3..a7a6d92cb 100755 --- a/content/en/shortcodes/qr.md +++ b/content/en/shortcodes/qr.md @@ -11,7 +11,7 @@ weight: toc: true --- -{{< new-in 0.141.0 >}} +{{< new-in 0.141.0 />}} {{% note %}} To override Hugo's embedded `qr` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory. @@ -44,7 +44,7 @@ https://gohugo.io Both of the above produce this image: -{{< qr text="https://gohugo.io" class="qrcode" />}} +{{< qr text="https://gohugo.io" class="qrcode" targetDir="images/qr" />}} To create a QR code for a phone number: @@ -52,7 +52,7 @@ To create a QR code for a phone number: {{}} ``` -{{< qr text="tel:+12065550101" class="qrcode" />}} +{{< qr text="tel:+12065550101" class="qrcode" targetDir="images/qr" />}} To create a QR code containing contact information in the [vCard] format: @@ -72,7 +72,7 @@ END:VCARD {{}} ``` -{{< qr level="low" scale=2 alt="QR code of vCard for John Smith" class="qrcode" >}} +{{< qr level="low" scale=2 alt="QR code of vCard for John Smith" class="qrcode" targetDir="images/qr" >}} BEGIN:VCARD VERSION:2.1 N;CHARSET=UTF-8:Smith;John;R.;Dr.;PhD diff --git a/content/en/shortcodes/vimeo.md b/content/en/shortcodes/vimeo.md index b82ad86d4..5ecdf5d02 100755 --- a/content/en/shortcodes/vimeo.md +++ b/content/en/shortcodes/vimeo.md @@ -46,7 +46,7 @@ id title : (`string`) The `title` attribute of the `iframe` element. -If you proivde a `class` or `title` you must use a named parameter for the `id`. +If you provide a `class` or `title` you must use a named parameter for the `id`. ```text {{}} diff --git a/content/en/shortcodes/x.md b/content/en/shortcodes/x.md index 6995d727f..e27bc996a 100755 --- a/content/en/shortcodes/x.md +++ b/content/en/shortcodes/x.md @@ -11,7 +11,7 @@ weight: toc: true --- -{{< new-in 0.141.0 >}} +{{< new-in 0.141.0 />}} {{% note %}} To override Hugo's embedded `x` shortcode, copy the [source code] to a file with the same name in the `layouts/shortcodes` directory. diff --git a/content/en/shortcodes/youtube.md b/content/en/shortcodes/youtube.md index 51e47a0c9..546529500 100755 --- a/content/en/shortcodes/youtube.md +++ b/content/en/shortcodes/youtube.md @@ -41,38 +41,38 @@ id : (`string`) The video `id`. Optional if the `id` is provided as a positional argument as shown in the example above. allowFullScreen -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`bool`) Whether the `iframe` element can activate full screen mode. Default is `true`. autoplay - {{< new-in 0.125.0 >}} + {{< new-in 0.125.0 />}} : (`bool`) Whether to automatically play the video. Forces `mute` to `true`. Default is `false`. class : (`string`) The `class` attribute of the wrapping `div` element. When specified, removes the `style` attributes from the `iframe` element and its wrapping `div` element. controls -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`bool`) Whether to display the video controls. Default is `true`. end -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`int`) The time, measured in seconds from the start of the video, when the player should stop playing the video. loading -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`string`) The loading attribute of the `iframe` element, either `eager` or `lazy`. Default is `eager`. loop -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`bool`) Whether to indefinitely repeat the video. Ignores the `start` and `end` arguments after the first play. Default is `false`. mute -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`bool`) Whether to mute the video. Always `true` when `autoplay` is `true`. Default is `false`. start -{{< new-in 0.125.0 >}} +{{< new-in 0.125.0 />}} : (`int`) The time, measured in seconds from the start of the video, when the player should start playing the video. title diff --git a/content/en/templates/404.md b/content/en/templates/404.md index 39df75c3d..ae18428fb 100644 --- a/content/en/templates/404.md +++ b/content/en/templates/404.md @@ -38,16 +38,16 @@ Your production server redirects the browser to the 404 page when a page is not Host|Capabilities and configuration :--|:-- -Amazon CloudFront|See [details](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GeneratingCustomErrorResponses.html). -Amazon S3|See [details](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CustomErrorDocSupport.html). -Apache|See [details](https://httpd.apache.org/docs/2.4/custom-error.html). -Azure Static Web Apps|See [details](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#response-overrides). -Azure Storage|See [details](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website#setting-up-a-static-website). -Caddy|See [details](https://caddyserver.com/docs/caddyfile/directives/handle_errors). -Cloudflare Pages|See [details](https://developers.cloudflare.com/pages/configuration/serving-pages/#not-found-behavior). -DigitalOcean App Platform|See [details](https://docs.digitalocean.com/products/app-platform/how-to/manage-static-sites/#configure-a-static-site). -Firebase|See [details](https://firebase.google.com/docs/hosting/full-config#404). +Amazon CloudFront|See [details](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GeneratingCustomErrorResponses.html). +Amazon S3|See [details](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CustomErrorDocSupport.html). +Apache|See [details](https://httpd.apache.org/docs/2.4/custom-error.html). +Azure Static Web Apps|See [details](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#response-overrides). +Azure Storage|See [details](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website#setting-up-a-static-website). +Caddy|See [details](https://caddyserver.com/docs/caddyfile/directives/handle_errors). +Cloudflare Pages|See [details](https://developers.cloudflare.com/pages/configuration/serving-pages/#not-found-behavior). +DigitalOcean App Platform|See [details](https://docs.digitalocean.com/products/app-platform/how-to/manage-static-sites/#configure-a-static-site). +Firebase|See [details](https://firebase.google.com/docs/hosting/full-config#404). GitHub Pages|Redirection to is automatic and not configurable. -GitLab Pages|See [details](https://docs.gitlab.com/ee/user/project/pages/introduction.html#custom-error-codes-pages). -NGINX|See [details](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page). -Netlify|See [details](https://docs.netlify.com/routing/redirects/redirect-options/). +GitLab Pages|See [details](https://docs.gitlab.com/ee/user/project/pages/introduction.html#custom-error-codes-pages). +NGINX|See [details](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page). +Netlify|See [details](https://docs.netlify.com/routing/redirects/redirect-options/). diff --git a/content/en/templates/_index.md b/content/en/templates/_index.md index 0d8f0a0ec..d205c9218 100644 --- a/content/en/templates/_index.md +++ b/content/en/templates/_index.md @@ -1,7 +1,7 @@ --- title: Templates -linkTitle: In this section -description: Go templating, template types and lookup order, shortcodes, and data. + +description: Create templates to render your content, resources, and data. categories: [] keywords: [] menu: diff --git a/content/en/templates/embedded.md b/content/en/templates/embedded.md index 246381db2..2e571256e 100644 --- a/content/en/templates/embedded.md +++ b/content/en/templates/embedded.md @@ -34,7 +34,7 @@ To include the embedded template: {{ template "_internal/disqus.html" . }} ``` -### Configure Disqus +### Configuration {#configuration-disqus} To use Hugo's Disqus template, first set up a single configuration value: @@ -55,6 +55,15 @@ You can also set the following in the front matter for a given piece of content: - `disqus_title` - `disqus_url` +### Privacy {#privacy-disqus} + +Adjust the relevant privacy settings in your site configuration. + +{{< code-toggle config=privacy.disqus />}} + +disable +: (`bool`) Whether to disable the template. Default is `false`. + ## Google Analytics {{% note %}} @@ -76,7 +85,7 @@ To include the embedded template: {{ template "_internal/google_analytics.html" . }} ``` -### Configure Google Analytics +### Configuration {#configuration-google-analytics} Provide your tracking ID in your configuration file: @@ -87,6 +96,18 @@ id = "G-MEASUREMENT_ID" To use this value in your own template, access the configured ID with `{{ site.Config.Services.GoogleAnalytics.ID }}`. +### Privacy {#privacy-google-analytics} + +Adjust the relevant privacy settings in your site configuration. + +{{< code-toggle config=privacy.googleAnalytics />}} + +disable +: (`bool`) Whether to disable the template. Default is `false`. + +respectDoNotTrack +: (`bool`) Whether to respect the browser's "do not track" setting. Default is `false`. + ## Open Graph {{% note %}} @@ -107,7 +128,7 @@ To include the embedded template: {{ template "_internal/opengraph.html" . }} ``` -### Configure Open Graph +### Configuration {#configuration-open-graph} Hugo's Open Graph template is configured using a mix of configuration settings and [front matter](/content-management/front-matter/) on individual pages. @@ -187,7 +208,7 @@ To include the embedded template: {{ template "_internal/twitter_cards.html" . }} ``` -### Configure X (Twitter) Cards +### Configuration {#configuration-x-cards} Hugo's X (Twitter) Card template is configured using a mix of configuration settings and [front-matter](/content-management/front-matter/) values on individual pages. diff --git a/content/en/templates/home.md b/content/en/templates/home.md index 088f40f26..18114eb0f 100644 --- a/content/en/templates/home.md +++ b/content/en/templates/home.md @@ -1,5 +1,5 @@ --- -title: Home templates +title: Home page templates description: The home page of a website is often formatted differently than the other pages. For this reason, Hugo makes it easy for you to define your new site's home page as a unique template. categories: [templates] keywords: [] @@ -12,43 +12,53 @@ toc: true aliases: [/layout/homepage/,/templates/homepage-template/,/templates/homepage/] --- -The home template is the *only* required template for building a site and therefore useful when bootstrapping a new site and template. It is also the only required template if you are developing a single-page website. +## Introduction -{{< youtube ut1xtRZ1QOA >}} - -## Home template lookup order - -See [Template Lookup](/templates/lookup-order/). - -## Add content and front matter to the home page - -The home page accepts content and front matter from an `_index.md` file. This file should live at the root of your `content` directory (i.e., `content/_index.md`). You can then add body copy and metadata to your home page the way you would any other content file. - -See the home template below or [Content Organization][contentorg] for more information on the role of `_index.md` in adding content and front matter to list pages. - -## Example home template +A home page template is used to render your site's home page, and is the only template required for a single-page website. For example, the home page template below inherits the site's shell from the base template and renders the home page content, such as a list of other pages. {{< code file=layouts/_default/home.html >}} {{ define "main" }} -
-
-

{{ .Title }}

- {{ with .Params.subtitle }} - {{ . }} - {{ end }} -
-
- - {{ .Content }} -
-
- {{ range first 10 .Site.RegularPages }} - {{ .Render "summary" }} - {{ end }} -
-
+ {{ .Content }} + {{ range site.RegularPages }} +

{{ .LinkTitle }}

+ {{ end }} {{ end }} {{< /code >}} -[contentorg]: /content-management/organization/ -[lookup]: /templates/lookup-order/ +{{% include "templates/_common/filter-sort-group.md" %}} + +## Lookup order + +Hugo's [template lookup order] determines the template path, allowing you to create unique templates for any page. + +[template lookup order]: /templates/lookup-order/#home-templates + +{{% note %}} +You must have thorough understanding of the template lookup order when creating templates. Template selection is based on template type, page kind, content type, section, language, and output format. +{{% /note %}} + +## Content and front matter + +The home page template uses content and front matter from an `_index.md` file located in the root of your content directory. + +{{< code-toggle file=content/_index.md fm=true >}} +--- +title: The Home Page +date: 2025-01-30T03:36:57-08:00 +draft: false +params: + subtitle: The Subtitle +--- +{{< /code-toggle >}} + +The home page template below inherits the site's shell from the base template, renders the subtitle and content as defined in the `_index.md` file, then renders of list of the site's [regular pages](g). + +{{< code file=layouts/_default/home.html >}} +{{ define "main" }} +

{{ .Params.Subtitle }}

+ {{ .Content }} + {{ range site.RegularPages }} +

{{ .LinkTitle }}

+ {{ end }} +{{ end }} +{{< /code >}} diff --git a/content/en/templates/output-formats.md b/content/en/templates/output-formats.md index c81d58488..c81a81264 100644 --- a/content/en/templates/output-formats.md +++ b/content/en/templates/output-formats.md @@ -22,7 +22,7 @@ This is the full set of built-in media types in Hugo: {{< datatable "config" "mediaTypes" "_key" "suffixes" >}} -**Note:** +Notes: - It is possible to add custom media types or change the defaults; e.g., if you want to change the suffix for `text/html` to `asp`. - `Suffixes` are the values that will be used for URLs and file names for that media type in Hugo. @@ -41,7 +41,7 @@ To add or modify a media type, define it in a `mediaTypes` section in your [site The above example adds one new media type, `text/enriched`, and changes the suffix for the built-in `text/html` media type. -**Note:** these media types are configured for **your output formats**. If you want to redefine one of Hugo's default output formats (e.g. `HTML`), you also need to redefine the media type. So, if you want to change the suffix of the `HTML` output format from `html` (default) to `htm`: +These media types are configured for your output formats. If you want to redefine one of Hugo's default output formats, you also need to redefine the media type. So, if you want to change the suffix of the `HTML` output format from `html` (default) to `htm`: {{< code-toggle file=hugo >}} [mediaTypes] @@ -65,7 +65,8 @@ This is the full set of Hugo's built-in output formats: {{< datatable "config" "outputFormats" "_key" "baseName" "isHTML" "isPlainText" "mediaType" "noUgly" "path" "permalinkable" "protocol" "rel" >}} -- A page can be output in as many output formats as you want, and you can have an infinite amount of output formats defined **as long as they resolve to a unique path on the file system**. In the above table, the best example of this is `amp` vs. `html`. `amp` has the value `amp` for `path` so it doesn't overwrite the `html` version; e.g. we can now have both `/index.html` and `/amp/index.html`. +- A page can be output in as many output formats as you want, and you can have an infinite amount of output formats defined as long as they resolve to a unique path on the file system. In the above table, the best example of this is `amp` vs. `html`. `amp` has the value `amp` for `path` so it doesn't overwrite the `html` version; e.g. we can now have both `/index.html` and `/amp/index.html`. + - The `mediaType` must match a defined media type. - You can define new output formats or redefine built-in output formats; e.g., if you want to put `amp` pages in a different path. @@ -89,10 +90,10 @@ baseName : (`string`) The base name of the published file. Default is `index`. isHTML -: (`bool`) If `true`, classifies the output format as HTML. Hugo uses this value to determine when to create alias redirects, when to inject the LiveReload script, etc. Default is `false`. +: (`bool`) Whether to classify the output format as HTML. Hugo uses this value to determine when to create alias redirects, when to inject the LiveReload script, etc. Default is `false`. isPlainText -: (`bool`) If `true`, Hugo parses templates for this output format with Go's [text/template] package instead of the [html/template] package. Default is `false`. +: (`bool`) Whether to parse templates for this output format with Go's [text/template] package instead of the [html/template] package. Default is `false`. [html/template]: https://pkg.go.dev/html/template [text/template]: https://pkg.go.dev/text/template @@ -103,12 +104,12 @@ mediaType [media type]: https://en.wikipedia.org/wiki/Media_type notAlternative -: (`bool`) If `true`, excludes this output format from the values returned by the [`AlternativeOutputFormats`] method on a `Page` object. Default is `false`. +: (`bool`) Whether to exclude this output format from the values returned by the [`AlternativeOutputFormats`] method on a `Page` object. Default is `false`. [`AlternativeOutputFormats`]: /methods/page/alternativeoutputformats/ noUgly -: (`bool`) If `true`, disables ugly URLs for this output format when `uglyURLs` is `true` in your site configuration. Default is `false`. +: (`bool`) Whether to disable ugly URLs for this output format when `uglyURLs` is `true` in your site configuration. Default is `false`. path : (`string`) The path to the directory containing the published files, relative to the root of the publish directory. @@ -126,10 +127,10 @@ rel : (`string`) If provided, you can assign this value to `rel` attributes in `link` elements when iterating over output formats in your templates. Default is `alternate`. root -: (`bool`) If `true`, files will be published to the root of the publish directory. Default is `false`. +: (`bool`) Whether to publish files to the root of the publish directory. Default is `false`. ugly -: (`bool`) If `true`, enables uglyURLs for this output format when `uglyURLs` is `false` in your site configuration. Default is `false`. +: (`bool`) Whether to enable uglyURLs for this output format when `uglyURLs` is `false` in your site configuration. Default is `false`. weight : (`int`) When set to a non-zero value, Hugo uses the `weight` as the first criteria when sorting output formats, falling back to the name of the output format. Lighter items float to the top, while heavier items sink to the bottom. Hugo renders output formats sequentially based on the sort order. @@ -160,11 +161,11 @@ Example from site configuration file: page = ["html"] {{}} -Note that in the above examples, the _output formats_ for `section`, +Note that in the examples above, the output formats for `section`, `taxonomy` and `term` will stay at their default value `['html','rss']`. -* The `outputs` definition is per page [`Kind`]. -* The names (e.g. `html`, `amp`) must match the `name` of a defined output format, and can be overridden per page in front matter. +- The `outputs` definition is per page [`Kind`]. +- The names (e.g. `html`, `amp`) must match the `name` of a defined output format, and can be overridden per page in front matter. The following is an example of front matter in a content file that defines output formats for the rendered `Page`: @@ -196,7 +197,8 @@ The [`Permalink`] and [`RelPermalink`] methods on a `Page` object return the fir [`Permalink`]: /methods/page/permalink [`RelPermalink`]: /methods/page/relpermalink -__from `single.json.json`:__ +From `single.json.json`: + ```go-html-template {{ .RelPermalink }} → /that-page/ {{ with .OutputFormats.Get "json" }} @@ -206,7 +208,7 @@ __from `single.json.json`:__ In order for them to return the output format of the current template file instead, the given output format should have its `permalinkable` setting set to true. -**Same template file as above with json output format's `permalinkable` set to true:** +This is the same template file as above with the `json` output format's `permalinkable` parameter set to `true`: ```go-html-template {{ .RelPermalink }} → /that-page/index.json @@ -215,34 +217,25 @@ In order for them to return the output format of the current template file inste {{ end }} ``` -From content files, you can use the `ref` or `relref` shortcodes: +## Template lookup order -```go-html-template -[Neat]({{}}) -[Who]({{}}) +Each output format requires a template conforming to the [template lookup order]. + +For the highest specificity in the template lookup order, include the page kind, output format, and suffix in the file name: + +[template lookup order]: /templates/lookup-order/ + +```text +[page kind].[output format].[suffix] ``` -## Templates for your output formats +For example, for section pages: -Each output format requires a corresponding template conforming to the [template lookup order](/templates/lookup-order/). Hugo considers both output format and suffix when selecting a template. - -For example, to generate a JSON file for the home page, the template with highest specificity is `layouts/index.json.json`. - -Hugo will now also detect the media type and output format of partials, if possible, and use that information to decide if the partial should be parsed as a plain text template or not. - -Hugo will look for the name given, so you can name it whatever you want. But if you want it treated as plain text, you should use the file suffix and, if needed, the name of the Output Format. The pattern is as follows: - -```go-html-template -[partial name].[OutputFormat].[suffix] -``` - -The partial below is a plain text template . The output format is `csv`, and since this is the only output format with the suffix `csv`, we don't need to include the output format `name`): - -```go-html-template -{{ partial "mytextpartial.csv" . }} -``` +Output format|Template path +:--|:-- +`html`|`layouts/_default/section.html.html` +`json`|`layouts/_default/section.json.json` +`rss`|`layouts/_default/section.rss.xml` [site configuration]: /getting-started/configuration/ -[lookup order]: /templates/lookup-order/ -[media type]: https://en.wikipedia.org/wiki/Media_type [`kind`]: /methods/page/kind/ diff --git a/content/en/templates/section.md b/content/en/templates/section.md index 28d931534..339fe6389 100644 --- a/content/en/templates/section.md +++ b/content/en/templates/section.md @@ -1,6 +1,6 @@ --- title: Section templates -description: Use section templates to list members of a section. +description: Create a section template to list its members. categories: [templates] keywords: [] menu: diff --git a/content/en/templates/shortcode.md b/content/en/templates/shortcode.md index 452aeda59..c73874673 100644 --- a/content/en/templates/shortcode.md +++ b/content/en/templates/shortcode.md @@ -1,7 +1,6 @@ --- -title: Create your own shortcodes -linkTitle: Shortcode templates -description: You can extend Hugo's embedded shortcodes by creating your own using the same templating syntax as that for single and list pages. +title: Shortcode templates +description: Create custom shortcodes to simplify and standardize content creation. categories: [templates] keywords: [] menu: @@ -13,31 +12,54 @@ aliases: [/templates/shortcode-templates/] toc: true --- -Shortcodes are a means to consolidate templating into small, reusable snippets that you can embed directly inside your content. - {{% note %}} -Hugo also ships with embedded shortcodes for common use cases. (See [Content Management: Shortcodes](/content-management/shortcodes/).) +Before creating custom shortcodes, please review the [shortcodes] page in the [content management] section. Understanding the usage details will help you design and create better templates. + +[shortcodes]: /content-management/shortcodes/ +[content management]: /content-management/shortcodes/ {{% /note %}} -## Create custom shortcodes +## Introduction -Hugo's embedded shortcodes cover many common, but not all, use cases. Luckily, Hugo provides the ability to easily create custom shortcodes to meet your website's needs. +Hugo provides [embedded shortcodes] for many common tasks, but you'll likely need to create your own for more specific needs. Some examples of custom shortcodes you might develop include: -{{< youtube Eu4zSaKOY4A >}} +- Audio players +- Video players +- Image galleries +- Diagrams +- Maps +- Tables +- And many other custom elements -### File location +[embedded shortcodes]: /shortcodes/ -To create a shortcode, place an HTML template in the `layouts/shortcodes` directory. Consider the file name carefully since the shortcode name will mirror that of the file but without the `.html` extension. For example, `layouts/shortcodes/myshortcode.html` will be called with either `{{}}` or `{{%/* myshortcode /*/%}}`. +## Directory structure -You can organize your shortcodes in subdirectories, e.g. in `layouts/shortcodes/boxes`. These shortcodes would then be accessible with their relative path, e.g: +Create shortcode templates within the `layouts/shortcodes` directory, either at its root or organized into subdirectories. -```go-html-template -{{}} +```text +layouts/ +└── shortcodes/ + ├── diagrams/ + │ ├── kroki.html + │ └── plotly.html + ├── media/ + │ ├── audio.html + │ ├── gallery.html + │ └── video.html + ├── capture.html + ├── column.html + ├── include.html + └── row.html ``` -Note the forward slash. +When calling a shortcode in a subdirectory, specify its path relative to the `shortcode` directory, excluding the file extension. -### Template lookup order +```text +{{}} +``` + +## Lookup order Hugo selects shortcode templates based on the shortcode name, the current output format, and the current language. The examples below are sorted by specificity in descending order. The least specific path is at the bottom of the list. @@ -50,277 +72,225 @@ foo|html|en|`layouts/shortcodes/foo.html.en.html` Shortcode name|Output format|Language|Template path :--|:--|:--|:-- -foo|rss|en|`layouts/shortcodes/foo.en.xml` -foo|rss|en|`layouts/shortcodes/foo.rss.xml` -foo|rss|en|`layouts/shortcodes/foo.en.html` -foo|rss|en|`layouts/shortcodes/foo.rss.en.xml` -foo|rss|en|`layouts/shortcodes/foo.xml` -foo|rss|en|`layouts/shortcodes/foo.html.en.html` -foo|rss|en|`layouts/shortcodes/foo.html.html` -foo|rss|en|`layouts/shortcodes/foo.html` +foo|json|en|`layouts/shortcodes/foo.en.json` +foo|json|en|`layouts/shortcodes/foo.json` +foo|json|en|`layouts/shortcodes/foo.json.json` +foo|json|en|`layouts/shortcodes/foo.json.en.json` -Note that templates provided by a theme or module always take precedence. +## Methods -### Positional vs. named arguments +Use these methods in your shortcode templates. Refer to each methods's documentation for details and examples. -You can create shortcodes using the following types of arguments: +{{< list-pages-in-section path=/methods/shortcode >}} -* Positional arguments -* Named arguments -* Positional *or* named arguments +## Examples -In shortcodes with positional arguments, the order of the arguments is important. If a shortcode has a single required value, positional arguments require less typing from content authors. +These examples range in complexity from simple to moderately advanced, with some simplified for clarity. -For more complex layouts with multiple or optional arguments, named arguments work best. While less terse, named arguments require less memorization from a content author and can be added in a shortcode declaration in any order. +### Insert year -Allowing both types of arguments is useful for complex layouts where you want to set default values that can be easily overridden by users. - -### Access arguments - -All shortcode arguments can be accessed via the `.Get` method. Whether you pass a string or a number to the `.Get` method depends on whether you are accessing a named or positional argument, respectively. - -To access an argument by name, use the `.Get` method followed by the named argument as a quoted string: - -```go-html-template -{{ .Get "class" }} -``` - -To access an argument by position, use the `.Get` followed by a numeric position, keeping in mind that positional arguments are zero-indexed: - -```go-html-template -{{ .Get 0 }} -``` - -For the second position, you would just use: - -```go-html-template -{{ .Get 1 }} -``` - -`with` is great when the output depends on a argument being set: - -```go-html-template -{{ with .Get "class" }} class="{{ . }}"{{ end }} -``` - -`.Get` can also be used to check if a argument has been provided. This is -most helpful when the condition depends on either of the values, or both: - -```go-html-template -{{ if or (.Get "title") (.Get "alt") }} alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "title" }}{{ end }}"{{ end }} -``` - -#### `.Inner` - -The `.Inner` method returns the content between the opening and closing shortcode tags. To check if `.Inner` returns anything other than whitespace: - -```go-html-template -{{ if strings.ContainsNonSpace .Inner }} - Inner is not empty -{{ end }} -``` - -{{% note %}} -Any shortcode that calls the `.Inner` method must be closed or self-closed. To call a shortcode using the self-closing syntax. - -```go-html-template -{{}} -``` - -{{% /note %}} - -#### `.Params` - -The `.Params` method in shortcodes returns the arguments passed to the shortcode for more complicated use cases. You can also access higher-scoped arguments with the following logic: - -$.Params -: these are the arguments passed directly into the shortcode declaration (e.g., a YouTube video ID) - -$.Page.Params -: refers to the page's parameters; the "page" in this case refers to the content file in which the shortcode is declared (e.g., a `shortcode_color` field in a content's front matter could be accessed via `$.Page.Params.shortcode_color`). - -$.Site.Params -: refers to parameters defined in your site configuration. - -#### `.IsNamedParams` - -The `.IsNamedParams` method checks whether the shortcode declaration uses named arguments and returns a boolean value. - -For example, you could create an `image` shortcode that can take either a `src` named argument or the first positional argument, depending on the preference of the content's author. Let's assume the `image` shortcode is called as follows: - -```go-html-template -{{}} -``` - -You could then include the following as part of your shortcode templating: - -```go-html-template -{{ if .IsNamedParams }} - -{{ else }} - -{{ end }} -``` - -See the [example Vimeo shortcode][vimeoexample] below for `.IsNamedParams` in action. - -{{% note %}} -While you can create shortcode templates that accept both positional and named arguments, you *cannot* declare shortcodes in content with a mix of argument types. Therefore, a shortcode declared like `{{}}` will return an error. -{{% /note %}} - -Shortcodes can also be nested. In a nested shortcode, you can access the parent shortcode context with the [`.Parent`] shortcode method. This can be very useful for inheritance from the root. - -### Checking for existence - -You can check if a specific shortcode is used on a page by calling `.HasShortcode` in that page template, providing the name of the shortcode. This is useful when you want to include specific scripts or styles in the header that are only used by that shortcode. - -## Custom shortcode examples - -The following are examples of the different types of shortcodes you can create via shortcode template files in `/layouts/shortcodes`. - -### Single-word example: `year` - -Let's assume you would like to keep mentions of your copyright year current in your content files without having to continually review your Markdown. Your goal is to be able to call the shortcode as follows: - -```go-html-template -{{}} -``` +Create a shortcode to insert the current year: {{< code file=layouts/shortcodes/year.html >}} -{{ now.Format "2006" }} +{{- now.Format "2006" -}} {{< /code >}} -### Single positional example: `youtube` +Then call the shortcode from within your markup: -Embedded videos are a common addition to Markdown content. The following is the code used by [Hugo's built-in YouTube shortcode][youtubeshortcode]: +{{< code file=content/example.md >}} +This is {{}}, and look at how far we've come. +{{< /code >}} -```go-html-template -{{}} +This shortcode can be used inline or as a block on its own line. If a shortcode might be used inline, remove the surrounding [whitespace] by using [template action](g) delimiters with hyphens. + +[whitespace]: /templates/introduction/#whitespace + +### Insert image + +This example assumes the following content structure, where `content/example/index.md` is a [page bundle](g) containing one or more [page resources](g). + +```text +content/ +├── example/ +│ ├── a.jpg +│ └── index.md +└── _index.md ``` -Would load the template at `/layouts/shortcodes/youtube.html`: +Create a shortcode to capture an image as a page resource, resize it to the given width, convert it to the WebP format, and add an `alt` attribute: -{{< code file=layouts/shortcodes/youtube.html >}} -
- -
+{{< code file=layouts/shortcodes/image.html >}} +{{- with .Page.Resources.Get (.Get "path") }} + {{- with .Process (printf "resize %dx wepb" ($.Get "width")) }} + {{ $.Get + {{- end }} +{{- end -}} {{< /code >}} -{{< code file=youtube-embed.html >}} -
- -
+Then call the shortcode from within your markup: + +{{< code file=content/example/index.md >}} +{{}} {{< /code >}} -### Single named example: `image` +The example above uses: -Let's say you want to create your own `img` shortcode rather than use Hugo's built-in [`figure` shortcode][figure]. Your goal is to be able to call the shortcode as follows in your content files: +- The [`with`] statement to rebind the [context](g) after each successful operation +- The [`Get`] method to retrieve arguments by name +- The `$` to access the template context -{{< code file=content-image.md >}} -{{}} +[`get`]: /methods/shortcode/get/ +[`with`]: /functions/go-template/with/ + +{{% note %}} +Make sure that you thoroughly understand the concept of context. The most common templating errors made by new users relate to context. + +Read more about context in the [introduction to templating]. + +[introduction to templating]: /templates/introduction/ +{{% /note %}} + +### Insert image with error handling + +The previous example, while functional, silently fails if the image is missing, and does not gracefully exit if a required argument is missing. We'll add error handling to address these issues: + +{{< code file=layouts/shortcodes/image.html >}} +{{ with .Get "path" }} + {{- with $r := $.Page.Resources.Get ($.Get "path") }} + {{- with $.Get "width" }} + {{- with $r.Process (printf "resize %dx wepb" ($.Get "width" )) }} + {{- $alt := or ($.Get "alt") "" }} + {{ $alt }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode requires a 'width' argument: see %s" $.Name $.Position }} + {{- end }} + {{- else }} + {{ warnf "The %q shortcode was unable to find %s: see %s" $.Name ($.Get "path") $.Position }} + {{- end }} +{{- else }} + {{ errorf "The %q shortcode requires a 'path' argument: see %s" .Name .Position }} +{{- end -}} {{< /code >}} -You have created the shortcode at `/layouts/shortcodes/img.html`, which loads the following shortcode template: +This template throws an error and gracefully fails the build if the author neglected to provide a `path` or `width` argument, and it emits a warning if it cannot find the image at the specified path. If the author does not provide an `alt` argument, the `alt` attribute is set to an empty string. -{{< code file=layouts/shortcodes/img.html >}} - -
- {{ with .Get "link" }}{{ end }} - - {{ if .Get "link" }}{{ end }} - {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr") }} -
{{ if isset .Params "title" }} -

{{ .Get "title" }}

{{ end }} - {{ if or (.Get "caption") (.Get "attr") }}

- {{ .Get "caption" }} - {{ with .Get "attrlink" }} {{ end }} - {{ .Get "attr" }} - {{ if .Get "attrlink" }} {{ end }} -

{{ end }} -
- {{ end }} -
- -{{< /code >}} +The [`Name`] and [`Position`] methods provide helpful context for errors and warnings. For example, a missing `width` argument causes the shortcode to throw this error: -Would be rendered as: +[`name`]: /methods/shortcode/name/ +[`position`]: /methods/shortcode/position/ -{{< code file=img-output.html >}} -
- -
-

Steve Francia

-
-
-{{< /code >}} - -### Single flexible example: `vimeo` - -```go-html-template -{{}} -{{}} +```text +ERROR The "image" shortcode requires a 'width' argument: see "/home/user/project/content/example/index.md:7:1" ``` -Would load the template found at `/layouts/shortcodes/vimeo.html`: +### Positional arguments -{{< code file=layouts/shortcodes/vimeo.html >}} -{{ if .IsNamedParams }} -
- -
-{{ else }} -
- -
-{{ end }} +Shortcode arguments can be [named or positional]. We used named arguments previously; let's explore positional arguments. Here's the named argument version of our example: + +[named or positional]: /content-management/shortcodes/#arguments + +{{< code file=content/example/index.md >}} +{{}} {{< /code >}} -Would be rendered as: +Here's how to call it with positional arguments: -{{< code file=vimeo-iframes.html >}} -
- -
-
- +{{< code file=content/example/index.md >}} +{{}} +{{< /code >}} + +Using the `Get` method with zero-indexed keys, we'll initialize variables with descriptive names in our template: + +{{< code file=layouts/shortcodes/image.html >}} +{{- $path := .Get 0 }} +{{- $width := .Get 1 }} +{{- $alt := .Get 2 }} +{{< /code >}} + +{{% note %}} +Positional arguments work well for frequently used shortcodes with one or two arguments. Since you'll use them often, the argument order will be easy to remember. For less frequently used shortcodes, or those with more than two arguments, named arguments improve readability and reduce the chance of errors. +{{% /note %}} + +### Named and positional arguments + +You can create a shortcode that will accept both named and positional arguments, but not at the same time. Use the [`IsNamedParams`] method to determine whether the shortcode call used named or positional arguments: + +{{< code file=layouts/shortcodes/image.html >}} +{{- $path := cond (.IsNamedParams) (.Get "path") (.Get 0) }} +{{- $width := cond (.IsNamedParams) (.Get "width") (.Get 1) }} +{{- $alt := cond (.IsNamedParams) (.Get "alt") (.Get 2) }} +{{< /code >}} + +This example uses the `cond` alias for the [`compare.Conditional`] function to get the argument by name if `IsNamedParams` returns `true`, otherwise get the argument by position. + +[`compare.Conditional`]: /functions/compare/conditional/ +[`IsNamedParams`]: /methods/shortcode/isnamedparams/ + +### Argument collection + +Use the [`Params`] method to access the arguments as a collection. + +[`Params`]: /methods/shortcode/params/ + +When using named arguments, the `Params` method returns a map: + +{{< code file=content/example/index.md >}} +{{}} +{{< /code >}} + +{{< code file=layouts/shortcodes/image.html >}} +{{- .Params.path }} → a.jpg +{{- .Params.width }} → 300 +{{- .Params.alt }} → A white kitten +{{< /code >}} + + When using positional arguments, the `Params` method returns a slice: + +{{< code file=content/example/index.md >}} +{{}} +{{< /code >}} + +{{< code file=layouts/shortcodes/image.html >}} +{{- index .Params 0 }} → a.jpg +{{- index .Params 1 }} → 300 +{{- index .Params 1 }} → A white kitten +{{< /code >}} + +Combine the `Params` method with the [`collections.IsSet`] function to determine if a parameter is set, even if its value is falsy. + +[`collections.IsSet`]: /functions/collections/isset/ + +### Inner content + +Extract the content enclosed within shortcode tags using the [`Inner`] method. This example demonstrates how to pass both content and a title to a shortcode. The shortcode then generates a `div` element containing an `h2` element (displaying the title) and the provided content. + +[`Inner`]: /methods/shortcode/inner/ + +{{< code file=content/example.md >}} +{{}} +This is a **bold** word, and this is an _emphasized_ word. +{{}} +{{< /code >}} + +{{< code file=layouts/shortcodes/contrived.html >}} +
+

{{ .Get "title" }}

+ {{ .Inner | .Page.RenderString }}
{{< /code >}} -### Paired example: `highlight` +The preceding example called the shortcode using [standard notation], requiring us to process the inner content with the [`RenderString`] method to convert the Markdown to HTML. This conversion is unnecessary when calling a shortcode using [Markdown notation]. -The following is taken from `highlight`, which is a [built-in shortcode] that ships with Hugo. +[`RenderString`]: /methods/page/renderstring/ +[markdown notation]: /content-management/shortcodes/#markdown-notation +[standard notation]: /content-management/shortcodes/#standard-notation -{{< code file=highlight-example.md >}} -{{}} - - This HTML - -{{}} -{{< /code >}} +### Nesting -The template for the `highlight` shortcode uses the following code, which is already included in Hugo: +The [`Parent`] method provides access to the parent shortcode context when the shortcode in question is called within the context of a parent shortcode. This provides an inheritance model. -```go-html-template -{{ .Get 0 | highlight .Inner }} -``` - -The rendered output of the HTML example code block will be as follows: - -{{< code file=syntax-highlighted.html >}} -
<html>
-    <body> This HTML </body>
-</html>
-
-{{< /code >}} - -### Nested shortcode: image gallery - -Hugo's [`.Parent`] shortcode method provides access to the parent shortcode context when the shortcode in question is called within the context of a parent shortcode. This provides an inheritance model. +[`Parent`]: /methods/shortcode/parent/ The following example is contrived but demonstrates the concept. Assume you have a `gallery` shortcode that expects one named `class` argument: @@ -333,23 +303,24 @@ The following example is contrived but demonstrates the concept. Assume you have You also have an `img` shortcode with a single named `src` argument that you want to call inside of `gallery` and other shortcodes, so that the parent defines the context of each `img`: {{< code file=layouts/shortcodes/img.html >}} -{{- $src := .Get "src" -}} -{{- with .Parent -}} +{{ $src := .Get "src" }} +{{ with .Parent }} -{{- else -}} +{{ else }} -{{- end -}} +{{ end }} {{< /code >}} You can then call your shortcode in your content as follows: -```go-html-template +{{< code file=content/example.md >}} {{}} {{}} {{}} {{}} {{}} -``` +{{< /code >}} + This will output the following HTML. Note how the first two `img` shortcodes inherit the `class` value of `content-gallery` set with the call to the parent `gallery`, whereas the third `img` only uses `src`: @@ -361,62 +332,30 @@ This will output the following HTML. Note how the first two `img` shortcodes inh ``` -## Error handling in shortcodes +### Other examples -Use the [`errorf`] template function with the [`Name`] and [`Position`] shortcode methods to generate useful error messages: +For guidance, consider examining Hugo's embedded shortcodes. The source code, available on [GitHub], can provide a useful model. -{{< code file=layouts/shortcodes/greeting.html >}} -{{ with .Get "name" }} -

Hello, my name is {{ . }}.

-{{ else }} - {{ errorf "The %q shortcode requires a 'name' argument. See %s" .Name .Position }} -{{ end }} +[GitHub]: https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/shortcodes + +## Detection + +The [`HasShortcode`] method allows you to check if a specific shortcode has been called on a page. For example, consider a custom audio shortcode: + +{{< code file=content/example.md >}} +{{}} {{< /code >}} -When the above fails, you will see an `ERROR` message such as: +You can use the `HasShortcode` method in your base template to conditionally load CSS if the audio shortcode was used on the page: -```sh -ERROR The "greeting" shortcode requires a 'name' argument. See "/home/user/project/content/_index.md:12:1" -``` +{{< code file=layouts/_default/baseof.html >}} + + ... + {{ if .HasShortcode "audio" }} + + {{ end }} + ... + +{{< /code >}} -## Inline shortcodes - -You can also implement your shortcodes inline -- e.g. where you use them in the content file. This can be useful for scripting that you only need in one place. - -This feature is disabled by default, but can be enabled in your site configuration: - -{{< code-toggle file=hugo >}} -[security] -enableInlineShortcodes = true -{{< /code-toggle >}} - -It is disabled by default for security reasons. The security model used by Hugo's template handling assumes that template authors are trusted, but that the content files are not, so the templates are injection-safe from malformed input data. But in most situations you have full control over the content, too, and then `enableInlineShortcodes = true` would be considered safe. But it's something to be aware of: It allows ad-hoc [Go Text templates](https://golang.org/pkg/text/template/) to be executed from the content files. - -And once enabled, you can do this in your content files: - - ```go-html-template - {{}}{{ now }}{{}} - ``` - -The above will print the current date and time. - -Note that an inline shortcode's inner content is parsed and executed as a Go text template with the same context as a regular shortcode template. - -This means that the current page can be accessed via `.Page.Title` etc. This also means that there are no concept of "nested inline shortcodes". - -The same inline shortcode can be reused later in the same content file, with different arguments if needed, using the self-closing syntax: - - ```go-html-template -{{}} -``` - -[`.Parent`]: /methods/shortcode/parent/ -[`errorf`]: /functions/fmt/errorf/ -[`Name`]: /methods/shortcode/name/ -[`Position`]: /methods/shortcode/position/ -[built-in shortcode]: /content-management/shortcodes/ -[figure]: /shortcodes/figure/ -[lookup order]: /templates/lookup-order/ -[source organization]: /getting-started/directory-structure/ -[vimeoexample]: #single-flexible-example-vimeo -[youtubeshortcode]: /shortcodes/youtube/ +[`HasShortcode`]: /methods/page/hasshortcode/ diff --git a/content/en/templates/sitemap.md b/content/en/templates/sitemap.md index 166f54933..b21372040 100644 --- a/content/en/templates/sitemap.md +++ b/content/en/templates/sitemap.md @@ -33,16 +33,16 @@ These are the default sitemap configuration values. They apply to all pages unle {{< code-toggle config=sitemap />}} changefreq -: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#changefreqdef). +: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#changefreqdef). -disable {{< new-in 0.125.0 >}} +disable {{< new-in 0.125.0 />}} : (`bool`) Whether to disable page inclusion. Default is `false`. Set to `true` in front matter to exclude the page. filename : (`string`) The name of the generated file. Default is `sitemap.xml`. priority -: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#priority). +: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#priority). ## Override default values diff --git a/content/en/templates/types/index.md b/content/en/templates/types/index.md index 0bd72ccc4..8369917bf 100644 --- a/content/en/templates/types/index.md +++ b/content/en/templates/types/index.md @@ -48,9 +48,7 @@ Hugo's [template lookup order] determines the template path, allowing you to cre [template lookup order]: /templates/lookup-order/ {{% note %}} -You must have thorough understanding of the [template lookup order] when creating templates. Template selection is based on template type, page kind, content type, section, language, and output format. - -[template lookup order]: /templates/lookup-order/ +You must have thorough understanding of the template lookup order when creating templates. Template selection is based on template type, page kind, content type, section, language, and output format. {{% /note %}} The purpose of each template type is described below. @@ -88,9 +86,7 @@ Learn more about [base templates](/templates/base/). ## Home -A home template renders your site's home page. For a single page site this is the only required template. - -For example, the home template below inherits the site's shell from the base template, and renders the home page content with a list of pages. +A home page template is used to render your site's home page, and is the only template required for a single-page website. For example, the home page template below inherits the site's shell from the base template and renders the home page content, such as a list of other pages. {{< code file=layouts/_default/home.html >}} {{ define "main" }} @@ -103,7 +99,7 @@ For example, the home template below inherits the site's shell from the base tem {{% include "templates/_common/filter-sort-group.md" %}} -Learn more about [home templates](/templates/home/). +Learn more about [home page templates](/templates/home/). ## Single @@ -241,7 +237,7 @@ For example, the render hook template below adds a `rel` attribute to external l {{- with .Title }} title="{{ . }}"{{ end -}} {{- if $u.IsAbs }} rel="external"{{ end -}} > - {{- with .Text | safeHTML }}{{ . }}{{ end -}} + {{- with .Text }}{{ . }}{{ end -}} {{- /* chomp trailing newline */ -}} {{< /code >}} @@ -260,10 +256,10 @@ For example, the shortcode template below renders an audio element from a [globa {{ end }} {{< /code >}} -Call the shortcode from your content page: +Then call the shortcode from within markup: {{< code file=content/example.md >}} -{{}} +{{}} {{< /code >}} Learn more about [shortcode templates](/templates/shortcode/). diff --git a/content/en/tools/_index.md b/content/en/tools/_index.md index 9cd72853a..a4df23800 100644 --- a/content/en/tools/_index.md +++ b/content/en/tools/_index.md @@ -1,7 +1,7 @@ --- title: Developer tools -linkTitle: In this section -description: In addition to Hugo's powerful CLI, there is a large number of community-developed tool chains for Hugo developers. + +description: Third-party tools to help you create and manage sites. categories: [] keywords: [] menu: diff --git a/content/en/tools/search.md b/content/en/tools/search.md index 73aea7ce4..152774304 100644 --- a/content/en/tools/search.md +++ b/content/en/tools/search.md @@ -29,7 +29,7 @@ A static website with a dynamic search function? Yes, Hugo provides an alternati : A bit like Hugo-lunr, but Hugo-lunr-zh can help you separate the Chinese keywords. [GitHub Gist for Fuse.js integration](https://gist.github.com/eddiewebb/735feb48f50f0ddd65ae5606a1cb41ae) -: This gist demonstrates how to leverage Hugo's existing build time processing to generate a searchable JSON index used by [Fuse.js](https://fusejs.io/) on the client-side. Although this gist uses Fuse.js for fuzzy matching, any client-side search tool capable of reading JSON indexes will work. Does not require npm, grunt, or other build-time tools except Hugo! +: This gist demonstrates how to leverage Hugo's existing build time processing to generate a searchable JSON index used by [Fuse.js](https://fusejs.io/) on the client side. Although this gist uses Fuse.js for fuzzy matching, any client-side search tool capable of reading JSON indexes will work. Does not require npm, grunt, or other build-time tools except Hugo! [hugo-search-index](https://www.npmjs.com/package/hugo-search-index) : A library containing Gulp tasks and a prebuilt browser script that implements search. Gulp generates a search index from project Markdown files. diff --git a/content/en/troubleshooting/_index.md b/content/en/troubleshooting/_index.md index 92ba6bc6d..a113fa9cf 100644 --- a/content/en/troubleshooting/_index.md +++ b/content/en/troubleshooting/_index.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -linkTitle: In this section + description: Use these techniques when troubleshooting your site. categories: [] keywords: [] diff --git a/content/en/troubleshooting/deprecation.md b/content/en/troubleshooting/deprecation.md index 81f2724a3..5dd9f3fa6 100644 --- a/content/en/troubleshooting/deprecation.md +++ b/content/en/troubleshooting/deprecation.md @@ -29,10 +29,16 @@ Common [reasons for deprecation]: After the project team deprecates something in code, Hugo will: -1. Log an INFO message for 6 minor releases[^1] -1. Log a WARN message for another 6 minor releases +1. Log an INFO message for 3 minor releases[^1] +1. Log a WARN message for another 12 minor releases 1. Log an ERROR message and fail the build thereafter +The project team will: + +1. On the deprecation date, update the documentation with a note describing the deprecation and any relevant alternatives. +1. Remove the code six or more minor releases after Hugo begins logging ERROR messages and failing the build. At that point, Hugo will throw an error, but the error message will no longer mention the deprecation. +1. Remove the corresponding documentation two years after the deprecation date. + To see the INFO messages, you must use the `--logLevel` command line flag: ```text diff --git a/content/en/troubleshooting/faq.md b/content/en/troubleshooting/faq.md index 4891c566b..2edb309be 100644 --- a/content/en/troubleshooting/faq.md +++ b/content/en/troubleshooting/faq.md @@ -51,11 +51,11 @@ If the answer to any of these questions is yes, either change the field values, ###### Why can't I see any of a page's descendants? -You may have an `index.md` file instead of an `_index.md` file. See [details](/content-management/page-bundles/). +You may have an `index.md` file instead of an `_index.md` file. See [details](/content-management/page-bundles/). -###### What is the difference between an index.md file and an _index.md file? +###### What is the difference between an `index.md` file and an `_index.md` file? -A directory with an `index.md file` is a [leaf bundle](g). A directory with an `_index.md` file is a [branch bundle](g). See [details](/content-management/page-bundles/). +A directory with an `index.md file` is a [leaf bundle](g). A directory with an `_index.md` file is a [branch bundle](g). See [details](/content-management/page-bundles/). ###### Why is my partial template not rendered as expected? diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/data/sponsors.toml b/data/sponsors.toml similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/data/sponsors.toml rename to data/sponsors.toml diff --git a/go.mod b/go.mod index 56a48deac..4b9e0a369 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/gohugoio/hugoDocs go 1.22.0 - -require github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743 // indirect diff --git a/hugo.toml b/hugo.toml index 2a271b6e3..c0094725f 100644 --- a/hugo.toml +++ b/hugo.toml @@ -1,9 +1,7 @@ -# This his the main configuration file. There are also environment specific configuration stored in the /config directory. - baseURL = "https://gohugo.io/" defaultContentLanguage = "en" enableEmoji = true -ignoreErrors = ["error-remote-getjson", "error-missing-instagram-accesstoken"] +ignoreLogs = ["error-missing-instagram-accesstoken"] languageCode = "en-us" pluralizeListTitles = false timeZone = "Europe/Oslo" @@ -13,90 +11,164 @@ title = "Hugo" disableAliases = true [pagination] -pagerSize = 100 + pagerSize = 100 [services.googleAnalytics] -ID = 'G-MBZGKNMDWC' - -[minify] - [minify.tdewolff] - [minify.tdewolff.html] - keepWhitespace = true - -[module] - [module.hugoVersion] - min = "0.56.0" - [[module.imports]] - path = "github.com/gohugoio/gohugoioTheme" + ID = 'G-MBZGKNMDWC' [outputs] - home = ["HTML", "RSS", "REDIR", "HEADERS"] - section = ["HTML"] + home = ["html", "rss", "redir", "headers"] + section = ["html"] + page = ["html"] + taxonomy = ["html"] + term = ["html"] -[mediaTypes] - [mediaTypes."text/netlify"] - delimiter = "" +[params] + description = "The world’s fastest framework for building websites" + ghrepo = "https://github.com/gohugoio/hugoDocs/" + + [params.render_hooks.link] + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) + +[languages] + [languages.en] + languageName = "English" + weight = 1 + +[security] + enableInlineShortcodes = false + [security.funcs] + getenv = ['^HUGO_', '^REPOSITORY_URL$', '^BRANCH$'] + [security.http] + methods = ['(?i)GET|POST'] + urls = ['.*'] [outputFormats] - [outputFormats.REDIR] - mediatype = "text/netlify" - baseName = "_redirects" - isPlainText = true - notAlternative = true - [outputFormats.HEADERS] - mediatype = "text/netlify" - baseName = "_headers" - isPlainText = true - notAlternative = true + [outputFormats.redir] + mediatype = "text/netlify" + baseName = "_redirects" + isPlainText = true + [outputFormats.headers] + mediatype = "text/netlify" + baseName = "_headers" + isPlainText = true + notAlternative = true -[caches] - [caches.getjson] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.getcsv] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.images] - dir = ":cacheDir/images" - maxAge = "1440h" - [caches.assets] - dir = ":resourceDir/_gen" - maxAge = -1 - [caches.getresource] - dir = ":cacheDir/:project" - maxage = '1h' +[markup] + [markup.highlight] + style = 'solarized-dark' + lineNumbersInTable = true + noClasses = false + wrapperClass = 'highlight not-prose' -[related] - threshold = 80 - includeNewer = true - toLower = false - [[related.indices]] - name = "keywords" - weight = 60 - [[related.indices]] - # Can be used as a front matter slice to link to other page fragments (headings) using their ID. - # This isn't particular useful in the current docs, but we're planning on getting a auto generated - # reference section with a better ID setup. - # For now, we just use it to give pages with same headings some similarity score. - name = "fragmentrefs" - type = "fragments" - applyFilter = false - weight = 60 - cardinalityThreshold = 50 + [markup.goldmark.renderer] + hardWraps = false + unsafe = false + xhtml = false -[imaging] - # See https://github.com/disintegration/imaging - # CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots. - # Note that you can also set this per image processing. - resampleFilter = "CatmullRom" - # Default JPEG quality setting. Default is 75. - quality = 75 - anchor = "smart" + [markup.goldmark.extensions] + definitionList = true + footnote = true + linkify = true + strikethrough = true + table = true + taskList = true + typographer = true -[taxonomies] - category = "categories" + [markup.goldmark.extensions.passthrough] + enable = true -[[cascade]] -categories = ['commands'] -[cascade._target] -path = '/commands/**' + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + inline = [['\(', '\)']] + + [markup.goldmark.parser] + autoHeadingID = true + autoHeadingIDType = "github" + + [markup.goldmark.parser.attribute] + block = true + title = true + +[mediaTypes] + [mediaTypes."text/netlify"] + delimiter = "" + +[module] + [module.hugoVersion] + min = "0.141.0" + [[module.mounts]] + source = "assets" + target = "assets" + [[module.mounts]] + lang = 'en' + source = 'content/en' + target = 'content' + [[module.mounts]] + source = "hugo_stats.json" + target = "assets/notwatching/hugo_stats.json" + disableWatch = true + +[build] + [build.buildStats] + disableIDs = true + enable = true + [[build.cachebusters]] + source = "assets/notwatching/hugo_stats\\.json" + target = "css" + [[build.cachebusters]] + source = "(postcss|tailwind)\\.config\\.js" + target = "css" + +[server] + [[server.headers]] + for = "/*" + + [server.headers.values] + X-Frame-Options = "DENY" + X-XSS-Protection = "1; mode=block" + X-Content-Type-Options = "nosniff" + Referrer-Policy = "no-referrer" + + [[server.headers]] + for = "/**.{css,js}" + +[minify] + [minify.tdewolff] + [minify.tdewolff.html] + keepSpecialComments = true + keepWhitespace = false + +######## GLOBAL ITEMS TO BE SHARED WITH THE HUGO SITES ######## +[menus] + [[menus.global]] + name = 'News' + weight = 1 + identifier = 'news' + pageRef = '/news/' + + [[menus.global]] + name = 'Docs' + weight = 5 + identifier = 'docs' + url = '/documentation/' + + [[menus.global]] + name = 'Themes' + weight = 10 + identifier = 'themes' + url = 'https://themes.gohugo.io/' + + [[menus.global]] + name = 'Community' + weight = 150 + identifier = 'community' + post = 'external' + url = 'https://discourse.gohugo.io/' + + [[menus.global]] + name = 'GitHub' + weight = 200 + identifier = 'github' + post = 'external' + url = 'https://github.com/gohugoio/hugo' diff --git a/hugo.work b/hugo.work index b2ae38c07..02c0ba91f 100644 --- a/hugo.work +++ b/hugo.work @@ -1,4 +1,4 @@ go 1.22.0 use . -use ../gohugoioTheme + diff --git a/layouts/404.html b/layouts/404.html new file mode 100644 index 000000000..bb92ea160 --- /dev/null +++ b/layouts/404.html @@ -0,0 +1,22 @@ +{{ define "main" }} +
+
+

+ Page not found + gopher +

+ + +
+
+{{ end }} diff --git a/layouts/_default/_markup/render-heading.html b/layouts/_default/_markup/render-heading.html new file mode 100644 index 000000000..c0e6c63ec --- /dev/null +++ b/layouts/_default/_markup/render-heading.html @@ -0,0 +1,10 @@ +{{ .Text | safeHTML }} + {{- if in (slice 2 3 4 6) .Level }}{{" " -}} + + + + + + +{{- end -}} + diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html index a0ee6b190..726610258 100644 --- a/layouts/_default/_markup/render-link.html +++ b/layouts/_default/_markup/render-link.html @@ -185,7 +185,7 @@ either of these shortcodes in conjunction with this render hook. {{- end }} {{- /* Render anchor element. */ -}} - + + + + + {{ .Title }} + + + + {{ partial "layouts/head/head-js.html" . }} + {{ with (templates.Defer (dict "key" "global")) }} + {{ $t := debug.Timer "tailwindcss" }} + {{ with resources.Get "css/styles.css" }} + {{ $opts := dict + "inlineImports" true + "minify" (not hugo.IsDevelopment) + }} + {{ with . | css.TailwindCSS $opts }} + {{ partial "helpers/linkcss.html" (dict "r" .) }} + {{ end }} + {{ end }} + {{ $t.Stop }} + {{ end }} + {{ partial "layouts/head/head.html" . }} + + + {{ partial "layouts/hooks/body-start.html" . }} + {{/* Layout. */}} + {{ block "header" . }} + {{ partial "layouts/header/header.html" . }} + {{ end }} + {{ block "hero" . }} + {{ end }} +
+
+ {{ block "main" . }}{{ end }} +
+ {{ block "rightsidebar" . }} + + {{ end }} +
+ {{/* Common icons. */}} + {{ partial "layouts/icons.html" . }} + {{/* Footer. */}} + {{ block "footer" . }} + {{ partial "layouts/footer.html" . }} + {{ end }} + {{ partial "layouts/hooks/body-end.html" . }} + + diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 000000000..76513788f --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,67 @@ +{{ define "main" }} + {{ $pages := "" }} + {{ $showDate := false }} + {{ if .IsPage }} + {{/* We currently have a slightly odd content structure with no top level /docs section. */}} + {{ $pages = .CurrentSection.Pages }} + {{ else }} + {{ if eq .Section "news" }} + {{ $pages = partial "news/get-news-items.html" . }} + {{ $showDate = true }} + {{ else }} + {{ $pages = .Pages }} + {{ end }} + {{ end }} + + +
+ {{ partial "layouts/docsheader.html" . }} + +
+{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 000000000..b6587633b --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,71 @@ +{{ define "main" }} + {{ $ttop := debug.Timer "single" }} +
+ {{ partial "layouts/docsheader.html" . }} +
+ {{ with .Params.description }} +
+ {{ . | markdownify }} +
+ {{ end }} + + {{ $t := debug.Timer "single.categories" }} + {{ $categories := .GetTerms "categories" }} + {{ with $categories }} +
+ {{ range . }} + {{ $text := .LinkTitle }} + {{ $class := "" }} + {{ range (slice true false ) }} + {{ $color := partial "helpers/funcs/color-from-string.html" (dict "text" $text "dark" . "--single" "green" ) }} + + {{ $prefix := "" }} + {{ if . }} + {{ $prefix = "dark:" }} + {{ end }} + {{ $class = printf "%sbg-%s-%d %stext-%s-%d border %sborder-%s-%d" + $prefix $color.color $color.shade1 + $prefix $color.color $color.shade2 + $prefix $color.color $color.shade3 + }} + {{ end }} + + + + {{ .LinkTitle }} + + {{ end }} +
+ {{ end }} + {{ $t.Stop }} + + {{ if .Params.action.signatures }} +
+ {{- partial "docs/functions-signatures.html" . -}} + {{- partial "docs/functions-return-type.html" . -}} + {{- partial "docs/functions-aliases.html" . -}} +
+ {{ end }} + {{ $t := debug.Timer "single.content" }} + {{ .Content }} + {{ $t.Stop }} + {{ $t := debug.Timer "single.page-edit" }} + {{ partial "layouts/page-edit.html" . }} + {{ $t.Stop }} +
+
+ {{ $ttop.Stop }} +{{ end }} + +{{ define "rightsidebar_content" }} + {{/* in-this-section.html depends on these being reneredc first. */}} + {{ $related := partial "layouts/related.html" . }} + {{ $toc := partial "layouts/toc.html" . }} + {{ if not .Params.hide_in_this_section }} + {{ partial "layouts/in-this-section.html" . }} + {{ end }} + {{ $related }} + {{ $toc }} +{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.headers b/layouts/index.headers similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/index.headers rename to layouts/index.headers diff --git a/layouts/index.html b/layouts/index.html new file mode 100644 index 000000000..50dc2fbf3 --- /dev/null +++ b/layouts/index.html @@ -0,0 +1,52 @@ +{{ define "main" }} +
+ {{ partial "layouts/home/opensource.html" . }} +
+ {{ partial "layouts/home/sponsors.html" (dict "ctx" . "gtag" "home" ) }} +
+ {{ partial "layouts/home/features.html" . }} +
+{{ end }} + +{{ define "hero" }} +
+
+
+ Hugo Logo +

+ The world’s fastest framework for building websites +

+
+ Hugo is one of the most popular open-source static site generators. + With its amazing speed and flexibility, Hugo makes building websites + fun again. +
+
+ {{ with site.GetPage "/getting-started" }} + {{ .LinkTitle }} + {{ end }} +
+ {{ partial "layouts/search/button.html" (dict "page" . "standalone" true) }} +
+
+
+
+
+{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} + +{{ define "leftsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.redir b/layouts/index.redir similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/index.redir rename to layouts/index.redir diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.rss.xml b/layouts/index.rss.xml similarity index 95% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/index.rss.xml rename to layouts/index.rss.xml index 26afc1816..460c4d30e 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.rss.xml +++ b/layouts/index.rss.xml @@ -9,7 +9,7 @@ {{- with site.Copyright }} {{ . }} {{- end }} - {{- with .OutputFormats.Get "RSS" }} + {{- with .OutputFormats.Get "rss" }} {{ printf "" .Permalink .MediaType | safeHTML }} {{- end }} @@ -17,7 +17,7 @@ {{- /* Get releases from GitHub. */}} {{- $u := "https://api.github.com/repos/gohugoio/hugo/releases" }} - {{- $releases := partial "utilities/get-remote-data.html" $u }} + {{- $releases := partial "helpers/funcs/get-remote-data.html" $u }} {{- $releases = where $releases "draft" false }} {{- $releases = where $releases "prerelease" false }} {{- range $releases | first 20 }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-aliases.html b/layouts/partials/docs/functions-aliases.html similarity index 56% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-aliases.html rename to layouts/partials/docs/functions-aliases.html index 3db9db4dc..dc562307a 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-aliases.html +++ b/layouts/partials/docs/functions-aliases.html @@ -3,10 +3,10 @@ {{- if gt (len .) 1 }} {{- $label = "Aliases" }} {{- end }} -

{{ $label }}

+

{{ $label }}

{{- range . }} -
+    
{{- . -}} -
+
{{- end }} {{- end -}} diff --git a/layouts/partials/docs/functions-return-type.html b/layouts/partials/docs/functions-return-type.html new file mode 100644 index 000000000..13c8c96f4 --- /dev/null +++ b/layouts/partials/docs/functions-return-type.html @@ -0,0 +1,6 @@ +{{- with .Params.action.returnType }} +

Returns

+
+ {{- . -}} +
+{{- end -}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html b/layouts/partials/docs/functions-signatures.html similarity index 63% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html rename to layouts/partials/docs/functions-signatures.html index dce160227..35d950265 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html +++ b/layouts/partials/docs/functions-signatures.html @@ -1,12 +1,12 @@ {{- with .Params.action.signatures }} -

Syntax

+

Syntax

{{- range . }} {{- $signature := . }} {{- if $.Params.function.returnType }} {{- $signature = printf "%s ⟼ %s" . $.Params.function.returnType }} {{- end }} -
+    
{{- $signature -}} -
+
{{- end }} {{- end -}} diff --git a/layouts/partials/helpers/funcs/color-from-string.html b/layouts/partials/helpers/funcs/color-from-string.html new file mode 100644 index 000000000..cd599530b --- /dev/null +++ b/layouts/partials/helpers/funcs/color-from-string.html @@ -0,0 +1,25 @@ +{{ $colors := slice "slate" "green" "cyan" "blue" }} +{{ with .single }} + {{ $colors = slice . }} +{{ end }} + +{{ $shades := slice 300 400 500 }} +{{ if not .dark }} + {{ $shades = slice 700 800 }} +{{ end }} +{{ $hash := (hash.FNV32a .text) }} +{{ $i := mod $hash (len $colors) }} +{{ $j := mod $hash (len $shades) }} +{{ $color := index $colors $i }} +{{ $shade1 := index $shades $j }} +{{ $shade2 := 0 }} +{{ $shade3 := 0 }} +{{ if gt $shade1 500 }} + {{ $shade2 = math.Min (sub $shade1 500) 100 | int }} + {{ $shade3 = sub $shade1 100 }} +{{ else }} + {{ $shade2 = math.Max (add $shade1 500) 700 | int }} + {{ $shade3 = add $shade1 200 }} +{{ end }} +{{ $res := dict "color" $color "shade1" $shade1 "shade2" $shade2 "shade3" $shade3 }} +{{ return $res }} diff --git a/layouts/partials/helpers/funcs/get-github-info.html b/layouts/partials/helpers/funcs/get-github-info.html new file mode 100644 index 000000000..7e2dc89fa --- /dev/null +++ b/layouts/partials/helpers/funcs/get-github-info.html @@ -0,0 +1,28 @@ +{{ $url := "https://api.github.com/repos/gohugoio/hugo" }} +{{ $cacheKey := print $url (now.Format "2006-01-02") }} +{{ $headers := dict }} +{{ with os.Getenv "HUGO_GH_TOKEN" }} + {{ $headers = dict "Authorization" (printf "Bearer %s" .) }} +{{ end }} +{{ $opts := dict "headers" $headers "key" $cacheKey }} +{{ $githubRepoInfo := dict }} +{{ with try (resources.GetRemote $url $opts) }} + {{ with .Err }} + {{ warnf "Failed to get GitHub repo info: %s" . }} + {{ else with (.Value | transform.Unmarshal) }} + {{ $githubRepoInfo = dict + "html_url" .html_url + "stargazers_url" .stargazers_url + "watchers_count" .watchers_count + "stargazers_count" .stargazers_count + "forks_count" .forks_count + "contributors_url" .contributors_url + "releases_url" .releases_url + "forks_count" .forks_count + }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} + {{ end }} +{{ end }} + +{{ return $githubRepoInfo }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html b/layouts/partials/helpers/funcs/get-remote-data.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html rename to layouts/partials/helpers/funcs/get-remote-data.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html b/layouts/partials/helpers/gtag.html similarity index 80% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html rename to layouts/partials/helpers/gtag.html index 7c6a9ade5..59bf36ba2 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html +++ b/layouts/partials/helpers/gtag.html @@ -1,11 +1,13 @@ {{ with site.Config.Services.GoogleAnalytics.ID }} - + +{{ else }} + {{ with $r | fingerprint }} + + {{ end }} +{{ end }} diff --git a/layouts/partials/helpers/picture.html b/layouts/partials/helpers/picture.html new file mode 100644 index 000000000..7f30f1437 --- /dev/null +++ b/layouts/partials/helpers/picture.html @@ -0,0 +1,25 @@ +{{ $image := .image }} +{{ $width := .width | default 1000 }} +{{ $width1x := div $width 2 }} +{{ $imageWebp := $image.Resize (printf "%dx webp" $width) }} +{{ $image1x := $image.Resize (printf "%dx" $width1x) }} +{{ $image1xWebp := $image.Resize (printf "%dx webp" $width1x) }} +{{ $class := .class | default "h-64 tablet:h-96 lg:h-full w-full object-cover lg:absolute" }} + + + + + + + diff --git a/layouts/partials/layouts/blocks/alert.html b/layouts/partials/layouts/blocks/alert.html new file mode 100644 index 000000000..705ce0e7e --- /dev/null +++ b/layouts/partials/layouts/blocks/alert.html @@ -0,0 +1,27 @@ +{{ $title := .title | default "" }} +{{ $color := .color | default "yellow" }} +{{ $icon := .icon | default "exclamation" }} +{{ $text := .text | default "" }} +{{ $class := .class | default "mt-6 mb-8" }} +
+
+
+ + + +
+
+ {{ with $title }} +

+ {{ . }} +

+ {{ end }} +
+

+ {{ $text }} +

+
+
+
+
diff --git a/layouts/partials/layouts/blocks/modal.html b/layouts/partials/layouts/blocks/modal.html new file mode 100644 index 000000000..7d825c06e --- /dev/null +++ b/layouts/partials/layouts/blocks/modal.html @@ -0,0 +1,30 @@ +
+
+ {{ .modal_button }} +
+ +
diff --git a/layouts/partials/layouts/breadcrumbs.html b/layouts/partials/layouts/breadcrumbs.html new file mode 100644 index 000000000..d0628c7ee --- /dev/null +++ b/layouts/partials/layouts/breadcrumbs.html @@ -0,0 +1,44 @@ +{{ $documentation := site.GetPage "/documentation" }} + + + + +{{ define "breadcrumbs-arrow" }} + + + +{{ end }} diff --git a/layouts/partials/layouts/docsheader.html b/layouts/partials/layouts/docsheader.html new file mode 100644 index 000000000..7e8e950f3 --- /dev/null +++ b/layouts/partials/layouts/docsheader.html @@ -0,0 +1,9 @@ +
+ {{ partial "layouts/breadcrumbs.html" . }} + {{ if and .IsPage (not (eq .Layout "list")) }} +

+ {{ .Title }} +

+ {{ end }} +
diff --git a/layouts/partials/layouts/explorer.html b/layouts/partials/layouts/explorer.html new file mode 100644 index 000000000..bb6f8e96a --- /dev/null +++ b/layouts/partials/layouts/explorer.html @@ -0,0 +1,47 @@ +{{/* This is currently not in use, but kept in case I change my mind. */}} + + +{{ define "docs-explorer-section" }} + {{ $p := .p }} + {{ $level := .level }} + {{ $pleft := $level }} + {{ if gt $level 0 }} + {{ $pleft = add $level 1 }} + {{ end }} + {{ $pl := printf "pl-%d" $pleft }} + {{ $pages := $p.Sections }} + + {{ range $pages }} + {{ $hasChildren := gt (len .Pages) 0 }} + {{ $class := cond (eq $level 0) "text-primary hover:text-primary/70" "text-gray-900 dark:text-gray-400 hover:dark:text-gray-300" }} +
  • + + {{ .LinkTitle }} + + {{ if $hasChildren }} +
      + {{ template "docs-explorer-section" (dict "p" . "level" (add $level 1)) }} +
    + {{ end }} +
  • + {{ end }} + +{{ end }} diff --git a/layouts/partials/layouts/footer.html b/layouts/partials/layouts/footer.html new file mode 100644 index 000000000..99a856f7e --- /dev/null +++ b/layouts/partials/layouts/footer.html @@ -0,0 +1,72 @@ +
    +
    +
    + {{/* Column 1 */}} +
    +
    + By the + Hugo Authors
    +
    + + Hugo Logo + + +
    + + {{/* Sponsors */}} +
    + {{ partial "layouts/home/sponsors.html" (dict + "ctx" . + "gtag" "footer" + + ) + }} +
    +
    +
    +

    + The Hugo logos are copyright © Steve Francia 2013–{{ now.Year }}. The + Hugo Gopher is based on an original work by Renée French. +

    +
    +
    +
    diff --git a/layouts/partials/layouts/head/head-js.html b/layouts/partials/layouts/head/head-js.html new file mode 100644 index 000000000..d83efcd0f --- /dev/null +++ b/layouts/partials/layouts/head/head-js.html @@ -0,0 +1,11 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +{{ $opts := dict "minify" true }} +{{ with resources.Get "js/head-early.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "async" "")) }} +{{ end }} +{{ with resources.Get "js/main.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} +{{ with resources.Get "js/turbo.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} diff --git a/layouts/partials/layouts/head/head.html b/layouts/partials/layouts/head/head.html new file mode 100644 index 000000000..391e80dca --- /dev/null +++ b/layouts/partials/layouts/head/head.html @@ -0,0 +1,52 @@ + + + + +{{ hugo.Generator }} + +{{ if hugo.IsProduction }} + +{{ else }} + +{{ end }} + + + {{ with .Title }}{{ . }} |{{ end }} + {{ .Site.Title }} + + + + + + + + + + + +{{ range .AlternativeOutputFormats -}} + +{{ end -}} + + + + + + +{{ partial "opengraph/opengraph.html" . }} +{{- template "_internal/schema.html" . -}} +{{- template "_internal/twitter_cards.html" . -}} + +{{ if hugo.IsProduction }} + {{ partial "helpers/gtag.html" . }} +{{ end }} diff --git a/layouts/partials/layouts/header/githubstars.html b/layouts/partials/layouts/header/githubstars.html new file mode 100644 index 000000000..75db5682a --- /dev/null +++ b/layouts/partials/layouts/header/githubstars.html @@ -0,0 +1,16 @@ +{{ with partialCached "helpers/funcs/get-github-info.html" . "-" }} + + + + + + + + {{ printf "%0.1fk" (div .stargazers_count 1000) }} + + +{{ end }} diff --git a/layouts/partials/layouts/header/header.html b/layouts/partials/layouts/header/header.html new file mode 100644 index 000000000..079c40bae --- /dev/null +++ b/layouts/partials/layouts/header/header.html @@ -0,0 +1,44 @@ +
    +
    + {{ with site.Home }} + HUGO + {{ end }} +
    +
    + {{ range .Site.Menus.global }} + {{ .Name }} + {{ end }} + +
    + +
    + {{/* Search. */}} + {{ partial "layouts/search/input.html" . }} +
    +
    + {{/* QR code. */}} + {{ partial "layouts/header/qr.html" . }} + {{/* Theme selector. */}} + {{ partial "layouts/header/theme.html" . }} + + {{/* Social. */}} + +
    +
    diff --git a/layouts/partials/layouts/header/qr.html b/layouts/partials/layouts/header/qr.html new file mode 100644 index 000000000..3d3f62e17 --- /dev/null +++ b/layouts/partials/layouts/header/qr.html @@ -0,0 +1,25 @@ +{{ $t := debug.Timer "qr" }} +{{ $qr := partial "partials/_inline/qr" (dict + "page" $ + "img_class" "w-10 bg-white view-transition-qr" ) +}} +{{ $qrBig := partial "partials/_inline/qr" (dict "page" $ "img_class" "w-64 p-4") }} +{{ $t.Stop }} + + +{{ define "partials/_inline/qr" }} + {{ $img_class := .img_class | default "w-10" }} + {{ with images.QR $.page.Permalink (dict "targetDir" "images/qr") }} + + QR code linking to {{ $.page.Permalink }} + {{ end }} +{{ end }} diff --git a/layouts/partials/layouts/header/theme.html b/layouts/partials/layouts/header/theme.html new file mode 100644 index 000000000..e0b356d1d --- /dev/null +++ b/layouts/partials/layouts/header/theme.html @@ -0,0 +1,35 @@ +
    + +
    diff --git a/layouts/partials/layouts/home/features.html b/layouts/partials/layouts/home/features.html new file mode 100644 index 000000000..527c98cb1 --- /dev/null +++ b/layouts/partials/layouts/home/features.html @@ -0,0 +1,56 @@ +{{/* icons source: https://heroicons.com/ */}} +{{ $dataTOML := ` + [[features]] + heading = "Optimized for speed" + copy = "Written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a large site in seconds, often less." + icon = """ + + + """ + [[features]] + heading = "Flexible framework" + copy = "With its multilingual support, and powerful taxonomy system, Hugo is widely used to create documentation sites, landing pages, corporate, government, nonprofit, education, news, event, and project sites." + icon = """ + + + """ + [[features]] + heading = "Fast assets pipeline" + copy = "Image processing (convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data), JavaScript bundling (tree shake, code splitting), Sass processing, great TailwindCSS support." + icon = """ + + + """ + [[features]] + heading = "Embedded web server" + copy = "Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. " + icon = """ + + + """ + ` +}} +{{ $data := $dataTOML | transform.Unmarshal }} +
    +
    +
    + {{ range $data.features }} +
    +
    +
    + {{ .icon | safeHTML }} +
    + {{ .heading }} +
    +
    + {{ .copy }} +
    +
    + {{ end }} + +
    +
    +
    diff --git a/layouts/partials/layouts/home/opensource.html b/layouts/partials/layouts/home/opensource.html new file mode 100644 index 000000000..b64bdd22b --- /dev/null +++ b/layouts/partials/layouts/home/opensource.html @@ -0,0 +1,110 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +
    +
    +
    +
    +

    + Open source +

    +

    + Hugo is open source and free to use. It is distributed under the + Apache 2.0 License. +

    +
    +
    +
    + + + + Popular. +
    +
    + As of writing this, Hugo has + {{ $githubInfo.stargazers_count }} + stars on GitHub. Join the crowd and hit the + Star button. +
    +
    +
    +
    + + + + Active. +
    +
    + Hugo has a large and active community. If you have questions or + need help, you can ask in the + Hugo forums. +
    +
    +
    +
    + + + + Frequent releases. +
    +
    + Hugo has a fast + release + cycle. The project is actively maintained and new features are + added regularly. +
    +
    +
    +
    +
    + {{ partial "helpers/picture.html" (dict + "image" (resources.Get "images/hugo-github-screenshot.png") + "alt" "Hugo GitHub Repository" + "width" 640 + "class" "w-full max-w-[38rem] ring-1 shadow-xl dark:shadow-gray-500 ring-gray-400/10") + }} +
    +
    diff --git a/layouts/partials/layouts/home/sponsors.html b/layouts/partials/layouts/home/sponsors.html new file mode 100644 index 000000000..1f92e1174 --- /dev/null +++ b/layouts/partials/layouts/home/sponsors.html @@ -0,0 +1,43 @@ +{{ $gtag := .gtag | default "unknown" }} +{{ $gtag := .gtag | default "unknown" }} +{{ $isFooter := (eq $gtag "footer") }} +{{ $utmSource := cond $isFooter "hugofooter" "hugohome" }} +{{ $containerClass := .containerClass | default "mx-auto max-w-7xl px-6 lg:px-8" }} +{{/* TODO1 prod: onclick="trackOutboundLink({{ printf "'%s', '%s'" $gtagID $url | safeJS }});" +*/}} +{{ with .ctx.Site.Data.sponsors }} +
    +

    Hugo Sponsors

    +
    + {{ range .banners }} +
    + {{ $query_params := .query_params | default "" }} + {{ $url := .link }} + {{ if not .no_query_params }} + {{ $url = printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" (.utm_medium | default "banner") "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }} + {{ end }} + {{ $logo := resources.Get .logo }} + {{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }} + + + +
    + {{ end }} +
    +
    +{{ end }} diff --git a/layouts/partials/layouts/hooks/body-end.html b/layouts/partials/layouts/hooks/body-end.html new file mode 100644 index 000000000..ba4a81300 --- /dev/null +++ b/layouts/partials/layouts/hooks/body-end.html @@ -0,0 +1 @@ +{{/* Empty for now */}} diff --git a/layouts/partials/layouts/hooks/body-start.html b/layouts/partials/layouts/hooks/body-start.html new file mode 100644 index 000000000..3430bd846 --- /dev/null +++ b/layouts/partials/layouts/hooks/body-start.html @@ -0,0 +1,3 @@ +{{ with resources.Get "js/body-start.js" | js.Build (dict "minify" true) }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "" "")) }} +{{ end }} diff --git a/layouts/partials/layouts/icons.html b/layouts/partials/layouts/icons.html new file mode 100644 index 000000000..c7194bc03 --- /dev/null +++ b/layouts/partials/layouts/icons.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/layouts/partials/layouts/in-this-section.html b/layouts/partials/layouts/in-this-section.html new file mode 100644 index 000000000..8be8cff8b --- /dev/null +++ b/layouts/partials/layouts/in-this-section.html @@ -0,0 +1,32 @@ +{{- with .CurrentSection.RegularPages }} + {{ $hasTocOrRelated := or ($.Store.Get "hasToc") ($.Store.Get "hasRelated") }} +
    +

    + In this section +

    + + +
    +{{- end }} diff --git a/layouts/partials/layouts/page-edit.html b/layouts/partials/layouts/page-edit.html new file mode 100644 index 000000000..b6f76a560 --- /dev/null +++ b/layouts/partials/layouts/page-edit.html @@ -0,0 +1,24 @@ +
    +
    + +
    + Last updated: + {{ .Lastmod.Format "January 2, 2006" }}{{ with .GitInfo }} + : + {{ .Subject }} ({{ .AbbreviatedHash }}) + {{ end }} +
    + + {{ with .File }} + {{ $href := printf "%sedit/master/content/%s/%s" site.Params.ghrepo $.Lang .Path }} + + Improve this page + + {{ end }} +
    diff --git a/layouts/partials/layouts/related.html b/layouts/partials/layouts/related.html new file mode 100644 index 000000000..16a04dce1 --- /dev/null +++ b/layouts/partials/layouts/related.html @@ -0,0 +1,37 @@ +{{- $heading := "See also" }} +{{- $related := slice }} + +{{- if .Params.action.related }} + {{- $related = slice }} + {{- range .Params.action.related }} + {{- $path := . | lower }} + {{- with or (site.GetPage $path) ($.GetPage $path) }} + {{- $related = $related | append . }} + {{- else }} + {{/* TODO1 make error */}} + {{- warnf "The 'related' partial was unable to get page %s" . }} + {{- end }} + {{- end }} +{{- else }} + {{- $related = site.RegularPages.Related . }} +{{- end }} +{{/* Avoid repeating pages that's listed in In this section. */}} +{{- $related = $related | complement .CurrentSection.RegularPages | first 7 }} +{{- with $related }} + {{ $.Store.Set "hasRelated" true }} +

    + {{ $heading }} +

    + +{{- end }} diff --git a/layouts/partials/layouts/search/button.html b/layouts/partials/layouts/search/button.html new file mode 100644 index 000000000..3dcb1eb9d --- /dev/null +++ b/layouts/partials/layouts/search/button.html @@ -0,0 +1,19 @@ + diff --git a/layouts/partials/layouts/search/input.html b/layouts/partials/layouts/search/input.html new file mode 100644 index 000000000..5f5ff07b9 --- /dev/null +++ b/layouts/partials/layouts/search/input.html @@ -0,0 +1,4 @@ +
    + {{ partial "layouts/search/button.html" (dict "page" . "standalone" false) }} + {{ partial "layouts/search/results.html" . }} +
    diff --git a/layouts/partials/layouts/search/results.html b/layouts/partials/layouts/search/results.html new file mode 100644 index 000000000..6ef519615 --- /dev/null +++ b/layouts/partials/layouts/search/results.html @@ -0,0 +1,82 @@ + diff --git a/layouts/partials/layouts/toc.html b/layouts/partials/layouts/toc.html new file mode 100644 index 000000000..06946d885 --- /dev/null +++ b/layouts/partials/layouts/toc.html @@ -0,0 +1,42 @@ +{{ with .Fragments.Headings }} + {{ $.Store.Set "hasToc" true }} +
    +

    + On this page +

    + +
    +{{ end }} + +{{ define "render-toc-level" }} + {{ range . }} + {{ if and .ID (or (ge .Level 2) (lt .Level 4)) }} +
  • + + {{ .Title | safeHTML }} + +
  • + {{ end }} + {{ with .Headings }} +
      + {{ template "render-toc-level" . }} +
    + {{ end }} + {{ end }} +{{ end }} diff --git a/layouts/partials/news/get-news-items.html b/layouts/partials/news/get-news-items.html new file mode 100644 index 000000000..cb1a930d3 --- /dev/null +++ b/layouts/partials/news/get-news-items.html @@ -0,0 +1,45 @@ +{{ $news_items := slice }} + +{{/* Get releases from GitHub. */}} +{{ $u := "https://api.github.com/repos/gohugoio/hugo/releases" }} +{{ $releases := partial "helpers/funcs/get-remote-data.html" $u }} +{{ $releases = where $releases "draft" false }} +{{ $releases = where $releases "prerelease" false }} +{{ range $releases | first 20 }} + {{ $publishDate := .published_at | time.AsTime }} + + {{/* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}} + {{ if eq .name "v0.138.0" }} + {{ $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }} + {{ end }} + + {{ $ctx := dict + "Date" $publishDate + "Title" (printf "Release %s" .name) + "LinkTitle" (printf "Release %s" .name) + "Permalink" .html_url + "RelPermalink" .html_url + "Section" "news" + "Summary" "" + }} + {{ $news_items = $news_items | append $ctx }} +{{ end }} + +{{/* Get content pages from news section. */}} +{{ range .Pages }} + {{ $ctx := dict + "Date" .Date + "Title" .Title + "LinkTitle" .Title + "RelPermalink" .RelPermalink + "Section" "news" + "Summary" .Summary + "Params" (dict "description" .Description) + }} + {{ $news_items = $news_items | append $ctx }} +{{ end }} + +{{/* Sort by date (descending) and render. */}} +{{ $news_items = sort $news_items "Date" "desc" }} + +{{ return $news_items }} diff --git a/layouts/partials/opengraph/get-featured-image.html b/layouts/partials/opengraph/get-featured-image.html new file mode 100644 index 000000000..50ee2a44d --- /dev/null +++ b/layouts/partials/opengraph/get-featured-image.html @@ -0,0 +1,26 @@ +{{ $images := $.Resources.ByType "image" }} +{{ $featured := $images.GetMatch "*feature*" }} +{{ if not $featured }} + {{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }} +{{ end }} +{{ if not $featured }} + {{ $featured = resources.Get "/opengraph/gohugoio-card-base-1.png" }} + {{ $size := 80 }} + {{ $title := $.LinkTitle }} + {{ if gt (len $title) 20 }} + {{ $size = 70 }} + {{ end }} + + {{ $text := $title }} + {{ $textOptions := dict + "color" "#FFF" + "size" $size + "lineSpacing" 10 + "x" 65 "y" 80 + "font" (resources.Get "/opengraph/mulish-black.ttf") + }} + + {{ $featured = $featured | images.Filter (images.Text $text $textOptions) }} +{{ end }} + +{{ return $featured }} diff --git a/layouts/partials/opengraph/opengraph.html b/layouts/partials/opengraph/opengraph.html new file mode 100644 index 000000000..1c89b0814 --- /dev/null +++ b/layouts/partials/opengraph/opengraph.html @@ -0,0 +1,84 @@ + + + + + +{{- with $.Params.images -}} + {{- range first 6 . }} + + {{ end -}} +{{- else -}} + {{- $featured := partial "opengraph/get-featured-image.html" . }} + {{- with $featured -}} + + {{- else -}} + {{- with $.Site.Params.images }} + + {{ end -}} + {{- end -}} +{{- end -}} + +{{- if .IsPage }} + {{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} + + {{ with .PublishDate }} + + {{ end }} + {{ with .Lastmod }} + + {{ end }} +{{- end -}} + +{{- with .Params.audio }}{{ end }} +{{- with .Params.locale }} + +{{ end }} +{{- with .Site.Params.title }} + +{{ end }} +{{- with .Params.videos }} + {{- range . }} + + {{ end }} + +{{ end }} + +{{- /* If it is part of a series, link to related articles */}} +{{- $permalink := .Permalink }} +{{- $siteSeries := .Site.Taxonomies.series }} +{{ with .Params.series }} + {{- range $name := . }} + {{- $series := index $siteSeries ($name | urlize) }} + {{- range $page := first 6 $series.Pages }} + {{- if ne $page.Permalink $permalink }} + + {{ end }} + {{- end }} + {{ end }} + +{{ end }} + +{{- /* Facebook Page Admin ID for Domain Insights */}} +{{- with site.Params.social.facebook_admin }} + +{{ end }} diff --git a/layouts/shortcodes/chroma-lexers.html b/layouts/shortcodes/chroma-lexers.html new file mode 100644 index 000000000..38241be33 --- /dev/null +++ b/layouts/shortcodes/chroma-lexers.html @@ -0,0 +1,7 @@ +
    + {{ range .Site.Data.docs.chroma.lexers }} +
    {{ .Name }}
    +
    {{ with .Aliases }}{{ delimit . ", " }}{{ end }}
    + {{ end }} +
    + \ No newline at end of file diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html b/layouts/shortcodes/code-toggle.html similarity index 59% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html rename to layouts/shortcodes/code-toggle.html index d1131132d..4e64fded0 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html +++ b/layouts/shortcodes/code-toggle.html @@ -60,27 +60,43 @@ {{- else }} {{- $code = $.Inner }} {{- end }} -
    -
    - {{- with $file }} -
    +
    + + + +
    -
    - {{- range $langs }} + {{ end }} + + {{ if $code }} + {{ range $i, $lang := $langs }}
    + class="max-h-96 overflow-y-auto border-l-1 border-b-1 border-r-1 border-gray-300 dark:border-gray-700" + x-ref="{{ $lang }}" + x-cloak + x-transition:enter.opacity.duration.300ms + x-show="$store.nav.userSettings.settings.configFileType === '{{ index $langs $i }}'"> {{- $hCode := $code | transform.Remarshal . }} {{- if and $fm (in (slice "toml" "yaml") .) }} {{- $hCode = printf "%s\n%s\n%s" $placeHolder $hCode $placeHolder }} @@ -88,14 +104,6 @@ {{- $hCode = $hCode | replaceRE `\n+` "\n" }} {{ highlight $hCode . "" | replaceRE $placeHolder (index $delimiters .) | safeHTML }}
    - {{- if $copy }} - - {{- /* Functionality located within filesaver.js The copy here is located in the css with .copy class so it can be replaced with JS on success */}} - {{- end }} - {{- end }} -
    + {{ end }} + {{ end }}
    diff --git a/layouts/shortcodes/code.html b/layouts/shortcodes/code.html new file mode 100644 index 000000000..1ccb44b7d --- /dev/null +++ b/layouts/shortcodes/code.html @@ -0,0 +1,38 @@ +{{- $codeLang := or (.Get "lang") "" }} +
    + {{ if (.Get "copy") }} + + + + {{ end }} + {{- with .Get "file" -}} + {{- if not $codeLang }} + {{- $ext := strings.TrimPrefix "." (path.Ext .) }} + {{- $codeLang = cond (eq $ext "html") "go-html-template" $ext }} + {{- end }} +
    + {{ . }} +
    + {{- end -}} + + +
    + {{ $inner := trim .Inner "\n" | safeHTML }} + {{ if .Get "nocode" }} + {{ $inner }} + {{ else }} + {{ with $codeLang }} + {{ highlight $inner . "" }} + {{ else }} +
    {{ $inner }}
    +        
    + {{ end }} + {{ end }} +
    +
    diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable-filtered.html b/layouts/shortcodes/datatable-filtered.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable-filtered.html rename to layouts/shortcodes/datatable-filtered.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable.html b/layouts/shortcodes/datatable.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable.html rename to layouts/shortcodes/datatable.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html b/layouts/shortcodes/deprecated-in.html similarity index 61% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html rename to layouts/shortcodes/deprecated-in.html index 7219d7f54..0272ea4a6 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html +++ b/layouts/shortcodes/deprecated-in.html @@ -3,12 +3,15 @@ {{ with .Get 0 }} {{ $version := printf "v%v" (strings.TrimLeft "vV" .) }} {{ $href := printf "https://github.com/gohugoio/hugo/releases/tag/%s" $version }} - + {{ $text := (printf `Deprecated in %s. +%s` $href $version $.Inner) | safeHTML }} + + {{ partial "layouts/blocks/alert.html" (dict + "text" $text + "color" "orange" + "icon" "exclamation" + ) +}} {{ else }} {{ errorf "The %q shortcode requires a single positional parameter indicating version. See %s" .Name .Position }} {{ end }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/eturl.html b/layouts/shortcodes/eturl.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/eturl.html rename to layouts/shortcodes/eturl.html diff --git a/layouts/shortcodes/glossary-term.html b/layouts/shortcodes/glossary-term.html index 92ad93340..7aace730e 100644 --- a/layouts/shortcodes/glossary-term.html +++ b/layouts/shortcodes/glossary-term.html @@ -9,12 +9,12 @@ Renders the definition of the given glossary term. */}} {{- with .Get 0 }} - {{- $path := printf "/getting-started/glossary/%s" (urlize .) }} + {{- $path := printf "/quick-reference/glossary/%s" (urlize .) }} {{- with site.GetPage $path }} -{{ .RenderShortcodes }}{{/* Do not indent. */}} +{{ .RenderShortcodes }} {{/* Do not indent. Do not remove non-breaking space. */}} {{- else }} {{- errorf "The glossary term (%s) shortcode was unable to find %s: see %s" $.Name $path $.Position }} {{- end }} {{- else }} {{- errorf "The glossary term (%s) shortcode requires one positional parameter: see %s" $.Name $.Position }} -{{- end }} +{{- end -}} diff --git a/layouts/shortcodes/glossary.html b/layouts/shortcodes/glossary.html index 3273efd8c..e91be32ce 100644 --- a/layouts/shortcodes/glossary.html +++ b/layouts/shortcodes/glossary.html @@ -13,13 +13,12 @@ shortcode. @example {{% glossary %}} */}} -{{- $path := "/getting-started/glossary" }} +{{- $path := "/quick-reference/glossary" }} {{- with site.GetPage $path }} - {{- with $p := .Pages.ByTitle }} {{- /* Build and render alphabetical index. */}} {{- $m := dict }} - {{- range $p }} + {{- range $p := .Pages.ByTitle }} {{- $k := substr .Title 0 1 | strings.ToUpper }} {{- if index $m $k }} {{- continue }} @@ -32,12 +31,26 @@ shortcode. {{- end }} {{- /* Render glossary terms. */}} - {{- range $p }} + {{- range $p := .Pages.ByTitle }} ###### {{ .Title }}{{/* Do not indent. */}} {{ .RenderShortcodes }}{{/* Do not indent. */}} + {{- with .Params.reference }} + {{- $destination := "" }} + {{- with $u := urls.Parse . }} + {{- if $u.IsAbs }} + {{- $destination = $u.String }} + {{- else }} + {{- with site.GetPage $u.Path -}} + {{- $destination = .RelPermalink }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the reference link %s: see %s" $.Name . $p.String }} + {{- end }} + {{- end }} + {{- end -}} + See [details]({{ $destination }}).{{/* Do not indent. */}} + {{- end }} {{- end }} - {{- end }} {{- else }} {{- errorf "The %q shortcode was unable to get %s: see %s" .Name $path .Position}} {{- end }} diff --git a/layouts/shortcodes/gomodules-info.html b/layouts/shortcodes/gomodules-info.html new file mode 100644 index 000000000..126d846c0 --- /dev/null +++ b/layouts/shortcodes/gomodules-info.html @@ -0,0 +1,12 @@ +{{ $text := ` + Most of the commands for **Hugo Modules** require a newer version (>= 1.18) of Go installed (see https://golang.org/dl/) and the relevant VCS client (e.g. Git, see https://git-scm.com/downloads/ ). + If you have an "older" site running on Netlify, you may have to set GO_VERSION to 1.19 or newer in your Environment settings. + + For more information about Go Modules, see: + + * https://go.dev/wiki/Modules + * https://blog.golang.org/using-go-modules + ` +}} + +{{ partial "layouts/blocks/alert.html" (dict "title" "Go Modules" "text" ($text | markdownify) "color" "orange" "icon" "exclamation") }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html b/layouts/shortcodes/hl.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/hl.html rename to layouts/shortcodes/hl.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html b/layouts/shortcodes/img.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html rename to layouts/shortcodes/img.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/imgproc.html b/layouts/shortcodes/imgproc.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/imgproc.html rename to layouts/shortcodes/imgproc.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html b/layouts/shortcodes/include.html similarity index 76% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html rename to layouts/shortcodes/include.html index 9456e197e..b4a20cd72 100644 --- a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html +++ b/layouts/shortcodes/include.html @@ -10,10 +10,11 @@ You must call this shortcode using the {{% %}} notation. */}} {{- with .Get 0 }} - {{- with $.Page.GetPage . }} + {{- with or ($.Page.GetPage .) (site.GetPage .) }} {{- .RenderShortcodes }} {{- else }} - {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} + {{/* TODO1 make error */}} + {{- warnf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} {{- end }} {{- else }} {{- errorf "The %q shortcode requires a positional parameter indicating the path of the file to include. See %s" .Name .Position }} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/list-pages-in-section.html b/layouts/shortcodes/list-pages-in-section.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/list-pages-in-section.html rename to layouts/shortcodes/list-pages-in-section.html diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/module-mounts-note.html b/layouts/shortcodes/module-mounts-note.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/module-mounts-note.html rename to layouts/shortcodes/module-mounts-note.html diff --git a/layouts/shortcodes/new-in.html b/layouts/shortcodes/new-in.html new file mode 100644 index 000000000..e99cc7bcf --- /dev/null +++ b/layouts/shortcodes/new-in.html @@ -0,0 +1,66 @@ +{{- /* + Renders a "new in" button indicating the version in which a feature was added. + + When comparing the current version to the specified version, the "new in" + button will be hidden if any of the following conditions is true: + + - The major version difference exceeds the majorVersionDiffThreshold + - The minor version difference exceeds the minorVersionDiffThreshold + + @param {string} version The semantic version string, with or without a leading v. + @returns {template.HTML} + + @examples {{< new-in 0.100.0 / +>}} + +{{< new-in 0.100.0 >}} +Some descriptive text here. +{{< /new-in >}} +*/}} +{{ $_hugo_config := `{ "version": 1 }` }} + +{{- /* Set defaults. */}} +{{- $majorVersionDiffThreshold := 0 }} +{{- $minorVersionDiffThreshold := 30 }} +{{- $displayExpirationWarning := true }} + +{{- /* Render. */}} +{{- with $version := .Get 0 | strings.TrimPrefix "v" }} + {{- $majorVersionDiff := sub (index (split hugo.Version ".") 0 | int) (index (split $version ".") 0 | int) }} + {{- $minorVersionDiff := sub (index (split hugo.Version ".") 1 | int) (index (split $version ".") 1 | int) }} + {{- if or (gt $majorVersionDiff $majorVersionDiffThreshold) (gt $minorVersionDiff $minorVersionDiffThreshold) }} + {{- if $displayExpirationWarning }} + {{- warnf "This call to the %q shortcode should be removed: %s. The button is now hidden because the specified version (%s) is older than the display threshold." $.Name $.Position $version }} + {{- end }} + {{- else }} + {{- $href := printf "https://github.com/gohugoio/hugo/releases/tag/v%s" $version }} + {{- with $.Inner }} + {{ $text := printf `

    New in v%s.

    %s` + $href $version (. | $.Page.RenderString (dict "display" "block")) + }} + + {{ partial "layouts/blocks/alert.html" (dict + "text" ($text | safeHTML) + "color" "green" + "icon" "exclamation" + ) + }} + {{- else }} + + + + + + New in + v{{ $version }} + + + {{- end }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a positional parameter (version). See %s" .Name .Position }} +{{- end -}} diff --git a/layouts/shortcodes/note.html b/layouts/shortcodes/note.html new file mode 100644 index 000000000..b18d53bc7 --- /dev/null +++ b/layouts/shortcodes/note.html @@ -0,0 +1,7 @@ +{{ $_hugo_config := `{ "version": 1 }` }} +{{ partial "layouts/blocks/alert.html" (dict + "text" .Inner + "color" "blue" + "icon" "exclamation" + ) +}} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/quick-reference.html b/layouts/shortcodes/quick-reference.html similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/quick-reference.html rename to layouts/shortcodes/quick-reference.html diff --git a/layouts/shortcodes/readfile.html b/layouts/shortcodes/readfile.html new file mode 100644 index 000000000..1a7d6a320 --- /dev/null +++ b/layouts/shortcodes/readfile.html @@ -0,0 +1 @@ +TODO readfile.html diff --git a/netlify.toml b/netlify.toml index 52699de3a..0cd557e7c 100644 --- a/netlify.toml +++ b/netlify.toml @@ -3,7 +3,7 @@ command = "hugo --gc --minify" [build.environment] - HUGO_VERSION = "0.142.0" + HUGO_VERSION = "0.143.1" [context.production.environment] HUGO_ENV = "production" @@ -16,7 +16,7 @@ HUGO_ENV = "production" [context.deploy-preview] - command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL" + command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL --enableGitInfo" [context.branch-deploy] command = "hugo --gc --minify -b $DEPLOY_PRIME_URL" @@ -24,7 +24,32 @@ [context.next.environment] HUGO_ENABLEGITINFO = "true" -[[redirects]] - from = "/npmjs/*" - to = "/npmjs/" - status = 200 +[[headers]] + for = "/*.jpg" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.png" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.css" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.js" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.ttf" + + [headers.values] + Cache-Control = "public, max-age=31536000" diff --git a/package.json b/package.json new file mode 100644 index 000000000..92339ba25 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "hugoDocs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "", + "devDependencies": { + "@tailwindcss/cli": "^4.0.0", + "@tailwindcss/typography": "^0.5.15", + "tailwindcss": "^4.0.0" + }, + "dependencies": { + "@alpinejs/focus": "^3.14.8", + "@alpinejs/persist": "^3.14.8", + "@hotwired/turbo": "^8.0.12", + "alpinejs": "^3.14.8" + } +} diff --git a/pull-theme.sh b/pull-theme.sh deleted file mode 100755 index 828b6cfb4..000000000 --- a/pull-theme.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -git subtree pull --prefix=themes/gohugoioTheme/ git@github.com:gohugoio/gohugoioTheme.git master --squash - diff --git a/src/css/_chroma.css b/src/css/_chroma.css deleted file mode 100644 index 1ad06604b..000000000 --- a/src/css/_chroma.css +++ /dev/null @@ -1,43 +0,0 @@ -/* Background */ .chroma { background-color: #f0f0f0 } -/* Error */ .chroma .ss4 { } -/* LineHighlight */ .chroma .hl { background-color: #ffffcc; display: block; width: 100% } -/* LineNumbers */ .chroma .ln { ; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* Keyword */ .chroma .s3e8 { color: #007020; font-weight: bold } -/* KeywordPseudo */ .chroma .s3ec { color: #007020 } -/* KeywordType */ .chroma .s3ee { color: #902000 } -/* NameAttribute */ .chroma .s7d1 { color: #4070a0 } -/* NameBuiltin */ .chroma .s7d2 { color: #007020 } -/* NameClass */ .chroma .s7d4 { color: #0e84b5; font-weight: bold } -/* NameConstant */ .chroma .s7d5 { color: #60add5 } -/* NameDecorator */ .chroma .s7d6 { color: #555555; font-weight: bold } -/* NameEntity */ .chroma .s7d7 { color: #d55537; font-weight: bold } -/* NameException */ .chroma .s7d8 { color: #007020 } -/* NameFunction */ .chroma .s7d9 { color: #06287e } -/* NameLabel */ .chroma .s7dc { color: #002070; font-weight: bold } -/* NameNamespace */ .chroma .s7dd { color: #0e84b5; font-weight: bold } -/* NameTag */ .chroma .s7e2 { color: #062873; font-weight: bold } -/* NameVariable */ .chroma .s7e3 { color: #bb60d5 } -/* LiteralString */ .chroma .sc1c { color: #4070a0 } -/* LiteralStringDoc */ .chroma .sc23 { color: #4070a0; font-style: italic } -/* LiteralStringEscape */ .chroma .sc25 { color: #4070a0; font-weight: bold } -/* LiteralStringInterpol */ .chroma .sc27 { color: #70a0d0; font-style: italic } -/* LiteralStringOther */ .chroma .sc29 { color: #c65d09 } -/* LiteralStringRegex */ .chroma .sc2a { color: #235388 } -/* LiteralStringSymbol */ .chroma .sc2c { color: #517918 } -/* LiteralNumber */ .chroma .sc80 { color: #40a070 } -/* Operator */ .chroma .sfa0 { color: #666666 } -/* OperatorWord */ .chroma .sfa1 { color: #007020; font-weight: bold } -/* Comment */ .chroma .s1770 { color: #60a0b0; font-style: italic } -/* CommentSpecial */ .chroma .s1774 { color: #60a0b0; background-color: #fff0f0 } -/* CommentPreproc */ .chroma .s17d4 { color: #007020 } -/* GenericDeleted */ .chroma .s1b59 { color: #a00000 } -/* GenericEmph */ .chroma .s1b5a { font-style: italic } -/* GenericError */ .chroma .s1b5b { color: #ff0000 } -/* GenericHeading */ .chroma .s1b5c { color: #000080; font-weight: bold } -/* GenericInserted */ .chroma .s1b5d { color: #00a000 } -/* GenericOutput */ .chroma .s1b5e { color: #888888 } -/* GenericPrompt */ .chroma .s1b5f { color: #c65d09; font-weight: bold } -/* GenericStrong */ .chroma .s1b60 { font-weight: bold } -/* GenericSubheading */ .chroma .s1b61 { color: #800080; font-weight: bold } -/* GenericTraceback */ .chroma .s1b62 { color: #0044dd } -/* TextWhitespace */ .chroma .s1f41 { color: #bbbbbb } diff --git a/src/package-lock.json b/src/package-lock.json deleted file mode 100644 index 48e341a09..000000000 --- a/src/package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lockfileVersion": 1 -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-144x144.png b/static/android-chrome-144x144.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-144x144.png rename to static/android-chrome-144x144.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-192x192.png b/static/android-chrome-192x192.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-192x192.png rename to static/android-chrome-192x192.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-256x256.png b/static/android-chrome-256x256.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-256x256.png rename to static/android-chrome-256x256.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-36x36.png b/static/android-chrome-36x36.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-36x36.png rename to static/android-chrome-36x36.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-48x48.png b/static/android-chrome-48x48.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-48x48.png rename to static/android-chrome-48x48.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-72x72.png b/static/android-chrome-72x72.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-72x72.png rename to static/android-chrome-72x72.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-96x96.png b/static/android-chrome-96x96.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-96x96.png rename to static/android-chrome-96x96.png diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png index 50e23ce1d6ad3f973bd3a72ebfb79cc55510687f..ecf1fc020af4ac75a5875c266315b26b72eebdd7 100644 GIT binary patch literal 6238 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%0DigS-Tk@$3S)my96 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 diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/browserconfig.xml b/static/browserconfig.xml similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/browserconfig.xml rename to static/browserconfig.xml diff --git a/static/css/hugofont.css b/static/css/hugofont.css deleted file mode 100644 index 09d6ce070..000000000 --- a/static/css/hugofont.css +++ /dev/null @@ -1,184 +0,0 @@ -@font-face { - font-family: 'hugo'; - src:url('../fonts/hugo.eot'); - src:url('../fonts/hugo.eot?#iefix') format('embedded-opentype'), - url('../fonts/hugo.woff') format('woff'), - url('../fonts/hugo.ttf') format('truetype'), - url('../fonts/hugo.svg#hugo') format('svg'); - font-weight: normal; - font-style: normal; -} - -[class^="icon-"], [class*=" icon-"] { - font-family: 'hugo'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.icon-home:before { - content: "\21"; -} -.icon-html5:before { - content: "\23"; -} -.icon-css3:before { - content: "\24"; -} -.icon-console:before { - content: "\25"; -} -.icon-link:before { - content: "\26"; -} -.icon-fire:before { - content: "\28"; -} -.icon-check-alt:before { - content: "\29"; -} -.icon-hugo_serif:before { - content: "\e600"; -} -.icon-x-altx-alt:before { - content: "\2a"; -} -.icon-circlestar:before { - content: "\2b"; -} -.icon-file-css:before { - content: "\2c"; -} -.icon-radio-checked:before { - content: "\2e"; -} -.icon-quote:before { - content: "\44"; -} -.icon-airplane2:before { - content: "\45"; -} -.icon-heart:before { - content: "\46"; -} -.icon-rocket:before { - content: "\47"; -} -.icon-house:before { - content: "\48"; -} -.icon-arrow-right:before { - content: "\e001"; -} -.icon-arrow-left:before { - content: "\e002"; -} -.icon-flow-branch:before { - content: "\e004"; -} -.icon-pen:before { - content: "\e005"; -} -.icon-idea:before { - content: "\3b"; -} -.icon-gears:before { - content: "\3c"; -} -.icon-talking:before { - content: "\3d"; -} -.icon-tag:before { - content: "\3e"; -} -.icon-rocket2:before { - content: "\3f"; -} -.icon-octocat:before { - content: "\41"; -} -.icon-announce:before { - content: "\42"; -} -.icon-edit:before { - content: "\43"; -} -.icon-power-cord:before { - content: "\50"; -} -.icon-apple:before { - content: "\51"; -} -.icon-windows8:before { - content: "\52"; -} -.icon-tux:before { - content: "\53"; -} -.icon-file-xml:before { - content: "\54"; -} -.icon-fork:before { - content: "\55"; -} -.icon-arrow-down:before { - content: "\56"; -} -.icon-pacman:before { - content: "\e000"; -} -.icon-embed:before { - content: "\2f"; -} -.icon-code:before { - content: "\30"; -} -.icon-cc:before { - content: "\31"; -} -.icon-cc-by:before { - content: "\32"; -} -.icon-cc-nc:before { - content: "\33"; -} -.icon-beaker-alt:before { - content: "\39"; -} -.icon-w3c:before { - content: "\3a"; -} -.icon-bolt:before { - content: "\49"; -} -.icon-flow-tree:before { - content: "\4a"; -} -.icon-twitter:before { - content: "\4b"; -} -.icon-beaker:before { - content: "\4c"; -} -.icon-images:before { - content: "\4d"; -} -.icon-bubbles:before { - content: "\4e"; -} -.icon-meter2:before { - content: "\4f"; -} -.icon-hugo_sans:before { - content: "\68"; -} -.icon-spf13:before { - content: "\27"; -} diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 312c247c9..000000000 --- a/static/css/style.css +++ /dev/null @@ -1,684 +0,0 @@ -/* Import fonts */ -@import url(//fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic); - -/* ****************************** - For the github btn -****************************** */ - -.github-btn { - font-size: 11px; -} -.github-btn, -.github-btn .btn { - font-weight: bold; -} -.github-btn .btn-default { - text-shadow: 0 1px 0 #fff; - background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e0e0e0)); - background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e0e0e0, 100%); - background-image: -moz-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); - background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%); - background-repeat: repeat-x; - border-color: #dbdbdb; - border-color: #ccc; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); -} - -.github-btn .btn-default:hover, .github-btn .btn-default:focus { - background-color: #e0e0e0; - background-position: 0 -15px; - color: #333; - border-color: #adadad; -} - -.nav-github { - width: 325px; -} - .nav-github > span { - padding-right: 0.5em; - } - - .icon-github { - display: inline-block; - font-family: FontAwesome; - font-style: normal; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - .github-watchers .icon-github:before{ - content: "\f005"; - } - - .github-forks .icon-github:before{ - content: "\f126"; - } - -.gh-count{ - padding: 2px 5px 3px 4px; - color: #555; - text-decoration: none; - text-shadow:0 1px 0 #fff; - white-space:nowrap; - cursor:pointer; - border-radius:3px; - position:relative; - display:none; - margin-left:4px; - background-color:#fafafa; - border:1px solid #d4d4d4; -} - -.gh-count:hover,.gh-count:focus{color:#4183c4;text-decoration: none;} -.gh-count:before,.gh-count:after{content:' ';position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid} -.gh-count:before{top:50%;left:-3px;margin-top:-4px;border-width:4px 4px 4px 0;border-right-color:#fafafa} -.gh-count:after{top:50%;left:-4px;z-index:-1;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#d4d4d4} - -thead { - font-weight: bold; -} - -table { - width: 100%; -} - - -h1, h2, h3 { - margin-top: .8em; - margin-bottom: .7em; -} - -pre code { - font-size: 15px !important; - font-family: Menlo, Consolas, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', Monaco, 'Droid Sans Mono', monospace; -} - -body { - color: #353b44; - background: #edece4; - font-family: 'Lato', sans-serif; - padding: 0px !important; - margin: 0px !important; - font-size: 16px !important; - font-weight: 400; -} - -h2,h3,h4,h5{ - font-weight: 700; -} - - -h1[id]:before, h2[id]:before, h3[id]:before, h4[id]:before, h5[id]:before { - display: block; - content: " "; - margin-top: -75px; - height: 75px; - visibility: hidden; -} - -label{ - font-weight: 400; -} - -.sidebar-menu .fa { - width: 30px; - text-align: center; -} - -a, a:hover, a:focus { - text-decoration: none; - outline: none; - outline: 0; -} - -img { - max-width: 100%; - height: auto; -} - -.panel-body a { - line-height: 1.1; - display: inline-block; -} -.panel-body a:after { - display: block; - content: ""; - height: 1px; - width: 0%; - background-color: #ff4088; - -webkit-transition: width 0.5s ease; - -moz-transition: width 0.5s ease; - -ms-transition: width 0.5s ease; - transition: width 0.5s ease; -} - -.panel-body a:hover:after, .panel-body a:focus:after { - width: 100%; -} - -input:focus, textarea:focus { outline: none; } -*:focus {outline: none;} -::selection { - background: #ff4088; - color: #fff; -} -::-moz-selection { - background: #ff4088; - color: #fff; -} - -#container { - width: 100%; - height: 100%; -} - -/*sidebar navigation*/ - -#sidebar { - width: 214px; - height: 100%; - position: fixed; - background: #ffffff; - overflow-y: auto; -} - - -ul.sidebar-menu , ul.sidebar-menu li ul.sub{ - margin: -2px 0 0; - padding: 0; -} - -ul.sidebar-menu { - margin-top: 60px; -} - -#sidebar > ul > li > ul.sub { - display: none; -} - -#sidebar > ul > li.active > ul.sub, #sidebar > ul > li > ul.sub > li > a { - display: block; -} - -ul.sidebar-menu li ul.sub li{ - background: #eeeeee; - margin-bottom: 0; - margin-left: 0; - margin-right: 0; -} - -ul.sidebar-menu li ul.sub li:last-child{ - border-radius: 0 0 4px 4px; - -webkit-border-radius: 0 0 4px 4px; -} - -ul.sidebar-menu li ul.sub li a { - font-size: 12px; - padding: 0 0 0 32px; - line-height: 35px; - height: 35px; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - color: #656C73; - font-size: 14px; -} - -ul.sidebar-menu li ul.sub li a:hover, ul.sidebar-menu li ul.sub li.active a { - color: #ff4088; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - display: block; -} - -ul.sidebar-menu li{ - line-height: 20px !important; -} - -ul.sidebar-menu li.sub-menu{ - line-height: 15px; - font-size: 16px; -} - -ul.sidebar-menu li a span{ - display: inline-block; -} - -ul.sidebar-menu li a{ - color: #72767D; - text-decoration: none; - display: block; - padding: 10px 0 10px 10px; - font-size: 16px; - font-weight: 400; - outline: none; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - border-right: 1px solid #D7D7D7; - border-bottom: 1px solid #D7D7D7; - white-space: nowrap; -} - -ul.sidebar-menu li.active a, ul.sidebar-menu li a:hover, ul.sidebar-menu li a:focus { - background: #eeeeee; - color: #ff4088; - display: block; - /*border-radius: 4px; - -webkit-border-radius: 4px;*/ - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; -} -ul.sidebar-menu li a:hover, ul.sidebar-menu li a:focus { - border-bottom: 1px solid #ff4088; -} -/*ul.sidebar-menu li.active a,*/ ul.sidebar-menu .sub-menu li.active a{ - border-bottom: 1px solid #ff4088; -} - -ul.sidebar-menu li a i { - font-size: 18px; - padding-right: 6px; - /*color: #ff4088;*/ -} - -ul.sidebar-menu li a:hover i, ul.sidebar-menu li a:focus i { - color: #ff4088; -} - -ul.sidebar-menu li.active a i { - color: #ff4088; -} - - -#sidebar ul > li > a .menu-arrow { - float: right; - margin-right: 8px; - margin-top: 6px; -} - -@-moz-document url-prefix() { - #sidebar ul > li > a .menu-arrow { - float: right; - margin-right: 8px; - margin-top: -16px; - } -} - -#main-content { - margin-left: 200px; - line-height: 1.8; - font-size: 18px; -} - -.header { - min-height: 60px; - padding: 0 10px; -} -.header { - position: fixed; - left: 0; - right: 0; - z-index: 1002; - text-align:center; -} - - -.black-bg { - background: rgba(20,20,20,0.9); - border-bottom: 1px solid #f1f2f7; -} - -.wrapper { - display: inline-block; - margin-top: 60px; - padding: 0px 15px 15px 0px; - width: 100%; -} - -a.logo { - font-size: 22px; - font-weight: 400; - color: #8E8E93; - float: left; - margin-top: 10px; - text-transform: uppercase; -} - -a.logo:hover, a.logo:focus { - text-decoration: none; - outline: none; -} - -h1.top-menu { - margin-top: -5px; -} -.title-row { - margin-top: 15px; - margin-left: 16px; - color: #EEE; -} -.notification-row { - float: right; - margin-top: 15px; - margin-left: 65px; -} - - -.top-nav { - margin-top: 15px; -} - -/*--sidebar toggle---*/ - -.toggle-nav { - float: left; - padding-right: 5px; - margin-top: 20px; - cursor: pointer; - color: gray; -} - -.toggle-nav .icon-reorder { - cursor: pointer; - display: inline-block; - font-size: 20px; -} - - -@-webkit-keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -@-ms-keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -@keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -.navigation { - position: absolute; - top: 0; - bottom: 0; - margin: 0; - max-width: 150px; - min-width: 90px; - width:100%; - min-height:1200px; - cursor:pointer; - display: flex; - justify-content: center; - align-content: center; - flex-direction: column; - font-size: 6em; - color: rgba(0,0,0,0.5); - text-align: center; - -webkit-transition: all 350ms ease; - transition: all 350ms ease; -} - -.navigation.next { - right:0; -} - - -.navigation:hover { - background-color: rgba(0,0,0,0.1); -} - -/* Google Custom Search box */ - -input.gsc-input, -.gsc-input-box, -.gsc-input-box-hover, -.gsc-input-box-focus, -.gsc-search-button, -.gsc-inline-block { - box-sizing: content-box; - line-height: normal; -} - -.gsc-control-cse { - padding: 0.1em 0 0.5em 1em !important; - width: 16em !important; - float: right; -} - -input.gsc-search-button-v2 { - padding: 6px 12px !important; -} - -.gsc-search-box-tools .gsc-search-box .gsc-input { - padding-right: 1px !important; -} - -/* Styled keypress from Wikipedia */ - -kbd { - border: 1px solid #aaa; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - border-radius: 0.2em; - -moz-box-shadow: 0.1em 0.2em 0.2em #ddd; - -webkit-box-shadow: 0.1em 0.2em 0.2em #ddd; - box-shadow: 0.1em 0.2em 0.2em #ddd; - background-color: #f9f9f9; - background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); - padding: 0.1em 0.3em; - font-family: inherit; - font-size: 0.85em; -} - -/* For definitions of variables */ - -dl { - margin: 1em; - border-bottom: 1px solid #ccc; -} - -dt { - float: left; - clear: left; - width: 9.5em; - margin: 0.125em; - padding: 2px 4px; -} - -dd { - padding: 0.2em 0 0.2em 10em; - border-top: 1px solid #ccc; -} - -/* Prevent linebreak right after an icon */ -#main-content .fa { - display: inline; -} - -/* Logo for FreeBSD until Font Awesome adds it, see https://github.com/FortAwesome/Font-Awesome/issues/1116 */ -i.freebsd-19px:before { - content: url(/img/freebsd-19px.svg); - vertical-align: -7%; -} - -/* Responsive videos */ -.video-container { - position: relative; - padding-bottom: 56.25%; /* 16:9 */ - padding-top: 30px; - height: 0; - overflow: hidden; - margin: 20px 0; -} - -.video-container iframe, -.video-container object, -.video-container embed { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -/* Google custom search */ -.cse { - margin-top: 20px; - padding-right: 20px; -} - - -/* Table of contents */ - -.toc ul { list-style: none; margin: 0; padding: 0 5px; } -.toc ul li { display: inline; } -#TableOfContents > ul > li > ul > li > ul li { margin-right: 8px; } -#TableOfContents > ul > li > ul > li > a, #TableOfContents > ul > li > a { font-weight: bold; background-color: #eeeeee; padding: 0 10px; margin: 0 2px; } -#TableOfContents > ul > li > ul > li > a { font-style: italic; } -.toc.compact ul > li > ul > li > ul { display: none; } - -#toc { - position:fixed; - background-color: rgba(0, 0, 0, 0.1); - padding: 10px 50px 10px 20px; -} - -.showcase-container { - display: inline-block; - position: relative; - width: 100%; -} - -.showcase-container img { - border: 1px solid #555; -} - -.showcase-container h4 { - margin-top: 0; - margin-bottom: 0; -} -.dummy { - padding-top: 90%; /* Making rows line up even if img proportions off */ -} - -.thumbnail { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -@media(max-width:1200px) { - .toc { - display: none; - } -} - - -/* Footer panel */ -.footer-panel { - width: 100%; - border-top:1px #efefef solid; - line-height: 30px; - padding: 25px 0px 15px; - margin-top: 15px; - background: #f9f9f9; - display: inline-block; - float: left; -} - -.footer-panel p { - padding-left: 20px; - padding-right: 20px; - font-size: medium; - font-style: italic; -} - - -/* Search form */ -#search-input { - width: 100%; - border: 1px solid #B3B3B3; - border-radius: 3px; - padding: 5px; -} - -#search-input:focus { - border-color: #F04A9C; -} - -/* Search result wrapper */ -.algolia-autocomplete { - width: 100%; -} - -/* List of search results */ -.aa-dropdown-menu { - box-sizing: border-box; - width: 100%; - background-color: #FFFFFF; - border: 1px solid #B3B3B3; - padding: 0; - font-size: 16px; - margin: 4 0 4 0; -} - -/* Highlight terms in search result headers */ -.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight { - background-color: #F04A9C; -} - -/* Highlight terms in search result body */ -.algolia-docsearch-suggestion--highlight { - color: #F04A9C; - font-weight: 900; -} - -/* Currently selected search result */ -.aa-cursor .algolia-docsearch-suggestion--content { - color: inherit; -} - -.aa-cursor .algolia-docsearch-suggestion { - background: #EFEFEF; - color: #353B44; -} - -.algolia-docsearch-suggestion { - font-size: 16px; - color: #9AA2AB; -} - -.algolia-docsearch-suggestion--category-header, -.algolia-docsearch-suggestion--subcategory-column { - display: none !important; -} diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-16x16.png b/static/favicon-16x16.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/favicon-16x16.png rename to static/favicon-16x16.png diff --git a/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-32x32.png b/static/favicon-32x32.png similarity index 100% rename from _vendor/github.com/gohugoio/gohugoioTheme/static/favicon-32x32.png rename to static/favicon-32x32.png diff --git a/static/favicon.ico b/static/favicon.ico index 36693330b4b4918c9f2c515be118cc1efe67a8ce..dc007a99e61445f212fe936b1f7af641e9747d30 100644 GIT binary patch literal 15086 zcmeHO32@Za8GkuwKt!UP1J-~g3PR-&1QBcPw9_(EXV7*~u@&l6aHh3@lq&?5qlrp6 z3lyjn5d=l<(~1%TqCm160^v$Hk_yNr5blH|`}Ozx|M%_wceA_fCcBW%keQG7_Py`; zz3>0d?>&@?QE{q62ZgkS`XpJYCzVnyTDX2EDs>EHiAY)hX1r4AU}y~Ma<{0jy%=16KVOu zYICiTD(mOKxDiz9!mP1dQ`T)H@7s`xG>&lfy`?t9j8wbY!mo3H>!L4ilncqXoV=8w z>|1JMOt^LXbKD?fThqEA2E+h*elbU78%5RfOZs82gXLzNMH&@$56GsF2>_4 zz`P4oZpBWFW8!wpv?hhJwBt2d|BM*KJj+<;rybO>i#mrvcPJRD!ZTEDi$}lhF-qB2 z!rpaX+^I1Rc`q?v7PT#wbv>N2;U^|`wt@L|Jt~Yk@{re0JD}6Pk~RjaO_)U^_lJI( z4BbaP>_?=o$$9 z`5>p&4*fT_)*hMl;0amLr6eeBjbEp12W2Qr9e%zAZROK8F@)QWq3WyJ=#RaiV=Ji4 z7x(Dc-7=-oQTe#TRrGJ~zM?VSDmD9;)6hX()aj=kw7r#Bi0M}CKp&3Hfwk%%k7Fd*31Xu+0To`*ejlK6{{_YjTa(px8C9P9Z~G)Jfg6Vc4?RHnG?j6B{x5 zYez3NF$V1)0Nztp+|1K26Ke01e?5F!R&^^g@a`HS^4~QgMXQm5idKny^+$};r#<7g zQagazF$Vl=J#vN1MKWJCeL&r`F`;&zzi&Zbo+3tK^=-rWMmfz!#X1gHu@ko~K38Tm z`&O29z9H+;Z;5GLcaej0#Ou#l0cIeT{yyeXK)@j%#1}i3F zB-YCK_j=*CVP5zIvOiGf&j8%+FrgW~7-;rzU++fBc|yj}R&4q_k{ zJ*LCL-?awd2Zp=&?@a$U`p;eeR~o~&b^P8&|JzeV)^s)Ja<SEa9AAcvj=tnP=ZSeSRbcVhQQ|80R>l z`Ix5FJ>ZV@Slbcp{U>edm=G~oF{J_{*DBxh`?v=7CWsyFvGOu@F&Z&r#>aS$*0HvL z_-*E%Q}J`k z!tGnmzQTI~-WO0;;5z}wN@5`$AgtPHh8}^JL#mXe6rN0)pOa_yLnjRx%35`~@`bb&={vCjYB}y2v)W<& z%!Qpwca5~2cJBe1|H${UrhB%->ju$>F4bhpGc zbJ7Dx4DuTXH)V3EgStYs8)?1~YUe}n>3FokEB21xhOhnjn|*NaLxBzCWw= zvFNu?p#Lrew#&pGfKX>&2;NSVsfw*=%KTMr<-OnYz`hw|9UIJVM%}N;6HN?}(2YCK zM(7_YzV`I#eb)a&ko@H9BRDGtIO2si9 zi}yuHom6a7s)T8hQeXq4QbkM?{(n>54&5%@PTg)n0~6>6?HBDQmKmh|sQs$_?6k3- z@3dNgehPBG(@MmT2>fB3Hb6fbhCXzL#5~I)pgO1u^xp*Z*`Bbu3FJEtHvvaa@&r{< zg-Yzvcz@mjbJTp;y#zAm;4xTBNU*EtHPqSXgSS1_yok(W@N0AEw87?aZI@@Csf`Qe zJEfsh}wd82H9=kw^@+6VA{n!J?>~{HajlMI+mDcnn33;oU`Y!uIDqMRLY`4qpSEaI+fQq}=V6@xj}s%`imb5g z_p4)HpPAV=sFS*BgSKeXV?WnzpKt5*xq@jV>}!40?XjPG*S_lmq%{u1|A$2W^DBH8 z^sRLqbFOi><(10)Pk;YN`v3A##AALZ@?|g33nqPm{5O9RDZSv`|D$2Q;-<*8lOk77 zAaxP**H2@NmPg;dX(9N(di+PD{cK}tG3cT<@qS9rQPH%2rBG!10J9Hd{vIjmRgtWr zCLVewn)Zv?|9@%DOU4-B9EE}EJ5Cdc`Jd|# zbyBxy{`agu;}Ub4W8ZX0ua9YXSM~4Yb4F~4YbfvkoMq;iD?S)t{l)xuAMNQhWT_(+x~P-7O&j(;w5jdu6#LaA;7A9L@z#?v++R;`QY1^; zUk|?jdd4*-uwNlBWqkJL&}mPn4QP)RnQhcTUH)u@QigLtL-=hFbm5M`^abO6lNub6kJ?=_hx56PB`ecnf-&OVL2q38^k2QhElA7tbI3OH7PuF)SZVtxhd$P=#KaQR(l zGoBX;N$^E~mW8W_{Nl8kAsU7eGUMDK8HOE-5Q4}=IHo#89n|FxU(g0^l_X(OvkBuA z3`?{Cx#JWedJz7ugSPhp9dZ%!*>#{Q=pTHAF>db+T7+}+<;e|>3W<4^fu6uT;diW} zj&a#E`2D+g>f+6O>x;%2$GF!-o@J~f4|z3^QSsTeQ2rVy4{z3;AGA9o+Xj@m@8~#> zY+Be;^7bsYOHQ=1N zA0ZEUDMMLX-JE2M(V`D=7US8CdzkVY=DRkoaa^m9EHdBuaDTf2c*si`%4&I@<+!eB z`ThUE{L>YLH++W|dgN(?yuswl@UH0WE|Ej;o5VMC+}qK9Nb=Xrw@D@E(VejO=9@3> z!9$g2`wyed8NE&7{}(95J~FucdD&p`-~Jh9+qnm`wx9l6(E7Zm{pR;8U_bk`;|GdG_Cc_PXdrmigm#+7JWA%3XxoOhhimLVGYG MPch=el%eeZ07#?~rvLx| literal 15086 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 O&%^d?wK=Z&YU^Z&Rz*6goGh7kkJ*T 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 literal 0 HcmV?d00001 diff --git a/static/fonts/Mulish-VariableFont_wght.ttf b/static/fonts/Mulish-VariableFont_wght.ttf new file mode 100644 index 0000000000000000000000000000000000000000..410f7aa63860fccf494f2f96c5b9bfbdfe0b2826 GIT binary patch literal 212500 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%0A(tM#-;mjK<-WjjiM>@8?a_qVb| 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
    {{- if not .pc.Simple -}} - + {{- end -}} {{- end -}} diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index b8a4ad833..d669bb333 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -256,7 +256,7 @@ Content: {{ .Content }} // Regular mode b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", "a7937c49665872d3") + b.AssertFileContent("public/index.html", "6e93404b93277876") // Simple mode files = strings.ReplaceAll(files, "privacy.instagram.simple = false", "privacy.instagram.simple = true") From b7ae24b9c2e4a9a99207110b439132afe5299908 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 08:49:17 +0000 Subject: [PATCH 188/374] build(deps): bump github.com/yuin/goldmark-emoji from 1.0.4 to 1.0.5 Bumps [github.com/yuin/goldmark-emoji](https://github.com/yuin/goldmark-emoji) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/yuin/goldmark-emoji/releases) - [Commits](https://github.com/yuin/goldmark-emoji/compare/v1.0.4...v1.0.5) --- updated-dependencies: - dependency-name: github.com/yuin/goldmark-emoji dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9809f3c5a..fe75b121a 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/tdewolff/parse/v2 v2.7.15 github.com/tetratelabs/wazero v1.9.0 github.com/yuin/goldmark v1.7.8 - github.com/yuin/goldmark-emoji v1.0.4 + github.com/yuin/goldmark-emoji v1.0.5 go.uber.org/automaxprocs v1.5.3 gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 diff --git a/go.sum b/go.sum index 564b2df46..55710da71 100644 --- a/go.sum +++ b/go.sum @@ -471,8 +471,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90= -github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk= +github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= From 521911a576af0091c9d07bb546e474bf98341b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 26 Feb 2025 10:15:04 +0100 Subject: [PATCH 189/374] all: Run modernize -fix ./... --- cache/dynacache/dynacache_test.go | 8 ++++---- cache/filecache/filecache_pruner_test.go | 6 +++--- cache/filecache/filecache_test.go | 6 +++--- codegen/methods.go | 15 ++++++++------- commands/commandeer.go | 8 ++++---- commands/config.go | 2 +- commands/gen.go | 2 +- commands/server.go | 7 +++---- common/collections/append.go | 4 ++-- common/collections/stack.go | 4 +++- common/hashing/hashing_test.go | 8 ++++---- common/herrors/error_locator.go | 10 ++-------- common/hreflect/helpers.go | 2 +- common/hstrings/strings.go | 15 +++------------ common/hugio/hasBytesWriter_test.go | 2 +- common/hugo/hugo.go | 5 +---- common/loggers/logger_test.go | 4 ++-- common/maps/ordered.go | 3 ++- common/maps/scratch_test.go | 2 +- common/para/para_test.go | 6 +++--- common/rungroup/rungroup.go | 2 +- common/tasks/tasks.go | 5 +---- common/types/convert.go | 2 +- common/types/evictingqueue.go | 3 ++- common/types/evictingqueue_test.go | 2 +- common/types/types.go | 2 +- config/allconfig/allconfig.go | 2 +- config/allconfig/load.go | 2 +- config/commonConfig.go | 3 ++- config/commonConfig_test.go | 2 +- config/defaultConfigProvider.go | 2 +- config/defaultConfigProvider_test.go | 2 +- config/namespace_test.go | 4 ++-- config/security/whitelist.go | 2 +- create/content_test.go | 2 +- helpers/content.go | 5 +---- helpers/emoji.go | 6 +----- helpers/general.go | 4 ++-- helpers/processing_stats.go | 2 +- helpers/url_test.go | 12 ++++++------ htesting/hqt/checkers.go | 4 ++-- hugofs/fileinfo.go | 2 +- hugofs/fs.go | 2 +- hugofs/glob/glob.go | 2 +- hugofs/walk_test.go | 4 ++-- hugolib/cascade_test.go | 2 +- hugolib/config_test.go | 2 +- hugolib/content_map_test.go | 12 ++++++------ hugolib/doctree/nodeshiftree_test.go | 10 +++++----- hugolib/doctree/nodeshifttree.go | 2 +- hugolib/doctree/treeshifttree.go | 2 +- hugolib/filesystems/basefs.go | 2 +- hugolib/filesystems/basefs_test.go | 10 +++++----- hugolib/hugo_sites.go | 2 +- hugolib/page.go | 2 +- hugolib/page__content.go | 5 ++--- hugolib/pagebundler_test.go | 2 +- hugolib/pagecollections_test.go | 14 +++++++------- hugolib/pages_language_merge_test.go | 4 ++-- hugolib/paginator_test.go | 4 ++-- hugolib/rebuild_test.go | 6 +++--- hugolib/resource_chain_test.go | 4 ++-- hugolib/shortcode_test.go | 8 ++++---- hugolib/site.go | 5 +---- hugolib/site_render.go | 2 +- hugolib/site_stats_test.go | 6 +++--- hugolib/site_test.go | 6 +++--- hugolib/site_url_test.go | 2 +- hugolib/taxonomy_test.go | 2 +- hugolib/template_test.go | 4 ++-- identity/finder.go | 2 +- identity/identity_test.go | 6 +++--- internal/js/esbuild/resolve.go | 15 ++++++--------- internal/warpc/warpc.go | 4 ++-- internal/warpc/warpc_test.go | 8 ++++---- langs/language_test.go | 6 +++--- lazy/init_test.go | 2 +- markup/goldmark/codeblocks/render.go | 2 +- markup/goldmark/hugocontext/hugocontext.go | 4 ++-- markup/goldmark/passthrough/passthrough.go | 2 +- markup/rst/convert.go | 5 +---- markup/tableofcontents/tableofcontents.go | 2 +- markup/tableofcontents/tableofcontents_test.go | 2 +- media/config.go | 10 +++------- modules/client.go | 12 +++++------- navigation/menu.go | 3 ++- navigation/menu_cache.go | 3 ++- navigation/menu_cache_test.go | 6 +++--- output/layouts/layout.go | 2 +- parser/lowercase_camel_json.go | 6 +++--- parser/pageparser/pagelexer_intro.go | 2 +- parser/pageparser/pageparser.go | 2 +- related/inverted_index.go | 6 +++--- related/inverted_index_test.go | 16 ++++++++-------- related/related_integration_test.go | 2 +- releaser/releaser.go | 4 ++-- resources/image_test.go | 8 ++++---- resources/images/color.go | 8 +++----- resources/images/imagetesting/testing.go | 2 +- resources/page/page_matcher.go | 9 ++------- resources/page/pagemeta/page_frontmatter.go | 2 +- resources/page/pagemeta/page_frontmatter_test.go | 2 +- resources/page/pages_cache.go | 5 +++-- resources/page/pages_cache_test.go | 6 +++--- resources/page/pages_sort_test.go | 6 +++--- resources/page/pagination_test.go | 2 +- resources/page/site.go | 6 +++--- resources/page/testhelpers_test.go | 2 +- resources/postpub/fields.go | 4 ++-- resources/resource/resources.go | 3 ++- resources/resource_factories/bundler/bundler.go | 2 +- .../resource_factories/bundler/bundler_test.go | 2 +- .../create/create_integration_test.go | 4 ++-- resources/resource_metadata.go | 7 +++---- .../cssjs/postcss_integration_test.go | 2 +- resources/resources_integration_test.go | 2 +- resources/transform_test.go | 12 ++++++------ tpl/collections/apply.go | 4 ++-- tpl/collections/collections.go | 16 ++++++++-------- tpl/collections/collections_integration_test.go | 4 ++-- tpl/collections/collections_test.go | 2 +- tpl/collections/complement.go | 2 +- tpl/collections/index.go | 2 +- tpl/collections/reflect_helpers.go | 2 +- tpl/collections/sort.go | 2 +- tpl/collections/symdiff.go | 2 +- tpl/collections/where.go | 10 +++++----- tpl/collections/where_test.go | 6 +++--- tpl/data/data.go | 8 ++------ tpl/data/resources_test.go | 4 ++-- tpl/internal/templatefuncsRegistry.go | 2 +- tpl/math/math.go | 2 +- tpl/page/init.go | 2 +- tpl/templates/defer_integration_test.go | 4 ++-- tpl/tplimpl/template.go | 4 ++-- tpl/tplimpl/template_ast_transformers.go | 10 +++------- tpl/tplimpl/template_funcs.go | 5 ++--- tpl/transform/unmarshal_test.go | 4 ++-- transform/livereloadinject/livereloadinject.go | 2 +- transform/metainject/hugogenerator.go | 4 ++-- watcher/filenotify/poller_test.go | 4 ++-- 141 files changed, 302 insertions(+), 354 deletions(-) diff --git a/cache/dynacache/dynacache_test.go b/cache/dynacache/dynacache_test.go index 14abf240d..78b2fc82e 100644 --- a/cache/dynacache/dynacache_test.go +++ b/cache/dynacache/dynacache_test.go @@ -191,16 +191,16 @@ func TestPanicInCreate(t *testing.T) { return err } - for i := 0; i < 3; i++ { - for j := 0; j < 3; j++ { + for i := range 3 { + for range 3 { c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("panic-%d", i)) c.Assert(willErr(i), qt.ErrorMatches, fmt.Sprintf("error-%d", i)) } } // Test the same keys again without the panic. - for i := 0; i < 3; i++ { - for j := 0; j < 3; j++ { + for i := range 3 { + for range 3 { v, err := p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { return testItem{ name: key, diff --git a/cache/filecache/filecache_pruner_test.go b/cache/filecache/filecache_pruner_test.go index f0cecfe9f..b49ba7645 100644 --- a/cache/filecache/filecache_pruner_test.go +++ b/cache/filecache/filecache_pruner_test.go @@ -59,7 +59,7 @@ dir = ":resourceDir/_gen" caches, err := filecache.NewCaches(p) c.Assert(err, qt.IsNil) cache := caches[name] - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) cache.GetOrCreateBytes(id, func() ([]byte, error) { return []byte("abc"), nil @@ -74,7 +74,7 @@ dir = ":resourceDir/_gen" c.Assert(err, qt.IsNil) c.Assert(count, qt.Equals, 5, msg) - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) v := cache.GetString(id) if i < 5 { @@ -97,7 +97,7 @@ dir = ":resourceDir/_gen" c.Assert(count, qt.Equals, 4) // Now only the i5 should be left. - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) v := cache.GetString(id) if i != 5 { diff --git a/cache/filecache/filecache_test.go b/cache/filecache/filecache_test.go index 59fb09276..a30aaa50b 100644 --- a/cache/filecache/filecache_test.go +++ b/cache/filecache/filecache_test.go @@ -105,7 +105,7 @@ dir = ":cacheDir/c" } for _, ca := range []*filecache.Cache{caches.ImageCache(), caches.AssetsCache(), caches.GetJSONCache(), caches.GetCSVCache()} { - for i := 0; i < 2; i++ { + for range 2 { info, r, err := ca.GetOrCreate("a", rf("abc")) c.Assert(err, qt.IsNil) c.Assert(r, qt.Not(qt.IsNil)) @@ -193,11 +193,11 @@ dir = "/cache/c" var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for i := range 50 { wg.Add(1) go func(i int) { defer wg.Done() - for j := 0; j < 20; j++ { + for range 20 { ca := caches.Get(cacheName) c.Assert(ca, qt.Not(qt.IsNil)) filename, data := filenameData(i) diff --git a/codegen/methods.go b/codegen/methods.go index 299063bb5..08ac97b00 100644 --- a/codegen/methods.go +++ b/codegen/methods.go @@ -26,6 +26,7 @@ import ( "path/filepath" "reflect" "regexp" + "slices" "sort" "strings" "sync" @@ -102,7 +103,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T } for _, t := range include { - for i := 0; i < t.NumMethod(); i++ { + for i := range t.NumMethod() { m := t.Method(i) if excludes[m.Name] || seen[m.Name] { @@ -122,7 +123,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T method := Method{Owner: t, OwnerName: ownerName, Name: m.Name} - for i := 0; i < numIn; i++ { + for i := range numIn { in := m.Type.In(i) name, pkg := nameAndPackage(in) @@ -137,7 +138,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T numOut := m.Type.NumOut() if numOut > 0 { - for i := 0; i < numOut; i++ { + for i := range numOut { out := m.Type.Out(i) name, pkg := nameAndPackage(out) @@ -304,7 +305,7 @@ func (m Method) inOutStr() string { } args := make([]string, len(m.In)) - for i := 0; i < len(args); i++ { + for i := range args { args[i] = fmt.Sprintf("arg%d", i) } return "(" + strings.Join(args, ", ") + ")" @@ -316,7 +317,7 @@ func (m Method) inStr() string { } args := make([]string, len(m.In)) - for i := 0; i < len(args); i++ { + for i := range args { args[i] = fmt.Sprintf("arg%d %s", i, m.In[i]) } return "(" + strings.Join(args, ", ") + ")" @@ -339,7 +340,7 @@ func (m Method) outStrNamed() string { } outs := make([]string, len(m.Out)) - for i := 0; i < len(outs); i++ { + for i := range outs { outs[i] = fmt.Sprintf("o%d %s", i, m.Out[i]) } @@ -435,7 +436,7 @@ func (m Methods) ToMarshalJSON(receiver, pkgPath string, excludes ...string) (st // Exclude self for i, pkgImp := range pkgImports { if pkgImp == pkgPath { - pkgImports = append(pkgImports[:i], pkgImports[i+1:]...) + pkgImports = slices.Delete(pkgImports, i, i+1) } } } diff --git a/commands/commandeer.go b/commands/commandeer.go index 697ece1f0..bf9655637 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -101,8 +101,8 @@ type configKey struct { // This is the root command. type rootCommand struct { - Printf func(format string, v ...interface{}) - Println func(a ...interface{}) + Printf func(format string, v ...any) + Println func(a ...any) StdOut io.Writer StdErr io.Writer @@ -431,12 +431,12 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { // Used by mkcert (server). log.SetOutput(r.StdOut) - r.Printf = func(format string, v ...interface{}) { + r.Printf = func(format string, v ...any) { if !r.quiet { fmt.Fprintf(r.StdOut, format, v...) } } - r.Println = func(a ...interface{}) { + r.Println = func(a ...any) { if !r.quiet { fmt.Fprintln(r.StdOut, a...) } diff --git a/commands/config.go b/commands/config.go index 89f14e0e6..7d166b9b8 100644 --- a/commands/config.go +++ b/commands/config.go @@ -90,7 +90,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg os.Stdout.Write(buf.Bytes()) default: // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. - var m map[string]interface{} + var m map[string]any if err := json.Unmarshal(buf.Bytes(), &m); err != nil { return err } diff --git a/commands/gen.go b/commands/gen.go index 5e49d9e7d..b77deeeb7 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -222,7 +222,7 @@ url: %s } // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. - var m map[string]interface{} + var m map[string]any if err := json.Unmarshal(buf.Bytes(), &m); err != nil { return err } diff --git a/commands/server.go b/commands/server.go index d42ce6d29..d3a72ec9a 100644 --- a/commands/server.go +++ b/commands/server.go @@ -65,6 +65,7 @@ import ( "github.com/spf13/fsync" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" + "maps" ) var ( @@ -195,9 +196,7 @@ func (f *fileChangeDetector) PrepareNew() { } f.prev = make(map[string]uint64) - for k, v := range f.current { - f.prev[k] = v - } + maps.Copy(f.prev, f.current) f.current = make(map[string]uint64) } @@ -759,7 +758,7 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error { c.serverPorts = make([]serverPortListener, len(conf.configs.Languages)) } currentServerPort := c.serverPort - for i := 0; i < len(c.serverPorts); i++ { + for i := range c.serverPorts { l, err := net.Listen("tcp", net.JoinHostPort(c.serverInterface, strconv.Itoa(currentServerPort))) if err == nil { c.serverPorts[i] = serverPortListener{ln: l, p: currentServerPort} diff --git a/common/collections/append.go b/common/collections/append.go index 8f1e21ea3..db9db8bf3 100644 --- a/common/collections/append.go +++ b/common/collections/append.go @@ -117,7 +117,7 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro tos = append(tos, nil) continue } - for i := 0; i < slice.Len(); i++ { + for i := range slice.Len() { tos = append(tos, slice.Index(i).Interface()) } } @@ -128,7 +128,7 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro func appendToInterfaceSlice(tov reflect.Value, from ...any) ([]any, error) { var tos []any - for i := 0; i < tov.Len(); i++ { + for i := range tov.Len() { tos = append(tos, tov.Index(i).Interface()) } diff --git a/common/collections/stack.go b/common/collections/stack.go index 96d32fe4b..ff0db2f02 100644 --- a/common/collections/stack.go +++ b/common/collections/stack.go @@ -13,6 +13,8 @@ package collections +import "slices" + import "sync" // Stack is a simple LIFO stack that is safe for concurrent use. @@ -73,7 +75,7 @@ func (s *Stack[T]) DrainMatching(predicate func(T) bool) []T { for i := len(s.items) - 1; i >= 0; i-- { if predicate(s.items[i]) { items = append(items, s.items[i]) - s.items = append(s.items[:i], s.items[i+1:]...) + s.items = slices.Delete(s.items, i, i+1) } } return items diff --git a/common/hashing/hashing_test.go b/common/hashing/hashing_test.go index bd66f3ebf..105b6d8b5 100644 --- a/common/hashing/hashing_test.go +++ b/common/hashing/hashing_test.go @@ -37,12 +37,12 @@ func TestXxHashFromReaderPara(t *testing.T) { c := qt.New(t) var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := range 10 { i := i wg.Add(1) go func() { defer wg.Done() - for j := 0; j < 100; j++ { + for j := range 100 { s := strings.Repeat("Hello ", i+j+1*42) r := strings.NewReader(s) got, size, err := XXHashFromReader(r) @@ -144,8 +144,8 @@ func BenchmarkHashString(b *testing.B) { } func BenchmarkHashMap(b *testing.B) { - m := map[string]interface{}{} - for i := 0; i < 1000; i++ { + m := map[string]any{} + for i := range 1000 { m[fmt.Sprintf("key%d", i)] = i } diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go index 1ece0cca4..acaebb4bc 100644 --- a/common/herrors/error_locator.go +++ b/common/herrors/error_locator.go @@ -152,10 +152,7 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext } if ectx.Position.LineNumber > 0 { - low := ectx.Position.LineNumber - 3 - if low < 0 { - low = 0 - } + low := max(ectx.Position.LineNumber-3, 0) if ectx.Position.LineNumber > 2 { ectx.LinesPos = 2 @@ -163,10 +160,7 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext ectx.LinesPos = ectx.Position.LineNumber - 1 } - high := ectx.Position.LineNumber + 2 - if high > len(lines) { - high = len(lines) - } + high := min(ectx.Position.LineNumber+2, len(lines)) ectx.Lines = lines[low:high] diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index 4d7339b5b..ab7883a47 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -245,7 +245,7 @@ func ToSliceAny(v any) ([]any, bool) { vvv := reflect.ValueOf(v) if vvv.Kind() == reflect.Slice { out := make([]any, vvv.Len()) - for i := 0; i < vvv.Len(); i++ { + for i := range vvv.Len() { out[i] = vvv.Index(i).Interface() } return out, true diff --git a/common/hstrings/strings.go b/common/hstrings/strings.go index 1232eee37..2df3486fc 100644 --- a/common/hstrings/strings.go +++ b/common/hstrings/strings.go @@ -20,6 +20,7 @@ import ( "sync" "github.com/gohugoio/hugo/compare" + "slices" ) var _ compare.Eqer = StringEqualFold("") @@ -50,12 +51,7 @@ func (s StringEqualFold) Eq(s2 any) bool { // EqualAny returns whether a string is equal to any of the given strings. func EqualAny(a string, b ...string) bool { - for _, s := range b { - if a == s { - return true - } - } - return false + return slices.Contains(b, a) } // regexpCache represents a cache of regexp objects protected by a mutex. @@ -103,12 +99,7 @@ func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) { // InSlice checks if a string is an element of a slice of strings // and returns a boolean value. func InSlice(arr []string, el string) bool { - for _, v := range arr { - if v == el { - return true - } - } - return false + return slices.Contains(arr, el) } // InSlicEqualFold checks if a string is an element of a slice of strings diff --git a/common/hugio/hasBytesWriter_test.go b/common/hugio/hasBytesWriter_test.go index f0d6c3a7b..9e689a112 100644 --- a/common/hugio/hasBytesWriter_test.go +++ b/common/hugio/hasBytesWriter_test.go @@ -46,7 +46,7 @@ func TestHasBytesWriter(t *testing.T) { return strings.Repeat("ab cfo", r.Intn(33)) } - for i := 0; i < 22; i++ { + for range 22 { h, w := neww() fmt.Fprint(w, rndStr()+"abc __foobar"+rndStr()) c.Assert(h.Patterns[0].Match, qt.Equals, true) diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index 815c25fa7..e745e5e90 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -416,10 +416,7 @@ func Deprecate(item, alternative string, version string) { // DeprecateLevelMin informs about a deprecation starting at the given version, but with a minimum log level. func DeprecateLevelMin(item, alternative string, version string, minLevel logg.Level) { - level := deprecationLogLevelFromVersion(version) - if level < minLevel { - level = minLevel - } + level := max(deprecationLogLevelFromVersion(version), minLevel) DeprecateLevel(item, alternative, version, level) } diff --git a/common/loggers/logger_test.go b/common/loggers/logger_test.go index b03e6d903..bc8975b06 100644 --- a/common/loggers/logger_test.go +++ b/common/loggers/logger_test.go @@ -37,7 +37,7 @@ func TestLogDistinct(t *testing.T) { l := loggers.New(opts) - for i := 0; i < 10; i++ { + for range 10 { l.Errorln("error 1") l.Errorln("error 2") l.Warnln("warn 1") @@ -137,7 +137,7 @@ func TestReset(t *testing.T) { l := loggers.New(opts) - for i := 0; i < 3; i++ { + for range 3 { l.Errorln("error 1") l.Errorln("error 2") l.Errorln("error 1") diff --git a/common/maps/ordered.go b/common/maps/ordered.go index eaa4d73c6..08dd77919 100644 --- a/common/maps/ordered.go +++ b/common/maps/ordered.go @@ -15,6 +15,7 @@ package maps import ( "github.com/gohugoio/hugo/common/hashing" + "slices" ) // Ordered is a map that can be iterated in the order of insertion. @@ -64,7 +65,7 @@ func (m *Ordered[K, T]) Delete(key K) { delete(m.values, key) for i, k := range m.keys { if k == key { - m.keys = append(m.keys[:i], m.keys[i+1:]...) + m.keys = slices.Delete(m.keys, i, i+1) break } } diff --git a/common/maps/scratch_test.go b/common/maps/scratch_test.go index 88fd73f2b..f07169e61 100644 --- a/common/maps/scratch_test.go +++ b/common/maps/scratch_test.go @@ -140,7 +140,7 @@ func TestScratchInParallel(t *testing.T) { for i := 1; i <= 10; i++ { wg.Add(1) go func(j int) { - for k := 0; k < 10; k++ { + for k := range 10 { newVal := int64(k + j) _, err := scratch.Add(key, newVal) diff --git a/common/para/para_test.go b/common/para/para_test.go index 2d9188ecf..cf24a4e37 100644 --- a/common/para/para_test.go +++ b/common/para/para_test.go @@ -42,7 +42,7 @@ func TestPara(t *testing.T) { c.Run("Order", func(c *qt.C) { n := 500 ints := make([]int, n) - for i := 0; i < n; i++ { + for i := range n { ints[i] = i } @@ -51,7 +51,7 @@ func TestPara(t *testing.T) { var result []int var mu sync.Mutex - for i := 0; i < n; i++ { + for i := range n { i := i r.Run(func() error { mu.Lock() @@ -78,7 +78,7 @@ func TestPara(t *testing.T) { var counter int64 - for i := 0; i < n; i++ { + for range n { r.Run(func() error { atomic.AddInt64(&counter, 1) time.Sleep(1 * time.Millisecond) diff --git a/common/rungroup/rungroup.go b/common/rungroup/rungroup.go index 96ec57883..80a730ca9 100644 --- a/common/rungroup/rungroup.go +++ b/common/rungroup/rungroup.go @@ -51,7 +51,7 @@ func Run[T any](ctx context.Context, cfg Config[T]) Group[T] { // Buffered for performance. ch := make(chan T, cfg.NumWorkers) - for i := 0; i < cfg.NumWorkers; i++ { + for range cfg.NumWorkers { g.Go(func() error { for { select { diff --git a/common/tasks/tasks.go b/common/tasks/tasks.go index 1f7e061f9..3f8a754e9 100644 --- a/common/tasks/tasks.go +++ b/common/tasks/tasks.go @@ -103,10 +103,7 @@ func (r *RunEvery) Add(name string, f Func) { f.IntervalHigh = 20 * time.Second } - start := f.IntervalHigh / 3 - if start < f.IntervalLow { - start = f.IntervalLow - } + start := max(f.IntervalHigh/3, f.IntervalLow) f.interval = start f.last = time.Now() diff --git a/common/types/convert.go b/common/types/convert.go index 0cb5035df..6b1750376 100644 --- a/common/types/convert.go +++ b/common/types/convert.go @@ -69,7 +69,7 @@ func ToStringSlicePreserveStringE(v any) ([]string, error) { switch vv.Kind() { case reflect.Slice, reflect.Array: result = make([]string, vv.Len()) - for i := 0; i < vv.Len(); i++ { + for i := range vv.Len() { s, err := cast.ToStringE(vv.Index(i).Interface()) if err != nil { return nil, err diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go index c3598f19f..a335be3b2 100644 --- a/common/types/evictingqueue.go +++ b/common/types/evictingqueue.go @@ -15,6 +15,7 @@ package types import ( + "slices" "sync" ) @@ -45,7 +46,7 @@ func (q *EvictingQueue[T]) Add(v T) *EvictingQueue[T] { if len(q.set) == q.size { // Full delete(q.set, q.vals[0]) - q.vals = append(q.vals[:0], q.vals[1:]...) + q.vals = slices.Delete(q.vals, 0, 1) } q.set[v] = true q.vals = append(q.vals, v) diff --git a/common/types/evictingqueue_test.go b/common/types/evictingqueue_test.go index cd10d3d8e..b93243f3c 100644 --- a/common/types/evictingqueue_test.go +++ b/common/types/evictingqueue_test.go @@ -55,7 +55,7 @@ func TestEvictingStringQueueConcurrent(t *testing.T) { queue := NewEvictingQueue[string](3) - for j := 0; j < 100; j++ { + for range 100 { wg.Add(1) go func() { defer wg.Done() diff --git a/common/types/types.go b/common/types/types.go index 062ecc403..082c058ff 100644 --- a/common/types/types.go +++ b/common/types/types.go @@ -59,7 +59,7 @@ func (k KeyValues) String() string { // KeyValues struct. func NewKeyValuesStrings(key string, values ...string) KeyValues { iv := make([]any, len(values)) - for i := 0; i < len(values); i++ { + for i := range values { iv[i] = values[i] } return KeyValues{Key: key, Values: iv} diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index af841fb54..3b3109aeb 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -82,7 +82,7 @@ func init() { } configLanguageKeys = make(map[string]bool) addKeys := func(v reflect.Value) { - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { name := strings.ToLower(v.Type().Field(i).Name) if skip[name] { continue diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 9e9c7a42a..f224009ac 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -305,7 +305,7 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error { _, ok := allDecoderSetups[key] if ok { // A map. - if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{}); err == nil { + if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil { val = v } } diff --git a/config/commonConfig.go b/config/commonConfig.go index a31c2312e..3dfd9b409 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -28,6 +28,7 @@ import ( "github.com/gohugoio/hugo/common/herrors" "github.com/mitchellh/mapstructure" "github.com/spf13/cast" + "slices" ) type BaseConfig struct { @@ -128,7 +129,7 @@ func (w BuildStats) Enabled() bool { } func (b BuildConfig) clone() BuildConfig { - b.CacheBusters = append([]CacheBuster{}, b.CacheBusters...) + b.CacheBusters = slices.Clone(b.CacheBusters) return b } diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go index ce68cec15..05ba185e3 100644 --- a/config/commonConfig_test.go +++ b/config/commonConfig_test.go @@ -166,7 +166,7 @@ func TestBuildConfigCacheBusters(t *testing.T) { func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) { c := qt.New(t) cfg := New() - cfg.Set("build", map[string]interface{}{ + cfg.Set("build", map[string]any{ "cacheBusters": []map[string]string{ { "source": "assets/watching/hugo_stats\\.json", diff --git a/config/defaultConfigProvider.go b/config/defaultConfigProvider.go index bb7c47412..8c1d63851 100644 --- a/config/defaultConfigProvider.go +++ b/config/defaultConfigProvider.go @@ -345,7 +345,7 @@ func (c *defaultConfigProvider) getNestedKeyAndMap(key string, create bool) (str c.keyCache.Store(key, parts) } current := c.root - for i := 0; i < len(parts)-1; i++ { + for i := range len(parts) - 1 { next, found := current[parts[i]] if !found { if create { diff --git a/config/defaultConfigProvider_test.go b/config/defaultConfigProvider_test.go index 65f10ec6a..cd6247e60 100644 --- a/config/defaultConfigProvider_test.go +++ b/config/defaultConfigProvider_test.go @@ -332,7 +332,7 @@ func TestDefaultConfigProvider(t *testing.T) { return nil } - for i := 0; i < 20; i++ { + for i := range 20 { i := i r.Run(func() error { const v = 42 diff --git a/config/namespace_test.go b/config/namespace_test.go index df27ae05c..f443523a4 100644 --- a/config/namespace_test.go +++ b/config/namespace_test.go @@ -29,7 +29,7 @@ func TestNamespace(t *testing.T) { // ns, err := config.DecodeNamespace[map[string]DocsMediaTypeConfig](in, defaultMediaTypesConfig, buildConfig) ns, err := DecodeNamespace[[]*tstNsExt]( - map[string]interface{}{"foo": "bar"}, + map[string]any{"foo": "bar"}, func(v any) (*tstNsExt, any, error) { t := &tstNsExt{} m, err := maps.ToStringMapE(v) @@ -42,7 +42,7 @@ func TestNamespace(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(ns, qt.Not(qt.IsNil)) - c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"}) + c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]any{"foo": "bar"}) c.Assert(ns.SourceHash, qt.Equals, "1420f6c7782f7459") c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"}) c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil)) diff --git a/config/security/whitelist.go b/config/security/whitelist.go index 92eb3102f..5ce369a1f 100644 --- a/config/security/whitelist.go +++ b/config/security/whitelist.go @@ -73,7 +73,7 @@ func NewWhitelist(patterns ...string) (Whitelist, error) { var patternsr []*regexp.Regexp - for i := 0; i < len(patterns); i++ { + for i := range patterns { p := strings.TrimSpace(patterns[i]) if p == "" { continue diff --git a/create/content_test.go b/create/content_test.go index 63045cbea..429edfc26 100644 --- a/create/content_test.go +++ b/create/content_test.go @@ -129,7 +129,7 @@ site RegularPages: {{ len site.RegularPages }} ` - c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil) + c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), fmt.Appendf(nil, contentFile, "index.md"), 0o755), qt.IsNil) c.Assert(afero.WriteFile(mm, filepath.Join(defaultArchetypeDir, "index.md"), []byte("default archetype index.md"), 0o755), qt.IsNil) c.Assert(initFs(mm), qt.IsNil) diff --git a/helpers/content.go b/helpers/content.go index 9d74a3d31..6edcf88d4 100644 --- a/helpers/content.go +++ b/helpers/content.go @@ -109,10 +109,7 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) { startOfTOC := bytes.Index(content, first) - peekEnd := len(content) - if peekEnd > 70+startOfTOC { - peekEnd = 70 + startOfTOC - } + peekEnd := min(len(content), 70+startOfTOC) if startOfTOC < 0 { return stripEmptyNav(content), toc diff --git a/helpers/emoji.go b/helpers/emoji.go index c103a5479..aa5540dca 100644 --- a/helpers/emoji.go +++ b/helpers/emoji.go @@ -43,11 +43,7 @@ func Emojify(source []byte) []byte { j := start + k - upper := j + emojiMaxSize - - if upper > len(source) { - upper = len(source) - } + upper := min(j+emojiMaxSize, len(source)) endEmoji := bytes.Index(source[j+1:upper], emojiDelim) nextWordDelim := bytes.Index(source[j:upper], emojiWordDelim) diff --git a/helpers/general.go b/helpers/general.go index 11cc185a8..76275a6b9 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -63,7 +63,7 @@ func UniqueStrings(s []string) []string { unique := make([]string, 0, len(s)) for i, val := range s { var seen bool - for j := 0; j < i; j++ { + for j := range i { if s[j] == val { seen = true break @@ -83,7 +83,7 @@ func UniqueStringsReuse(s []string) []string { for i, val := range s { var seen bool - for j := 0; j < i; j++ { + for j := range i { if s[j] == val { seen = true break diff --git a/helpers/processing_stats.go b/helpers/processing_stats.go index 540060aa2..3f48466c7 100644 --- a/helpers/processing_stats.go +++ b/helpers/processing_stats.go @@ -89,7 +89,7 @@ func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) { var data [][]string - for i := 0; i < len(stats); i++ { + for i := range stats { stat := stats[i] names[i+1] = stat.Name diff --git a/helpers/url_test.go b/helpers/url_test.go index ce1b24487..7f625035c 100644 --- a/helpers/url_test.go +++ b/helpers/url_test.go @@ -101,10 +101,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, v := config.New() if multilingual { v.Set("languages", map[string]any{ - "fr": map[string]interface{}{ + "fr": map[string]any{ "weight": 20, }, - "en": map[string]interface{}{ + "en": map[string]any{ "weight": 10, }, }) @@ -112,7 +112,7 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, } else { v.Set("defaultContentLanguage", lang) v.Set("languages", map[string]any{ - lang: map[string]interface{}{ + lang: map[string]any{ "weight": 10, }, }) @@ -167,10 +167,10 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool, v := config.New() if multilingual { v.Set("languages", map[string]any{ - "fr": map[string]interface{}{ + "fr": map[string]any{ "weight": 20, }, - "en": map[string]interface{}{ + "en": map[string]any{ "weight": 10, }, }) @@ -178,7 +178,7 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool, } else { v.Set("defaultContentLanguage", lang) v.Set("languages", map[string]any{ - lang: map[string]interface{}{ + lang: map[string]any{ "weight": 10, }, }) diff --git a/htesting/hqt/checkers.go b/htesting/hqt/checkers.go index a05206535..b06185756 100644 --- a/htesting/hqt/checkers.go +++ b/htesting/hqt/checkers.go @@ -151,7 +151,7 @@ func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { structTypes(v.Elem(), m) } case reflect.Slice, reflect.Array: - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { structTypes(v.Index(i), m) } case reflect.Map: @@ -160,7 +160,7 @@ func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { } case reflect.Struct: m[v.Type()] = struct{}{} - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { structTypes(v.Field(i), m) } } diff --git a/hugofs/fileinfo.go b/hugofs/fileinfo.go index 60d2a38df..5e6a87acc 100644 --- a/hugofs/fileinfo.go +++ b/hugofs/fileinfo.go @@ -93,7 +93,7 @@ func (m *FileMeta) Merge(from *FileMeta) { dstv := reflect.Indirect(reflect.ValueOf(m)) srcv := reflect.Indirect(reflect.ValueOf(from)) - for i := 0; i < dstv.NumField(); i++ { + for i := range dstv.NumField() { v := dstv.Field(i) if !v.CanSet() { continue diff --git a/hugofs/fs.go b/hugofs/fs.go index fab0d3886..aecf72a7f 100644 --- a/hugofs/fs.go +++ b/hugofs/fs.go @@ -214,7 +214,7 @@ func WalkFilesystems(fs afero.Fs, fn WalkFn) bool { } } } else if cfs, ok := fs.(overlayfs.FilesystemIterator); ok { - for i := 0; i < cfs.NumFilesystems(); i++ { + for i := range cfs.NumFilesystems() { if WalkFilesystems(cfs.Filesystem(i), fn) { return true } diff --git a/hugofs/glob/glob.go b/hugofs/glob/glob.go index 42aa1fa3b..a4e5c49e8 100644 --- a/hugofs/glob/glob.go +++ b/hugofs/glob/glob.go @@ -166,7 +166,7 @@ func FilterGlobParts(a []string) []string { // HasGlobChar returns whether s contains any glob wildcards. func HasGlobChar(s string) bool { - for i := 0; i < len(s); i++ { + for i := range len(s) { if syntax.Special(s[i]) { return true } diff --git a/hugofs/walk_test.go b/hugofs/walk_test.go index 7366d008d..03b808533 100644 --- a/hugofs/walk_test.go +++ b/hugofs/walk_test.go @@ -91,7 +91,7 @@ func TestWalkRootMappingFs(t *testing.T) { p := para.New(4) r, _ := p.Start(context.Background()) - for i := 0; i < 8; i++ { + for range 8 { r.Run(func() error { _, err := collectPaths(bfs, "") if err != nil { @@ -153,7 +153,7 @@ func BenchmarkWalk(b *testing.B) { fs := NewBaseFileDecorator(afero.NewMemMapFs()) writeFiles := func(dir string, numfiles int) { - for i := 0; i < numfiles; i++ { + for i := range numfiles { filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", i)) c.Assert(afero.WriteFile(fs, filename, []byte("content"), 0o777), qt.IsNil) } diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index f3060814c..d0a6730db 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -871,7 +871,7 @@ Background: {{ .Params.background }}| {{ .Title }}| ` - for i := 0; i < 10; i++ { + for range 10 { b := Test(t, files) b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg") } diff --git a/hugolib/config_test.go b/hugolib/config_test.go index c0bfde37d..cbf821ee7 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -793,7 +793,7 @@ Single. files := strings.ReplaceAll(filesTemplate, "WEIGHT_EN", "2") files = strings.ReplaceAll(files, "WEIGHT_SV", "1") - for i := 0; i < 20; i++ { + for range 20 { cfg := config.New() b, err := NewIntegrationTestBuilder( IntegrationTestConfig{ diff --git a/hugolib/content_map_test.go b/hugolib/content_map_test.go index 4cbdaf53a..7202840f3 100644 --- a/hugolib/content_map_test.go +++ b/hugolib/content_map_test.go @@ -323,7 +323,7 @@ R: {{ with $r }}{{ .Content }}{{ end }}|Len: {{ len $bundle.Resources }}|$ ` - for i := 0; i < 3; i++ { + for range 3 { b := Test(t, files) b.AssertFileContent("public/index.html", "R: Data 1.txt|", "Len: 1|") } @@ -435,14 +435,14 @@ func TestContentTreeReverseIndex(t *testing.T) { pageReverseIndex := newContentTreeTreverseIndex( func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) { - for i := 0; i < 10; i++ { + for i := range 10 { key := fmt.Sprint(i) set(key, &testContentNode{key: key}) } }, ) - for i := 0; i < 10; i++ { + for i := range 10 { key := fmt.Sprint(i) v := pageReverseIndex.Get(key) c.Assert(v, qt.Not(qt.IsNil)) @@ -456,17 +456,17 @@ func TestContentTreeReverseIndexPara(t *testing.T) { var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for range 10 { pageReverseIndex := newContentTreeTreverseIndex( func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) { - for i := 0; i < 10; i++ { + for i := range 10 { key := fmt.Sprint(i) set(key, &testContentNode{key: key}) } }, ) - for j := 0; j < 10; j++ { + for j := range 10 { wg.Add(1) go func(i int) { defer wg.Done() diff --git a/hugolib/doctree/nodeshiftree_test.go b/hugolib/doctree/nodeshiftree_test.go index ac89037ac..0f4fb6f68 100644 --- a/hugolib/doctree/nodeshiftree_test.go +++ b/hugolib/doctree/nodeshiftree_test.go @@ -193,7 +193,7 @@ func TestTreePara(t *testing.T) { }, ) - for i := 0; i < 8; i++ { + for i := range 8 { i := i r.Run(func() error { a := &testValue{ID: "/a"} @@ -289,7 +289,7 @@ func BenchmarkTreeInsert(b *testing.B) { }, ) - for i := 0; i < numElements; i++ { + for i := range numElements { lang := rand.Intn(2) tree.InsertIntoValuesDimension(fmt.Sprintf("/%d", i), &testValue{ID: fmt.Sprintf("/%d", i), Lang: lang, Weight: i, NoCopy: true}) } @@ -323,7 +323,7 @@ func BenchmarkWalk(b *testing.B) { }, ) - for i := 0; i < numElements; i++ { + for i := range numElements { lang := rand.Intn(2) tree.InsertIntoValuesDimension(fmt.Sprintf("/%d", i), &testValue{ID: fmt.Sprintf("/%d", i), Lang: lang, Weight: i, NoCopy: true}) } @@ -355,8 +355,8 @@ func BenchmarkWalk(b *testing.B) { base := createTree() b.ResetTimer() for i := 0; i < b.N; i++ { - for d1 := 0; d1 < 1; d1++ { - for d2 := 0; d2 < 2; d2++ { + for d1 := range 1 { + for d2 := range 2 { tree := base.Shape(d1, d2) w := &doctree.NodeShiftTreeWalker[*testValue]{ Tree: tree, diff --git a/hugolib/doctree/nodeshifttree.go b/hugolib/doctree/nodeshifttree.go index 497e9f02e..298b24d1b 100644 --- a/hugolib/doctree/nodeshifttree.go +++ b/hugolib/doctree/nodeshifttree.go @@ -363,7 +363,7 @@ func (r *NodeShiftTreeWalker[T]) Walk(ctx context.Context) error { main := r.Tree var err error - fnMain := func(s string, v interface{}) bool { + fnMain := func(s string, v any) bool { if r.ShouldSkip(s) { return false } diff --git a/hugolib/doctree/treeshifttree.go b/hugolib/doctree/treeshifttree.go index cd13b9f61..059eaaf88 100644 --- a/hugolib/doctree/treeshifttree.go +++ b/hugolib/doctree/treeshifttree.go @@ -34,7 +34,7 @@ func NewTreeShiftTree[T comparable](d, length int) *TreeShiftTree[T] { panic("length must be > 0") } trees := make([]*SimpleTree[T], length) - for i := 0; i < length; i++ { + for i := range length { trees[i] = NewSimpleTree[T]() } return &TreeShiftTree[T]{d: d, trees: trees} diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go index cb7846cd1..3e9b92087 100644 --- a/hugolib/filesystems/basefs.go +++ b/hugolib/filesystems/basefs.go @@ -634,7 +634,7 @@ func (b *sourceFilesystemsBuilder) createMainOverlayFs(p *paths.Paths) (*filesys mounts := make([]mountsDescriptor, len(mods)) - for i := 0; i < len(mods); i++ { + for i := range mods { mod := mods[i] dir := mod.Dir() diff --git a/hugolib/filesystems/basefs_test.go b/hugolib/filesystems/basefs_test.go index 3f189c860..abe06ac4a 100644 --- a/hugolib/filesystems/basefs_test.go +++ b/hugolib/filesystems/basefs_test.go @@ -57,14 +57,14 @@ func TestNewBaseFs(t *testing.T) { filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme)) filenameOverlap := filepath.Join(base, "f3.txt") afs.Mkdir(base, 0o755) - content := []byte(fmt.Sprintf("content:%s:%s", theme, dir)) + content := fmt.Appendf(nil, "content:%s:%s", theme, dir) afero.WriteFile(afs, filenameTheme, content, 0o755) afero.WriteFile(afs, filenameOverlap, content, 0o755) } // Write some files to the root of the theme base := filepath.Join(workingDir, "themes", theme) - afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0o755) - afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0o755) + afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), fmt.Appendf(nil, "content:%s", theme), 0o755) + afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), fmt.Appendf(nil, "content:%s", theme), 0o755) } afero.WriteFile(afs, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0o755) @@ -683,8 +683,8 @@ func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val strin workingDir := v.GetString("workingDir") v.Set(key, val) fs.Mkdir(val, 0o755) - for i := 0; i < num; i++ { + for i := range num { filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1)) - afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0o755) + afero.WriteFile(fs, filename, fmt.Appendf(nil, "content:%s:%d", key, i+1), 0o755) } } diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index b17c761aa..5373f9832 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -311,7 +311,7 @@ func (h *HugoSites) NumLogErrors() int { func (h *HugoSites) PrintProcessingStats(w io.Writer) { stats := make([]*helpers.ProcessingStats, len(h.Sites)) - for i := 0; i < len(h.Sites); i++ { + for i := range h.Sites { stats[i] = h.Sites[i].PathSpec.ProcessingStats } helpers.ProcessingStatsTable(w, stats...) diff --git a/hugolib/page.go b/hugolib/page.go index 701e0f11b..de64767df 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -707,7 +707,7 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error { cp := p.pageOutput.pco if cp == nil && p.reusePageOutputContent() { // Look for content to reuse. - for i := 0; i < len(p.pageOutputs); i++ { + for i := range p.pageOutputs { if i == idx { continue } diff --git a/hugolib/page__content.go b/hugolib/page__content.go index f7579f182..3cfea1727 100644 --- a/hugolib/page__content.go +++ b/hugolib/page__content.go @@ -45,6 +45,7 @@ import ( "github.com/gohugoio/hugo/tpl" "github.com/mitchellh/mapstructure" "github.com/spf13/cast" + maps0 "maps" ) const ( @@ -696,9 +697,7 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont cp.otherOutputs.Set(cp2.po.p.pid, cp2) // Merge content placeholders - for k, v := range ct2.contentPlaceholders { - ct.contentPlaceholders[k] = v - } + maps0.Copy(ct.contentPlaceholders, ct2.contentPlaceholders) if p.s.conf.Internal.Watch { for _, s := range cp2.po.p.m.content.shortcodeState.shortcodes { diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go index e4da75a72..e5521412b 100644 --- a/hugolib/pagebundler_test.go +++ b/hugolib/pagebundler_test.go @@ -690,7 +690,7 @@ bundle min min key: {{ $jsonMinMin.Key }} `) - for i := 0; i < 3; i++ { + for range 3 { b.Build(BuildCfg{}) diff --git a/hugolib/pagecollections_test.go b/hugolib/pagecollections_test.go index 692ae9ef6..10c973b7e 100644 --- a/hugolib/pagecollections_test.go +++ b/hugolib/pagecollections_test.go @@ -47,8 +47,8 @@ func BenchmarkGetPage(b *testing.B) { b.Fatal(err) } - for i := 0; i < 10; i++ { - for j := 0; j < 100; j++ { + for i := range 10 { + for j := range 100 { writeSource(b, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), "CONTENT") } } @@ -91,8 +91,8 @@ func createGetPageRegularBenchmarkSite(t testing.TB) *Site { return fmt.Sprintf(pageCollectionsPageTemplate, title) } - for i := 0; i < 10; i++ { - for j := 0; j < 100; j++ { + for i := range 10 { + for j := range 100 { content := pc(fmt.Sprintf("Title%d_%d", i, j)) writeSource(c, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), content) } @@ -105,7 +105,7 @@ func TestBenchmarkGetPageRegular(t *testing.T) { c := qt.New(t) s := createGetPageRegularBenchmarkSite(t) - for i := 0; i < 10; i++ { + for i := range 10 { pp := path.Join("/", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", i)) page, _ := s.getPage(nil, pp) c.Assert(page, qt.Not(qt.IsNil), qt.Commentf(pp)) @@ -192,8 +192,8 @@ func TestGetPage(t *testing.T) { return fmt.Sprintf(pageCollectionsPageTemplate, title) } - for i := 0; i < 10; i++ { - for j := 0; j < 10; j++ { + for i := range 10 { + for j := range 10 { content := pc(fmt.Sprintf("Title%d_%d", i, j)) writeSource(t, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), content) } diff --git a/hugolib/pages_language_merge_test.go b/hugolib/pages_language_merge_test.go index 3862d7cf0..ba1ed83de 100644 --- a/hugolib/pages_language_merge_test.go +++ b/hugolib/pages_language_merge_test.go @@ -42,7 +42,7 @@ func TestMergeLanguages(t *testing.T) { c.Assert(len(frSite.RegularPages()), qt.Equals, 6) c.Assert(len(nnSite.RegularPages()), qt.Equals, 12) - for i := 0; i < 2; i++ { + for range 2 { mergedNN := nnSite.RegularPages().MergeByLanguage(enSite.RegularPages()) c.Assert(len(mergedNN), qt.Equals, 31) for i := 1; i <= 31; i++ { @@ -163,7 +163,7 @@ date: "2018-02-28" // Add a bundles j := 100 contentPairs = append(contentPairs, []string{"bundle/index.md", fmt.Sprintf(contentTemplate, j, j)}...) - for i := 0; i < 6; i++ { + for i := range 6 { contentPairs = append(contentPairs, []string{fmt.Sprintf("bundle/pb%d.md", i), fmt.Sprintf(contentTemplate, i+j, i+j)}...) } contentPairs = append(contentPairs, []string{"bundle/index.nn.md", fmt.Sprintf(contentTemplate, j, j)}...) diff --git a/hugolib/paginator_test.go b/hugolib/paginator_test.go index dcee6e38e..2470a9046 100644 --- a/hugolib/paginator_test.go +++ b/hugolib/paginator_test.go @@ -40,7 +40,7 @@ contentDir = "content/nn" ` b := newTestSitesBuilder(t).WithConfigFile("toml", configFile) var content []string - for i := 0; i < 9; i++ { + for i := range 9 { for _, contentDir := range []string{"content/en", "content/nn"} { content = append(content, fmt.Sprintf(contentDir+"/blog/page%d.md", i), fmt.Sprintf(`--- title: Page %d @@ -118,7 +118,7 @@ cascade: - JSON ---`) - for i := 0; i < 22; i++ { + for i := range 22 { b.WithContent(fmt.Sprintf("p%d.md", i+1), fmt.Sprintf(`--- title: "Page" weight: %d diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go index dc2c6524f..fab47679f 100644 --- a/hugolib/rebuild_test.go +++ b/hugolib/rebuild_test.go @@ -124,7 +124,7 @@ func TestRebuildEditTextFileInLeafBundle(t *testing.T) { func TestRebuildEditTextFileInShortcode(t *testing.T) { t.Parallel() - for i := 0; i < 3; i++ { + for range 3 { b := TestRunning(t, rebuildFilesSimple) b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", "Text: Assets My Shortcode Text.") @@ -138,7 +138,7 @@ func TestRebuildEditTextFileInShortcode(t *testing.T) { func TestRebuildEditTextFileInHook(t *testing.T) { t.Parallel() - for i := 0; i < 3; i++ { + for range 3 { b := TestRunning(t, rebuildFilesSimple) b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", "Text: Assets My Other Text.") @@ -1545,7 +1545,7 @@ title: "P%d" P%d Content. ` - for i := 0; i < count; i++ { + for i := range count { files += fmt.Sprintf("-- content/mysect/p%d/index.md --\n%s", i, fmt.Sprintf(contentTemplate, i, i)) } diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 942873ae4..00e4c0060 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -99,7 +99,7 @@ FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with b.Running() - for i := 0; i < 2; i++ { + for i := range 2 { b.Logf("Test run %d", i) b.Build(BuildCfg{}) @@ -200,7 +200,7 @@ func BenchmarkResourceChainPostProcess(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() s := newTestSitesBuilder(b) - for i := 0; i < 300; i++ { + for i := range 300 { s.WithContent(fmt.Sprintf("page%d.md", i+1), "---\ntitle: Page\n---") } s.WithTemplates("_default/single.html", `Start. diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go index 5799de452..93edd9345 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -865,13 +865,13 @@ Content: {{ .Content }}| func TestShortcodeStableOutputFormatTemplates(t *testing.T) { t.Parallel() - for i := 0; i < 5; i++ { + for range 5 { b := newTestSitesBuilder(t) const numPages = 10 - for i := 0; i < numPages; i++ { + for i := range numPages { b.WithContent(fmt.Sprintf("page%d.md", i), `--- title: "Page" outputs: ["html", "css", "csv", "json"] @@ -894,14 +894,14 @@ outputs: ["html", "css", "csv", "json"] // helpers.PrintFs(b.Fs.Destination, "public", os.Stdout) - for i := 0; i < numPages; i++ { + for i := range numPages { b.AssertFileContent(fmt.Sprintf("public/page%d/index.html", i), "Short-HTML") b.AssertFileContent(fmt.Sprintf("public/page%d/index.csv", i), "Short-CSV") b.AssertFileContent(fmt.Sprintf("public/page%d/index.json", i), "Short-HTML") } - for i := 0; i < numPages; i++ { + for i := range numPages { b.AssertFileContent(fmt.Sprintf("public/page%d/styles.css", i), "Short-HTML") } diff --git a/hugolib/site.go b/hugolib/site.go index 7c09ba346..dab23d670 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -330,10 +330,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) { numWorkers := config.GetNumWorkerMultiplier() - numWorkersSite := numWorkers - if numWorkersSite > len(sites) { - numWorkersSite = len(sites) - } + numWorkersSite := min(numWorkers, len(sites)) workersSite := para.New(numWorkersSite) h := &HugoSites{ diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 5ac3d5a75..c88036f26 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -78,7 +78,7 @@ func (s *Site) renderPages(ctx *siteRenderContext) error { wg := &sync.WaitGroup{} - for i := 0; i < numWorkers; i++ { + for range numWorkers { wg.Add(1) go pageRenderer(ctx, s, pages, results, wg) } diff --git a/hugolib/site_stats_test.go b/hugolib/site_stats_test.go index da09ec368..02f2c0a8c 100644 --- a/hugolib/site_stats_test.go +++ b/hugolib/site_stats_test.go @@ -69,15 +69,15 @@ aliases: [/Ali%d] "_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}", ) - for i := 0; i < 2; i++ { - for j := 0; j < 2; j++ { + for i := range 2 { + for j := range 2 { pageID := i + j + 1 b.WithContent(fmt.Sprintf("content/sect/p%d.md", pageID), fmt.Sprintf(pageTemplate, pageID, fmt.Sprintf("- tag%d", j), fmt.Sprintf("- category%d", j), pageID)) } } - for i := 0; i < 5; i++ { + for i := range 5 { b.WithContent(fmt.Sprintf("assets/image%d.png", i+1), "image") } diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 2ee33da24..e611897fe 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -372,14 +372,14 @@ func TestMainSections(t *testing.T) { b := newTestSitesBuilder(c).WithViper(v) - for i := 0; i < 20; i++ { + for i := range 20 { b.WithContent(fmt.Sprintf("page%d.md", i), `--- title: "Page" --- `) } - for i := 0; i < 5; i++ { + for i := range 5 { b.WithContent(fmt.Sprintf("blog/page%d.md", i), `--- title: "Page" tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] @@ -387,7 +387,7 @@ tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] `) } - for i := 0; i < 3; i++ { + for i := range 3 { b.WithContent(fmt.Sprintf("docs/page%d.md", i), `--- title: "Page" --- diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go index 29170118f..091251f80 100644 --- a/hugolib/site_url_test.go +++ b/hugolib/site_url_test.go @@ -97,7 +97,7 @@ Do not go gentle into that good night. writeSource(t, fs, filepath.Join("content", "sect1", "_index.md"), fmt.Sprintf(st, "/ss1/")) writeSource(t, fs, filepath.Join("content", "sect2", "_index.md"), fmt.Sprintf(st, "/ss2/")) - for i := 0; i < 5; i++ { + for i := range 5 { writeSource(t, fs, filepath.Join("content", "sect1", fmt.Sprintf("p%d.md", i+1)), pt) writeSource(t, fs, filepath.Join("content", "sect2", fmt.Sprintf("p%d.md", i+1)), pt) } diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go index 26148dd1b..6577a22c1 100644 --- a/hugolib/taxonomy_test.go +++ b/hugolib/taxonomy_test.go @@ -314,7 +314,7 @@ func TestTaxonomiesNextGenLoops(t *testing.T) { `) - for i := 0; i < 10; i++ { + for i := range 10 { b.WithContent(fmt.Sprintf("page%d.md", i+1), ` --- Title: "Taxonomy!" diff --git a/hugolib/template_test.go b/hugolib/template_test.go index 055d9593c..01dfc7eba 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -250,7 +250,7 @@ Content. Base %d: {{ block "main" . }}FOO{{ end }} ` - for i := 0; i < numPages; i++ { + for i := range numPages { id := i + 1 b.WithContent(fmt.Sprintf("page%d.md", id), fmt.Sprintf(pageTemplate, id, id)) b.WithTemplates(fmt.Sprintf("_default/layout%d.html", id), fmt.Sprintf(singleTemplate, id)) @@ -258,7 +258,7 @@ Base %d: {{ block "main" . }}FOO{{ end }} } b.Build(BuildCfg{}) - for i := 0; i < numPages; i++ { + for i := range numPages { id := i + 1 b.AssertFileContent(fmt.Sprintf("public/page%d/index.html", id), fmt.Sprintf(`Base %d: %d`, id, id)) } diff --git a/identity/finder.go b/identity/finder.go index b1a08d061..fd1055aef 100644 --- a/identity/finder.go +++ b/identity/finder.go @@ -27,7 +27,7 @@ func NewFinder(cfg FinderConfig) *Finder { } var searchIDPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &searchID{seen: make(map[Manager]bool)} }, } diff --git a/identity/identity_test.go b/identity/identity_test.go index d003caaf0..f9b04aa14 100644 --- a/identity/identity_test.go +++ b/identity/identity_test.go @@ -25,7 +25,7 @@ import ( func BenchmarkIdentityManager(b *testing.B) { createIds := func(num int) []identity.Identity { ids := make([]identity.Identity, num) - for i := 0; i < num; i++ { + for i := range num { name := fmt.Sprintf("id%d", i) ids[i] = &testIdentity{base: name, name: name} } @@ -108,10 +108,10 @@ func BenchmarkIsNotDependent(b *testing.B) { newNestedManager := func(depth, count int) identity.Manager { m1 := identity.NewManager("") - for i := 0; i < depth; i++ { + for range depth { m2 := identity.NewManager("") m1.AddIdentity(m2) - for j := 0; j < count; j++ { + for j := range count { id := fmt.Sprintf("id%d", j) m2.AddIdentity(&testIdentity{id, id, "", ""}) } diff --git a/internal/js/esbuild/resolve.go b/internal/js/esbuild/resolve.go index 8ceec97ef..a2516dbd2 100644 --- a/internal/js/esbuild/resolve.go +++ b/internal/js/esbuild/resolve.go @@ -27,6 +27,7 @@ import ( "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/afero" + "slices" ) const ( @@ -167,15 +168,11 @@ func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsMana } } - for _, ext := range opts.Externals { - // ESBuild will do a more thorough check for packages resolved in node_modules, - // but we need to make sure that we don't try to resolve these in the /assets folder. - if ext == impPath { - return api.OnResolveResult{ - Path: impPath, - External: true, - }, nil - } + if slices.Contains(opts.Externals, impPath) { + return api.OnResolveResult{ + Path: impPath, + External: true, + }, nil } if opts.ImportOnResolveFunc != nil { diff --git a/internal/warpc/warpc.go b/internal/warpc/warpc.go index 1159944a4..f2dfc6244 100644 --- a/internal/warpc/warpc.go +++ b/internal/warpc/warpc.go @@ -384,7 +384,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { } inOuts := make([]*inOut, opts.PoolSize) - for i := 0; i < opts.PoolSize; i++ { + for i := range opts.PoolSize { var stdin, stdout hugio.ReadWriteCloser stdin = hugio.NewPipeReadWriteCloser() @@ -478,7 +478,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { close(dp.donec) }() - for i := 0; i < len(inOuts); i++ { + for i := range inOuts { d := &dispatcher[Q, R]{ pending: make(map[uint32]*call[Q, R]), inOut: inOuts[i], diff --git a/internal/warpc/warpc_test.go b/internal/warpc/warpc_test.go index 613a9706f..2ee4c3de5 100644 --- a/internal/warpc/warpc_test.go +++ b/internal/warpc/warpc_test.go @@ -101,7 +101,7 @@ func TestGreet(t *testing.T) { Infof: t.Logf, } - for i := 0; i < 2; i++ { + for range 2 { func() { d, err := Start[person, greeting](opts) if err != nil { @@ -123,7 +123,7 @@ func TestGreet(t *testing.T) { }, } - for j := 0; j < 20; j++ { + for j := range 20 { inputMessage.Header.ID = uint32(j + 1) g, err := d.Execute(ctx, inputMessage) if err != nil { @@ -163,7 +163,7 @@ func TestGreetParallel(t *testing.T) { ctx := context.Background() - for j := 0; j < 5; j++ { + for j := range 5 { base := i * 100 id := uint32(base + j) @@ -217,7 +217,7 @@ func TestKatexParallel(t *testing.T) { ctx := context.Background() - for j := 0; j < 1; j++ { + for j := range 1 { base := i * 100 id := uint32(base + j) diff --git a/langs/language_test.go b/langs/language_test.go index 543f4a133..33240f3f4 100644 --- a/langs/language_test.go +++ b/langs/language_test.go @@ -29,13 +29,13 @@ func TestCollator(t *testing.T) { coll := &Collator{c: collate.New(language.English, collate.Loose)} - for i := 0; i < 10; i++ { + for range 10 { wg.Add(1) go func() { coll.Lock() defer coll.Unlock() defer wg.Done() - for j := 0; j < 10; j++ { + for range 10 { k := coll.CompareStrings("abc", "def") c.Assert(k, qt.Equals, -1) } @@ -48,7 +48,7 @@ func BenchmarkCollator(b *testing.B) { s := []string{"foo", "bar", "éntre", "baz", "qux", "quux", "corge", "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud"} doWork := func(coll *Collator) { - for i := 0; i < len(s); i++ { + for i := range s { for j := i + 1; j < len(s); j++ { _ = coll.CompareStrings(s[i], s[j]) } diff --git a/lazy/init_test.go b/lazy/init_test.go index 96a959494..94736fab8 100644 --- a/lazy/init_test.go +++ b/lazy/init_test.go @@ -79,7 +79,7 @@ func TestInit(t *testing.T) { // Add some concurrency and randomness to verify thread safety and // init order. - for i := 0; i < 100; i++ { + for i := range 100 { wg.Add(1) go func(i int) { defer wg.Done() diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go index 4164f0e0a..c29632b90 100644 --- a/markup/goldmark/codeblocks/render.go +++ b/markup/goldmark/codeblocks/render.go @@ -77,7 +77,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No var buff bytes.Buffer l := n.Lines().Len() - for i := 0; i < l; i++ { + for i := range l { line := n.Lines().At(i) buff.Write(line.Value(src)) } diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go index e68acb8c3..7a556083c 100644 --- a/markup/goldmark/hugocontext/hugocontext.go +++ b/markup/goldmark/hugocontext/hugocontext.go @@ -182,7 +182,7 @@ func (r *hugoContextRenderer) renderHTMLBlock( if entering { if r.Unsafe { l := n.Lines().Len() - for i := 0; i < l; i++ { + for i := range l { line := n.Lines().At(i) linev := line.Value(source) var stripped bool @@ -226,7 +226,7 @@ func (r *hugoContextRenderer) renderRawHTML( n := node.(*ast.RawHTML) l := n.Segments.Len() if r.Unsafe { - for i := 0; i < l; i++ { + for i := range l { segment := n.Segments.At(i) _, _ = w.Write(segment.Value(source)) } diff --git a/markup/goldmark/passthrough/passthrough.go b/markup/goldmark/passthrough/passthrough.go index 4d72e7c80..c56842f3d 100644 --- a/markup/goldmark/passthrough/passthrough.go +++ b/markup/goldmark/passthrough/passthrough.go @@ -110,7 +110,7 @@ func (r *htmlRenderer) renderPassthroughBlock(w util.BufWriter, src []byte, node case (*passthrough.PassthroughBlock): l := nn.Lines().Len() var buff bytes.Buffer - for i := 0; i < l; i++ { + for i := range l { line := nn.Lines().At(i) buff.Write(line.Value(src)) } diff --git a/markup/rst/convert.go b/markup/rst/convert.go index 398f5eb0c..5bb0adb15 100644 --- a/markup/rst/convert.go +++ b/markup/rst/convert.go @@ -100,10 +100,7 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) bodyEnd := bytes.Index(result, []byte("\n")) if bodyEnd < 0 || bodyEnd >= len(result) { - bodyEnd = len(result) - 1 - if bodyEnd < 0 { - bodyEnd = 0 - } + bodyEnd = max(len(result)-1, 0) } return result[bodyStart+7 : bodyEnd], err diff --git a/markup/tableofcontents/tableofcontents.go b/markup/tableofcontents/tableofcontents.go index 741179d96..6c40c9a59 100644 --- a/markup/tableofcontents/tableofcontents.go +++ b/markup/tableofcontents/tableofcontents.go @@ -250,7 +250,7 @@ func (b *tocBuilder) writeHeading(level, indent int, h *Heading) { } func (b *tocBuilder) indent(n int) { - for i := 0; i < n; i++ { + for range n { b.s.WriteString(" ") } } diff --git a/markup/tableofcontents/tableofcontents_test.go b/markup/tableofcontents/tableofcontents_test.go index 9ec7ec293..b07d9e3ad 100644 --- a/markup/tableofcontents/tableofcontents_test.go +++ b/markup/tableofcontents/tableofcontents_test.go @@ -196,7 +196,7 @@ func TestTocMisc(t *testing.T) { func BenchmarkToc(b *testing.B) { newTocs := func(n int) []*Fragments { var tocs []*Fragments - for i := 0; i < n; i++ { + for range n { tocs = append(tocs, newTestToc()) } return tocs diff --git a/media/config.go b/media/config.go index e50d8499d..394159d04 100644 --- a/media/config.go +++ b/media/config.go @@ -26,6 +26,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/spf13/cast" + "slices" ) // DefaultTypes is the default media types supported by Hugo. @@ -46,7 +47,7 @@ func init() { // Initialize the Builtin types with values from DefaultTypes. v := reflect.ValueOf(&Builtin).Elem() - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { f := v.Field(i) fieldName := v.Type().Field(i).Name builtinType := f.Interface().(Type) @@ -149,12 +150,7 @@ func (t ContentTypes) IsIndexContentFile(filename string) bool { // IsHTMLSuffix returns whether the given suffix is a HTML media type. func (t ContentTypes) IsHTMLSuffix(suffix string) bool { - for _, s := range t.HTML.Suffixes() { - if s == suffix { - return true - } - } - return false + return slices.Contains(t.HTML.Suffixes(), suffix) } // Types is a slice of media types. diff --git a/modules/client.go b/modules/client.go index 011d43014..a8998bb8d 100644 --- a/modules/client.go +++ b/modules/client.go @@ -380,14 +380,12 @@ func (c *Client) Verify(clean bool) error { if err != nil { if clean { m := verifyErrorDirRe.FindAllStringSubmatch(err.Error(), -1) - if m != nil { - for i := 0; i < len(m); i++ { - c, err := hugofs.MakeReadableAndRemoveAllModulePkgDir(c.fs, m[i][1]) - if err != nil { - return err - } - fmt.Println("Cleaned", c) + for i := range m { + c, err := hugofs.MakeReadableAndRemoveAllModulePkgDir(c.fs, m[i][1]) + if err != nil { + return err } + fmt.Println("Cleaned", c) } // Try to verify it again. err = c.runVerify() diff --git a/navigation/menu.go b/navigation/menu.go index 3802014b1..a971f2e74 100644 --- a/navigation/menu.go +++ b/navigation/menu.go @@ -25,6 +25,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/spf13/cast" + "slices" ) var smc = newMenuCache() @@ -267,7 +268,7 @@ func (m Menu) Reverse() Menu { // Clone clones the menu entries. // This is for internal use only. func (m Menu) Clone() Menu { - return append(Menu(nil), m...) + return slices.Clone(m) } func DecodeConfig(in any) (*config.ConfigNamespace[map[string]MenuConfig, Menus], error) { diff --git a/navigation/menu_cache.go b/navigation/menu_cache.go index b2c46f7ac..065781780 100644 --- a/navigation/menu_cache.go +++ b/navigation/menu_cache.go @@ -14,6 +14,7 @@ package navigation import ( + "slices" "sync" ) @@ -84,7 +85,7 @@ func (c *menuCache) getP(key string, apply func(m *Menu), menuLists ...Menu) (Me } m := menuLists[0] - menuCopy := append(Menu(nil), m...) + menuCopy := slices.Clone(m) if apply != nil { apply(&menuCopy) diff --git a/navigation/menu_cache_test.go b/navigation/menu_cache_test.go index 9943db517..8fa17ffc3 100644 --- a/navigation/menu_cache_test.go +++ b/navigation/menu_cache_test.go @@ -23,7 +23,7 @@ import ( func createSortTestMenu(num int) Menu { menu := make(Menu, num) - for i := 0; i < num; i++ { + for i := range num { m := &MenuEntry{} menu[i] = m } @@ -49,11 +49,11 @@ func TestMenuCache(t *testing.T) { var testMenuSets []Menu - for i := 0; i < 50; i++ { + for i := range 50 { testMenuSets = append(testMenuSets, createSortTestMenu(i+1)) } - for j := 0; j < 100; j++ { + for range 100 { wg.Add(1) go func() { defer wg.Done() diff --git a/output/layouts/layout.go b/output/layouts/layout.go index 09606dba1..79f718dda 100644 --- a/output/layouts/layout.go +++ b/output/layouts/layout.go @@ -321,7 +321,7 @@ func uniqueStringsReuse(s []string) []string { for i, val := range s { var seen bool - for j := 0; j < i; j++ { + for j := range i { if s[j] == val { seen = true break diff --git a/parser/lowercase_camel_json.go b/parser/lowercase_camel_json.go index 468c1a8fe..9d89ff020 100644 --- a/parser/lowercase_camel_json.go +++ b/parser/lowercase_camel_json.go @@ -99,7 +99,7 @@ func (c ReplacingJSONMarshaller) MarshalJSON() ([]byte, error) { if c.OmitEmpty { // It's tricky to do this with a regexp, so convert it to a map, remove zero values and convert back. - var m map[string]interface{} + var m map[string]any err = json.Unmarshal(converted, &m) if err != nil { return nil, err @@ -111,9 +111,9 @@ func (c ReplacingJSONMarshaller) MarshalJSON() ([]byte, error) { delete(m, k) } else { switch vv := v.(type) { - case map[string]interface{}: + case map[string]any: removeZeroVAlues(vv) - case []interface{}: + case []any: for _, vvv := range vv { if m, ok := vvv.(map[string]any); ok { removeZeroVAlues(m) diff --git a/parser/pageparser/pagelexer_intro.go b/parser/pageparser/pagelexer_intro.go index 334d9a79c..a68a9e03a 100644 --- a/parser/pageparser/pagelexer_intro.go +++ b/parser/pageparser/pagelexer_intro.go @@ -123,7 +123,7 @@ LOOP: // Handle YAML or TOML front matter. func (l *pageLexer) lexFrontMatterSection(tp ItemType, delimr rune, name string, delim []byte) stateFunc { - for i := 0; i < 2; i++ { + for range 2 { if r := l.next(); r != delimr { return l.errorf("invalid %s delimiter", name) } diff --git a/parser/pageparser/pageparser.go b/parser/pageparser/pageparser.go index 1cf87bb70..5c6f4b2ff 100644 --- a/parser/pageparser/pageparser.go +++ b/parser/pageparser/pageparser.go @@ -192,7 +192,7 @@ func (t *Iterator) PeekWalk(walkFn func(item Item) bool) { // Consume is a convenience method to consume the next n tokens, // but back off Errors and EOF. func (t *Iterator) Consume(cnt int) { - for i := 0; i < cnt; i++ { + for range cnt { token := t.Next() if token.Type == tError || token.Type == tEOF { t.Backup() diff --git a/related/inverted_index.go b/related/inverted_index.go index 9197a6135..b8f1ad3e2 100644 --- a/related/inverted_index.go +++ b/related/inverted_index.go @@ -292,7 +292,7 @@ func (r *rank) addWeight(w int) { } var rankPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &rank{} }, } @@ -433,7 +433,7 @@ func (cfg IndexConfig) ToKeywords(v any) ([]Keyword, error) { keywords = append(keywords, cfg.stringToKeyword(vv)) case []string: vvv := make([]Keyword, len(vv)) - for i := 0; i < len(vvv); i++ { + for i := range vvv { vvv[i] = cfg.stringToKeyword(vv[i]) } keywords = append(keywords, vvv...) @@ -623,7 +623,7 @@ type Keyword interface { func (cfg IndexConfig) StringsToKeywords(s ...string) []Keyword { kw := make([]Keyword, len(s)) - for i := 0; i < len(s); i++ { + for i := range s { kw[i] = cfg.stringToKeyword(s[i]) } diff --git a/related/inverted_index_test.go b/related/inverted_index_test.go index 568486d1f..d57237e11 100644 --- a/related/inverted_index_test.go +++ b/related/inverted_index_test.go @@ -65,7 +65,7 @@ func (d *testDoc) addKeywords(name string, keywords ...string) *testDoc { for k, v := range keywordm { keywords := make([]Keyword, len(v)) - for i := 0; i < len(v); i++ { + for i := range v { keywords[i] = StringKeyword(v[i]) } d.keywords[k] = keywords @@ -221,7 +221,7 @@ func TestSearch(t *testing.T) { doc := newTestDocWithDate("keywords", date, "a", "b") doc.name = "thedoc" - for i := 0; i < 10; i++ { + for i := range 10 { docc := *doc docc.name = fmt.Sprintf("doc%d", i) idx.Add(context.Background(), &docc) @@ -230,7 +230,7 @@ func TestSearch(t *testing.T) { m, err := idx.Search(context.Background(), SearchOpts{Document: doc, Indices: []string{"keywords"}}) c.Assert(err, qt.IsNil) c.Assert(len(m), qt.Equals, 10) - for i := 0; i < 10; i++ { + for i := range 10 { c.Assert(m[i].Name(), qt.Equals, fmt.Sprintf("doc%d", i)) } }) @@ -311,11 +311,11 @@ func BenchmarkRelatedNewIndex(b *testing.B) { pages := make([]*testDoc, 100) numkeywords := 30 allKeywords := make([]string, numkeywords) - for i := 0; i < numkeywords; i++ { + for i := range numkeywords { allKeywords[i] = fmt.Sprintf("keyword%d", i+1) } - for i := 0; i < len(pages); i++ { + for i := range pages { start := rand.Intn(len(allKeywords)) end := start + 3 if end >= len(allKeywords) { @@ -356,7 +356,7 @@ func BenchmarkRelatedNewIndex(b *testing.B) { for i := 0; i < b.N; i++ { idx := NewInvertedIndex(cfg) docs := make([]Document, len(pages)) - for i := 0; i < len(pages); i++ { + for i := range pages { docs[i] = pages[i] } idx.Add(context.Background(), docs...) @@ -372,7 +372,7 @@ func BenchmarkRelatedMatchesIn(b *testing.B) { docs := make([]*testDoc, 1000) numkeywords := 20 allKeywords := make([]string, numkeywords) - for i := 0; i < numkeywords; i++ { + for i := range numkeywords { allKeywords[i] = fmt.Sprintf("keyword%d", i+1) } @@ -386,7 +386,7 @@ func BenchmarkRelatedMatchesIn(b *testing.B) { idx := NewInvertedIndex(cfg) - for i := 0; i < len(docs); i++ { + for i := range docs { start := rand.Intn(len(allKeywords)) end := start + 3 if end >= len(allKeywords) { diff --git a/related/related_integration_test.go b/related/related_integration_test.go index 291bfdbf7..6d3c6d6de 100644 --- a/related/related_integration_test.go +++ b/related/related_integration_test.go @@ -160,7 +160,7 @@ keywords: ['k%d'] --- ` - for i := 0; i < 32; i++ { + for range 32 { base += fmt.Sprintf("\n## Title %d", rand.Intn(100)) } diff --git a/releaser/releaser.go b/releaser/releaser.go index f2244842a..4c3db2c14 100644 --- a/releaser/releaser.go +++ b/releaser/releaser.go @@ -230,10 +230,10 @@ func git(args ...string) (string, error) { return string(out), nil } -func logf(format string, args ...interface{}) { +func logf(format string, args ...any) { fmt.Fprintf(os.Stderr, format, args...) } -func logln(args ...interface{}) { +func logln(args ...any) { fmt.Fprintln(os.Stderr, args...) } diff --git a/resources/image_test.go b/resources/image_test.go index 1ba5a149a..ee5de8bec 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -348,13 +348,13 @@ func TestImageTransformConcurrent(t *testing.T) { image := fetchImageForSpec(spec, c, "sunset.jpg") - for i := 0; i < 4; i++ { + for i := range 4 { wg.Add(1) go func(id int) { defer wg.Done() - for j := 0; j < 5; j++ { + for j := range 5 { img := image - for k := 0; k < 2; k++ { + for k := range 2 { r1, err := img.Resize(fmt.Sprintf("%dx", id-k)) if err != nil { t.Error(err) @@ -499,7 +499,7 @@ func BenchmarkImageExif(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - for j := 0; j < 10; j++ { + for range 10 { getAndCheckExif(c, images[i]) } } diff --git a/resources/images/color.go b/resources/images/color.go index e2ff2377f..c7f3b9eb6 100644 --- a/resources/images/color.go +++ b/resources/images/color.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/gohugoio/hugo/common/hstrings" + "slices" ) type colorGoProvider interface { @@ -91,11 +92,8 @@ func (c Color) toSRGB(i uint8) float64 { // that the palette is valid for the relevant format. func AddColorToPalette(c color.Color, p color.Palette) color.Palette { var found bool - for _, cc := range p { - if c == cc { - found = true - break - } + if slices.Contains(p, c) { + found = true } if !found { diff --git a/resources/images/imagetesting/testing.go b/resources/images/imagetesting/testing.go index 22a2317a1..25f2ab087 100644 --- a/resources/images/imagetesting/testing.go +++ b/resources/images/imagetesting/testing.go @@ -209,7 +209,7 @@ func goldenEqual(img1, img2 *image.NRGBA) bool { if len(img1.Pix) != len(img2.Pix) { return false } - for i := 0; i < len(img1.Pix); i++ { + for i := range img1.Pix { diff := int(img1.Pix[i]) - int(img2.Pix[i]) if diff < 0 { diff = -diff diff --git a/resources/page/page_matcher.go b/resources/page/page_matcher.go index 8155be99d..1e98b0836 100644 --- a/resources/page/page_matcher.go +++ b/resources/page/page_matcher.go @@ -24,6 +24,7 @@ import ( "github.com/gohugoio/hugo/hugofs/glob" "github.com/gohugoio/hugo/resources/kinds" "github.com/mitchellh/mapstructure" + "slices" ) // A PageMatcher can be used to match a Page with Glob patterns. @@ -208,13 +209,7 @@ func decodePageMatcher(m any, v *PageMatcher) error { v.Kind = strings.ToLower(v.Kind) if v.Kind != "" { g, _ := glob.GetGlob(v.Kind) - found := false - for _, k := range kinds.AllKindsInPages { - if g.Match(k) { - found = true - break - } - } + found := slices.ContainsFunc(kinds.AllKindsInPages, g.Match) if !found { return fmt.Errorf("%q did not match a valid Page Kind", v.Kind) } diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go index c26662fc2..7f98b1b88 100644 --- a/resources/page/pagemeta/page_frontmatter.go +++ b/resources/page/pagemeta/page_frontmatter.go @@ -539,7 +539,7 @@ func expandDefaultValues(values []string, defaults []string) []string { func toLowerSlice(in any) []string { out := cast.ToStringSlice(in) - for i := 0; i < len(out); i++ { + for i := range out { out[i] = strings.ToLower(out[i]) } diff --git a/resources/page/pagemeta/page_frontmatter_test.go b/resources/page/pagemeta/page_frontmatter_test.go index 18f9e5aa1..fe9d3d99c 100644 --- a/resources/page/pagemeta/page_frontmatter_test.go +++ b/resources/page/pagemeta/page_frontmatter_test.go @@ -31,7 +31,7 @@ import ( func newTestFd() *pagemeta.FrontMatterDescriptor { return &pagemeta.FrontMatterDescriptor{ PageConfig: &pagemeta.PageConfig{ - Params: make(map[string]interface{}), + Params: make(map[string]any), }, Location: time.UTC, } diff --git a/resources/page/pages_cache.go b/resources/page/pages_cache.go index 9435cb308..5300d5521 100644 --- a/resources/page/pages_cache.go +++ b/resources/page/pages_cache.go @@ -14,6 +14,7 @@ package page import ( + "slices" "sync" ) @@ -92,7 +93,7 @@ func (c *pageCache) getP(key string, apply func(p *Pages), pageLists ...Pages) ( } p := pageLists[0] - pagesCopy := append(Pages(nil), p...) + pagesCopy := slices.Clone(p) if apply != nil { apply(&pagesCopy) @@ -126,7 +127,7 @@ func pagesEqual(p1, p2 Pages) bool { return true } - for i := 0; i < len(p1); i++ { + for i := range p1 { if p1[i] != p2[i] { return false } diff --git a/resources/page/pages_cache_test.go b/resources/page/pages_cache_test.go index 825bdc31f..9e6af1c28 100644 --- a/resources/page/pages_cache_test.go +++ b/resources/page/pages_cache_test.go @@ -41,11 +41,11 @@ func TestPageCache(t *testing.T) { var testPageSets []Pages - for i := 0; i < 50; i++ { + for i := range 50 { testPageSets = append(testPageSets, createSortTestPages(i+1)) } - for j := 0; j < 100; j++ { + for range 100 { wg.Add(1) go func() { defer wg.Done() @@ -75,7 +75,7 @@ func TestPageCache(t *testing.T) { func BenchmarkPageCache(b *testing.B) { cache := newPageCache() pages := make(Pages, 30) - for i := 0; i < 30; i++ { + for i := range 30 { pages[i] = &testPage{title: "p" + strconv.Itoa(i)} } key := "key" diff --git a/resources/page/pages_sort_test.go b/resources/page/pages_sort_test.go index 12fa4a1e1..70c7bc8a8 100644 --- a/resources/page/pages_sort_test.go +++ b/resources/page/pages_sort_test.go @@ -139,7 +139,7 @@ func TestLimit(t *testing.T) { p := createSortTestPages(10) firstFive := p.Limit(5) c.Assert(len(firstFive), qt.Equals, 5) - for i := 0; i < 5; i++ { + for i := range 5 { c.Assert(firstFive[i], qt.Equals, p[i]) } c.Assert(p.Limit(10), eq, p) @@ -197,7 +197,7 @@ func TestPageSortByParamNumeric(t *testing.T) { n := 10 unsorted := createSortTestPages(n) - for i := 0; i < n; i++ { + for i := range n { v := 100 - i if i%2 == 0 { v = 100.0 - i @@ -269,7 +269,7 @@ func setSortVals(dates [4]time.Time, titles [4]string, weights [4]int, pages Pag func createSortTestPages(num int) Pages { pages := make(Pages, num) - for i := 0; i < num; i++ { + for i := range num { p := newTestPage() p.path = fmt.Sprintf("/x/y/p%d.md", i) p.title = fmt.Sprintf("Title %d", i%((num+1)/2)) diff --git a/resources/page/pagination_test.go b/resources/page/pagination_test.go index d3a770eaf..64ee9a998 100644 --- a/resources/page/pagination_test.go +++ b/resources/page/pagination_test.go @@ -28,7 +28,7 @@ func TestSplitPages(t *testing.T) { chunks := splitPages(pages, 5) c.Assert(len(chunks), qt.Equals, 5) - for i := 0; i < 4; i++ { + for i := range 4 { c.Assert(chunks[i].Len(), qt.Equals, 5) } diff --git a/resources/page/site.go b/resources/page/site.go index 47e1454c8..3c9e9e78c 100644 --- a/resources/page/site.go +++ b/resources/page/site.go @@ -105,7 +105,7 @@ type Site interface { Config() SiteConfig // Deprecated: Use taxonomies instead. - Author() map[string]interface{} + Author() map[string]any // Deprecated: Use taxonomies instead. Authors() AuthorList @@ -173,7 +173,7 @@ func (s *siteWrapper) Social() map[string]string { } // Deprecated: Use taxonomies instead. -func (s *siteWrapper) Author() map[string]interface{} { +func (s *siteWrapper) Author() map[string]any { return s.s.Author() } @@ -316,7 +316,7 @@ type testSite struct { } // Deprecated: Use taxonomies instead. -func (s testSite) Author() map[string]interface{} { +func (s testSite) Author() map[string]any { return nil } diff --git a/resources/page/testhelpers_test.go b/resources/page/testhelpers_test.go index 59b8cf0e8..19bdc0068 100644 --- a/resources/page/testhelpers_test.go +++ b/resources/page/testhelpers_test.go @@ -587,7 +587,7 @@ func (p *testPage) WordCount(context.Context) int { func createTestPages(num int) Pages { pages := make(Pages, num) - for i := 0; i < num; i++ { + for i := range num { m := &testPage{ path: fmt.Sprintf("/x/y/z/p%d.md", i), weight: 5, diff --git a/resources/postpub/fields.go b/resources/postpub/fields.go index 13b2963ce..12b3be2eb 100644 --- a/resources/postpub/fields.go +++ b/resources/postpub/fields.go @@ -31,7 +31,7 @@ func structToMap(s any) map[string]any { m := make(map[string]any) t := reflect.TypeOf(s) - for i := 0; i < t.NumMethod(); i++ { + for i := range t.NumMethod() { method := t.Method(i) if method.PkgPath != "" { continue @@ -41,7 +41,7 @@ func structToMap(s any) map[string]any { } } - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { field := t.Field(i) if field.PkgPath != "" { continue diff --git a/resources/resource/resources.go b/resources/resource/resources.go index 480c703b5..6b7311bad 100644 --- a/resources/resource/resources.go +++ b/resources/resource/resources.go @@ -24,6 +24,7 @@ import ( "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/hugofs/glob" "github.com/spf13/cast" + "slices" ) var _ ResourceFinder = (*Resources)(nil) @@ -222,7 +223,7 @@ type translatedResource interface { // MergeByLanguage adds missing translations in r1 from r2. func (r Resources) MergeByLanguage(r2 Resources) Resources { - result := append(Resources(nil), r...) + result := slices.Clone(r) m := make(map[string]bool) for _, rr := range r { if translated, ok := rr.(translatedResource); ok { diff --git a/resources/resource_factories/bundler/bundler.go b/resources/resource_factories/bundler/bundler.go index 8b268ebbe..aef644b7f 100644 --- a/resources/resource_factories/bundler/bundler.go +++ b/resources/resource_factories/bundler/bundler.go @@ -141,7 +141,7 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou if resolvedm.MainType == media.Builtin.JavascriptType.MainType && resolvedm.SubType == media.Builtin.JavascriptType.SubType { readers := make([]hugio.ReadSeekCloser, 2*len(rcsources)-1) j := 0 - for i := 0; i < len(rcsources); i++ { + for i := range rcsources { if i > 0 { readers[j] = hugio.NewReadSeekerNoOpCloserFromString("\n;\n") j++ diff --git a/resources/resource_factories/bundler/bundler_test.go b/resources/resource_factories/bundler/bundler_test.go index 17a74cc88..66f0c2340 100644 --- a/resources/resource_factories/bundler/bundler_test.go +++ b/resources/resource_factories/bundler/bundler_test.go @@ -31,7 +31,7 @@ func TestMultiReadSeekCloser(t *testing.T) { hugio.NewReadSeekerNoOpCloserFromString("C"), ) - for i := 0; i < 3; i++ { + for range 3 { s1 := helpers.ReaderToString(rc) c.Assert(s1, qt.Equals, "ABC") _, err := rc.Seek(0, 0) diff --git a/resources/resource_factories/create/create_integration_test.go b/resources/resource_factories/create/create_integration_test.go index faa2de565..0ed43721c 100644 --- a/resources/resource_factories/create/create_integration_test.go +++ b/resources/resource_factories/create/create_integration_test.go @@ -136,7 +136,7 @@ mediaTypes = ['text/plain'] {{ end }} ` - for i := 0; i < numPages; i++ { + for i := range numPages { filesTemplate += fmt.Sprintf("-- content/post/p%d.md --\n", i) } @@ -153,7 +153,7 @@ mediaTypes = ['text/plain'] b.Build() - for i := 0; i < numPages; i++ { + for i := range numPages { b.AssertFileContent(fmt.Sprintf("public/post/p%d/index.html", i), fmt.Sprintf("Content: Response for /post/p%d/.", i)) } }) diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go index 8861ded5c..2a4faa315 100644 --- a/resources/resource_metadata.go +++ b/resources/resource_metadata.go @@ -28,6 +28,7 @@ import ( "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/paths" + maps0 "maps" ) var ( @@ -85,11 +86,9 @@ func (r *metaResource) setName(name string) { func (r *metaResource) updateParams(params map[string]any) { if r.params == nil { - r.params = make(map[string]interface{}) - } - for k, v := range params { - r.params[k] = v + r.params = make(map[string]any) } + maps0.Copy(r.params, params) r.changed = true } diff --git a/resources/resource_transformers/cssjs/postcss_integration_test.go b/resources/resource_transformers/cssjs/postcss_integration_test.go index 8f2132789..a05f340fd 100644 --- a/resources/resource_transformers/cssjs/postcss_integration_test.go +++ b/resources/resource_transformers/cssjs/postcss_integration_test.go @@ -239,7 +239,7 @@ func TestTransformPostCSSResourceCacheWithPathInBaseURL(t *testing.T) { c.Assert(err, qt.IsNil) c.Cleanup(clean) - for i := 0; i < 2; i++ { + for i := range 2 { files := postCSSIntegrationTestFiles if i == 1 { diff --git a/resources/resources_integration_test.go b/resources/resources_integration_test.go index 0c45b775a..0d02b45d5 100644 --- a/resources/resources_integration_test.go +++ b/resources/resources_integration_test.go @@ -122,7 +122,7 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA ` - for i := 0; i < 3; i++ { + for range 3 { b := hugolib.NewIntegrationTestBuilder( hugolib.IntegrationTestConfig{ diff --git a/resources/transform_test.go b/resources/transform_test.go index 79d4841b1..eac85ada9 100644 --- a/resources/transform_test.go +++ b/resources/transform_test.go @@ -207,7 +207,7 @@ func TestTransform(t *testing.T) { fs := afero.NewMemMapFs() - for i := 0; i < 2; i++ { + for i := range 2 { spec := newTestResourceSpec(specDescriptor{c: c, fs: fs}) r := createTransformer(c, spec, "f1.txt", "color is blue") @@ -337,12 +337,12 @@ func TestTransform(t *testing.T) { const count = 26 // A-Z transformations := make([]resources.ResourceTransformation, count) - for i := 0; i < count; i++ { + for i := range count { transformations[i] = createContentReplacer(fmt.Sprintf("t%d", i), fmt.Sprint(i), string(rune(i+65))) } var countstr strings.Builder - for i := 0; i < count; i++ { + for i := range count { countstr.WriteString(fmt.Sprint(i)) } @@ -405,18 +405,18 @@ func TestTransform(t *testing.T) { transformers := make([]resources.Transformer, 10) transformations := make([]resources.ResourceTransformation, 10) - for i := 0; i < 10; i++ { + for i := range 10 { transformers[i] = createTransformer(c, spec, fmt.Sprintf("f%d.txt", i), fmt.Sprintf("color is %d", i)) transformations[i] = createContentReplacer("test", strconv.Itoa(i), "blue") } var wg sync.WaitGroup - for i := 0; i < 13; i++ { + for i := range 13 { wg.Add(1) go func(i int) { defer wg.Done() - for j := 0; j < 23; j++ { + for j := range 23 { id := (i + j) % 10 tr, err := transformers[id].Transform(transformations[id]) c.Assert(err, qt.IsNil) diff --git a/tpl/collections/apply.go b/tpl/collections/apply.go index 3d50395b9..39b66a27f 100644 --- a/tpl/collections/apply.go +++ b/tpl/collections/apply.go @@ -48,7 +48,7 @@ func (ns *Namespace) Apply(ctx context.Context, c any, fname string, args ...any switch seqv.Kind() { case reflect.Array, reflect.Slice: r := make([]any, seqv.Len()) - for i := 0; i < seqv.Len(); i++ { + for i := range seqv.Len() { vv := seqv.Index(i) vvv, err := applyFnToThis(ctx, fnv, vv, args...) @@ -91,7 +91,7 @@ func applyFnToThis(ctx context.Context, fn, this reflect.Value, args ...any) (re return reflect.ValueOf(nil), errors.New("Too many arguments") }*/ - for i := 0; i < num; i++ { + for i := range num { // AssignableTo reports whether xt is assignable to type targ. if xt, targ := n[i].Type(), fn.Type().In(i); !xt.AssignableTo(targ) { return reflect.ValueOf(nil), errors.New("called apply using " + xt.String() + " as type " + targ.String()) diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index c1e7286ce..0653a453a 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -125,7 +125,7 @@ func (ns *Namespace) Delimit(ctx context.Context, l, sep any, last ...any) (stri lv = reflect.ValueOf(sortSeq) fallthrough case reflect.Array, reflect.Slice, reflect.String: - for i := 0; i < lv.Len(); i++ { + for i := range lv.Len() { val := lv.Index(i).Interface() valStr, err := cast.ToStringE(val) if err != nil { @@ -165,7 +165,7 @@ func (ns *Namespace) Dictionary(values ...any) (map[string]any, error) { case string: key = v case []string: - for i := 0; i < len(v)-1; i++ { + for i := range len(v) - 1 { key = v[i] var m map[string]any v, found := dict[key] @@ -235,7 +235,7 @@ func (ns *Namespace) In(l any, v any) (bool, error) { switch lv.Kind() { case reflect.Array, reflect.Slice: - for i := 0; i < lv.Len(); i++ { + for i := range lv.Len() { lvv, isNil := indirectInterface(lv.Index(i)) if isNil { continue @@ -277,13 +277,13 @@ func (ns *Namespace) Intersect(l1, l2 any) (any, error) { ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[any]bool)} switch l2v.Kind() { case reflect.Array, reflect.Slice: - for i := 0; i < l1v.Len(); i++ { + for i := range l1v.Len() { l1vv := l1v.Index(i) if !l1vv.Type().Comparable() { return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types") } - for j := 0; j < l2v.Len(); j++ { + for j := range l2v.Len() { l2vv := l2v.Index(j) if !l2vv.Type().Comparable() { return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types") @@ -590,7 +590,7 @@ func (ns *Namespace) Union(l1, l2 any) (any, error) { isNil bool ) - for i := 0; i < l1v.Len(); i++ { + for i := range l1v.Len() { l1vv, isNil = indirectInterface(l1v.Index(i)) if !l1vv.Type().Comparable() { @@ -610,7 +610,7 @@ func (ns *Namespace) Union(l1, l2 any) (any, error) { } } - for j := 0; j < l2v.Len(); j++ { + for j := range l2v.Len() { l2vv := l2v.Index(j) switch kind := l1vv.Kind(); { @@ -661,7 +661,7 @@ func (ns *Namespace) Uniq(l any) (any, error) { seen := make(map[any]bool) - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { ev, _ := indirectInterface(v.Index(i)) key := normalize(ev) diff --git a/tpl/collections/collections_integration_test.go b/tpl/collections/collections_integration_test.go index 2aabee03e..cc60770f9 100644 --- a/tpl/collections/collections_integration_test.go +++ b/tpl/collections/collections_integration_test.go @@ -52,7 +52,7 @@ Desc: {{ sort (sort $values "b" "desc") "a" "desc" }} ` - for i := 0; i < 4; i++ { + for range 4 { b := hugolib.NewIntegrationTestBuilder( hugolib.IntegrationTestConfig{ @@ -122,7 +122,7 @@ func TestAppendNilsToSliceWithNils(t *testing.T) { ` - for i := 0; i < 4; i++ { + for range 4 { b := hugolib.NewIntegrationTestBuilder( hugolib.IntegrationTestConfig{ diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index 2cd6bfc3f..fe7f2144d 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -856,7 +856,7 @@ func ToTstXIs(slice any) []TstXI { } tis := make([]TstXI, s.Len()) - for i := 0; i < s.Len(); i++ { + for i := range s.Len() { tsti, ok := s.Index(i).Interface().(TstXI) if !ok { return nil diff --git a/tpl/collections/complement.go b/tpl/collections/complement.go index 0cc2b5857..606d77dde 100644 --- a/tpl/collections/complement.go +++ b/tpl/collections/complement.go @@ -44,7 +44,7 @@ func (ns *Namespace) Complement(ls ...any) (any, error) { switch v.Kind() { case reflect.Array, reflect.Slice: sl := reflect.MakeSlice(v.Type(), 0, 0) - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { ev, _ := indirectInterface(v.Index(i)) if _, found := aset[normalize(ev)]; !found { sl = reflect.Append(sl, ev) diff --git a/tpl/collections/index.go b/tpl/collections/index.go index df932f7c6..a319ea298 100644 --- a/tpl/collections/index.go +++ b/tpl/collections/index.go @@ -52,7 +52,7 @@ func (ns *Namespace) doIndex(item any, args ...any) (any, error) { if len(args) == 1 { v := reflect.ValueOf(args[0]) if v.Kind() == reflect.Slice { - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { indices = append(indices, v.Index(i).Interface()) } } else { diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go index 6b986cbc4..05816a009 100644 --- a/tpl/collections/reflect_helpers.go +++ b/tpl/collections/reflect_helpers.go @@ -74,7 +74,7 @@ func collectIdentities(seqs ...any) (map[any]bool, error) { v := reflect.ValueOf(seq) switch v.Kind() { case reflect.Array, reflect.Slice: - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { ev, _ := indirectInterface(v.Index(i)) if !ev.Type().Comparable() { diff --git a/tpl/collections/sort.go b/tpl/collections/sort.go index 20862a451..0c09f6af4 100644 --- a/tpl/collections/sort.go +++ b/tpl/collections/sort.go @@ -73,7 +73,7 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error) switch seqv.Kind() { case reflect.Array, reflect.Slice: - for i := 0; i < seqv.Len(); i++ { + for i := range seqv.Len() { p.Pairs[i].Value = seqv.Index(i) if sortByField == "" || sortByField == "value" { p.Pairs[i].Key = p.Pairs[i].Value diff --git a/tpl/collections/symdiff.go b/tpl/collections/symdiff.go index 8ecee3c4a..4b9dc6e42 100644 --- a/tpl/collections/symdiff.go +++ b/tpl/collections/symdiff.go @@ -44,7 +44,7 @@ func (ns *Namespace) SymDiff(s2, s1 any) (any, error) { slice = reflect.MakeSlice(sliceType, 0, 0) } - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { ev, _ := indirectInterface(v.Index(i)) key := normalize(ev) diff --git a/tpl/collections/where.go b/tpl/collections/where.go index a14a4863d..b15cfe781 100644 --- a/tpl/collections/where.go +++ b/tpl/collections/where.go @@ -148,7 +148,7 @@ func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: iv := v.Int() ivp = &iv - for i := 0; i < mv.Len(); i++ { + for i := range mv.Len() { if anInt, err := toInt(mv.Index(i)); err == nil { ima = append(ima, anInt) } @@ -156,7 +156,7 @@ func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error case reflect.String: sv := v.String() svp = &sv - for i := 0; i < mv.Len(); i++ { + for i := range mv.Len() { if aString, err := toString(mv.Index(i)); err == nil { sma = append(sma, aString) } @@ -164,7 +164,7 @@ func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error case reflect.Float64: fv := v.Float() fvp = &fv - for i := 0; i < mv.Len(); i++ { + for i := range mv.Len() { if aFloat, err := toFloat(mv.Index(i)); err == nil { fma = append(fma, aFloat) } @@ -173,7 +173,7 @@ func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error if hreflect.IsTime(v.Type()) { iv := ns.toTimeUnix(v) ivp = &iv - for i := 0; i < mv.Len(); i++ { + for i := range mv.Len() { ima = append(ima, ns.toTimeUnix(mv.Index(i))) } } @@ -397,7 +397,7 @@ func parseWhereArgs(args ...any) (mv reflect.Value, op string, err error) { func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) { rv := reflect.MakeSlice(seqv.Type(), 0, 0) - for i := 0; i < seqv.Len(); i++ { + for i := range seqv.Len() { var vvv reflect.Value rvv := seqv.Index(i) diff --git a/tpl/collections/where_test.go b/tpl/collections/where_test.go index c66a5d608..60f97e607 100644 --- a/tpl/collections/where_test.go +++ b/tpl/collections/where_test.go @@ -865,10 +865,10 @@ func BenchmarkWhereOps(b *testing.B) { ns := newNs() var seq []map[string]string ctx := context.Background() - for i := 0; i < 500; i++ { + for range 500 { seq = append(seq, map[string]string{"foo": "bar"}) } - for i := 0; i < 500; i++ { + for range 500 { seq = append(seq, map[string]string{"foo": "baz"}) } // Shuffle the sequence. @@ -907,7 +907,7 @@ func BenchmarkWhereMap(b *testing.B) { ns := newNs() seq := map[string]string{} - for i := 0; i < 1000; i++ { + for i := range 1000 { seq[fmt.Sprintf("key%d", i)] = "value" } diff --git a/tpl/data/data.go b/tpl/data/data.go index 097cfe4a8..ca1796826 100644 --- a/tpl/data/data.go +++ b/tpl/data/data.go @@ -36,6 +36,7 @@ import ( "github.com/spf13/cast" "github.com/gohugoio/hugo/deps" + "slices" ) // New returns a new instance of the data-namespaced template functions. @@ -170,12 +171,7 @@ func hasHeaderValue(m http.Header, key, value string) bool { return false } - for _, v := range s { - if v == value { - return true - } - } - return false + return slices.Contains(s, value) } func hasHeaderKey(m http.Header, key string) bool { diff --git a/tpl/data/resources_test.go b/tpl/data/resources_test.go index b8003bf43..d49e74d4c 100644 --- a/tpl/data/resources_test.go +++ b/tpl/data/resources_test.go @@ -155,11 +155,11 @@ func TestScpGetRemoteParallel(t *testing.T) { var wg sync.WaitGroup - for i := 0; i < 1; i++ { + for i := range 1 { wg.Add(1) go func(gor int) { defer wg.Done() - for j := 0; j < 10; j++ { + for range 10 { var cb []byte f := func(b []byte) (bool, error) { cb = b diff --git a/tpl/internal/templatefuncsRegistry.go b/tpl/internal/templatefuncsRegistry.go index 1b74bf443..425938b07 100644 --- a/tpl/internal/templatefuncsRegistry.go +++ b/tpl/internal/templatefuncsRegistry.go @@ -213,7 +213,7 @@ func (t *TemplateFuncsNamespace) toJSON(ctx context.Context) ([]byte, error) { return nil, nil } ctxType := reflect.TypeOf(tctx) - for i := 0; i < ctxType.NumMethod(); i++ { + for i := range ctxType.NumMethod() { method := ctxType.Method(i) if ignoreFuncs[method.Name] { continue diff --git a/tpl/math/math.go b/tpl/math/math.go index 15c0db22c..5a5e42f4e 100644 --- a/tpl/math/math.go +++ b/tpl/math/math.go @@ -314,7 +314,7 @@ func (ns *Namespace) toFloatsE(v any) ([]float64, bool, error) { switch vv.Kind() { case reflect.Slice, reflect.Array: var floats []float64 - for i := 0; i < vv.Len(); i++ { + for i := range vv.Len() { f, err := cast.ToFloat64E(vv.Index(i).Interface()) if err != nil { return nil, true, err diff --git a/tpl/page/init.go b/tpl/page/init.go index 826aa45d3..106552630 100644 --- a/tpl/page/init.go +++ b/tpl/page/init.go @@ -31,7 +31,7 @@ func init() { f := func(d *deps.Deps) *internal.TemplateFuncsNamespace { ns := &internal.TemplateFuncsNamespace{ Name: name, - Context: func(ctx context.Context, args ...interface{}) (interface{}, error) { + Context: func(ctx context.Context, args ...any) (any, error) { v := tpl.Context.Page.Get(ctx) if v == nil { // The multilingual sitemap does not have a page as its context. diff --git a/tpl/templates/defer_integration_test.go b/tpl/templates/defer_integration_test.go index 77be91cee..27b8fbf8c 100644 --- a/tpl/templates/defer_integration_test.go +++ b/tpl/templates/defer_integration_test.go @@ -87,7 +87,7 @@ func TestDeferRepeatedBuildsEditOutside(t *testing.T) { b := hugolib.TestRunning(t, deferFilesCommon) - for i := 0; i < 5; i++ { + for i := range 5 { old := fmt.Sprintf("EDIT_COUNTER_OUTSIDE_%d", i) new := fmt.Sprintf("EDIT_COUNTER_OUTSIDE_%d", i+1) b.EditFileReplaceAll("layouts/index.html", old, new).Build() @@ -100,7 +100,7 @@ func TestDeferRepeatedBuildsEditDefer(t *testing.T) { b := hugolib.TestRunning(t, deferFilesCommon) - for i := 0; i < 8; i++ { + for i := range 8 { old := fmt.Sprintf("EDIT_COUNTER_DEFER_%d", i) new := fmt.Sprintf("EDIT_COUNTER_DEFER_%d", i+1) b.EditFileReplaceAll("layouts/index.html", old, new).Build() diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 0ea7117a3..3b643162a 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -431,7 +431,7 @@ func (t *templateHandler) LookupVariants(name string) []tpl.Template { } variants := make([]tpl.Template, len(s.variants)) - for i := 0; i < len(variants); i++ { + for i := range variants { variants[i] = s.variants[i].ts } @@ -599,7 +599,7 @@ func (t *templateHandler) addFileContext(templ tpl.Template, inerr error) error func (t *templateHandler) extractIdentifiers(line string) []string { m := identifiersRe.FindAllStringSubmatch(line, -1) identifiers := make([]string, len(m)) - for i := 0; i < len(m); i++ { + for i := range m { identifiers[i] = m[i][1] } return identifiers diff --git a/tpl/tplimpl/template_ast_transformers.go b/tpl/tplimpl/template_ast_transformers.go index f95335779..4deadd052 100644 --- a/tpl/tplimpl/template_ast_transformers.go +++ b/tpl/tplimpl/template_ast_transformers.go @@ -27,6 +27,7 @@ import ( "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/tpl" "github.com/mitchellh/mapstructure" + "slices" ) type templateType int @@ -187,7 +188,7 @@ func (c *templateContext) applyTransformations(n parse.Node) (bool, error) { for i, cmd := range x.Cmds { keep, _ := c.applyTransformations(cmd) if !keep { - x.Cmds = append(x.Cmds[:i], x.Cmds[i+1:]...) + x.Cmds = slices.Delete(x.Cmds, i, i+1) } } @@ -271,12 +272,7 @@ func (c *templateContext) applyTransformationsToNodes(nodes ...parse.Node) { } func (c *templateContext) hasIdent(idents []string, ident string) bool { - for _, id := range idents { - if id == ident { - return true - } - } - return false + return slices.Contains(idents, ident) } // collectConfig collects and parses any leading template config variable declaration. diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index d73d4d336..b181db061 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -64,6 +64,7 @@ import ( _ "github.com/gohugoio/hugo/tpl/time" _ "github.com/gohugoio/hugo/tpl/transform" _ "github.com/gohugoio/hugo/tpl/urls" + maps0 "maps" ) var ( @@ -290,9 +291,7 @@ func createFuncMap(d *deps.Deps) map[string]any { } if d.OverloadedTemplateFuncs != nil { - for k, v := range d.OverloadedTemplateFuncs { - funcMap[k] = v - } + maps0.Copy(funcMap, d.OverloadedTemplateFuncs) } d.TmplFuncMap = funcMap diff --git a/tpl/transform/unmarshal_test.go b/tpl/transform/unmarshal_test.go index d65f05fd4..9b34e1daa 100644 --- a/tpl/transform/unmarshal_test.go +++ b/tpl/transform/unmarshal_test.go @@ -192,7 +192,7 @@ func BenchmarkUnmarshalString(b *testing.B) { const numJsons = 100 var jsons [numJsons]string - for i := 0; i < numJsons; i++ { + for i := range numJsons { jsons[i] = strings.Replace(testJSON, "ROOT_KEY", fmt.Sprintf("root%d", i), 1) } @@ -220,7 +220,7 @@ func BenchmarkUnmarshalResource(b *testing.B) { const numJsons = 100 var jsons [numJsons]testContentResource - for i := 0; i < numJsons; i++ { + for i := range numJsons { key := fmt.Sprintf("root%d", i) jsons[i] = testContentResource{key: key, content: strings.Replace(testJSON, "ROOT_KEY", key, 1), mime: media.Builtin.JSONType} } diff --git a/transform/livereloadinject/livereloadinject.go b/transform/livereloadinject/livereloadinject.go index e88e3895b..425d268b3 100644 --- a/transform/livereloadinject/livereloadinject.go +++ b/transform/livereloadinject/livereloadinject.go @@ -56,7 +56,7 @@ func New(baseURL *url.URL) transform.Transformer { src += "&port=" + baseURL.Port() src += "&path=" + strings.TrimPrefix(path+"/livereload", "/") - script := []byte(fmt.Sprintf(``, html.EscapeString(src))) + script := fmt.Appendf(nil, ``, html.EscapeString(src)) c := make([]byte, len(b)+len(script)) copy(c, b[:idx]) diff --git a/transform/metainject/hugogenerator.go b/transform/metainject/hugogenerator.go index 43a477354..b3dda9b15 100644 --- a/transform/metainject/hugogenerator.go +++ b/transform/metainject/hugogenerator.go @@ -39,12 +39,12 @@ func HugoGenerator(ft transform.FromTo) error { } head := "" - replace := []byte(fmt.Sprintf("%s\n\t%s", head, hugoGeneratorTag)) + replace := fmt.Appendf(nil, "%s\n\t%s", head, hugoGeneratorTag) newcontent := bytes.Replace(b, []byte(head), replace, 1) if len(newcontent) == len(b) { head := "" - replace := []byte(fmt.Sprintf("%s\n\t%s", head, hugoGeneratorTag)) + replace := fmt.Appendf(nil, "%s\n\t%s", head, hugoGeneratorTag) newcontent = bytes.Replace(b, []byte(head), replace, 1) } diff --git a/watcher/filenotify/poller_test.go b/watcher/filenotify/poller_test.go index 9b52b9780..77feb459d 100644 --- a/watcher/filenotify/poller_test.go +++ b/watcher/filenotify/poller_test.go @@ -220,11 +220,11 @@ func prepareTestDirWithSomeFiles(c *qt.C, id string) string { c.Assert(os.MkdirAll(filepath.Join(dir, subdir1), 0o777), qt.IsNil) c.Assert(os.MkdirAll(filepath.Join(dir, subdir2), 0o777), qt.IsNil) - for i := 0; i < 3; i++ { + for i := range 3 { c.Assert(os.WriteFile(filepath.Join(dir, subdir1, fmt.Sprintf("file%d", i)), []byte("hello1"), 0o600), qt.IsNil) } - for i := 0; i < 3; i++ { + for i := range 3 { c.Assert(os.WriteFile(filepath.Join(dir, subdir2, fmt.Sprintf("file%d", i)), []byte("hello2"), 0o600), qt.IsNil) } From c498d0fe1e982f59d314627878ef9b744240268d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 26 Feb 2025 12:16:58 +0100 Subject: [PATCH 190/374] Use the page path and not the backing filename as the last resort in the default sort This should: 1. Fix some (rare) tiebreaker issues when sorting pages from multiple content adapters. 2. Improve the sorting for pages without a backing file. --- common/paths/pathparser_test.go | 2 ++ .../pagesfromgotmpl_integration_test.go | 22 +++++++++++++++++++ resources/page/pages_sort.go | 8 +++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/common/paths/pathparser_test.go b/common/paths/pathparser_test.go index e8fee96e1..584b1a78a 100644 --- a/common/paths/pathparser_test.go +++ b/common/paths/pathparser_test.go @@ -165,6 +165,7 @@ func TestParse(t *testing.T) { c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no"}) c.Assert(p.Base(), qt.Equals, "/a/b.a.b.txt") c.Assert(p.BaseNoLeadingSlash(), qt.Equals, "a/b.a.b.txt") + c.Assert(p.Path(), qt.Equals, "/a/b.a.b.no.txt") c.Assert(p.PathNoLang(), qt.Equals, "/a/b.a.b.txt") c.Assert(p.Ext(), qt.Equals, "txt") c.Assert(p.PathNoIdentifier(), qt.Equals, "/a/b.a.b") @@ -220,6 +221,7 @@ func TestParse(t *testing.T) { c.Assert(p.NameNoExt(), qt.Equals, "index.no") c.Assert(p.NameNoIdentifier(), qt.Equals, "index") c.Assert(p.NameNoLang(), qt.Equals, "index.md") + c.Assert(p.Path(), qt.Equals, "/a/b/index.no.md") c.Assert(p.PathNoLang(), qt.Equals, "/a/b/index.md") c.Assert(p.Section(), qt.Equals, "a") }, diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go index b674ef0b6..c00a95bc1 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go @@ -382,6 +382,28 @@ Single: {{ .Title }}|{{ .Content }}| } } +func TestPagesFromGoTmplDefaultPageSort(t *testing.T) { + t.Parallel() + files := ` +-- hugo.toml -- +defaultContentLanguage = "en" +-- layouts/index.html -- +{{ range site.RegularPages }}{{ .RelPermalink }}|{{ end}} +-- content/_content.gotmpl -- +{{ $.AddPage (dict "kind" "page" "path" "docs/_p22" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "docs/p12" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "docs/_p12" "title" "A" ) }} +-- content/docs/_content.gotmpl -- +{{ $.AddPage (dict "kind" "page" "path" "_p21" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "p11" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "_p11" "title" "A" ) }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "/docs/_p11/|/docs/_p12/|/docs/_p21/|/docs/_p22/|/docs/p11/|/docs/p12/|") +} + func TestPagesFromGoTmplEnableAllLanguages(t *testing.T) { t.Parallel() diff --git a/resources/page/pages_sort.go b/resources/page/pages_sort.go index 3f4875702..e77bb7e7c 100644 --- a/resources/page/pages_sort.go +++ b/resources/page/pages_sort.go @@ -90,14 +90,14 @@ var ( if w01 != w02 && w01 != -1 && w02 != -1 { return w01 < w02 } + if p1.Weight() == p2.Weight() { if p1.Date().Unix() == p2.Date().Unix() { c := collatorStringCompare(func(p Page) string { return p.LinkTitle() }, p1, p2) if c == 0 { - if p1.File() == nil || p2.File() == nil { - return p1.File() == nil - } - return compare.LessStrings(p1.File().Filename(), p2.File().Filename()) + // This is the full normalized path, which will contain extension and any language code preserved, + // which is what we want for sorting. + return compare.LessStrings(p1.PathInfo().Path(), p2.PathInfo().Path()) } return c < 0 } From 6927e6f04826b5fbf28de0d042f205e107628b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 26 Feb 2025 11:01:08 +0100 Subject: [PATCH 191/374] deps: Upgrade github.com/rogpeppe/go-internal v1.13.1 => v1.14.1 Closes #13449 --- go.mod | 2 +- go.sum | 2 ++ main_test.go | 23 ++++++++++------------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index fe75b121a..d62eeb7c4 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/pelletier/go-toml/v2 v2.2.3 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/rogpeppe/go-internal v1.13.1 + github.com/rogpeppe/go-internal v1.14.1 github.com/sanity-io/litter v1.5.8 github.com/spf13/afero v1.11.0 github.com/spf13/cast v1.7.1 diff --git a/go.sum b/go.sum index 55710da71..c8836a440 100644 --- a/go.sum +++ b/go.sum @@ -420,6 +420,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= diff --git a/main_test.go b/main_test.go index 9dd2734d2..683defb1a 100644 --- a/main_test.go +++ b/main_test.go @@ -56,19 +56,16 @@ func TestUnfinished(t *testing.T) { } func TestMain(m *testing.M) { - os.Exit( - testscript.RunMain(m, map[string]func() int{ - // The main program. - "hugo": func() int { - err := commands.Execute(os.Args[1:]) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 - } - return 0 - }, - }), - ) + testscript.Main(m, map[string]func(){ + // The main program. + "hugo": func() { + err := commands.Execute(os.Args[1:]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + }, + }) } var commonTestScriptsParam = testscript.Params{ From 4094a1e12c593ebaffe5bf28a1cd0a12330b1a60 Mon Sep 17 00:00:00 2001 From: coliff Date: Wed, 26 Feb 2025 20:35:12 +0900 Subject: [PATCH 192/374] all: Typo fixes --- CONTRIBUTING.md | 2 +- cache/httpcache/httpcache.go | 4 ++-- hugolib/pagesfromdata/pagesfromgotmpl.go | 2 +- hugolib/segments/segments.go | 2 +- hugolib/site_output.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1f56927d..ddd3efcf2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ ->**Note:** We would apprecitate if you hold on with any big refactorings (like renaming deprecated Go packages), mainly because of potential for extra merge work for future coming in in the near future. +>**Note:** We would appreciate if you hold on with any big refactoring (like renaming deprecated Go packages), mainly because of potential for extra merge work for future coming in in the near future. # Contributing to Hugo diff --git a/cache/httpcache/httpcache.go b/cache/httpcache/httpcache.go index 7fa76e5c3..2e8d9b8b4 100644 --- a/cache/httpcache/httpcache.go +++ b/cache/httpcache/httpcache.go @@ -42,7 +42,7 @@ var DefaultConfig = Config{ // Config holds the configuration for the HTTP cache. type Config struct { - // Configures the HTTP cache behaviour (RFC 9111). + // Configures the HTTP cache behavior (RFC 9111). // When this is not enabled for a resource, Hugo will go straight to the file cache. Cache Cache @@ -52,7 +52,7 @@ type Config struct { } type Cache struct { - // Enable HTTP cache behaviour (RFC 9111) for these rsources. + // Enable HTTP cache behavior (RFC 9111) for these resources. For GlobMatcher } diff --git a/hugolib/pagesfromdata/pagesfromgotmpl.go b/hugolib/pagesfromdata/pagesfromgotmpl.go index 2ee273718..58bb72978 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl.go @@ -270,7 +270,7 @@ type sourceInfo struct { } func (p PagesFromTemplate) CloneForSite(s page.Site) *PagesFromTemplate { - // We deliberately make them share the same DepenencyManager and Store. + // We deliberately make them share the same DependencyManager and Store. p.PagesFromTemplateOptions.Site = s p.PagesFromTemplateDeps = p.PagesFromTemplateOptions.DepsFromSite(s) p.buildState = &BuildState{ diff --git a/hugolib/segments/segments.go b/hugolib/segments/segments.go index 8f7c18121..941c4ea5c 100644 --- a/hugolib/segments/segments.go +++ b/hugolib/segments/segments.go @@ -44,7 +44,7 @@ func (e excludeInclude) ShouldExcludeCoarse(fields SegmentMatcherFields) bool { } // ShouldExcludeFine returns whether the given fields should be excluded. -// This is used for the finer grained checks, e.g. on invididual pages. +// This is used for the finer grained checks, e.g. on individual pages. func (e excludeInclude) ShouldExcludeFine(fields SegmentMatcherFields) bool { if e.exclude != nil && e.exclude(fields) { return true diff --git a/hugolib/site_output.go b/hugolib/site_output.go index 2744c0133..47778b8b0 100644 --- a/hugolib/site_output.go +++ b/hugolib/site_output.go @@ -80,7 +80,7 @@ func createSiteOutputFormats(allFormats output.Formats, outputs map[string]any, f, found := allFormats.GetByName(format) if !found { if rssDisabled && strings.EqualFold(format, "RSS") { - // This is legacy behaviour. We used to have both + // This is legacy behavior. We used to have both // a RSS page kind and output format. continue } From e5eecbd9bc580d28b9c1d34a5035eac486d06a88 Mon Sep 17 00:00:00 2001 From: margau Date: Wed, 26 Feb 2025 16:33:22 +0100 Subject: [PATCH 193/374] github: Build docker image with both extended and withdeploy tags --- .github/workflows/image.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 95a4ad0b6..c4f3c34c3 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -46,3 +46,4 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: HUGO_BUILD_TAGS=extended,withdeploy \ No newline at end of file From 666444f0a52132f9fec9f71cf25b441cc6a4f355 Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Wed, 26 Feb 2025 15:41:25 +0000 Subject: [PATCH 194/374] releaser: Bump versions for release of 0.145.0 [ci skip] --- common/hugo/version_current.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index ce8a296fb..29a952817 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -19,5 +19,5 @@ var CurrentVersion = Version{ Major: 0, Minor: 145, PatchLevel: 0, - Suffix: "-DEV", + Suffix: "", } From cb4a097190890d45c6424fb0f8732ebbcc4e80aa Mon Sep 17 00:00:00 2001 From: hugoreleaser Date: Wed, 26 Feb 2025 15:58:38 +0000 Subject: [PATCH 195/374] releaser: Prepare repository for 0.146.0-DEV [ci skip] --- common/hugo/version_current.go | 4 ++-- hugoreleaser.env | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 29a952817..5b0f2529b 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 145, + Minor: 146, PatchLevel: 0, - Suffix: "", + Suffix: "-DEV", } diff --git a/hugoreleaser.env b/hugoreleaser.env index 04ad21aa2..1ae37206e 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,8 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.144.1 -HUGORELEASER_COMMITISH=a79d63a44659b6bc76dcdf223de1637e0bd70ff6 +HUGORELEASER_TAG=v0.145.0 +HUGORELEASER_COMMITISH=666444f0a52132f9fec9f71cf25b441cc6a4f355 + From 3a11d22da365434ee47df818371d4f32a397b73d Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 26 Feb 2025 21:17:06 +0100 Subject: [PATCH 196/374] resources/image: Mark loong64 as FMA-using architecture --- resources/images/imagetesting/testing.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/images/imagetesting/testing.go b/resources/images/imagetesting/testing.go index 25f2ab087..f7a6af7ea 100644 --- a/resources/images/imagetesting/testing.go +++ b/resources/images/imagetesting/testing.go @@ -231,4 +231,5 @@ var UsesFMA = runtime.GOARCH == "s390x" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "arm64" || - runtime.GOARCH == "riscv64" + runtime.GOARCH == "riscv64" || + runtime.GOARCH == "loong64" From 3f2e1c08e3d81fd566eae419c1a14c5612cd24ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 3 Mar 2025 11:20:22 +0100 Subject: [PATCH 197/374] commands: Skip flaky test on Windows Closes #13465 --- testscripts/commands/mod_npm_withexisting.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testscripts/commands/mod_npm_withexisting.txt b/testscripts/commands/mod_npm_withexisting.txt index 073af0f07..9f72eefdc 100644 --- a/testscripts/commands/mod_npm_withexisting.txt +++ b/testscripts/commands/mod_npm_withexisting.txt @@ -1,5 +1,8 @@ # Test mod npm. +# See https://github.com/gohugoio/hugo/issues/13465 +[windows] skip + dostounix golden/package.json hugo mod npm pack From eebea9ec41f4c47c25191b450e6407fda0f2422b Mon Sep 17 00:00:00 2001 From: Will Faught Date: Sun, 2 Mar 2025 21:55:49 -0800 Subject: [PATCH 198/374] tpl: Add trailing newline to robots.txt template --- tpl/tplimpl/embedded/templates/_default/robots.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tpl/tplimpl/embedded/templates/_default/robots.txt b/tpl/tplimpl/embedded/templates/_default/robots.txt index 4f9540ba3..7d329b1db 100644 --- a/tpl/tplimpl/embedded/templates/_default/robots.txt +++ b/tpl/tplimpl/embedded/templates/_default/robots.txt @@ -1 +1 @@ -User-agent: * \ No newline at end of file +User-agent: * From 93df17661fc4c995008907cb8fbfe70cf52a5ca8 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Thu, 6 Mar 2025 21:37:23 -0800 Subject: [PATCH 199/374] commands: Add --omitEmpty flag to gen chromastyles Closes #13475 --- commands/gen.go | 12 +++++++++++- testscripts/commands/gen.txt | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/commands/gen.go b/commands/gen.go index b77deeeb7..895340dbb 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -50,6 +50,7 @@ func newGenCommand() *genCommand { highlightStyle string lineNumbersInlineStyle string lineNumbersTableStyle string + omitEmpty bool ) newChromaStyles := func() simplecobra.Commander { @@ -79,7 +80,14 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl if err != nil { return err } - formatter := html.New(html.WithAllClasses(true)) + + var formatter *html.Formatter + if omitEmpty { + formatter = html.New(html.WithClasses(true)) + } else { + formatter = html.New(html.WithAllClasses(true)) + } + w := os.Stdout fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " ")) formatter.WriteCSS(w, style) @@ -95,6 +103,8 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl _ = cmd.RegisterFlagCompletionFunc("lineNumbersInlineStyle", cobra.NoFileCompletions) cmd.PersistentFlags().StringVar(&lineNumbersTableStyle, "lineNumbersTableStyle", "", `foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"`) _ = cmd.RegisterFlagCompletionFunc("lineNumbersTableStyle", cobra.NoFileCompletions) + cmd.PersistentFlags().BoolVar(&omitEmpty, "omitEmpty", false, `omit empty CSS rules`) + _ = cmd.RegisterFlagCompletionFunc("omitEmpty", cobra.NoFileCompletions) }, } } diff --git a/testscripts/commands/gen.txt b/testscripts/commands/gen.txt index 2d57c8ec0..e83e9982f 100644 --- a/testscripts/commands/gen.txt +++ b/testscripts/commands/gen.txt @@ -14,3 +14,9 @@ hugo gen chromastyles --style monokai stdout 'Generated using: hugo gen chromastyles --style monokai' ! hugo gen chromastyles --style __invalid_style__ stderr 'invalid style: __invalid_style__' + +# Issue 13475 +hugo gen chromastyles --style monokai +stdout '{ }' +hugo gen chromastyles --omitEmpty --style monokai +! stdout '\{ \}' From 93d9c0533a204c44b228627559f46f8b300b130a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 08:42:43 +0000 Subject: [PATCH 200/374] build(deps): bump github.com/bep/simplecobra from 0.5.0 to 0.6.0 Bumps [github.com/bep/simplecobra](https://github.com/bep/simplecobra) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/bep/simplecobra/releases) - [Commits](https://github.com/bep/simplecobra/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: github.com/bep/simplecobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index d62eeb7c4..ebf2afc0b 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 github.com/bep/overlayfs v0.9.2 - github.com/bep/simplecobra v0.5.0 + github.com/bep/simplecobra v0.6.0 github.com/bep/tmc v0.5.1 github.com/cespare/xxhash/v2 v2.3.0 github.com/clbanning/mxj/v2 v2.7.0 diff --git a/go.sum b/go.sum index c8836a440..70186efbe 100644 --- a/go.sum +++ b/go.sum @@ -149,8 +149,8 @@ github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69 github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY= github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs= github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40= -github.com/bep/simplecobra v0.5.0 h1:b6rElBI1mtSrhhIvk/5KrAddKwiNXjxhChs9W1zRep4= -github.com/bep/simplecobra v0.5.0/go.mod h1:QdxH1jmpb2DB+rkDYbuk7EM7XiluIt9+ACJmoY92igg= +github.com/bep/simplecobra v0.6.0 h1:PpY/0PvYp6jt4OC/9SGoNPi6HzvpYzu8IPogVV6Xk90= +github.com/bep/simplecobra v0.6.0/go.mod h1:q0ecBAefJZYpzgkbPbQ901hzA98g3ZvCZWZRhzNtB5o= github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg= @@ -418,8 +418,6 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= From b0686712ba5fa459e341c9c6f262e8d7dfdb2539 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 08:42:40 +0000 Subject: [PATCH 201/374] build(deps): bump github.com/bep/imagemeta from 0.8.4 to 0.9.0 Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.8.4 to 0.9.0. - [Release notes](https://github.com/bep/imagemeta/releases) - [Commits](https://github.com/bep/imagemeta/compare/v0.8.4...v0.9.0) --- updated-dependencies: - dependency-name: github.com/bep/imagemeta dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ebf2afc0b..59ec45b9d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/bep/goportabletext v0.1.0 github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 - github.com/bep/imagemeta v0.8.4 + github.com/bep/imagemeta v0.9.0 github.com/bep/lazycache v0.7.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 diff --git a/go.sum b/go.sum index 70186efbe..d1c7ee541 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY= github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI= github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o= github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q= -github.com/bep/imagemeta v0.8.4 h1:hA2liDZ4a3BINdsg4dD7kyR3MXNqw8Z00U3znCvtH3M= -github.com/bep/imagemeta v0.8.4/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s= +github.com/bep/imagemeta v0.9.0 h1:EdmtCoMSVHKsqZJxqgV0kL79Jr3wGbzdzZHr6YeVrxs= +github.com/bep/imagemeta v0.9.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= github.com/bep/lazycache v0.7.0 h1:VM257SkkjcR9z55eslXTkUIX8QMNKoqQRNKV/4xIkCY= github.com/bep/lazycache v0.7.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= From b9add1c7027d0eeceb821451e2a03b3f36a2cf18 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Thu, 27 Feb 2025 14:45:00 -0800 Subject: [PATCH 202/374] tpl/tplimpl: Add loading attribute to Vimeo shortcode Closes #13445 --- .../embedded/templates/shortcodes/vimeo.html | 66 +++++++++++++++---- tpl/tplimpl/shortcodes_integration_test.go | 4 +- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html index b3fc781a3..d1ec746bb 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html @@ -1,14 +1,52 @@ -{{- $pc := site.Config.Privacy.Vimeo -}} -{{- if not $pc.Disable -}} -{{- if $pc.Simple -}} -{{ template "_internal/shortcodes/vimeo_simple.html" . }} -{{- else -}} -{{ if .IsNamedParams }}
    - -
    {{ else }} -
    - -
    -{{ end }} -{{- end -}} -{{- end -}} +{{- /* +Renders an embedded Vimeo video. + +Accepts named or positional arguments. If positional, order is id, class, +title, then loading. + +@param {string} [class] The class attribute of the wrapping div element. When specified, removes the style attributes from the iframe element and its wrapping div element. +@param {string} [id] The video id. Optional if the id is provided as first positional argument. +@param {string} [loading=eager] The loading attribute of the iframe element. +@param {string} [title=Vimeo video] The title attribute of the iframe element. + +@returns {template.HTML} + +@example {{< vimeo 55073825 >}} +@example {{< vimeo id=55073825 class="foo bar" loading=lazy title="My Video" >}} +*/}} +{{- $pc := site.Config.Privacy.Vimeo }} +{{- if not $pc.Disable }} + {{- if $pc.Simple }} + {{- template "_internal/shortcodes/vimeo_simple.html" . }} + {{- else }} + {{- $id := or (.Get "id") (.Get 0) "" }} + {{- $class := or (.Get "class") (.Get 1) "" }} + {{- $title := or (.Get "title") (.Get 2) "Vimeo video" }} + {{- $loading := or (.Get "loading") (.Get 3) "eager" }} + {{- $dnt := cond $pc.EnableDNT 1 0 }} + + {{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }} + {{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }} + + {{- with $id }} + {{- $src := printf "https://player.vimeo.com/video/%v?dnt=%v" . $dnt }} +
    + +
    + {{- else }} + {{- errorf "The %q shortcode requires a video id, either as the first positional argument or an argument named id. See %s" .Name .Position }} + {{- end }} + {{- end }} +{{- end }} diff --git a/tpl/tplimpl/shortcodes_integration_test.go b/tpl/tplimpl/shortcodes_integration_test.go index d669bb333..dd3cab88f 100644 --- a/tpl/tplimpl/shortcodes_integration_test.go +++ b/tpl/tplimpl/shortcodes_integration_test.go @@ -478,12 +478,12 @@ Content: {{ .Content }} // Regular mode b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", "d5b2a079cc37d0ed") + b.AssertFileContent("public/index.html", "d1f592d2256ac3ff") // Simple mode files = strings.ReplaceAll(files, "privacy.vimeo.simple = false", "privacy.vimeo.simple = true") b = hugolib.Test(t, files) - b.AssertFileContent("public/index.html", "73b8767ce8bdf694") + b.AssertFileContent("public/index.html", "c5bf16d87e2a370b") // Simple mode with non-existent id files = strings.ReplaceAll(files, "{{< vimeo 55073825 >}}", "{{< vimeo __id_does_not_exist__ >}}") From f9aae1581bfc3fb64a35d63b134af224befa8062 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:52:45 +0000 Subject: [PATCH 203/374] build(deps): bump github.com/bep/lazycache from 0.7.0 to 0.8.0 Bumps [github.com/bep/lazycache](https://github.com/bep/lazycache) from 0.7.0 to 0.8.0. - [Release notes](https://github.com/bep/lazycache/releases) - [Commits](https://github.com/bep/lazycache/compare/v0.7.0...v0.8.0) --- updated-dependencies: - dependency-name: github.com/bep/lazycache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 59ec45b9d..0c763f47b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 github.com/bep/imagemeta v0.9.0 - github.com/bep/lazycache v0.7.0 + github.com/bep/lazycache v0.8.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 github.com/bep/overlayfs v0.9.2 diff --git a/go.sum b/go.sum index d1c7ee541..8d5ce4645 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o= github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q= github.com/bep/imagemeta v0.9.0 h1:EdmtCoMSVHKsqZJxqgV0kL79Jr3wGbzdzZHr6YeVrxs= github.com/bep/imagemeta v0.9.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= -github.com/bep/lazycache v0.7.0 h1:VM257SkkjcR9z55eslXTkUIX8QMNKoqQRNKV/4xIkCY= -github.com/bep/lazycache v0.7.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= +github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8= +github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA= From d78d4cf161ca4ab7e55f932ef028714b931bc199 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:52:48 +0000 Subject: [PATCH 204/374] build(deps): bump github.com/bep/overlayfs from 0.9.2 to 0.10.0 Bumps [github.com/bep/overlayfs](https://github.com/bep/overlayfs) from 0.9.2 to 0.10.0. - [Release notes](https://github.com/bep/overlayfs/releases) - [Commits](https://github.com/bep/overlayfs/compare/v0.9.2...v0.10.0) --- updated-dependencies: - dependency-name: github.com/bep/overlayfs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0c763f47b..970741884 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/bep/lazycache v0.8.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 - github.com/bep/overlayfs v0.9.2 + github.com/bep/overlayfs v0.10.0 github.com/bep/simplecobra v0.6.0 github.com/bep/tmc v0.5.1 github.com/cespare/xxhash/v2 v2.3.0 diff --git a/go.sum b/go.sum index 8d5ce4645..4ecbba2c4 100644 --- a/go.sum +++ b/go.sum @@ -147,8 +147,8 @@ github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA= github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY= -github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs= -github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40= +github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE= +github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro= github.com/bep/simplecobra v0.6.0 h1:PpY/0PvYp6jt4OC/9SGoNPi6HzvpYzu8IPogVV6Xk90= github.com/bep/simplecobra v0.6.0/go.mod h1:q0ecBAefJZYpzgkbPbQ901hzA98g3ZvCZWZRhzNtB5o= github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= From 2037137fbf06e3b71e3ce246f7f3afa83822f788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 12 Mar 2025 14:52:37 +0100 Subject: [PATCH 205/374] deps: Upgrade golang.org/x/mod v0.23.0 => v0.24.0 Closes #13484 --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 970741884..05ab14f1d 100644 --- a/go.mod +++ b/go.mod @@ -77,7 +77,7 @@ require ( gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 golang.org/x/image v0.24.0 - golang.org/x/mod v0.23.0 + golang.org/x/mod v0.24.0 golang.org/x/net v0.35.0 golang.org/x/sync v0.11.0 golang.org/x/text v0.22.0 @@ -172,4 +172,4 @@ require ( software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect ) -go 1.23 +go 1.23.0 diff --git a/go.sum b/go.sum index 4ecbba2c4..626bc7eea 100644 --- a/go.sum +++ b/go.sum @@ -557,8 +557,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 61c39ae63b62667d965c2ff96d085f4eda59bcb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 08:22:13 +0000 Subject: [PATCH 206/374] build(deps): bump golang.org/x/image from 0.24.0 to 0.25.0 Bumps [golang.org/x/image](https://github.com/golang/image) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/image/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/image dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 05ab14f1d..d5d3021da 100644 --- a/go.mod +++ b/go.mod @@ -76,11 +76,11 @@ require ( go.uber.org/automaxprocs v1.5.3 gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 - golang.org/x/image v0.24.0 + golang.org/x/image v0.25.0 golang.org/x/mod v0.24.0 golang.org/x/net v0.35.0 - golang.org/x/sync v0.11.0 - golang.org/x/text v0.22.0 + golang.org/x/sync v0.12.0 + golang.org/x/text v0.23.0 golang.org/x/tools v0.30.0 google.golang.org/api v0.221.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 626bc7eea..04605bff9 100644 --- a/go.sum +++ b/go.sum @@ -529,8 +529,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= -golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -626,8 +626,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -695,8 +695,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From d28c84a871937dfed1e429136d28676df9d60c93 Mon Sep 17 00:00:00 2001 From: khayyam Date: Fri, 14 Mar 2025 09:37:26 -0400 Subject: [PATCH 207/374] cache: Apply httpcache defaults for polling config Previously, compiling the config with partial or missing poll configs would introduce a panic. This ensures that the default poll configs are applied in such scenarios to ensure config is valid. Fixes #13471 --- cache/httpcache/httpcache.go | 13 ++++++++++++- cache/httpcache/httpcache_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/cache/httpcache/httpcache.go b/cache/httpcache/httpcache.go index 2e8d9b8b4..bd6d4bf7d 100644 --- a/cache/httpcache/httpcache.go +++ b/cache/httpcache/httpcache.go @@ -188,7 +188,7 @@ func (gm *GlobMatcher) CompilePredicate() (func(string) bool, error) { return p, nil } -func DecodeConfig(bcfg config.BaseConfig, m map[string]any) (Config, error) { +func DecodeConfig(_ config.BaseConfig, m map[string]any) (Config, error) { if len(m) == 0 { return DefaultConfig, nil } @@ -214,5 +214,16 @@ func DecodeConfig(bcfg config.BaseConfig, m map[string]any) (Config, error) { c.Cache.For = DefaultConfig.Cache.For } + for pci := range c.Polls { + if c.Polls[pci].For.IsZero() { + c.Polls[pci].For = DefaultConfig.Cache.For + c.Polls[pci].Disable = true + } + } + + if len(c.Polls) == 0 { + c.Polls = DefaultConfig.Polls + } + return c, nil } diff --git a/cache/httpcache/httpcache_test.go b/cache/httpcache/httpcache_test.go index e3659f97b..60c07d056 100644 --- a/cache/httpcache/httpcache_test.go +++ b/cache/httpcache/httpcache_test.go @@ -17,6 +17,7 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" ) func TestGlobMatcher(t *testing.T) { @@ -40,3 +41,33 @@ func TestGlobMatcher(t *testing.T) { c.Assert(p("foo/bar/foo.css"), qt.IsFalse) c.Assert(p("foo/bar/foo.xml"), qt.IsTrue) } + +func TestDefaultConfig(t *testing.T) { + c := qt.New(t) + + _, err := DefaultConfig.Compile() + c.Assert(err, qt.IsNil) +} + +func TestDecodeConfigInjectsDefaultAndCompiles(t *testing.T) { + c := qt.New(t) + + cfg, err := DecodeConfig(config.BaseConfig{}, map[string]interface{}{}) + c.Assert(err, qt.IsNil) + c.Assert(cfg, qt.DeepEquals, DefaultConfig) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) + + cfg, err = DecodeConfig(config.BaseConfig{}, map[string]any{ + "cache": map[string]any{ + "polls": []map[string]any{ + {"disable": true}, + }, + }, + }) + c.Assert(err, qt.IsNil) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) +} From a98ec3bd019361dae29273638201750d03e534a1 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 12 Mar 2025 17:43:24 -0700 Subject: [PATCH 208/374] commands/gen: Set url in command pages to /docs/reference/commands/ --- commands/gen.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/gen.go b/commands/gen.go index 895340dbb..1c5361840 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -184,13 +184,13 @@ url: %s prepender := func(filename string) string { name := filepath.Base(filename) base := strings.TrimSuffix(name, path.Ext(name)) - url := "/commands/" + strings.ToLower(base) + "/" + url := "/docs/reference/commands/" + strings.ToLower(base) + "/" return fmt.Sprintf(gendocFrontmatterTemplate, strings.Replace(base, "_", " ", -1), base, url) } linkHandler := func(name string) string { base := strings.TrimSuffix(name, path.Ext(name)) - return "/commands/" + strings.ToLower(base) + "/" + return "/docs/reference/commands/" + strings.ToLower(base) + "/" } r.Println("Generating Hugo command-line documentation in", gendocdir, "...") doc.GenMarkdownTreeCustom(cd.CobraCommand.Root(), gendocdir, prepender, linkHandler) From f4f21f5ea34ed98f78f70cd53581d4dfa8b7916c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 20:05:20 +0000 Subject: [PATCH 209/374] build(deps): bump github.com/bep/godartsass/v2 from 2.3.2 to 2.4.0 Bumps [github.com/bep/godartsass/v2](https://github.com/bep/godartsass) from 2.3.2 to 2.4.0. - [Release notes](https://github.com/bep/godartsass/releases) - [Commits](https://github.com/bep/godartsass/compare/v2.3.2...v2.4.0) --- updated-dependencies: - dependency-name: github.com/bep/godartsass/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d5d3021da..86a30f381 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bep/debounce v1.2.0 github.com/bep/gitmap v1.6.0 github.com/bep/goat v0.5.0 - github.com/bep/godartsass/v2 v2.3.2 + github.com/bep/godartsass/v2 v2.4.0 github.com/bep/golibsass v1.2.0 github.com/bep/goportabletext v0.1.0 github.com/bep/gowebp v0.3.0 diff --git a/go.sum b/go.sum index 04605bff9..05932ea07 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= -github.com/bep/godartsass/v2 v2.3.2 h1:meuc76J1C1soSCAnlnJRdGqJ5S4m6/GW+8hmOe9tOog= -github.com/bep/godartsass/v2 v2.3.2/go.mod h1:Qe5WOS9nVJy7G0jHssXPd3c+Pqk/f7+Tm6k/vahbVgs= +github.com/bep/godartsass/v2 v2.4.0 h1:4oS9aKyT1P3+U+MiK2qZ3ZtPw8v96dfrL5NNu6dusAY= +github.com/bep/godartsass/v2 v2.4.0/go.mod h1:jU8p5LhQsSaAVv6Oj4HnQlDmTEv5VqVBwWbQxc23KzI= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw= From 52561d561a591a6fb757b6d417138e0a79bce790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 15 Mar 2025 18:26:11 +0100 Subject: [PATCH 210/374] identity: Use clear to clear the finder seen map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Faster: ``` Finder/Find_one-10 172.8n ± 26% 129.3n ± 2% -25.18% (p=0.002 n=6) Finder/Find_none-10 174.0n ± 1% 130.6n ± 0% -24.94% (p=0.002 n=6) ``` --- identity/finder.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/identity/finder.go b/identity/finder.go index fd1055aef..9d9f9d138 100644 --- a/identity/finder.go +++ b/identity/finder.go @@ -45,9 +45,7 @@ func putSearchID(sid *searchID) { sid.dp = nil sid.peq = nil sid.eqer = nil - for k := range sid.seen { - delete(sid.seen, k) - } + clear(sid.seen) searchIDPool.Put(sid) } From f34cdc382a1f8df8ffef3772e289225e6fd52d93 Mon Sep 17 00:00:00 2001 From: Ville Vesilehto Date: Sat, 22 Mar 2025 19:48:23 +0200 Subject: [PATCH 211/374] parser/metadecoder: Improve errors for non-map XML root values Previously, the XML decoder would panic when encountering a root element with a non-map value due to an unsafe type assertion. The fix adds proper type checking before the map conversion and provides clear error messages to help users identify and fix invalid XML structures. Example error for invalid XML like: just text Will now return: "XML root element 'root' must be a map/object, got string" --- parser/metadecoders/decoder.go | 14 +++++++++++++- parser/metadecoders/decoder_test.go | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index eb33f1ee9..1655ea513 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -152,7 +152,19 @@ func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error { if err != nil { return toFileError(f, data, fmt.Errorf("failed to unmarshal XML: %w", err)) } - xmlValue = xmlRoot[xmlRootName].(map[string]any) + + // Get the root value and verify it's a map + rootValue := xmlRoot[xmlRootName] + if rootValue == nil { + return toFileError(f, data, fmt.Errorf("XML root element '%s' has no value", xmlRootName)) + } + + // Type check before conversion + mapValue, ok := rootValue.(map[string]any) + if !ok { + return toFileError(f, data, fmt.Errorf("XML root element '%s' must be a map/object, got %T", xmlRootName, rootValue)) + } + xmlValue = mapValue } switch v := v.(type) { diff --git a/parser/metadecoders/decoder_test.go b/parser/metadecoders/decoder_test.go index f0ebe57e5..d78293402 100644 --- a/parser/metadecoders/decoder_test.go +++ b/parser/metadecoders/decoder_test.go @@ -99,6 +99,7 @@ func TestUnmarshalToMap(t *testing.T) { // errors {`a = b`, TOML, false}, {`a,b,c`, CSV, false}, // Use Unmarshal for CSV + {`just a string`, XML, false}, } { msg := qt.Commentf("%d: %s", i, test.format) m, err := d.UnmarshalToMap([]byte(test.data), test.format) From a6bd67793bc9e22fe456b90dfbc1fbf3f45c7fd5 Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Sat, 22 Mar 2025 12:26:41 -0700 Subject: [PATCH 212/374] common/hexec: Remove github.com/cli/safeexec We began using the safeexec package in v0.79.1 to address https://github.com/gohugoio/hugo/security/advisories/GHSA-8j34-9876-pvfq. The vulnerability was addressed by the Go team in 1.19, so the safeexec package is no longer needed. Closes #13516 --- README.md | 1 - common/hexec/exec.go | 23 +++++------------------ go.mod | 2 +- releaser/releaser.go | 4 ++-- scripts/fork_go_templates/main.go | 9 ++++----- 5 files changed, 12 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a453c7320..e0b3f1567 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,6 @@ github.com/bep/simplecobra="v0.5.0" github.com/bep/tmc="v0.5.1" github.com/cespare/xxhash/v2="v2.3.0" github.com/clbanning/mxj/v2="v2.7.0" -github.com/cli/safeexec="v1.0.1" github.com/cpuguy83/go-md2man/v2="v2.0.4" github.com/disintegration/gift="v1.2.1" github.com/dlclark/regexp2="v1.11.5" diff --git a/common/hexec/exec.go b/common/hexec/exec.go index 1369e847c..c3a6ebf57 100644 --- a/common/hexec/exec.go +++ b/common/hexec/exec.go @@ -27,7 +27,6 @@ import ( "sync" "github.com/bep/logg" - "github.com/cli/safeexec" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/config" @@ -113,18 +112,6 @@ func IsNotFound(err error) bool { return errors.As(err, ¬FoundErr) } -// SafeCommand is a wrapper around os/exec Command which uses a LookPath -// implementation that does not search in current directory before looking in PATH. -// See https://github.com/cli/safeexec and the linked issues. -func SafeCommand(name string, arg ...string) (*exec.Cmd, error) { - bin, err := safeexec.LookPath(name) - if err != nil { - return nil, err - } - - return exec.Command(bin, arg...), nil -} - // Exec enforces a security policy for commands run via os/exec. type Exec struct { sc security.Config @@ -197,7 +184,7 @@ func (e *Exec) Npx(name string, arg ...any) (Runner, error) { tryFuncs := map[binaryLocation]tryFunc{ binaryLocationNodeModules: func() func(...any) (Runner, error) { nodeBinFilename := filepath.Join(e.workingDir, nodeModulesBinPath, name) - _, err := safeexec.LookPath(nodeBinFilename) + _, err := exec.LookPath(nodeBinFilename) if err != nil { return nil } @@ -215,7 +202,7 @@ func (e *Exec) Npx(name string, arg ...any) (Runner, error) { } }, binaryLocationPath: func() func(...any) (Runner, error) { - if _, err := safeexec.LookPath(name); err != nil { + if _, err := exec.LookPath(name); err != nil { return nil } return func(arg2 ...any) (Runner, error) { @@ -346,7 +333,7 @@ func (c *commandeer) command(arg ...any) (*cmdWrapper, error) { bin = c.fullyQualifiedName } else { var err error - bin, err = safeexec.LookPath(c.name) + bin, err = exec.LookPath(c.name) if err != nil { return nil, &NotFoundError{ name: c.name, @@ -384,7 +371,7 @@ func InPath(binaryName string) bool { if strings.Contains(binaryName, "/") { panic("binary name should not contain any slash") } - _, err := safeexec.LookPath(binaryName) + _, err := exec.LookPath(binaryName) return err == nil } @@ -394,7 +381,7 @@ func LookPath(binaryName string) string { if strings.Contains(binaryName, "/") { panic("binary name should not contain any slash") } - s, err := safeexec.LookPath(binaryName) + s, err := exec.LookPath(binaryName) if err != nil { return "" } diff --git a/go.mod b/go.mod index 86a30f381..9e639d1d5 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/bep/tmc v0.5.1 github.com/cespare/xxhash/v2 v2.3.0 github.com/clbanning/mxj/v2 v2.7.0 - github.com/cli/safeexec v1.0.1 github.com/disintegration/gift v1.2.1 github.com/dustin/go-humanize v1.0.1 github.com/evanw/esbuild v0.24.2 @@ -120,6 +119,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/aws/smithy-go v1.22.2 // indirect + github.com/cli/safeexec v1.0.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect diff --git a/releaser/releaser.go b/releaser/releaser.go index 4c3db2c14..46cee1b13 100644 --- a/releaser/releaser.go +++ b/releaser/releaser.go @@ -19,11 +19,11 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "regexp" "strings" - "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hugo" ) @@ -222,7 +222,7 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error } func git(args ...string) (string, error) { - cmd, _ := hexec.SafeCommand("git", args...) + cmd := exec.Command("git", args...) out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args) diff --git a/scripts/fork_go_templates/main.go b/scripts/fork_go_templates/main.go index cd461043c..e4895c87a 100644 --- a/scripts/fork_go_templates/main.go +++ b/scripts/fork_go_templates/main.go @@ -4,12 +4,11 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "regexp" "strings" - "github.com/gohugoio/hugo/common/hexec" - "github.com/gohugoio/hugo/common/hugio" "github.com/spf13/afero" @@ -208,7 +207,7 @@ func removeAll(expression, content string) string { } func rewrite(filename, rule string) { - cmf, _ := hexec.SafeCommand("gofmt", "-w", "-r", rule, filename) + cmf := exec.Command("gofmt", "-w", "-r", rule, filename) out, err := cmf.CombinedOutput() if err != nil { log.Fatal("gofmt failed:", string(out)) @@ -217,7 +216,7 @@ func rewrite(filename, rule string) { func goimports(dir string) { // Needs go install golang.org/x/tools/cmd/goimports@latest - cmf, _ := hexec.SafeCommand("goimports", "-w", dir) + cmf := exec.Command("goimports", "-w", dir) out, err := cmf.CombinedOutput() if err != nil { log.Fatal("goimports failed:", string(out)) @@ -225,7 +224,7 @@ func goimports(dir string) { } func gofmt(dir string) { - cmf, _ := hexec.SafeCommand("gofmt", "-w", dir) + cmf := exec.Command("gofmt", "-w", dir) out, err := cmf.CombinedOutput() if err != nil { log.Fatal("gofmt failed:", string(out)) From e9bd2373a017e8f7427acc21d692e36f1918f64a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:19:20 +0000 Subject: [PATCH 213/374] build(deps): bump golang.org/x/net from 0.35.0 to 0.37.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.35.0 to 0.37.0. - [Commits](https://github.com/golang/net/compare/v0.35.0...v0.37.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 9e639d1d5..0c724e991 100644 --- a/go.mod +++ b/go.mod @@ -77,7 +77,7 @@ require ( golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 golang.org/x/image v0.25.0 golang.org/x/mod v0.24.0 - golang.org/x/net v0.35.0 + golang.org/x/net v0.37.0 golang.org/x/sync v0.12.0 golang.org/x/text v0.23.0 golang.org/x/tools v0.30.0 @@ -157,9 +157,9 @@ require ( go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect - golang.org/x/crypto v0.33.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/sys v0.31.0 // indirect golang.org/x/time v0.10.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28 // indirect diff --git a/go.sum b/go.sum index 05932ea07..f4048360c 100644 --- a/go.sum +++ b/go.sum @@ -512,8 +512,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -598,8 +598,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -675,8 +675,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 3968f9158e1e97a0a105d0484c980fef2818be7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 08:24:11 +0000 Subject: [PATCH 214/374] build(deps): bump github.com/bep/godartsass/v2 from 2.4.0 to 2.4.1 Bumps [github.com/bep/godartsass/v2](https://github.com/bep/godartsass) from 2.4.0 to 2.4.1. - [Release notes](https://github.com/bep/godartsass/releases) - [Commits](https://github.com/bep/godartsass/compare/v2.4.0...v2.4.1) --- updated-dependencies: - dependency-name: github.com/bep/godartsass/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 3 +-- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0c724e991..bbb49c83b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bep/debounce v1.2.0 github.com/bep/gitmap v1.6.0 github.com/bep/goat v0.5.0 - github.com/bep/godartsass/v2 v2.4.0 + github.com/bep/godartsass/v2 v2.4.1 github.com/bep/golibsass v1.2.0 github.com/bep/goportabletext v0.1.0 github.com/bep/gowebp v0.3.0 @@ -119,7 +119,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/aws/smithy-go v1.22.2 // indirect - github.com/cli/safeexec v1.0.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect diff --git a/go.sum b/go.sum index f4048360c..5c0784e06 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= -github.com/bep/godartsass/v2 v2.4.0 h1:4oS9aKyT1P3+U+MiK2qZ3ZtPw8v96dfrL5NNu6dusAY= -github.com/bep/godartsass/v2 v2.4.0/go.mod h1:jU8p5LhQsSaAVv6Oj4HnQlDmTEv5VqVBwWbQxc23KzI= +github.com/bep/godartsass/v2 v2.4.1 h1:ktbimHvS+FUZ2FQsSEqm5DDfKnr56DVf7GNWuIbA1M8= +github.com/bep/godartsass/v2 v2.4.1/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw= @@ -163,8 +163,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= -github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= From 17db4edb020ded8a1caf203b5d1c2bb21028e591 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 Mar 2025 12:56:59 +0000 Subject: [PATCH 215/374] build(deps): bump github.com/evanw/esbuild from 0.24.2 to 0.25.1 Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.24.2 to 0.25.1. - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.24.2...v0.25.1) --- updated-dependencies: - dependency-name: github.com/evanw/esbuild dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bbb49c83b..44d8df7cf 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 github.com/disintegration/gift v1.2.1 github.com/dustin/go-humanize v1.0.1 - github.com/evanw/esbuild v0.24.2 + github.com/evanw/esbuild v0.25.1 github.com/fatih/color v1.18.0 github.com/fortytw2/leaktest v1.3.0 github.com/frankban/quicktest v1.14.6 diff --git a/go.sum b/go.sum index 5c0784e06..c38ea98eb 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanw/esbuild v0.24.2 h1:PQExybVBrjHjN6/JJiShRGIXh1hWVm6NepVnhZhrt0A= -github.com/evanw/esbuild v0.24.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= +github.com/evanw/esbuild v0.25.1 h1:u/16OWK0l4ymFFuHOApuv/xXg8U0P3QYBSMToll89HI= +github.com/evanw/esbuild v0.25.1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= From 1c691358f7e2d52bbaa97ffea92cc7d89828d051 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 22:17:09 +0000 Subject: [PATCH 216/374] build(deps): bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 Bumps [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) from 5.2.1 to 5.2.2. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v5.2.1...v5.2.2) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 44d8df7cf..c54293003 100644 --- a/go.mod +++ b/go.mod @@ -126,7 +126,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index c38ea98eb..1fd49a306 100644 --- a/go.sum +++ b/go.sum @@ -241,8 +241,8 @@ github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTH github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0= github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 h1:sgew0XCnZwnzpWxTt3V8LLiCO7OQi3C6dycaE67wfkU= github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95/go.mod h1:bOlVlCa1/RajcHpXkrUXPSHB/Re1UnlXxD1Qp8SKOd8= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From bddd2f9001fe404beb7427fe775c47f4cad103d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 08:24:06 +0000 Subject: [PATCH 217/374] build(deps): bump github.com/bep/imagemeta from 0.9.0 to 0.10.0 Bumps [github.com/bep/imagemeta](https://github.com/bep/imagemeta) from 0.9.0 to 0.10.0. - [Release notes](https://github.com/bep/imagemeta/releases) - [Commits](https://github.com/bep/imagemeta/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: github.com/bep/imagemeta dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c54293003..d19186d9c 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/bep/goportabletext v0.1.0 github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 - github.com/bep/imagemeta v0.9.0 + github.com/bep/imagemeta v0.10.0 github.com/bep/lazycache v0.8.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 diff --git a/go.sum b/go.sum index 1fd49a306..98dfb0921 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY= github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI= github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o= github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q= -github.com/bep/imagemeta v0.9.0 h1:EdmtCoMSVHKsqZJxqgV0kL79Jr3wGbzdzZHr6YeVrxs= -github.com/bep/imagemeta v0.9.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= +github.com/bep/imagemeta v0.10.0 h1:kbQxe8SLHTUEEOT/DvLMGslIJy9Z1R+ZAckkIUnUBw4= +github.com/bep/imagemeta v0.10.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8= github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= From 7ca6bb06b6164085a51334d8fda772ab680f2c49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:50:23 +0000 Subject: [PATCH 218/374] build(deps): bump github.com/spf13/afero from 1.11.0 to 1.14.0 Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.11.0 to 1.14.0. - [Release notes](https://github.com/spf13/afero/releases) - [Commits](https://github.com/spf13/afero/compare/v1.11.0...v1.14.0) --- updated-dependencies: - dependency-name: github.com/spf13/afero dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d19186d9c..571fd02d7 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/rogpeppe/go-internal v1.14.1 github.com/sanity-io/litter v1.5.8 - github.com/spf13/afero v1.11.0 + github.com/spf13/afero v1.14.0 github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.9.1 github.com/spf13/fsync v0.10.1 diff --git a/go.sum b/go.sum index 98dfb0921..c82cbfc8d 100644 --- a/go.sum +++ b/go.sum @@ -426,8 +426,8 @@ github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rY github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= From ebc06936596053735bca795af7f62bf990e7a957 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:50:19 +0000 Subject: [PATCH 219/374] build(deps): bump github.com/getkin/kin-openapi from 0.129.0 to 0.131.0 Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.129.0 to 0.131.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.129.0...v0.131.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 571fd02d7..1fe785350 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/fortytw2/leaktest v1.3.0 github.com/frankban/quicktest v1.14.6 github.com/fsnotify/fsnotify v1.8.0 - github.com/getkin/kin-openapi v0.129.0 + github.com/getkin/kin-openapi v0.131.0 github.com/ghodss/yaml v1.0.0 github.com/gobuffalo/flect v1.0.3 github.com/gobwas/glob v0.2.3 @@ -144,8 +144,8 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect - github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect - github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index c82cbfc8d..81a61f81d 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30= -github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI= +github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= +github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -390,10 +390,10 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= -github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU= -github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= -github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= -github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= From 26d986fc0d872ee0b01d776229486e22929b36b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:46:57 +0000 Subject: [PATCH 220/374] build(deps): bump golang.org/x/tools from 0.30.0 to 0.31.0 Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.30.0 to 0.31.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1fe785350..2a76b3bea 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( golang.org/x/net v0.37.0 golang.org/x/sync v0.12.0 golang.org/x/text v0.23.0 - golang.org/x/tools v0.30.0 + golang.org/x/tools v0.31.0 google.golang.org/api v0.221.0 gopkg.in/yaml.v2 v2.4.0 rsc.io/qr v0.2.0 diff --git a/go.sum b/go.sum index 81a61f81d..fc3eae36b 100644 --- a/go.sum +++ b/go.sum @@ -752,8 +752,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 8d2379bcb3ca4c5fdf6b9322bbe05e42b7a81bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 26 Mar 2025 11:11:06 +0100 Subject: [PATCH 221/374] common/hreflect: Replace the map/RWMutex method cache with sync.Map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's much faster when running in parallel: ``` GetMethodByName-10 125.1n ± 6% 181.7n ± 7% +45.30% (p=0.002 n=6) GetMethodByNamePara-10 770.10n ± 1% 24.77n ± 9% -96.78% (p=0.002 n=6) ``` --- common/hreflect/helpers.go | 21 +++++---------------- common/hreflect/helpers_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index ab7883a47..545371374 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -134,12 +134,7 @@ type methodKey struct { name string } -type methods struct { - sync.RWMutex - cache map[methodKey]int -} - -var methodCache = &methods{cache: make(map[methodKey]int)} +var methodCache sync.Map // GetMethodByName is the same as reflect.Value.MethodByName, but it caches the // type lookup. @@ -157,22 +152,16 @@ func GetMethodByName(v reflect.Value, name string) reflect.Value { // -1 if no such method exists. func GetMethodIndexByName(tp reflect.Type, name string) int { k := methodKey{tp, name} - methodCache.RLock() - index, found := methodCache.cache[k] - methodCache.RUnlock() + v, found := methodCache.Load(k) if found { - return index + return v.(int) } - - methodCache.Lock() - defer methodCache.Unlock() - m, ok := tp.MethodByName(name) - index = m.Index + index := m.Index if !ok { index = -1 } - methodCache.cache[k] = index + methodCache.Store(k, index) if !ok { return -1 diff --git a/common/hreflect/helpers_test.go b/common/hreflect/helpers_test.go index 119722261..cbcad0f22 100644 --- a/common/hreflect/helpers_test.go +++ b/common/hreflect/helpers_test.go @@ -134,3 +134,17 @@ func BenchmarkGetMethodByName(b *testing.B) { } } } + +func BenchmarkGetMethodByNamePara(b *testing.B) { + v := reflect.ValueOf(&testStruct{}) + methods := []string{"Method1", "Method2", "Method3", "Method4", "Method5"} + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + for _, method := range methods { + _ = GetMethodByName(v, method) + } + } + }) +} From 6f14dbe24c2c951ff5cf70986170d87aa56cb03e Mon Sep 17 00:00:00 2001 From: Joe Mooring Date: Wed, 26 Mar 2025 16:01:01 -0700 Subject: [PATCH 222/374] tpl/tplimpl: Fix full screen option in vimeo and youtube shortcodes Closes #13531 Co-authored-by: Stefan Ritter <60473875+gideonstar-git@users.noreply.github.com> --- .../embedded/templates/shortcodes/vimeo.html | 21 ++++++++++++-- .../templates/shortcodes/youtube.html | 13 ++++----- tpl/tplimpl/shortcodes_integration_test.go | 28 +++++++++++++------ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html index d1ec746bb..3ce470c6e 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/vimeo.html @@ -4,10 +4,11 @@ Renders an embedded Vimeo video. Accepts named or positional arguments. If positional, order is id, class, title, then loading. -@param {string} [class] The class attribute of the wrapping div element. When specified, removes the style attributes from the iframe element and its wrapping div element. @param {string} [id] The video id. Optional if the id is provided as first positional argument. +@param {string} [class] The class attribute of the wrapping div element. When specified, removes the style attributes from the iframe element and its wrapping div element. @param {string} [loading=eager] The loading attribute of the iframe element. @param {string} [title=Vimeo video] The title attribute of the iframe element. +@param {bool} [allowFullScreen=true] Whether the iframe element can activate full screen mode. @returns {template.HTML} @@ -19,11 +20,24 @@ title, then loading. {{- if $pc.Simple }} {{- template "_internal/shortcodes/vimeo_simple.html" . }} {{- else }} + {{- $dnt := cond $pc.EnableDNT 1 0 }} + {{- $id := or (.Get "id") (.Get 0) "" }} {{- $class := or (.Get "class") (.Get 1) "" }} {{- $title := or (.Get "title") (.Get 2) "Vimeo video" }} {{- $loading := or (.Get "loading") (.Get 3) "eager" }} - {{- $dnt := cond $pc.EnableDNT 1 0 }} + {{- $allowFullScreen := or (.Get "allowFullScreen") (.Get 4) true }} + + {{- if in (slice "false" false 0) ($.Get "allowFullScreen") }} + {{- $allowFullScreen = false }} + {{- else if in (slice "true" true 1) ($.Get "allowFullScreen") }} + {{- $allowFullScreen = true }} + {{- end }} + + {{- $iframeAllowList := "" }} + {{- if $allowFullScreen }} + {{- $iframeAllowList = "fullscreen" }} + {{- end }} {{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }} {{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }} @@ -36,11 +50,12 @@ title, then loading. {{- else }} style="{{ $divStyle | safeCSS }}" {{- end }}> - diff --git a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html index afb1d132f..cebe50626 100644 --- a/tpl/tplimpl/embedded/templates/shortcodes/youtube.html +++ b/tpl/tplimpl/embedded/templates/shortcodes/youtube.html @@ -27,7 +27,7 @@ Renders an embedded YouTube video. {{- with $id := or (.Get "id") (.Get 0) }} {{- /* Set defaults. */}} - {{- $allowFullScreen := "allowfullscreen" }} + {{- $allowFullScreen := true }} {{- $autoplay := 0 }} {{- $class := "" }} {{- $controls := 1 }} @@ -37,12 +37,11 @@ Renders an embedded YouTube video. {{- $mute := 0 }} {{- $start := 0 }} {{- $title := "YouTube video" }} + {{- $iframeAllowList := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }} {{- /* Get arguments. */}} - {{- if in (slice "false" false 0) ($.Get "allowFullScreen") }} - {{- $allowFullScreen = "" }} - {{- else if in (slice "true" true 1) ($.Get "allowFullScreen") }} - {{- $allowFullScreen = "allowfullscreen" }} + {{- if in (slice "true" true 1) ($.Get "allowFullScreen") }} + {{- $iframeAllowList = printf "%s; fullscreen" $iframeAllowList }} {{- end }} {{- if in (slice "false" false 0) ($.Get "autoplay") }} {{- $autoplay = 0 }} @@ -99,7 +98,6 @@ Renders an embedded YouTube video. {{- if $class }} {{- $iframeStyle = "" }} {{- end }} - {{- $allow := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }} {{- $referrerpolicy := "strict-origin-when-cross-origin" }} {{- /* Render. */ -}} @@ -108,8 +106,7 @@ Renders an embedded YouTube video. {{- with $divStyle }} style="{{ . | safeCSS }}" {{- end -}} >