URI: 
       Create default link and image render hooks - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 5b7cb258ec26d7de690099f5dc39935b8d728155
   DIR parent 80595bbe3e7901ecd6200e59d43af142c3c85b6b
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Tue, 30 Jan 2024 11:43:20 +0100
       
       Create default link and image render hooks
       
       Fixes #11933
       
       Diffstat:
         A check_gofmt.sh                      |       3 +++
         M common/paths/path.go                |       5 +++++
         M common/types/types.go               |       5 +++++
         M config/allconfig/allconfig.go       |      13 +++++++++++++
         M hugolib/content_render_hooks_test.… |      72 +++++++++++++++++++++++++++++++
         M hugolib/page__per_output.go         |      15 +++++++++++++++
         M hugolib/pagecollections.go          |       2 +-
         M hugolib/pagecollections_test.go     |       8 ++++++++
         M magefile.go                         |      41 +++++++------------------------
         M markup/goldmark/goldmark_config/co… |      31 ++++++++++++++++++++++++++++++-
         M tpl/template_info.go                |       4 ++++
         A tpl/tplimpl/embedded/templates/_de… |      15 +++++++++++++++
         A tpl/tplimpl/embedded/templates/_de… |      26 ++++++++++++++++++++++++++
         M tpl/tplimpl/template.go             |      25 ++++++++++++++++++++-----
         M tpl/tplimpl/template_errors.go      |       7 ++++---
       
       15 files changed, 230 insertions(+), 42 deletions(-)
       ---
   DIR diff --git a/check_gofmt.sh b/check_gofmt.sh
       @@ -0,0 +1,2 @@
       +#!/usr/bin/env bash
       +diff <(gofmt -d .) <(printf '')
       +\ No newline at end of file
   DIR diff --git a/common/paths/path.go b/common/paths/path.go
       @@ -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 {
   DIR diff --git a/common/types/types.go b/common/types/types.go
       @@ -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
       +}
   DIR diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
       @@ -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:
   DIR diff --git a/hugolib/content_render_hooks_test.go b/hugolib/content_render_hooks_test.go
       @@ -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\">")
       +        })
       +}
   DIR diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
       @@ -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
                                }
        
   DIR diff --git a/hugolib/pagecollections.go b/hugolib/pagecollections.go
       @@ -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
                }
   DIR diff --git a/hugolib/pagecollections_test.go b/hugolib/pagecollections_test.go
       @@ -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|
   DIR diff --git a/magefile.go b/magefile.go
       @@ -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") != ""
        }
   DIR diff --git a/markup/goldmark/goldmark_config/config.go b/markup/goldmark/goldmark_config/config.go
       @@ -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 {
   DIR diff --git a/tpl/template_info.go b/tpl/template_info.go
       @@ -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
   DIR diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
       @@ -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 -}}>
       +{{- /**/ -}}
   DIR diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-link.html
       @@ -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>
       +{{- /**/ -}}
   DIR diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
       @@ -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
        }
   DIR diff --git a/tpl/tplimpl/template_errors.go b/tpl/tplimpl/template_errors.go
       @@ -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
        }