]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Create default link and image render hooks
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 30 Jan 2024 10:43:20 +0000 (11:43 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 30 Jan 2024 19:12:19 +0000 (20:12 +0100)
Fixes #11933

15 files changed:
check_gofmt.sh [new file with mode: 0755]
common/paths/path.go
common/types/types.go
config/allconfig/allconfig.go
hugolib/content_render_hooks_test.go
hugolib/page__per_output.go
hugolib/pagecollections.go
hugolib/pagecollections_test.go
magefile.go
markup/goldmark/goldmark_config/config.go
tpl/template_info.go
tpl/tplimpl/embedded/templates/_default/_markup/render-image.html [new file with mode: 0644]
tpl/tplimpl/embedded/templates/_default/_markup/render-link.html [new file with mode: 0644]
tpl/tplimpl/template.go
tpl/tplimpl/template_errors.go

diff --git a/check_gofmt.sh b/check_gofmt.sh
new file mode 100755 (executable)
index 0000000..c77517d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+diff <(gofmt -d .) <(printf '')
\ No newline at end of file
index da99b16ac3c08eee76607532ddfbbc8ea12b673c..906270cae383756eee8aed6db169a26d3ff3a08c 100644 (file)
@@ -387,6 +387,11 @@ func ToSlashTrimLeading(s string) string {
        return strings.TrimPrefix(filepath.ToSlash(s), "/")
 }
 
+// ToSlashTrimTrailing is just a filepath.ToSlash with an added / suffix trimmer.
+func ToSlashTrimTrailing(s string) string {
+       return strings.TrimSuffix(filepath.ToSlash(s), "/")
+}
+
 // ToSlashPreserveLeading converts the path given to a forward slash separated path
 // and preserves the leading slash if present trimming any trailing slash.
 func ToSlashPreserveLeading(s string) string {
index 11683c196bcffacd7464a4a623366f2c7f5d3b4b..801c511a0fd237b2224a18521727a41b0a6f4abd 100644 (file)
@@ -107,3 +107,8 @@ type LowHigh struct {
 
 // This is only used for debugging purposes.
 var InvocationCounter atomic.Int64
+
+// NewTrue returns a pointer to b.
+func NewBool(b bool) *bool {
+       return &b
+}
index 26e402305377c84bb56b8a08e64879dfc14224ff..742a281504c5a86c74b21c8a629d8230e56aec7e 100644 (file)
@@ -31,6 +31,7 @@ import (
        "github.com/gohugoio/hugo/common/loggers"
        "github.com/gohugoio/hugo/common/maps"
        "github.com/gohugoio/hugo/common/paths"
+       "github.com/gohugoio/hugo/common/types"
        "github.com/gohugoio/hugo/common/urls"
        "github.com/gohugoio/hugo/config"
        "github.com/gohugoio/hugo/config/privacy"
@@ -899,6 +900,18 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
                                return nil, err
                        }
 
+                       // Adjust Goldmark config defaults for multilingual, single-host sites.
+                       if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
+                               if !clone.Markup.Goldmark.DuplicateResourceFiles {
+                                       if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
+                                               clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
+                                       }
+                                       if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
+                                               clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
+                                       }
+                               }
+                       }
+
                        langConfigMap[k] = clone
                case maps.ParamsMergeStrategy:
                default:
index 93b0e1621afdc9a8a2dc55a7f221022179c06797..36d1e626f6cba0cc0fc5d1d1345d6b556f6e2337 100644 (file)
@@ -14,6 +14,7 @@
 package hugolib
 
 import (
+       "strings"
        "testing"
 )
 
@@ -169,3 +170,74 @@ Self Fragments: [d e f]
 P1 Fragments: [b c z]
        `)
 }
+
+func TestDefaultRenderHooksMultilingual(t *testing.T) {
+       files := `
+-- hugo.toml --
+baseURL = "https://example.org"
+disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT"]
+defaultContentLanguage = "nn"
+defaultContentLanguageInSubdir = true
+[markup]
+[markup.goldmark]
+duplicateResourceFiles = false
+[markup.goldmark.renderhooks]
+[markup.goldmark.renderhooks.link]
+#enableDefault = false
+[markup.goldmark.renderhooks.image]
+#enableDefault = false
+[languages]
+[languages.en]
+weight = 1
+[languages.nn]
+weight = 2
+-- content/p1/index.md --
+---
+title: "p1"
+---
+[P2](p2)
+![Pixel](pixel.png)
+-- content/p2/index.md --
+---
+title: "p2"
+---
+[P1](p1)
+![Pixel](pixel.jpg)
+-- content/p1/index.en.md --
+---
+title: "p1 en"
+---
+[P2](p2)
+![Pixel](pixel.png)
+-- content/p2/index.en.md --
+---
+title: "p2 en"
+---
+[P1](p1)
+![Pixel](pixel.png)
+
+-- content/p1/pixel.nn.png --
+iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
+-- content/p2/pixel.png --
+iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
+-- layouts/_default/single.html --
+{{ .Title }}|{{ .Content }}|$
+       
+`
+
+       t.Run("Default multilingual", func(t *testing.T) {
+               b := Test(t, files)
+
+               b.AssertFileContent("public/nn/p1/index.html",
+                       "p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
+               b.AssertFileContent("public/en/p1/index.html",
+                       "p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
+       })
+
+       t.Run("Disabled", func(t *testing.T) {
+               b := Test(t, strings.ReplaceAll(files, "#enableDefault = false", "enableDefault = false"))
+
+               b.AssertFileContent("public/nn/p1/index.html",
+                       "p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
+       })
+}
index e0026ed1fbda71497a0bf3add2f3f0ed2bbcbe70..7220c86fe43fabe5640383d02d1efc17eb62aff2 100644 (file)
@@ -470,6 +470,21 @@ func (pco *pageContentOutput) initRenderHooks() error {
                                if err != nil {
                                        panic(err)
                                }
+                               if found {
+                                       if isitp, ok := templ.(tpl.IsInternalTemplateProvider); ok && isitp.IsInternalTemplate() {
+                                               renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks
+                                               switch templ.Name() {
+                                               case "_default/_markup/render-link.html":
+                                                       if !renderHookConfig.Link.IsEnableDefault() {
+                                                               return nil, false
+                                                       }
+                                               case "_default/_markup/render-image.html":
+                                                       if !renderHookConfig.Image.IsEnableDefault() {
+                                                               return nil, false
+                                                       }
+                                               }
+                                       }
+                               }
                                return templ, found
                        }
 
index 8e05ad7e60d37a949ebee24204fcf4af1a1c9854..505b10bd77296f579c3aebd64bd4b56f604da3a6 100644 (file)
@@ -56,7 +56,7 @@ func (c *pageFinder) getPageRef(context page.Page, ref string) (page.Page, error
 }
 
 func (c *pageFinder) getPage(context page.Page, ref string) (page.Page, error) {
-       n, err := c.getContentNode(context, false, filepath.ToSlash(ref))
+       n, err := c.getContentNode(context, false, paths.ToSlashTrimTrailing(ref))
        if err != nil {
                return nil, err
        }
index 8fd4f07395c0f4774c020455c92de54e9e7bd3d3..eaa80a10995f343c3e9a55e2d13f4fe0ed67e0a1 100644 (file)
@@ -413,6 +413,10 @@ title: p2
 func TestPageGetPageVariations(t *testing.T) {
        files := `
 -- hugo.toml --
+-- content/s1/_index.md --
+---
+title: s1 section
+---
 -- content/s1/p1/index.md --
 ---
 title: p1
@@ -430,6 +434,8 @@ title: p3
 title: p2_root
 ---
 -- layouts/index.html --
+/s1: {{ with .GetPage "/s1" }}{{ .Title }}{{ end }}|
+/s1/: {{ with .GetPage "/s1/" }}{{ .Title }}{{ end }}|
 /s1/p2.md: {{ with .GetPage "/s1/p2.md" }}{{ .Title }}{{ end }}|
 /s1/p2: {{ with .GetPage "/s1/p2" }}{{ .Title }}{{ end }}|
 /s1/p1/index.md: {{ with .GetPage "/s1/p1/index.md" }}{{ .Title }}{{ end }}|
@@ -444,6 +450,8 @@ p1/index.md: {{ with .GetPage "p1/index.md" }}{{ .Title }}{{ end }}|
        b := Test(t, files)
 
        b.AssertFileContent("public/index.html", `
+/s1: s1 section|
+/s1/: s1 section|
 /s1/p2.md: p2|
 /s1/p2: p2|
 /s1/p1/index.md: p1|
index c8542dad7c4134f39e0c3ac527c4a47e4dcdfa5b..d4c7b65de43cfc8c76c54a62d94effdce8a01904 100644 (file)
@@ -185,42 +185,15 @@ func TestRace() error {
 
 // Run gofmt linter
 func Fmt() error {
-       if !isGoLatest() {
+       if !isGoLatest() && !isUnix() {
                return nil
        }
-       pkgs, err := hugoPackages()
+       s, err := sh.Output("./check_gofmt.sh")
        if err != nil {
-               return err
-       }
-       failed := false
-       first := true
-       for _, pkg := range pkgs {
-               files, err := filepath.Glob(filepath.Join(pkg, "*.go"))
-               if err != nil {
-                       return nil
-               }
-               for _, f := range files {
-                       // gofmt doesn't exit with non-zero when it finds unformatted code
-                       // so we have to explicitly look for output, and if we find any, we
-                       // should fail this target.
-                       s, err := sh.Output("gofmt", "-l", f)
-                       if err != nil {
-                               fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err)
-                               failed = true
-                       }
-                       if s != "" {
-                               if first {
-                                       fmt.Println("The following files are not gofmt'ed:")
-                                       first = false
-                               }
-                               failed = true
-                               fmt.Println(s)
-                       }
-               }
-       }
-       if failed {
-               return errors.New("improperly formatted go files")
+               fmt.Println(s)
+               return fmt.Errorf("gofmt needs to be run: %s", err)
        }
+
        return nil
 }
 
@@ -332,6 +305,10 @@ func isGoLatest() bool {
        return strings.Contains(runtime.Version(), "1.21")
 }
 
+func isUnix() bool {
+       return runtime.GOOS != "windows"
+}
+
 func isCI() bool {
        return os.Getenv("CI") != ""
 }
index 1c393e3f44e415f233059fde3be3b9d05b25851f..9a14bf9b88477f57560e55acd4ff552036713a74 100644 (file)
@@ -73,10 +73,39 @@ var Default = Config{
 
 // Config configures Goldmark.
 type Config struct {
-       DuplicateResourceFiles bool
        Renderer               Renderer
        Parser                 Parser
        Extensions             Extensions
+       DuplicateResourceFiles bool
+       RenderHooks            RenderHooks
+}
+
+// RenderHooks contains configuration for Goldmark render hooks.
+type RenderHooks struct {
+       Image ImageRenderHook
+       Link  LinkRenderHook
+}
+
+// ImageRenderHook contains configuration for the image render hook.
+type ImageRenderHook struct {
+       // Enable the default image render hook.
+       // We need to know if it is set or not, hence the pointer.
+       EnableDefault *bool
+}
+
+func (h ImageRenderHook) IsEnableDefault() bool {
+       return h.EnableDefault != nil && *h.EnableDefault
+}
+
+// LinkRenderHook contains configuration for the link render hook.
+type LinkRenderHook struct {
+       // Disable the default image render hook.
+       // We need to know if it is set or not, hence the pointer.
+       EnableDefault *bool
+}
+
+func (h LinkRenderHook) IsEnableDefault() bool {
+       return h.EnableDefault != nil && *h.EnableDefault
 }
 
 type Extensions struct {
index b27debf1fcf8f6509a8cd4b09de450582c722458..fd126d80f642934f2f70621042f3d94bec6de700 100644 (file)
@@ -25,6 +25,10 @@ type FileInfo interface {
        Filename() string
 }
 
+type IsInternalTemplateProvider interface {
+       IsInternalTemplate() bool
+}
+
 type ParseInfo struct {
        // Set for shortcode templates with any {{ .Inner }}
        IsInner bool
diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
new file mode 100644 (file)
index 0000000..c90abab
--- /dev/null
@@ -0,0 +1,15 @@
+{{- $u := urls.Parse .Destination -}}
+{{- $src := $u.String -}}
+{{- if not $u.IsAbs -}}
+  {{- with or (.Page.Resources.Get $u.Path) (resources.Get $u.Path) -}}
+    {{- $src = .RelPermalink -}}
+  {{- end -}}
+{{- end -}}
+{{- $attributes := dict "alt" .Text "src" $src "title" .Title -}}
+<img
+  {{- range $k, $v := $attributes -}}
+    {{- if $v -}}
+      {{- printf " %s=%q" $k $v | safeHTMLAttr -}}
+    {{- end -}}
+  {{- end -}}>
+{{- /**/ -}}
diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html
new file mode 100644 (file)
index 0000000..bd64b20
--- /dev/null
@@ -0,0 +1,26 @@
+{{- $u := urls.Parse .Destination -}}
+{{- $href := $u.String -}}
+{{- if not $u.IsAbs -}}
+  {{- with or
+    ($.Page.GetPage $u.Path)
+    ($.Page.Resources.Get $u.Path)
+    (resources.Get $u.Path)
+  -}}
+    {{- $href = .RelPermalink -}}
+    {{- with $u.RawQuery -}}
+      {{- $href = printf "%s?%s" $href . -}}
+    {{- end -}}
+    {{- with $u.Fragment -}}
+      {{- $href = printf "%s#%s" $href . -}}
+    {{- end -}}
+  {{- end -}}
+{{- end -}}
+{{- $attributes := dict "href" $href "title" .Title -}}
+<a
+  {{- range $k, $v := $attributes -}}
+    {{- if $v -}}
+      {{- printf " %s=%q" $k $v | safeHTMLAttr -}}
+    {{- end -}}
+  {{- end -}}
+  >{{ .Text | safeHTML }}</a>
+{{- /**/ -}}
index a8ba6815d55f6d434c8967cb229936851f9ae6ff..63dc29662e4fa54b54b70ec1dde6ac5b725e711e 100644 (file)
@@ -55,6 +55,7 @@ const (
 
        shortcodesPathPrefix = "shortcodes/"
        internalPathPrefix   = "_internal/"
+       embeddedPathPrefix   = "_embedded/"
        baseFileBase         = "baseof"
 )
 
@@ -517,11 +518,19 @@ func (t *templateHandler) findLayout(d layouts.LayoutDescriptor, f output.Format
 
 func (t *templateHandler) newTemplateInfo(name, tpl string) templateInfo {
        var isText bool
+       var isEmbedded bool
+
+       if strings.HasPrefix(name, embeddedPathPrefix) {
+               isEmbedded = true
+               name = strings.TrimPrefix(name, embeddedPathPrefix)
+       }
+
        name, isText = t.nameIsText(name)
        return templateInfo{
-               name:     name,
-               isText:   isText,
-               template: tpl,
+               name:       name,
+               isText:     isText,
+               isEmbedded: isEmbedded,
+               template:   tpl,
        }
 }
 
@@ -772,7 +781,7 @@ func (t *templateHandler) loadEmbedded() error {
                }
 
                if _, found := t.Lookup(templateName); !found {
-                       if err := t.AddTemplate(templateName, templ); err != nil {
+                       if err := t.AddTemplate(embeddedPathPrefix+templateName, templ); err != nil {
                                return err
                        }
                }
@@ -781,7 +790,7 @@ func (t *templateHandler) loadEmbedded() error {
                        // TODO(bep) avoid reparsing these aliases
                        for _, alias := range aliases {
                                alias = internalPathPrefix + alias
-                               if err := t.AddTemplate(alias, templ); err != nil {
+                               if err := t.AddTemplate(embeddedPathPrefix+alias, templ); err != nil {
                                        return err
                                }
                        }
@@ -1026,6 +1035,8 @@ func (t *templateNamespace) parse(info templateInfo) (*templateState, error) {
        return ts, nil
 }
 
+var _ tpl.IsInternalTemplateProvider = (*templateState)(nil)
+
 type templateState struct {
        tpl.Template
 
@@ -1037,6 +1048,10 @@ type templateState struct {
        baseInfo templateInfo // Set when a base template is used.
 }
 
+func (t *templateState) IsInternalTemplate() bool {
+       return t.info.isEmbedded
+}
+
 func (t *templateState) GetIdentity() identity.Identity {
        return t.id
 }
index 34e73a07a1f339d60790efb1d9287bc3d1be73f1..a9d259220050418f38809676429d86eb73716fbf 100644 (file)
@@ -24,9 +24,10 @@ import (
 var _ identity.Identity = (*templateInfo)(nil)
 
 type templateInfo struct {
-       name     string
-       template string
-       isText   bool // HTML or plain text template.
+       name       string
+       template   string
+       isText     bool // HTML or plain text template.
+       isEmbedded bool
 
        meta *hugofs.FileMeta
 }