markup/goldmark: Change link and image render hook enablement to enums

Closes #13535
This commit is contained in:
Joe Mooring
2025-06-21 06:41:08 -07:00
committed by Bjørn Erik Pedersen
parent b8ba33ca95
commit 84b31721bf
9 changed files with 293 additions and 35 deletions

View File

@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"regexp" "regexp"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -32,7 +33,6 @@ import (
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/urls" "github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/privacy" "github.com/gohugoio/hugo/config/privacy"
@@ -42,6 +42,7 @@ import (
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugolib/segments" "github.com/gohugoio/hugo/hugolib/segments"
"github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/langs"
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/markup/markup_config" "github.com/gohugoio/hugo/markup/markup_config"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/minifiers" "github.com/gohugoio/hugo/minifiers"
@@ -399,7 +400,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
hugo.DeprecateWithLogger("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0", logger.Logger()) hugo.DeprecateWithLogger("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0", logger.Logger())
c.Pagination.PagerSize = c.Paginate c.Pagination.PagerSize = c.Paginate
} }
if c.PaginatePath != "" { if c.PaginatePath != "" {
hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger()) hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger())
c.Pagination.Path = c.PaginatePath c.Pagination.Path = c.PaginatePath
@@ -410,12 +410,10 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
hugo.DeprecateWithLogger("site config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger()) hugo.DeprecateWithLogger("site config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger())
c.Privacy.X.Disable = c.Privacy.Twitter.Disable c.Privacy.X.Disable = c.Privacy.Twitter.Disable
} }
if c.Privacy.Twitter.EnableDNT { if c.Privacy.Twitter.EnableDNT {
hugo.DeprecateWithLogger("site config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger()) hugo.DeprecateWithLogger("site config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger())
c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT
} }
if c.Privacy.Twitter.Simple { if c.Privacy.Twitter.Simple {
hugo.DeprecateWithLogger("site config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger()) hugo.DeprecateWithLogger("site config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger())
c.Privacy.X.Simple = c.Privacy.Twitter.Simple c.Privacy.X.Simple = c.Privacy.Twitter.Simple
@@ -436,6 +434,45 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger()) hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger())
} }
// Legacy render hook values.
alternativeDetails := fmt.Sprintf(
"Set to %q if previous value was false, or set to %q if previous value was true.",
gc.RenderHookUseEmbeddedNever,
gc.RenderHookUseEmbeddedFallback,
)
if c.Markup.Goldmark.RenderHooks.Image.EnableDefault != nil {
alternative := "Use markup.goldmark.renderHooks.image.useEmbedded instead." + " " + alternativeDetails
hugo.DeprecateWithLogger("site config key markup.goldmark.renderHooks.image.enableDefault", alternative, "0.148.0", logger.Logger())
if *c.Markup.Goldmark.RenderHooks.Image.EnableDefault {
c.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
} else {
c.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedNever
}
}
if c.Markup.Goldmark.RenderHooks.Link.EnableDefault != nil {
alternative := "Use markup.goldmark.renderHooks.link.useEmbedded instead." + " " + alternativeDetails
hugo.DeprecateWithLogger("site config key markup.goldmark.renderHooks.link.enableDefault", alternative, "0.148.0", logger.Logger())
if *c.Markup.Goldmark.RenderHooks.Link.EnableDefault {
c.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
} else {
c.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedNever
}
}
// Validate render hook configuration.
renderHookUseEmbeddedModes := []string{
gc.RenderHookUseEmbeddedAlways,
gc.RenderHookUseEmbeddedAuto,
gc.RenderHookUseEmbeddedFallback,
gc.RenderHookUseEmbeddedNever,
}
if !slices.Contains(renderHookUseEmbeddedModes, c.Markup.Goldmark.RenderHooks.Image.UseEmbedded) {
return fmt.Errorf("site config markup.goldmark.renderHooks.image must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
}
if !slices.Contains(renderHookUseEmbeddedModes, c.Markup.Goldmark.RenderHooks.Link.UseEmbedded) {
return fmt.Errorf("site config markup.goldmark.renderHooks.link must be one of %s", helpers.StringSliceToList(renderHookUseEmbeddedModes, "or"))
}
c.C = &ConfigCompiled{ c.C = &ConfigCompiled{
Timeout: timeout, Timeout: timeout,
BaseURL: baseURL, BaseURL: baseURL,
@@ -1082,13 +1119,11 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
// Adjust Goldmark config defaults for multilingual, single-host sites. // Adjust Goldmark config defaults for multilingual, single-host sites.
if len(languagesConfig) > 1 && !isMultihost && !clone.Markup.Goldmark.DuplicateResourceFiles { if len(languagesConfig) > 1 && !isMultihost && !clone.Markup.Goldmark.DuplicateResourceFiles {
if !clone.Markup.Goldmark.DuplicateResourceFiles { if clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil { clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
}
if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
} }
if clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
} }
} }

View File

@@ -2,12 +2,14 @@ package allconfig_test
import ( import (
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib"
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
) )
@@ -379,3 +381,28 @@ weight = 3
b.Assert(len(b.H.Sites), qt.Equals, 1) b.Assert(len(b.H.Sites), qt.Equals, 1)
} }
// Issue 13535
// We changed enablement of the embedded link and image render hooks from
// booleans to enums in v0.148.0.
func TestLegacyEmbeddedRenderHookEnablement(t *testing.T) {
files := `
-- hugo.toml --
[markup.goldmark.renderHooks.image]
#KEY_VALUE
[markup.goldmark.renderHooks.link]
#KEY_VALUE
`
f := strings.ReplaceAll(files, "#KEY_VALUE", "enableDefault = false")
b := hugolib.Test(t, f)
c := b.H.Configs.Base.Markup.Goldmark.RenderHooks
b.Assert(c.Link.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedNever)
b.Assert(c.Image.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedNever)
f = strings.ReplaceAll(files, "#KEY_VALUE", "enableDefault = true")
b = hugolib.Test(t, f)
c = b.H.Configs.Base.Markup.Goldmark.RenderHooks
b.Assert(c.Link.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedFallback)
b.Assert(c.Image.UseEmbedded, qt.Equals, gc.RenderHookUseEmbeddedFallback)
}

View File

@@ -255,6 +255,27 @@ func SliceToLower(s []string) []string {
return l return l
} }
// StringSliceToList formats a string slice into a human-readable list.
// It joins the elements of the slice s with commas, using an Oxford comma,
// and precedes the final element with the conjunction c.
func StringSliceToList(s []string, c string) string {
const defaultConjunction = "and"
if c == "" {
c = defaultConjunction
}
if len(s) == 0 {
return ""
}
if len(s) == 1 {
return s[0]
}
if len(s) == 2 {
return fmt.Sprintf("%s %s %s", s[0], c, s[1])
}
return fmt.Sprintf("%s, %s %s", strings.Join(s[:len(s)-1], ", "), c, s[len(s)-1])
}
// IsWhitespace determines if the given rune is whitespace. // IsWhitespace determines if the given rune is whitespace.
func IsWhitespace(r rune) bool { func IsWhitespace(r rune) bool {
return r == ' ' || r == '\t' || r == '\n' || r == '\r' return r == ' ' || r == '\t' || r == '\n' || r == '\r'

View File

@@ -18,9 +18,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/helpers"
) )
func TestResolveMarkup(t *testing.T) { func TestResolveMarkup(t *testing.T) {
@@ -304,3 +303,26 @@ func BenchmarkUniqueStrings(b *testing.B) {
} }
}) })
} }
func TestStringSliceToList(t *testing.T) {
for _, tt := range []struct {
slice []string
conjunction string
want string
}{
{[]string{}, "", ""},
{[]string{"foo"}, "", "foo"},
{[]string{"foo"}, "and", "foo"},
{[]string{"foo", "bar"}, "", "foo and bar"},
{[]string{"foo", "bar"}, "and", "foo and bar"},
{[]string{"foo", "bar"}, "or", "foo or bar"},
{[]string{"foo", "bar", "baz"}, "", "foo, bar, and baz"},
{[]string{"foo", "bar", "baz"}, "and", "foo, bar, and baz"},
{[]string{"foo", "bar", "baz"}, "or", "foo, bar, or baz"},
} {
got := helpers.StringSliceToList(tt.slice, tt.conjunction)
if got != tt.want {
t.Errorf("StringSliceToList() got: %q, want: %q", got, tt.want)
}
}
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/spf13/cast" "github.com/spf13/cast"
"github.com/gohugoio/hugo/markup/converter/hooks" "github.com/gohugoio/hugo/markup/converter/hooks"
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/converter"
@@ -304,12 +305,17 @@ func (pco *pageContentOutput) initRenderHooks() error {
} }
renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks
var ignoreInternal bool var ignoreEmbedded bool
// For multilingual single-host sites, "auto" becomes "fallback"
// earlier in the process.
switch layoutDescriptor.Variant1 { switch layoutDescriptor.Variant1 {
case "link": case "link":
ignoreInternal = !renderHookConfig.Link.IsEnableDefault() ignoreEmbedded = renderHookConfig.Link.UseEmbedded == gc.RenderHookUseEmbeddedNever ||
renderHookConfig.Link.UseEmbedded == gc.RenderHookUseEmbeddedAuto
case "image": case "image":
ignoreInternal = !renderHookConfig.Image.IsEnableDefault() ignoreEmbedded = renderHookConfig.Image.UseEmbedded == gc.RenderHookUseEmbeddedNever ||
renderHookConfig.Image.UseEmbedded == gc.RenderHookUseEmbeddedAuto
} }
candidates := pco.po.p.s.renderFormats candidates := pco.po.p.s.renderFormats
@@ -323,8 +329,8 @@ func (pco *pageContentOutput) initRenderHooks() error {
return false return false
} }
if ignoreInternal && candidate.SubCategory() == tplimpl.SubCategoryEmbedded { if ignoreEmbedded && candidate.SubCategory() == tplimpl.SubCategoryEmbedded {
// Don't consider the internal hook templates. // Don't consider the embedded hook templates.
return false return false
} }

View File

@@ -406,6 +406,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
MediaTypes: s.conf.MediaTypes.Config, MediaTypes: s.conf.MediaTypes.Config,
DefaultOutputFormat: s.conf.DefaultOutputFormat, DefaultOutputFormat: s.conf.DefaultOutputFormat,
TaxonomySingularPlural: s.conf.Taxonomies, TaxonomySingularPlural: s.conf.Taxonomies,
RenderHooks: s.conf.Markup.Goldmark.RenderHooks,
}, tplimpl.SiteOptions{ }, tplimpl.SiteOptions{
Site: s, Site: s,
TemplateFuncs: tplimplinit.CreateFuncMap(s.Deps), TemplateFuncs: tplimplinit.CreateFuncMap(s.Deps),

View File

@@ -15,9 +15,13 @@
package goldmark_config package goldmark_config
const ( const (
AutoIDTypeBlackfriday = "blackfriday"
AutoIDTypeGitHub = "github" AutoIDTypeGitHub = "github"
AutoIDTypeGitHubAscii = "github-ascii" AutoIDTypeGitHubAscii = "github-ascii"
AutoIDTypeBlackfriday = "blackfriday" RenderHookUseEmbeddedAlways = "always"
RenderHookUseEmbeddedAuto = "auto"
RenderHookUseEmbeddedFallback = "fallback"
RenderHookUseEmbeddedNever = "never"
) )
// Default holds the default Goldmark configuration. // Default holds the default Goldmark configuration.
@@ -87,6 +91,14 @@ var Default = Config{
Block: false, Block: false,
}, },
}, },
RenderHooks: RenderHooks{
Image: ImageRenderHook{
UseEmbedded: RenderHookUseEmbeddedAuto,
},
Link: LinkRenderHook{
UseEmbedded: RenderHookUseEmbeddedAuto,
},
},
} }
// Config configures Goldmark. // Config configures Goldmark.
@@ -118,22 +130,24 @@ type RenderHooks struct {
type ImageRenderHook struct { type ImageRenderHook struct {
// Enable the default image render hook. // Enable the default image render hook.
// We need to know if it is set or not, hence the pointer. // We need to know if it is set or not, hence the pointer.
// Deprecated: Use UseEmbedded instead.
EnableDefault *bool EnableDefault *bool
}
func (h ImageRenderHook) IsEnableDefault() bool { // When to use the embedded image render hook.
return h.EnableDefault != nil && *h.EnableDefault // One of auto, never, always, or fallback. Default is auto.
UseEmbedded string
} }
// LinkRenderHook contains configuration for the link render hook. // LinkRenderHook contains configuration for the link render hook.
type LinkRenderHook struct { type LinkRenderHook struct {
// Disable the default image render hook. // Disable the default image render hook.
// We need to know if it is set or not, hence the pointer. // We need to know if it is set or not, hence the pointer.
// Deprecated: Use UseEmbedded instead.
EnableDefault *bool EnableDefault *bool
}
func (h LinkRenderHook) IsEnableDefault() bool { // When to use the embedded link render hook.
return h.EnableDefault != nil && *h.EnableDefault // One of auto, never, always, or fallback. Default is auto.
UseEmbedded string
} }
type Extensions struct { type Extensions struct {

View File

@@ -192,3 +192,115 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
`<img src="/dir/p1/pixel.png" alt="alt4" id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`, `<img src="/dir/p1/pixel.png" alt="alt4" id="&#34;&gt;&lt;script&gt;alert()&lt;/script&gt;">`,
) )
} }
// Issue 13535
func TestEmbeddedLinkAndImageRenderHookConfig(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
[markup.goldmark]
duplicateResourceFiles = false
[markup.goldmark.renderHooks.image]
#KEY_VALUE
[markup.goldmark.renderHooks.link]
#KEY_VALUE
#LANGUAGES
-- content/s1/p1/index.md --
---
title: p1
---
[p2](p2)
[a](a.txt)
![b](b.jpg)
-- content/s1/p1/a.txt --
-- content/s1/p1/b.jpg --
-- content/s1/p2.md --
---
title: p2
---
-- layouts/all.html --
{{ .Content }}
`
const customHooks string = `
-- layouts/s1/_markup/render-link.html --
custom link render hook: {{ .Text }}|{{ .Destination }}
-- layouts/s1/_markup/render-image.html --
custom image render hook: {{ .Text }}|{{ .Destination }}
`
const languages string = `
[languages.en]
[languages.fr]
`
const (
fileToCheck = "public/s1/p1/index.html"
wantCustom string = "<p>custom link render hook: p2|p2</p>\n<p>custom link render hook: a|a.txt</p>\n<p>custom image render hook: b|b.jpg</p>"
wantEmbedded string = "<p><a href=\"/s1/p2/\">p2</a></p>\n<p><a href=\"/s1/p1/a.txt\">a</a></p>\n<p><img src=\"/s1/p1/b.jpg\" alt=\"b\"></p>"
wantGoldmark string = "<p><a href=\"p2\">p2</a></p>\n<p><a href=\"a.txt\">a</a></p>\n<p><img src=\"b.jpg\" alt=\"b\"></p>"
)
tests := []struct {
id string // the test id
isMultilingual bool // whether the site is multilingual single-host
hasCustomHooks bool // whether the site has custom link and image render hooks
keyValuePair string // the enableDefault (deprecated in v0.148.0) or useEmbedded key-value pair
want string // the expected content of public/s1/p1/index.html
}{
{"01", false, false, "", wantGoldmark}, // monolingual
{"02", false, false, "enableDefault = false", wantGoldmark}, // monolingual, enableDefault = false
{"03", false, false, "enableDefault = true", wantEmbedded}, // monolingual, enableDefault = true
{"04", false, false, "useEmbedded = 'always'", wantEmbedded}, // monolingual, useEmbedded = 'always'
{"05", false, false, "useEmbedded = 'auto'", wantGoldmark}, // monolingual, useEmbedded = 'auto'
{"06", false, false, "useEmbedded = 'fallback'", wantEmbedded}, // monolingual, useEmbedded = 'fallback'
{"07", false, false, "useEmbedded = 'never'", wantGoldmark}, // monolingual, useEmbedded = 'never'
{"08", false, true, "", wantCustom}, // monolingual, with custom hooks
{"09", false, true, "enableDefault = false", wantCustom}, // monolingual, with custom hooks, enableDefault = false
{"10", false, true, "enableDefault = true", wantCustom}, // monolingual, with custom hooks, enableDefault = true
{"11", false, true, "useEmbedded = 'always'", wantEmbedded}, // monolingual, with custom hooks, useEmbedded = 'always'
{"12", false, true, "useEmbedded = 'auto'", wantCustom}, // monolingual, with custom hooks, useEmbedded = 'auto'
{"13", false, true, "useEmbedded = 'fallback'", wantCustom}, // monolingual, with custom hooks, useEmbedded = 'fallback'
{"14", false, true, "useEmbedded = 'never'", wantCustom}, // monolingual, with custom hooks, useEmbedded = 'never'
{"15", true, false, "", wantEmbedded}, // multilingual
{"16", true, false, "enableDefault = false", wantGoldmark}, // multilingual, enableDefault = false
{"17", true, false, "enableDefault = true", wantEmbedded}, // multilingual, enableDefault = true
{"18", true, false, "useEmbedded = 'always'", wantEmbedded}, // multilingual, useEmbedded = 'always'
{"19", true, false, "useEmbedded = 'auto'", wantEmbedded}, // multilingual, useEmbedded = 'auto'
{"20", true, false, "useEmbedded = 'fallback'", wantEmbedded}, // multilingual, useEmbedded = 'fallback'
{"21", true, false, "useEmbedded = 'never'", wantGoldmark}, // multilingual, useEmbedded = 'never'
{"22", true, true, "", wantCustom}, // multilingual, with custom hooks
{"23", true, true, "enableDefault = false", wantCustom}, // multilingual, with custom hooks, enableDefault = false
{"24", true, true, "enableDefault = true", wantCustom}, // multilingual, with custom hooks, enableDefault = true
{"25", true, true, "useEmbedded = 'always'", wantEmbedded}, // multilingual, with custom hooks, useEmbedded = 'always'
{"26", true, true, "useEmbedded = 'auto'", wantCustom}, // multilingual, with custom hooks, useEmbedded = 'auto'
{"27", true, true, "useEmbedded = 'fallback'", wantCustom}, // multilingual, with custom hooks, useEmbedded = 'fallback'
{"28", true, true, "useEmbedded = 'never'", wantCustom}, // multilingual, with custom hooks, useEmbedded = 'never'
}
for _, tt := range tests {
t.Run(tt.id, func(t *testing.T) {
t.Parallel()
f := files
if tt.isMultilingual {
f = strings.ReplaceAll(f, "#LANGUAGES", languages)
}
if tt.hasCustomHooks {
f = f + customHooks
}
f = strings.ReplaceAll(f, "#KEY_VALUE", tt.keyValuePair)
b := hugolib.Test(t, f)
b.AssertFileContent(fileToCheck, tt.want)
})
}
}

View File

@@ -44,6 +44,7 @@ import (
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/hugolib/doctree" "github.com/gohugoio/hugo/hugolib/doctree"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/metrics" "github.com/gohugoio/hugo/metrics"
"github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/output"
@@ -202,6 +203,9 @@ type StoreOptions struct {
// Whether we are in watch or server mode. // Whether we are in watch or server mode.
Watching bool Watching bool
// The render hook configuration.
RenderHooks gc.RenderHooks
// compiled. // compiled.
legacyMappingTaxonomy map[string]legacyOrdinalMapping legacyMappingTaxonomy map[string]legacyOrdinalMapping
legacyMappingTerm map[string]legacyOrdinalMapping legacyMappingTerm map[string]legacyOrdinalMapping
@@ -1422,21 +1426,37 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
var ti *TemplInfo var ti *TemplInfo
var err error var err error
if pi.Type() == paths.TypeShortcode { var insertFunc func() (*TemplInfo, error)
ti, err = s.insertShortcode(pi, fi, partialRebuild, s.treeShortcodes)
if err != nil || ti == nil { insertFunc = func() (*TemplInfo, error) {
return err return s.insertTemplate(pi, fi, SubCategoryMain, partialRebuild, s.treeMain)
} }
} else {
ti, err = s.insertTemplate(pi, fi, SubCategoryMain, partialRebuild, s.treeMain) switch pi.Type() {
case paths.TypeShortcode:
insertFunc = func() (*TemplInfo, error) {
return s.insertShortcode(pi, fi, partialRebuild, s.treeShortcodes)
}
case paths.TypeMarkup:
skipImageRenderHook := pi.Name() == "render-image.html" && s.opts.RenderHooks.Image.UseEmbedded == "always"
skipLinkRenderHook := pi.Name() == "render-link.html" && s.opts.RenderHooks.Link.UseEmbedded == "always"
if skipImageRenderHook || skipLinkRenderHook {
insertFunc = nil
}
}
if insertFunc != nil {
ti, err = insertFunc()
if err != nil || ti == nil { if err != nil || ti == nil {
return err return err
} }
} }
if ti != nil {
if err := s.tns.readTemplateInto(ti); err != nil { if err := s.tns.readTemplateInto(ti); err != nil {
return err return err
} }
}
return nil return nil
} }