]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
markup/goldmark: Change link and image render hook enablement to enums
authorJoe Mooring <joe.mooring@veriphor.com>
Sat, 21 Jun 2025 13:41:08 +0000 (06:41 -0700)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 7 Jul 2025 09:19:34 +0000 (11:19 +0200)
Closes #13535

config/allconfig/allconfig.go
config/allconfig/allconfig_integration_test.go
helpers/general.go
helpers/general_test.go
hugolib/page__per_output.go
hugolib/site.go
markup/goldmark/goldmark_config/config.go
tpl/tplimpl/render_hook_integration_test.go
tpl/tplimpl/templatestore.go

index 0db0be1d8d50b4d9341c5f39c976967b4366a7f3..3366defd78c0fae23be299568796b2cf2172cace 100644 (file)
@@ -20,6 +20,7 @@ import (
        "fmt"
        "reflect"
        "regexp"
+       "slices"
        "sort"
        "strconv"
        "strings"
@@ -32,7 +33,6 @@ 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"
@@ -42,6 +42,7 @@ import (
        "github.com/gohugoio/hugo/helpers"
        "github.com/gohugoio/hugo/hugolib/segments"
        "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/media"
        "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())
                c.Pagination.PagerSize = c.Paginate
        }
-
        if c.PaginatePath != "" {
                hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger())
                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())
                c.Privacy.X.Disable = c.Privacy.Twitter.Disable
        }
-
        if c.Privacy.Twitter.EnableDNT {
                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
        }
-
        if c.Privacy.Twitter.Simple {
                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
@@ -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())
        }
 
+       // 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{
                Timeout:             timeout,
                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.
                        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)
-                                       }
+                               if clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
+                                       clone.Markup.Goldmark.RenderHooks.Image.UseEmbedded = gc.RenderHookUseEmbeddedFallback
+                               }
+                               if clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded == gc.RenderHookUseEmbeddedAuto {
+                                       clone.Markup.Goldmark.RenderHooks.Link.UseEmbedded = gc.RenderHookUseEmbeddedFallback
                                }
                        }
 
index 8f6cacf84aa884b9ba899b6e269cdf4a0ccb202f..79d2299de3af4ba0422da677a63de096c7577af0 100644 (file)
@@ -2,12 +2,14 @@ package allconfig_test
 
 import (
        "path/filepath"
+       "strings"
        "testing"
 
        qt "github.com/frankban/quicktest"
        "github.com/gohugoio/hugo/common/hugo"
        "github.com/gohugoio/hugo/config/allconfig"
        "github.com/gohugoio/hugo/hugolib"
+       gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
        "github.com/gohugoio/hugo/media"
 )
 
@@ -379,3 +381,28 @@ weight = 3
 
        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)
+}
index 76275a6b92166bdecfe5c918cbaba210983631d4..5c3af97128cc8c9455973137c2a2cdeed021d421 100644 (file)
@@ -255,6 +255,27 @@ func SliceToLower(s []string) []string {
        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.
 func IsWhitespace(r rune) bool {
        return r == ' ' || r == '\t' || r == '\n' || r == '\r'
index 686e95ded74d3ddba361fede7d2cf1af6959790b..6d2a78e0220209184a8b7a94518cee4b45b97c11 100644 (file)
@@ -18,9 +18,8 @@ import (
        "strings"
        "testing"
 
-       "github.com/gohugoio/hugo/helpers"
-
        qt "github.com/frankban/quicktest"
+       "github.com/gohugoio/hugo/helpers"
 )
 
 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)
+               }
+       }
+}
index 1f7f3411e35975fd158e6c3046d880cdc5f1f954..15e9a890cb09f96617faeb496156e01086993ca4 100644 (file)
@@ -29,6 +29,7 @@ import (
        "github.com/spf13/cast"
 
        "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/converter"
@@ -304,12 +305,17 @@ func (pco *pageContentOutput) initRenderHooks() error {
                        }
 
                        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 {
                        case "link":
-                               ignoreInternal = !renderHookConfig.Link.IsEnableDefault()
+                               ignoreEmbedded = renderHookConfig.Link.UseEmbedded == gc.RenderHookUseEmbeddedNever ||
+                                       renderHookConfig.Link.UseEmbedded == gc.RenderHookUseEmbeddedAuto
                        case "image":
-                               ignoreInternal = !renderHookConfig.Image.IsEnableDefault()
+                               ignoreEmbedded = renderHookConfig.Image.UseEmbedded == gc.RenderHookUseEmbeddedNever ||
+                                       renderHookConfig.Image.UseEmbedded == gc.RenderHookUseEmbeddedAuto
                        }
 
                        candidates := pco.po.p.s.renderFormats
@@ -323,8 +329,8 @@ func (pco *pageContentOutput) initRenderHooks() error {
                                        return false
                                }
 
-                               if ignoreInternal && candidate.SubCategory() == tplimpl.SubCategoryEmbedded {
-                                       // Don't consider the internal hook templates.
+                               if ignoreEmbedded && candidate.SubCategory() == tplimpl.SubCategoryEmbedded {
+                                       // Don't consider the embedded hook templates.
                                        return false
                                }
 
index acd3b54104cb4464ac92dbe50fbf02511d963a58..e37dbd5ef7a970d2abfa1348336eb1b9efce58d4 100644 (file)
@@ -406,6 +406,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
                                        MediaTypes:             s.conf.MediaTypes.Config,
                                        DefaultOutputFormat:    s.conf.DefaultOutputFormat,
                                        TaxonomySingularPlural: s.conf.Taxonomies,
+                                       RenderHooks:            s.conf.Markup.Goldmark.RenderHooks,
                                }, tplimpl.SiteOptions{
                                        Site:          s,
                                        TemplateFuncs: tplimplinit.CreateFuncMap(s.Deps),
index 04eb371d90a9f85bb22426e4720802a3edc10664..168a3b8fad1f186a208ec517e013682c5f677404 100644 (file)
 package goldmark_config
 
 const (
-       AutoIDTypeGitHub      = "github"
-       AutoIDTypeGitHubAscii = "github-ascii"
-       AutoIDTypeBlackfriday = "blackfriday"
+       AutoIDTypeBlackfriday         = "blackfriday"
+       AutoIDTypeGitHub              = "github"
+       AutoIDTypeGitHubAscii         = "github-ascii"
+       RenderHookUseEmbeddedAlways   = "always"
+       RenderHookUseEmbeddedAuto     = "auto"
+       RenderHookUseEmbeddedFallback = "fallback"
+       RenderHookUseEmbeddedNever    = "never"
 )
 
 // Default holds the default Goldmark configuration.
@@ -87,6 +91,14 @@ var Default = Config{
                        Block: false,
                },
        },
+       RenderHooks: RenderHooks{
+               Image: ImageRenderHook{
+                       UseEmbedded: RenderHookUseEmbeddedAuto,
+               },
+               Link: LinkRenderHook{
+                       UseEmbedded: RenderHookUseEmbeddedAuto,
+               },
+       },
 }
 
 // Config configures Goldmark.
@@ -118,22 +130,24 @@ type RenderHooks struct {
 type ImageRenderHook struct {
        // Enable the default image render hook.
        // We need to know if it is set or not, hence the pointer.
+       // Deprecated: Use UseEmbedded instead.
        EnableDefault *bool
-}
 
-func (h ImageRenderHook) IsEnableDefault() bool {
-       return h.EnableDefault != nil && *h.EnableDefault
+       // When to use the embedded image render hook.
+       // One of auto, never, always, or fallback. Default is auto.
+       UseEmbedded string
 }
 
 // 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.
+       // Deprecated: Use UseEmbedded instead.
        EnableDefault *bool
-}
 
-func (h LinkRenderHook) IsEnableDefault() bool {
-       return h.EnableDefault != nil && *h.EnableDefault
+       // When to use the embedded link render hook.
+       // One of auto, never, always, or fallback. Default is auto.
+       UseEmbedded string
 }
 
 type Extensions struct {
index 45c6cced1148f22795cb3bac4e72fb140dcdcaec..e6770159403c71307886c445ba285de24e1073a5 100644 (file)
@@ -192,3 +192,115 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
                `<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)
+               })
+       }
+}
index 23c821cac3ef1041f7faf8fdc25a3fbc1a4bad6d..008b743c7e3a0fa6088a4246071e7be3805a4cb6 100644 (file)
@@ -44,6 +44,7 @@ import (
        "github.com/gohugoio/hugo/hugofs/files"
        "github.com/gohugoio/hugo/hugolib/doctree"
        "github.com/gohugoio/hugo/identity"
+       gc "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
        "github.com/gohugoio/hugo/media"
        "github.com/gohugoio/hugo/metrics"
        "github.com/gohugoio/hugo/output"
@@ -202,6 +203,9 @@ type StoreOptions struct {
        // Whether we are in watch or server mode.
        Watching bool
 
+       // The render hook configuration.
+       RenderHooks gc.RenderHooks
+
        // compiled.
        legacyMappingTaxonomy map[string]legacyOrdinalMapping
        legacyMappingTerm     map[string]legacyOrdinalMapping
@@ -1422,20 +1426,36 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
 
                var ti *TemplInfo
                var err error
-               if pi.Type() == paths.TypeShortcode {
-                       ti, err = s.insertShortcode(pi, fi, partialRebuild, s.treeShortcodes)
-                       if err != nil || ti == nil {
-                               return err
+               var insertFunc func() (*TemplInfo, error)
+
+               insertFunc = func() (*TemplInfo, error) {
+                       return 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)
                        }
-               } else {
-                       ti, err = s.insertTemplate(pi, fi, SubCategoryMain, partialRebuild, s.treeMain)
+               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 {
                                return err
                        }
                }
 
-               if err := s.tns.readTemplateInto(ti); err != nil {
-                       return err
+               if ti != nil {
+                       if err := s.tns.readTemplateInto(ti); err != nil {
+                               return err
+                       }
                }
 
                return nil