aboutsummaryrefslogtreecommitdiffhomepage
path: root/hugolib
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2024-10-30 18:10:09 +0100
committerBjørn Erik Pedersen <[email protected]>2024-11-03 13:33:31 +0100
commit1f23b4949c70a2a8f2084fa937e19e93a9fe890a (patch)
tree28643be27a3059f8286bcd5188238cecc6d06430 /hugolib
parent5fc16390355f32b336836163907fc215034f5b73 (diff)
downloadhugo-1f23b4949c70a2a8f2084fa937e19e93a9fe890a.tar.gz
hugo-1f23b4949c70a2a8f2084fa937e19e93a9fe890a.zip
Fix some RenderShortcodes error cases
This issue fixes two cases where `{{__hugo_ctx` artifacts were left in the rendered output: 1. Inclusion when `.RenderShortcodes` is wrapped in HTML. 2. Inclusion of Markdown file without a trailing newline in some cases. Closes #12854 Updates #12998
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/collections_test.go16
-rw-r--r--hugolib/integrationtest_builder.go23
-rw-r--r--hugolib/menu_test.go124
-rw-r--r--hugolib/page.go19
-rw-r--r--hugolib/rendershortcodes_test.go112
-rw-r--r--hugolib/shortcode_page.go4
6 files changed, 183 insertions, 115 deletions
diff --git a/hugolib/collections_test.go b/hugolib/collections_test.go
index fff57337f..a8c817bec 100644
--- a/hugolib/collections_test.go
+++ b/hugolib/collections_test.go
@@ -71,9 +71,9 @@ tags_weight: %d
{{ $pageGroups := slice $cool $blue }}
{{ $weighted := slice $wp1 $wp2 }}
-{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }}
-{{ printf "pageGroups:%d:%T:%v/%v" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0) (index (index $pageGroups 1).Pages 0)}}
-{{ printf "weightedPages:%d::%T:%v" (len $weighted) $weighted $weighted | safeHTML }}
+{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }}
+{{ printf "pageGroups:%d:%T:%s|%s" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0).Path (index (index $pageGroups 1).Pages 0).Path}}
+{{ printf "weightedPages:%d:%T" (len $weighted) $weighted | safeHTML }}
`)
b.CreateSites().Build(BuildCfg{})
@@ -82,9 +82,9 @@ tags_weight: %d
c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2)
b.AssertFileContent("public/index.html",
- "pages:2:page.Pages:Page(/page1)/Page(/page2)",
- "pageGroups:2:page.PagesGroup:Page(/page1)/Page(/page2)",
- `weightedPages:2::page.WeightedPages:[WeightedPage(10,"Page") WeightedPage(20,"Page")]`)
+ "pages:2:page.Pages:/page1|/page2",
+ "pageGroups:2:page.PagesGroup:/page1|/page2",
+ `weightedPages:2:page.WeightedPages`)
}
func TestUnionFunc(t *testing.T) {
@@ -189,7 +189,7 @@ tags_weight: %d
{{ $appendStrings := slice "a" "b" | append "c" "d" "e" }}
{{ $appendStringsSlice := slice "a" "b" "c" | append (slice "c" "d") }}
-{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }}
+{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }}
{{ printf "appendPages:%d:%T:%v/%v" (len $appendPages) $appendPages (index $appendPages 0).Kind (index $appendPages 8).Kind }}
{{ printf "appendStrings:%T:%v" $appendStrings $appendStrings }}
{{ printf "appendStringsSlice:%T:%v" $appendStringsSlice $appendStringsSlice }}
@@ -207,7 +207,7 @@ tags_weight: %d
c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2)
b.AssertFileContent("public/index.html",
- "pages:2:page.Pages:Page(/page2)/Page(/page1)",
+ "pages:2:page.Pages:/page2|/page1",
"appendPages:9:page.Pages:home/page",
"appendStrings:[]string:[a b c d e]",
"appendStringsSlice:[]string:[a b c c d]",
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index b806ad7c1..5dc13592f 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -2,6 +2,7 @@ package hugolib
import (
"bytes"
+ "context"
"encoding/base64"
"errors"
"fmt"
@@ -32,6 +33,7 @@ import (
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
+ "github.com/spf13/cast"
"golang.org/x/text/unicode/norm"
"golang.org/x/tools/txtar"
)
@@ -294,6 +296,12 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s
}
}
+func (s *IntegrationTestBuilder) AssertFileContentEquals(filename string, match string) {
+ s.Helper()
+ content := s.FileContent(filename)
+ s.Assert(content, qt.Equals, match, qt.Commentf(match))
+}
+
func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches ...string) {
s.Helper()
content := s.FileContent(filename)
@@ -302,6 +310,16 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
}
}
+func (s *IntegrationTestBuilder) AssertNoRenderShortcodesArtifacts() {
+ s.Helper()
+ for _, p := range s.H.Pages() {
+ content, err := p.Content(context.Background())
+ s.Assert(err, qt.IsNil)
+ comment := qt.Commentf("Page: %s\n%s", p.Path(), content)
+ s.Assert(strings.Contains(cast.ToString(content), "__hugo_ctx"), qt.IsFalse, comment)
+ }
+}
+
func (s *IntegrationTestBuilder) AssertPublishDir(matches ...string) {
s.AssertFs(s.fs.PublishDir, matches...)
}
@@ -835,6 +853,11 @@ type IntegrationTestConfig struct {
// The files to use on txtar format, see
// https://pkg.go.dev/golang.org/x/exp/cmd/txtar
+ // There are some conentions used in this test setup.
+ // - §§§ can be used to wrap code fences.
+ // - §§ can be used to wrap multiline strings.
+ // - filenames prefixed with sourcefilename: will be read from the file system relative to the current dir.
+ // - filenames with a .png or .jpg extension will be treated as binary and base64 decoded.
TxtarString string
// COnfig to use as the base. We will also read the config from the txtar.
diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go
index 304b4fbf4..6ee62771b 100644
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -105,94 +105,6 @@ Menu Main: {{ partial "menu.html" (dict "page" . "menu" "main") }}`,
"/sect3/|Sect3s|Sect3s|0|-|-|")
}
-// related issue #7594
-func TestMenusSort(t *testing.T) {
- b := newTestSitesBuilder(t).WithSimpleConfigFile()
-
- b.WithTemplatesAdded("index.html", `
-{{ range $k, $v := .Site.Menus.main }}
-Default1|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-{{ range $k, $v := .Site.Menus.main.ByWeight }}
-ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-{{ range $k, $v := (.Site.Menus.main.ByWeight).Reverse }}
-Reverse|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-{{ range $k, $v := .Site.Menus.main }}
-Default2|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-{{ range $k, $v := .Site.Menus.main.ByWeight }}
-ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-{{ range $k, $v := .Site.Menus.main }}
-Default3|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
-`)
-
- b.WithContent("_index.md", `
----
-title: Home
-menu:
- main:
- weight: 100
----`)
-
- b.WithContent("blog/A.md", `
----
-title: "A"
-menu:
- main:
- weight: 10
----
-`)
-
- b.WithContent("blog/B.md", `
----
-title: "B"
-menu:
- main:
- weight: 20
----
-`)
- b.WithContent("blog/C.md", `
----
-title: "C"
-menu:
- main:
- weight: 30
----
-`)
-
- b.Build(BuildCfg{})
-
- b.AssertFileContent("public/index.html",
- `Default1|0|10|A|/blog/a/|Page(/blog/a)
- Default1|1|20|B|/blog/b/|Page(/blog/b)
- Default1|2|30|C|/blog/c/|Page(/blog/c)
- Default1|3|100|Home|/|Page(/)
-
- ByWeight|0|10|A|/blog/a/|Page(/blog/a)
- ByWeight|1|20|B|/blog/b/|Page(/blog/b)
- ByWeight|2|30|C|/blog/c/|Page(/blog/c)
- ByWeight|3|100|Home|/|Page(/)
-
- Reverse|0|100|Home|/|Page(/)
- Reverse|1|30|C|/blog/c/|Page(/blog/c)
- Reverse|2|20|B|/blog/b/|Page(/blog/b)
- Reverse|3|10|A|/blog/a/|Page(/blog/a)
-
- Default2|0|10|A|/blog/a/|Page(/blog/a)
- Default2|1|20|B|/blog/b/|Page(/blog/b)
- Default2|2|30|C|/blog/c/|Page(/blog/c)
- Default2|3|100|Home|/|Page(/)
-
- ByWeight|0|10|A|/blog/a/|Page(/blog/a)
- ByWeight|1|20|B|/blog/b/|Page(/blog/b)
- ByWeight|2|30|C|/blog/c/|Page(/blog/c)
- ByWeight|3|100|Home|/|Page(/)
-
- Default3|0|10|A|/blog/a/|Page(/blog/a)
- Default3|1|20|B|/blog/b/|Page(/blog/b)
- Default3|2|30|C|/blog/c/|Page(/blog/c)
- Default3|3|100|Home|/|Page(/)`,
- )
-}
-
func TestMenusFrontMatter(t *testing.T) {
b := newTestSitesBuilder(t).WithSimpleConfigFile()
@@ -437,8 +349,8 @@ url = "/blog/post3"
commonTempl := `
Main: {{ len .Site.Menus.main }}
{{ range .Site.Menus.main }}
-{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page }}
-{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page }}
+{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page.Path }}
+{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page.Path }}
{{ end }}
`
@@ -494,34 +406,34 @@ title: "Contact: With No Menu Defined"
b.AssertFileContent("public/index.html", `
Main: 5
-Home|HasMenuCurrent: false|Page: Page(/)
-Blog|HasMenuCurrent: false|Page: Page(/blog)
-My Post 2: With Menu Defined|HasMenuCurrent: false|Page: Page(/blog/post2)
-My Post 3|HasMenuCurrent: false|Page: Page(/blog/post3)
-Contact Us|HasMenuCurrent: false|Page: Page(/contact)
+Home|HasMenuCurrent: false|Page: /
+Blog|HasMenuCurrent: false|Page: /blog
+My Post 2: With Menu Defined|HasMenuCurrent: false|Page: /blog/post2
+My Post 3|HasMenuCurrent: false|Page: /blog/post3
+Contact Us|HasMenuCurrent: false|Page: /contact
`)
b.AssertFileContent("public/blog/post1/index.html", `
-Home|HasMenuCurrent: false|Page: Page(/)
-Blog|HasMenuCurrent: true|Page: Page(/blog)
+Home|HasMenuCurrent: false|Page: /
+Blog|HasMenuCurrent: true|Page: /blog
`)
b.AssertFileContent("public/blog/post2/index.html", `
-Home|HasMenuCurrent: false|Page: Page(/)
-Blog|HasMenuCurrent: true|Page: Page(/blog)
-Blog|IsMenuCurrent: false|Page: Page(/blog)
+Home|HasMenuCurrent: false|Page: /
+Blog|HasMenuCurrent: true|Page: /blog
+Blog|IsMenuCurrent: false|Page: /blog
`)
b.AssertFileContent("public/blog/post3/index.html", `
-Home|HasMenuCurrent: false|Page: Page(/)
-Blog|HasMenuCurrent: true|Page: Page(/blog)
+Home|HasMenuCurrent: false|Page: /
+Blog|HasMenuCurrent: true|Page: /blog
`)
b.AssertFileContent("public/contact/index.html", `
-Contact Us|HasMenuCurrent: false|Page: Page(/contact)
-Contact Us|IsMenuCurrent: true|Page: Page(/contact)
-Blog|HasMenuCurrent: false|Page: Page(/blog)
-Blog|IsMenuCurrent: false|Page: Page(/blog)
+Contact Us|HasMenuCurrent: false|Page: /contact
+Contact Us|IsMenuCurrent: true|Page: /contact
+Blog|HasMenuCurrent: false|Page: /blog
+Blog|IsMenuCurrent: false|Page: /blog
`)
}
diff --git a/hugolib/page.go b/hugolib/page.go
index 7525ab672..2bc1da044 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -16,7 +16,9 @@ package hugolib
import (
"context"
"fmt"
+ "path/filepath"
"strconv"
+ "strings"
"sync"
"sync/atomic"
@@ -358,7 +360,22 @@ func (p *pageState) Site() page.Site {
}
func (p *pageState) String() string {
- return fmt.Sprintf("Page(%s)", p.Path())
+ var sb strings.Builder
+ if p.File() != nil {
+ // The forward slashes even on Windows is motivated by
+ // getting stable tests.
+ // This information is meant for getting positional information in logs,
+ // so the direction of the slashes should not matter.
+ sb.WriteString(filepath.ToSlash(p.File().Filename()))
+ if p.File().IsContentAdapter() {
+ // Also include the path.
+ sb.WriteString(":")
+ sb.WriteString(p.Path())
+ }
+ } else {
+ sb.WriteString(p.Path())
+ }
+ return sb.String()
}
// IsTranslated returns whether this content file is translated to
diff --git a/hugolib/rendershortcodes_test.go b/hugolib/rendershortcodes_test.go
index 313c80a73..9a31b6536 100644
--- a/hugolib/rendershortcodes_test.go
+++ b/hugolib/rendershortcodes_test.go
@@ -14,6 +14,7 @@
package hugolib
import (
+ "path/filepath"
"strings"
"testing"
)
@@ -69,6 +70,7 @@ Content: {{ .Content }}|
b := Test(t, files)
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html",
"Fragments: [p1-h1 p2-h1 p2-h2 p2-h3 p2-withmarkdown p3-h1 p3-h2 p3-withmarkdown]|",
"HasShortcode Level 1: true|",
@@ -115,6 +117,7 @@ JSON: {{ .Content }}
b := Test(t, files)
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html", "Myshort HTML")
b.AssertFileContent("public/p1/index.json", "Myshort JSON")
}
@@ -147,9 +150,11 @@ Myshort Original.
{{ .Content }}
`
b := TestRunning(t, files)
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html", "Myshort Original.")
b.EditFileReplaceAll("layouts/shortcodes/myshort.html", "Original", "Edited").Build()
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html", "Myshort Edited.")
}
@@ -192,12 +197,14 @@ Myshort Original.
},
).Build()
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html", "Original")
b.EditFileReplaceFunc("content/p2.md", func(s string) string {
return strings.Replace(s, "Original", "Edited", 1)
})
b.Build()
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/p1/index.html", "Edited")
}
@@ -233,8 +240,10 @@ Myshort Original.
`
b := TestRunning(t, files)
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/mysection/index.html", "p1-h1")
b.EditFileReplaceAll("content/mysection/_index.md", "p1-h1", "p1-h1 Edited").Build()
+ b.AssertNoRenderShortcodesArtifacts()
b.AssertFileContent("public/mysection/index.html", "p1-h1 Edited")
}
@@ -314,6 +323,8 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
b := Test(t, files)
+ b.AssertNoRenderShortcodesArtifacts()
+
b.AssertFileContent("public/markdown/index.html",
// Images.
"Image: /posts/p1/pixel1.png|\nImage: /posts/p1/pixel2.png|\n|\nImage: /markdown/pixel3.png|</p>\n|",
@@ -333,3 +344,104 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
b.AssertFileContent("public/html/index.html", "! hugo_ctx")
}
+
+// Issue 12854.
+func TestRenderShortcodesWithHTML(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+disableLiveReload = true
+disableKinds = ["home", "taxonomy", "term"]
+markup.goldmark.renderer.unsafe = true
+-- content/p1.md --
+---
+title: "p1"
+---
+{{% include "p2" %}}
+-- content/p2.md --
+---
+title: "p2"
+---
+Hello <b>world</b>. Some **bold** text. Some Unicode: 神真美好.
+-- layouts/shortcodes/include.html --
+{{ with site.GetPage (.Get 0) }}
+<div>{{ .RenderShortcodes }}</div>
+{{ end }}
+-- layouts/_default/single.html --
+{{ .Content }}
+`
+
+ b := TestRunning(t, files, TestOptWarn())
+
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertLogContains(filepath.ToSlash("WARN .RenderShortcodes detected inside HTML block in \"/content/p1.md\"; this may not be what you intended, see https://gohugo.io/methods/page/rendershortcodes/#limitations\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-rendershortcodes-in-html']"))
+ b.AssertFileContent("public/p1/index.html", "<div>Hello <b>world</b>. Some **bold** text. Some Unicode: 神真美好.\n</div>")
+ b.EditFileReplaceAll("content/p2.md", "Hello", "Hello Edited").Build()
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertFileContent("public/p1/index.html", "<div>Hello Edited <b>world</b>. Some **bold** text. Some Unicode: 神真美好.\n</div>")
+}
+
+func TestRenderShortcodesIncludeMarkdownFileWithoutTrailingNewline(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+disableLiveReload = true
+disableKinds = ["home", "taxonomy", "term"]
+markup.goldmark.renderer.unsafe = true
+-- content/p1.md --
+---
+title: "p1"
+---
+Content p1 id-1000.{{% include "p2" %}}{{% include "p3" %}}
+
+§§§ go
+code_p1
+§§§
+§§§ go
+code_p1_2
+§§§
+
+§§§ go
+code_p1_3
+§§§
+-- content/p2.md --
+---
+title: "p2"
+---
+§§§ bash
+code_p2
+§§§
+Foo.
+-- content/p3.md --
+---
+title: "p3"
+---
+§§§ php
+code_p3
+§§§
+-- layouts/shortcodes/include.html --
+{{ with site.GetPage (.Get 0) -}}
+{{ .RenderShortcodes -}}
+{{ end -}}
+-- layouts/_default/single.html --
+{{ .Content }}
+-- layouts/_default/_markup/render-codeblock.html --
+<code>{{ .Inner | safeHTML }}</code>
+`
+
+ b := TestRunning(t, files, TestOptWarn())
+
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-1000.</p>\n<code>code_p2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
+ b.EditFileReplaceAll("content/p1.md", "id-1000.", "id-100.").Build()
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>code_p2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
+ b.EditFileReplaceAll("content/p2.md", "code_p2", "codep2").Build()
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
+ b.EditFileReplaceAll("content/p3.md", "code_p3", "code_p3_edited").Build()
+ b.AssertNoRenderShortcodesArtifacts()
+ b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3_edited</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
+}
diff --git a/hugolib/shortcode_page.go b/hugolib/shortcode_page.go
index 8030b0285..3d27cc93c 100644
--- a/hugolib/shortcode_page.go
+++ b/hugolib/shortcode_page.go
@@ -125,3 +125,7 @@ func newPageForRenderHook(p *pageState) page.Page {
func (p *pageForRenderHooks) Unwrapv() any {
return p.p
}
+
+func (p *pageForRenderHooks) String() string {
+ return p.p.String()
+}