Repository: gohugoio/hugo
Pull request #12199 (2024-03-05 08:48:27)

build(deps): bump golang.org/x/mod from 0.15.0 to 0.16.0
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.15.0 to 0.16.0.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/mod/commit/766dc5df63e3e3e5cd6b1682f522a01c99723beb"><code>766dc5d</code></a> modfile: use new go version string format in WorkFile.add error</li>
<li>See full diff in <a href="https://github.com/golang/mod/compare/v0.15.0...v0.16.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/mod&package-manager=go_modules&previous-version=0.15.0&new-version=0.16.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/commands/server.go b/commands/server.go
index d08bc5277..afc539c44 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -512,9 +512,9 @@ func (c *serverCommand) Init(cd *simplecobra.Commandeer) error {
 	cmd.Long = `Hugo provides its own webserver which builds and serves the site.
 While hugo server is high performance, it is a webserver with limited options.
 
-'hugo server' will by default write and server files from disk, but you can
-render to memory by using the '--renderToMemory' flag. This can be faster
-in some cases, but it will consume more memory.
+The ` + "`" + `hugo server` + "`" + ` command will by default write and serve files from disk, but
+you can render to memory by using the ` + "`" + `--renderToMemory` + "`" + ` flag. This can be
+faster in some cases, but it will consume more memory.
 
 By default hugo will also watch your files for any changes you make and
 automatically rebuild the site. It will then live reload any open browser pages
@@ -617,9 +617,9 @@ func (c *serverCommand) setBaseURLsInConfig() error {
 	}
 	return c.withConfE(func(conf *commonConfig) error {
 		for i, language := range conf.configs.Languages {
-			isMultiHost := conf.configs.IsMultihost
+			isMultihost := conf.configs.IsMultihost
 			var serverPort int
-			if isMultiHost {
+			if isMultihost {
 				serverPort = c.serverPorts[i].p
 			} else {
 				serverPort = c.serverPorts[0].p
@@ -737,9 +737,9 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
 	flags := cd.CobraCommand.Flags()
 	var cerr error
 	c.withConf(func(conf *commonConfig) {
-		isMultiHost := conf.configs.IsMultihost
+		isMultihost := conf.configs.IsMultihost
 		c.serverPorts = make([]serverPortListener, 1)
-		if isMultiHost {
+		if isMultihost {
 			if !c.serverAppend {
 				cerr = errors.New("--appendPort=false not supported when in multihost mode")
 				return
@@ -852,7 +852,7 @@ func (c *serverCommand) serve() error {
 		h        *hugolib.HugoSites
 	)
 	err := c.withConfE(func(conf *commonConfig) error {
-		isMultiHost := conf.configs.IsMultihost
+		isMultihost := conf.configs.IsMultihost
 		var err error
 		h, err = c.r.HugFromConfig(conf)
 		if err != nil {
@@ -862,7 +862,7 @@ func (c *serverCommand) serve() error {
 		// We need the server to share the same logger as the Hugo build (for error counts etc.)
 		c.r.logger = h.Log
 
-		if isMultiHost {
+		if isMultihost {
 			for _, l := range conf.configs.ConfigLangs() {
 				baseURLs = append(baseURLs, l.BaseURL())
 				roots = append(roots, l.Language().Lang)
@@ -1005,7 +1005,6 @@ func (c *serverCommand) serve() error {
 			}
 		}
 	}()
-
 	if err != nil {
 		c.r.Println("Error:", err)
 	}
diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go
index be43e2a38..0589ac9a3 100644
--- a/common/hugo/hugo.go
+++ b/common/hugo/hugo.go
@@ -111,11 +111,29 @@ func (i HugoInfo) Deps() []*Dependency {
 	return i.deps
 }
 
+// Deprecated: Use hugo.IsMultihost instead.
+func (i HugoInfo) IsMultiHost() bool {
+	Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
+	return i.conf.IsMultihost()
+}
+
+// IsMultihost reports whether each configured language has a unique baseURL.
+func (i HugoInfo) IsMultihost() bool {
+	return i.conf.IsMultihost()
+}
+
+// IsMultilingual reports whether there are two or more configured languages.
+func (i HugoInfo) IsMultilingual() bool {
+	return i.conf.IsMultilingual()
+}
+
 // ConfigProvider represents the config options that are relevant for HugoInfo.
 type ConfigProvider interface {
 	Environment() string
 	Running() bool
 	WorkingDir() string
+	IsMultihost() bool
+	IsMultilingual() bool
 }
 
 // NewInfo creates a new Hugo Info object.
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index 9cafc876e..590f2ba60 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -826,7 +826,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
 	langConfigMap := make(map[string]*Config)
 
 	languagesConfig := cfg.GetStringMap("languages")
-	var isMultiHost bool
+	var isMultihost bool
 
 	if err := all.CompileConfig(logger); err != nil {
 		return nil, err
@@ -863,7 +863,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
 				}
 				if kk == "baseurl" {
 					// baseURL configure don the language level is a multihost setup.
-					isMultiHost = true
+					isMultihost = true
 				}
 				mergedConfig.Set(kk, vv)
 				rootv := cfg.Get(kk)
@@ -913,7 +913,7 @@ 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 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)
@@ -943,7 +943,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
 		Base:              all,
 		LanguageConfigMap: langConfigMap,
 		LoadingInfo:       res,
-		IsMultihost:       isMultiHost,
+		IsMultihost:       isMultihost,
 	}
 
 	return cm, nil
diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go
index 9971be65f..900db75ce 100644
--- a/config/allconfig/configlanguage.go
+++ b/config/allconfig/configlanguage.go
@@ -52,7 +52,7 @@ func (c ConfigLanguage) LanguagePrefix() string {
 		return c.Language().Lang
 	}
 
-	if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang {
+	if !c.IsMultilingual() || c.DefaultContentLanguage() == c.Language().Lang {
 		return ""
 	}
 	return c.Language().Lang
@@ -78,7 +78,7 @@ func (c ConfigLanguage) FastRenderMode() bool {
 	return c.config.Internal.FastRenderMode
 }
 
-func (c ConfigLanguage) IsMultiLingual() bool {
+func (c ConfigLanguage) IsMultilingual() bool {
 	return len(c.m.Languages) > 1
 }
 
diff --git a/config/configProvider.go b/config/configProvider.go
index 586a9b758..8f74202ab 100644
--- a/config/configProvider.go
+++ b/config/configProvider.go
@@ -35,7 +35,7 @@ type AllProvider interface {
 	PathParser() *paths.PathParser
 	Environment() string
 	IsMultihost() bool
-	IsMultiLingual() bool
+	IsMultilingual() bool
 	NoBuildLock() bool
 	BaseConfig() BaseConfig
 	Dirs() CommonDirs
diff --git a/docs/content/en/commands/hugo_server.md b/docs/content/en/commands/hugo_server.md
index 84e655954..cfc9f4aaf 100644
--- a/docs/content/en/commands/hugo_server.md
+++ b/docs/content/en/commands/hugo_server.md
@@ -12,9 +12,9 @@ A high performance webserver
 Hugo provides its own webserver which builds and serves the site.
 While hugo server is high performance, it is a webserver with limited options.
 
-'hugo server' will by default write and server files from disk, but you can
-render to memory by using the '--renderToMemory' flag. This can be faster
-in some cases, but it will consume more memory.
+The `hugo server` command will by default write and serve files from disk, but
+you can render to memory by using the `--renderToMemory` flag. This can be
+faster in some cases, but it will consume more memory.
 
 By default hugo will also watch your files for any changes you make and
 automatically rebuild the site. It will then live reload any open browser pages
diff --git a/go.mod b/go.mod
index 839838b2b..99864666a 100644
--- a/go.mod
+++ b/go.mod
@@ -72,7 +72,7 @@ require (
 	gocloud.dev v0.36.0
 	golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
 	golang.org/x/image v0.15.0
-	golang.org/x/mod v0.15.0
+	golang.org/x/mod v0.16.0
 	golang.org/x/net v0.21.0
 	golang.org/x/sync v0.6.0
 	golang.org/x/text v0.14.0
diff --git a/go.sum b/go.sum
index 33f0ededc..5a9468a2a 100644
--- a/go.sum
+++ b/go.sum
@@ -508,8 +508,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
-golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index ddc22bb66..906fced77 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -485,7 +485,7 @@ func (m *pageMap) forEachResourceInPage(
 	rw.Handle = func(resourceKey string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
 		if isBranch {
 			ownerKey, _ := m.treePages.LongestPrefixAll(resourceKey)
-			if ownerKey != keyPage {
+			if ownerKey != keyPage && path.Dir(ownerKey) != path.Dir(resourceKey) {
 				// Stop walking downwards, someone else owns this resource.
 				rw.SkipPrefix(ownerKey + "/")
 				return false, nil
@@ -1778,7 +1778,7 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
 
 	if sitemapEnabled {
 		addStandalone("/_sitemap", kinds.KindSitemap, output.SitemapFormat)
-		skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultiLingual())
+		skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultilingual())
 
 		if !skipSitemapIndex {
 			addStandalone("/_sitemapindex", kinds.KindSitemapIndex, output.SitemapIndexFormat)
@@ -1814,6 +1814,14 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
 				return false, nil
 			}
 
+			switch ps.Kind() {
+			case kinds.KindPage, kinds.KindSection:
+				// OK
+			default:
+				// Skip taxonomy nodes etc.
+				return false, nil
+			}
+
 			p := ps.m.pathInfo
 			section := p.Section()
 			if section == "" || seen[section] {
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 99dc88b10..671785f44 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -269,7 +269,7 @@ func (h *HugoSites) pickOneAndLogTheRest(errors []error) error {
 	return errors[i]
 }
 
-func (h *HugoSites) isMultiLingual() bool {
+func (h *HugoSites) isMultilingual() bool {
 	return len(h.Sites) > 1
 }
 
diff --git a/hugolib/page__new.go b/hugolib/page__new.go
index 9dd2f2cdf..e14de6925 100644
--- a/hugolib/page__new.go
+++ b/hugolib/page__new.go
@@ -145,9 +145,11 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
 					// Either a taxonomy or a term.
 					if tc.pluralTreeKey == m.Path() {
 						m.pageConfig.Kind = kinds.KindTaxonomy
+						m.singular = tc.singular
 					} else {
 						m.pageConfig.Kind = kinds.KindTerm
 						m.term = m.pathInfo.Unnormalized().BaseNameNoIdentifier()
+						m.singular = tc.singular
 					}
 				}
 			} else if m.f != nil {
diff --git a/hugolib/site.go b/hugolib/site.go
index 5a58e8373..117e10144 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -297,7 +297,6 @@ func (s *siteRefLinker) refLink(ref string, source any, relative bool, outputFor
 	ref = filepath.ToSlash(ref)
 
 	refURL, err = url.Parse(ref)
-
 	if err != nil {
 		return s.notFoundURL, err
 	}
@@ -681,7 +680,7 @@ func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string {
 		return ""
 	}
 
-	if s.h.Conf.IsMultiLingual() && alwaysInSubDir {
+	if s.h.Conf.IsMultilingual() && alwaysInSubDir {
 		return s.Language().Lang
 	}
 
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index 3bcc307ad..21d5ace96 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -361,8 +361,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
 	return h, nil
 }
 
-// Returns true if we're running in a server.
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (s *Site) IsServer() bool {
 	hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
 	return s.conf.Internal.Running
@@ -382,8 +381,9 @@ func (s *Site) Copyright() string {
 	return s.conf.Copyright
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s *Site) RSSLink() template.URL {
-	hugo.Deprecate("Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
+	hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
 	rssOutputFormat := s.home.OutputFormats().Get("rss")
 	return template.URL(rssOutputFormat.Permalink())
 }
@@ -431,9 +431,9 @@ func (s *Site) BaseURL() string {
 	return s.conf.C.BaseURL.WithPath
 }
 
-// Returns the last modification date of the content.
-// Deprecated: Use .Lastmod instead.
+// Deprecated: Use .Site.Lastmod instead.
 func (s *Site) LastChange() time.Time {
+	hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
 	return s.lastmod
 }
 
@@ -459,13 +459,13 @@ func (s *Site) Social() map[string]string {
 	return s.conf.Social
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (s *Site) DisqusShortname() string {
 	hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
 	return s.Config().Services.Disqus.Shortname
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (s *Site) GoogleAnalytics() string {
 	hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
 	return s.Config().Services.GoogleAnalytics.ID
@@ -484,8 +484,10 @@ func (s *Site) BuildDrafts() bool {
 	return s.conf.BuildDrafts
 }
 
+// Deprecated: Use hugo.IsMultilingual instead.
 func (s *Site) IsMultiLingual() bool {
-	return s.h.isMultiLingual()
+	hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
+	return s.h.isMultilingual()
 }
 
 func (s *Site) LanguagePrefix() string {
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index 1cf4b9a61..86be897fa 100644
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -334,7 +334,7 @@ func (s *Site) renderAliases() error {
 // renderMainLanguageRedirect creates a redirect to the main language home,
 // depending on if it lives in sub folder (e.g. /en) or not.
 func (s *Site) renderMainLanguageRedirect() error {
-	if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultiLingual()) {
+	if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultilingual()) {
 		// No need for a redirect
 		return nil
 	}
diff --git a/hugoreleaser.env b/hugoreleaser.env
index 1f74f3c19..335e5bd60 100644
--- a/hugoreleaser.env
+++ b/hugoreleaser.env
@@ -1,7 +1,8 @@
 # Release env. 
 # These will be replaced by script before release.
-HUGORELEASER_TAG=v0.123.7
-HUGORELEASER_COMMITISH=312735366b20d64bd61bff8627f593749f86c964
+HUGORELEASER_TAG=v0.123.8
+HUGORELEASER_COMMITISH=5fed9c591b694f314e5939548e11cc3dcb79a79c
+
 
 
 
diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go
index de06bedff..d835d17cf 100644
--- a/markup/goldmark/convert.go
+++ b/markup/goldmark/convert.go
@@ -18,14 +18,14 @@ import (
 	"bytes"
 
 	"github.com/gohugoio/hugo-goldmark-extensions/passthrough"
+	"github.com/yuin/goldmark/util"
+
 	"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
 	"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
 	"github.com/gohugoio/hugo/markup/goldmark/images"
 	"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
 	"github.com/gohugoio/hugo/markup/goldmark/internal/render"
 
-	"github.com/gohugoio/hugo/markup/converter"
-	"github.com/gohugoio/hugo/markup/tableofcontents"
 	"github.com/yuin/goldmark"
 	emoji "github.com/yuin/goldmark-emoji"
 	"github.com/yuin/goldmark/ast"
@@ -34,6 +34,9 @@ import (
 	"github.com/yuin/goldmark/renderer"
 	"github.com/yuin/goldmark/renderer/html"
 	"github.com/yuin/goldmark/text"
+
+	"github.com/gohugoio/hugo/markup/converter"
+	"github.com/gohugoio/hugo/markup/tableofcontents"
 )
 
 const (
@@ -91,10 +94,17 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
 		rendererOptions = append(rendererOptions, html.WithUnsafe())
 	}
 
+	tocRendererOptions := make([]renderer.Option, len(rendererOptions))
+	if rendererOptions != nil {
+		copy(tocRendererOptions, rendererOptions)
+	}
+	tocRendererOptions = append(tocRendererOptions,
+		renderer.WithNodeRenderers(util.Prioritized(extension.NewStrikethroughHTMLRenderer(), 500)),
+		renderer.WithNodeRenderers(util.Prioritized(emoji.NewHTMLRenderer(), 200)))
 	var (
 		extensions = []goldmark.Extender{
 			newLinks(cfg),
-			newTocExtension(rendererOptions),
+			newTocExtension(tocRendererOptions),
 		}
 		parserOptions []parser.Option
 	)
diff --git a/markup/goldmark/toc.go b/markup/goldmark/toc.go
index bc209adf2..b0f7e703f 100644
--- a/markup/goldmark/toc.go
+++ b/markup/goldmark/toc.go
@@ -16,6 +16,10 @@ package goldmark
 import (
 	"bytes"
 
+	strikethroughAst "github.com/yuin/goldmark/extension/ast"
+
+	emojiAst "github.com/yuin/goldmark-emoji/ast"
+
 	"github.com/gohugoio/hugo/markup/tableofcontents"
 
 	"github.com/yuin/goldmark"
@@ -86,7 +90,9 @@ func (t *tocTransformer) Transform(n *ast.Document, reader text.Reader, pc parse
 			ast.KindCodeSpan,
 			ast.KindLink,
 			ast.KindImage,
-			ast.KindEmphasis:
+			ast.KindEmphasis,
+			strikethroughAst.KindStrikethrough,
+			emojiAst.KindEmoji:
 			err := t.r.Render(&headingText, reader.Source(), n)
 			if err != nil {
 				return s, err
diff --git a/modules/config.go b/modules/config.go
index 62671613c..abb938ac0 100644
--- a/modules/config.go
+++ b/modules/config.go
@@ -86,7 +86,7 @@ func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
 
 		first := cfgs[0]
 		dirsBase := first.DirsBase()
-		isMultiHost := first.IsMultihost()
+		isMultihost := first.IsMultihost()
 
 		for i, cfg := range cfgs {
 			dirs := cfg.Dirs()
@@ -113,7 +113,7 @@ func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
 				dir = dirs.AssetDir
 			case files.ComponentFolderStatic:
 				// For static dirs, we only care about the language in multihost setups.
-				dropLang = !isMultiHost
+				dropLang = !isMultihost
 			}
 
 			var perLang bool
diff --git a/resources/images/text.go b/resources/images/text.go
index cc67a5d1d..2d3370c61 100644
--- a/resources/images/text.go
+++ b/resources/images/text.go
@@ -91,15 +91,19 @@ func (f textFilter) Draw(dst draw.Image, src image.Image, options *gift.Options)
 	y := f.y
 	d.Dot = fixed.P(f.x, f.y)
 
-	// Draw text and break line at max width
-	parts := strings.Fields(f.text)
-	for _, str := range parts {
-		strWith := font.MeasureString(face, str)
-		if (d.Dot.X.Ceil() + strWith.Ceil()) >= maxWidth {
-			y = y + fontHeight + f.linespacing
-			d.Dot = fixed.P(f.x, y)
+	// Draw text line by line, breaking each line at the maximum width.
+	f.text = strings.ReplaceAll(f.text, "\r", "")
+	for _, line := range strings.Split(f.text, "\n") {
+		for _, str := range strings.Fields(line) {
+			strWidth := font.MeasureString(face, str)
+			if (d.Dot.X.Ceil() + strWidth.Ceil()) >= maxWidth {
+				y = y + fontHeight + f.linespacing
+				d.Dot = fixed.P(f.x, y)
+			}
+			d.DrawString(str + " ")
 		}
-		d.DrawString(str + " ")
+		y = y + fontHeight + f.linespacing
+		d.Dot = fixed.P(f.x, y)
 	}
 }
 
diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go
index 8abd3807f..4e412c666 100644
--- a/resources/page/pagemeta/page_frontmatter.go
+++ b/resources/page/pagemeta/page_frontmatter.go
@@ -421,18 +421,22 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
 			return false, nil
 		}
 
-		date, err := htime.ToTimeInDefaultLocationE(v, d.Location)
-		if err != nil {
-			return false, nil
+		var date time.Time
+		if vt, ok := v.(time.Time); ok && vt.Location() == d.Location {
+			date = vt
+		} else {
+			var err error
+			date, err = htime.ToTimeInDefaultLocationE(v, d.Location)
+			if err != nil {
+				return false, nil
+			}
+			d.PageConfig.Params[key] = date
 		}
 
 		// We map several date keys to one, so, for example,
 		// "expirydate", "unpublishdate" will all set .ExpiryDate (first found).
 		setter(d, date)
 
-		// This is the params key as set in front matter.
-		d.PageConfig.Params[key] = date
-
 		return true, nil
 	}
 }
diff --git a/resources/page/site.go b/resources/page/site.go
index 132ab9fe8..8f268091f 100644
--- a/resources/page/site.go
+++ b/resources/page/site.go
@@ -54,8 +54,7 @@ type Site interface {
 	// A shortcut to the home
 	Home() Page
 
-	// Returns true if we're running in a server.
-	// Deprecated: use hugo.IsServer instead
+	// Deprecated: Use hugo.IsServer instead.
 	IsServer() bool
 
 	// Returns the server port.
@@ -64,7 +63,6 @@ type Site interface {
 	// Returns the configured title for this Site.
 	Title() string
 
-	// Returns the configured language code for this Site.
 	// Deprecated: Use .Language.LanguageCode instead.
 	LanguageCode() string
 
@@ -86,7 +84,6 @@ type Site interface {
 	// Returns a taxonomy map.
 	Taxonomies() TaxonomyList
 
-	// Returns the last modification date of the content.
 	// Deprecated: Use .Lastmod instead.
 	LastChange() time.Time
 
@@ -129,13 +126,13 @@ type Site interface {
 	// BuildDrafts is deprecated and will be removed in a future release.
 	BuildDrafts() bool
 
-	// IsMultiLingual reports whether this site is configured with more than one language.
+	// Deprecated: Use hugo.IsMultilingual instead.
 	IsMultiLingual() bool
 
 	// LanguagePrefix returns the language prefix for this site.
 	LanguagePrefix() string
 
-	// Deprecated. Use site.Home.OutputFormats.Get "rss" instead.
+	// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 	RSSLink() template.URL
 }
 
@@ -180,7 +177,7 @@ func (s *siteWrapper) Authors() AuthorList {
 	return AuthorList{}
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (s *siteWrapper) GoogleAnalytics() string {
 	return s.s.GoogleAnalytics()
 }
@@ -217,7 +214,7 @@ func (s *siteWrapper) Home() Page {
 	return s.s.Home()
 }
 
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (s *siteWrapper) IsServer() bool {
 	return s.s.IsServer()
 }
@@ -262,9 +259,9 @@ func (s *siteWrapper) Taxonomies() TaxonomyList {
 	return s.s.Taxonomies()
 }
 
+// Deprecated: Use .Site.Lastmod instead.
 func (s *siteWrapper) LastChange() time.Time {
-	hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
-	return s.s.Lastmod()
+	return s.s.LastChange()
 }
 
 func (s *siteWrapper) Lastmod() time.Time {
@@ -295,11 +292,12 @@ func (s *siteWrapper) BuildDrafts() bool {
 	return s.s.BuildDrafts()
 }
 
+// Deprecated: Use hugo.IsMultilingual instead.
 func (s *siteWrapper) IsMultiLingual() bool {
 	return s.s.IsMultiLingual()
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (s *siteWrapper) DisqusShortname() string {
 	return s.s.DisqusShortname()
 }
@@ -308,6 +306,7 @@ func (s *siteWrapper) LanguagePrefix() string {
 	return s.s.LanguagePrefix()
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s *siteWrapper) RSSLink() template.URL {
 	return s.s.RSSLink()
 }
@@ -342,6 +341,7 @@ func (t testSite) ServerPort() int {
 	return 1313
 }
 
+// Deprecated: Use .Site.Lastmod instead.
 func (testSite) LastChange() (t time.Time) {
 	return
 }
@@ -386,7 +386,7 @@ func (t testSite) Languages() langs.Languages {
 	return nil
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (t testSite) GoogleAnalytics() string {
 	return ""
 }
@@ -395,7 +395,7 @@ func (t testSite) MainSections() []string {
 	return nil
 }
 
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (t testSite) IsServer() bool {
 	return false
 }
@@ -444,7 +444,7 @@ func (s testSite) Config() SiteConfig {
 	return SiteConfig{}
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (testSite) DisqusShortname() string {
 	return ""
 }
@@ -453,6 +453,7 @@ func (s testSite) BuildDrafts() bool {
 	return false
 }
 
+// Deprecated: Use hugo.IsMultilingual instead.
 func (s testSite) IsMultiLingual() bool {
 	return false
 }
@@ -461,6 +462,7 @@ func (s testSite) Param(key any) (any, error) {
 	return nil, nil
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s testSite) RSSLink() template.URL {
 	return ""
 }
diff --git a/resources/resource/resources.go b/resources/resource/resources.go
index aa01d4ce2..32bcdbb08 100644
--- a/resources/resource/resources.go
+++ b/resources/resource/resources.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/gohugoio/hugo/common/paths"
 	"github.com/gohugoio/hugo/hugofs/glob"
 	"github.com/spf13/cast"
 )
@@ -61,13 +62,14 @@ func (r Resources) Get(name any) Resource {
 	if err != nil {
 		panic(err)
 	}
-	namestr = strings.ToLower(namestr)
+
+	namestr = paths.AddLeadingSlash(namestr)
 
 	// First check the Name.
 	// Note that this can be modified by the user in the front matter,
 	// also, it does not contain any language code.
 	for _, resource := range r {
-		if strings.EqualFold(namestr, resource.Name()) {
+		if strings.EqualFold(namestr, paths.AddLeadingSlash(resource.Name())) {
 			return resource
 		}
 	}
@@ -75,7 +77,7 @@ func (r Resources) Get(name any) Resource {
 	// Finally, check the normalized name.
 	for _, resource := range r {
 		if nop, ok := resource.(NameNormalizedProvider); ok {
-			if strings.EqualFold(namestr, nop.NameNormalized()) {
+			if strings.EqualFold(namestr, paths.AddLeadingSlash(nop.NameNormalized())) {
 				return resource
 			}
 		}
@@ -92,21 +94,21 @@ func (r Resources) GetMatch(pattern any) Resource {
 		panic(err)
 	}
 
-	g, err := glob.GetGlob(patternstr)
+	g, err := glob.GetGlob(paths.AddLeadingSlash(patternstr))
 	if err != nil {
 		panic(err)
 	}
 
 	for _, resource := range r {
-		if g.Match(resource.Name()) {
+		if g.Match(paths.AddLeadingSlash(resource.Name())) {
 			return resource
 		}
 	}
 
-	// Finally, check the original name.
+	// Finally, check the normalized name.
 	for _, resource := range r {
 		if nop, ok := resource.(NameNormalizedProvider); ok {
-			if g.Match(nop.NameNormalized()) {
+			if g.Match(paths.AddLeadingSlash(nop.NameNormalized())) {
 				return resource
 			}
 		}
@@ -130,14 +132,14 @@ func (r Resources) Match(pattern any) Resources {
 		panic(err)
 	}
 
-	g, err := glob.GetGlob(patternstr)
+	g, err := glob.GetGlob(paths.AddLeadingSlash(patternstr))
 	if err != nil {
 		panic(err)
 	}
 
 	var matches Resources
 	for _, resource := range r {
-		if g.Match(resource.Name()) {
+		if g.Match(paths.AddLeadingSlash(resource.Name())) {
 			matches = append(matches, resource)
 		}
 	}
@@ -145,7 +147,7 @@ func (r Resources) Match(pattern any) Resources {
 		// 	Fall back to the normalized name.
 		for _, resource := range r {
 			if nop, ok := resource.(NameNormalizedProvider); ok {
-				if g.Match(nop.NameNormalized()) {
+				if g.Match(paths.AddLeadingSlash(nop.NameNormalized())) {
 					matches = append(matches, resource)
 				}
 			}
diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go
index ac1a2af07..061ecf1e7 100644
--- a/resources/resource_factories/create/create.go
+++ b/resources/resource_factories/create/create.go
@@ -128,13 +128,16 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
 
 		handle := func(info hugofs.FileMetaInfo) (bool, error) {
 			meta := info.Meta()
+
 			r, err := c.rs.NewResource(resources.ResourceSourceDescriptor{
 				LazyPublish: true,
 				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
 					return meta.Open()
 				},
-				GroupIdentity: meta.PathInfo,
-				TargetPath:    meta.PathInfo.Unnormalized().Path(),
+				NameNormalized: meta.PathInfo.Path(),
+				NameOriginal:   meta.PathInfo.Unnormalized().Path(),
+				GroupIdentity:  meta.PathInfo,
+				TargetPath:     meta.PathInfo.Unnormalized().Path(),
 			})
 			if err != nil {
 				return true, err
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index cbf2cf0dd..e35e849cc 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,5 +1,5 @@
 name: hugo
-base: core20
+base: core22
 confinement: strict
 adopt-info: hugo
 title: Hugo
@@ -75,19 +75,19 @@ environment:
   npm_config_userconfig: $SNAP_USER_DATA/.npmrc
   pandoc_datadir: $SNAP/usr/share/pandoc
   PYTHONHOME: /usr:$SNAP/usr
-  RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/2.7.0
+  RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/3.0.0:$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/ruby/vendor_ruby/3.0.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/3.0.0:$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/ruby/3.0.0
+
   # HUGO_SECURITY_EXEC_OSENV
   #
   # Default value:
-  #   (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$
+  #   (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$
   # Bundled applications require additional access:
   #   git:          GIT_EXEC_PATH and LD_LIBRARY_PATH
   #   npx:          npm_config_{cache,init_module,userconfig}
   #   pandoc:       pandoc_datadir
   #   rst2html:     PYTHONHOME and SNAP
   #   asciidoctor:  RUBYLIB
-  HUGO_SECURITY_EXEC_OSENV: (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|SNAP|RUBYLIB)$
-
+  HUGO_SECURITY_EXEC_OSENV: (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|SNAP|RUBYLIB)$
 apps:
   hugo:
     command: bin/hugo
@@ -116,7 +116,7 @@ parts:
   go:
     plugin: nil
     stage-snaps:
-      - go/1.21/stable
+      - go/1.22/stable
     prime:
       - bin/go
       - pkg/tool
@@ -129,12 +129,12 @@ parts:
       - git
       - go
     override-pull: |
-      snapcraftctl pull
-      snapcraftctl set-version "$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')"
+      craftctl default
+      craftctl set version=$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')
       if grep -q 'Suffix:\s*""' common/hugo/version_current.go; then
-        snapcraftctl set-grade "stable"
+        craftctl set grade=stable
       else
-        snapcraftctl set-grade "devel"
+        craftctl set grade=devel
       fi
     override-build: |
       echo "\nStarting override-build:"
@@ -152,15 +152,15 @@ parts:
       echo " * Building shell completion..."
       ./hugo completion bash > hugo-completion
 
-      echo " * Installing to ${SNAPCRAFT_PART_INSTALL}..."
-      install -d $SNAPCRAFT_PART_INSTALL/bin
-      cp -av hugo $SNAPCRAFT_PART_INSTALL/bin/
-      mv -v hugo-completion $SNAPCRAFT_PART_INSTALL/
+      echo " * Installing to ${CRAFT_PART_INSTALL}..."
+      install -d $CRAFT_PART_INSTALL/bin
+      cp -av hugo $CRAFT_PART_INSTALL/bin/
+      mv -v hugo-completion $CRAFT_PART_INSTALL/
 
       echo " * Stripping binary..."
-      ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
-      strip --remove-section=.comment --remove-section=.note $SNAPCRAFT_PART_INSTALL/bin/hugo
-      ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
+      ls -l $CRAFT_PART_INSTALL/bin/hugo
+      strip --remove-section=.comment --remove-section=.note $CRAFT_PART_INSTALL/bin/hugo
+      ls -l $CRAFT_PART_INSTALL/bin/hugo
 
   asciidoctor:
     plugin: nil
@@ -168,8 +168,12 @@ parts:
       - asciidoctor
     override-build: |
       set -ex
-      snapcraftctl build
-      sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $SNAPCRAFT_PART_INSTALL/usr/bin/asciidoctor
+      craftctl default
+      sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $CRAFT_PART_INSTALL/usr/bin/asciidoctor
+      # don't try and flock() gemspecs since this is blocked by AppArmor - see
+      # https://github.com/rubygems/rubygems/pull/5278 in particular
+      # https://github.com/rubygems/rubygems/pull/5278/commits/27b682c81226838b1254ac5843a3f5b1cb20f076
+      sed -i 's|!solaris_platform|win_platform|' $CRAFT_PART_INSTALL/usr/lib/ruby/vendor_ruby/rubygems.rb
 
   dart-sass:
     plugin: nil
@@ -177,8 +181,8 @@ parts:
       - curl
     override-build: |
       set -ex
-      snapcraftctl build
-      case "$SNAPCRAFT_TARGET_ARCH" in
+      craftctl default
+      case "$CRAFT_TARGET_ARCH" in
         amd64)  arch=x64    ;;
         arm64)  arch=arm64  ;;
         armhf)  arch=arm    ;;
@@ -189,8 +193,8 @@ parts:
         url=$(curl -s https://api.github.com/repos/sass/dart-sass/releases/latest | awk -F\" "/browser_download_url.*-linux-${arch}.tar.gz/{print \$(NF-1)}")
         curl -LO --retry-connrefused --retry 10 "${url}"
         tar xf dart-sass-*-linux-${arch}.tar.gz
-        install -d $SNAPCRAFT_PART_INSTALL/bin
-        cp -av dart-sass/* $SNAPCRAFT_PART_INSTALL/bin/
+        install -d $CRAFT_PART_INSTALL/bin
+        cp -av dart-sass/* $CRAFT_PART_INSTALL/bin/
       fi
 
   node:
@@ -209,7 +213,7 @@ parts:
       - python3-docutils
     override-build: |
       set -ex
-      snapcraftctl build
-      sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $SNAPCRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
+      craftctl default
+      sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
     organize:
       usr/share/docutils/scripts/python3: usr/bin
diff --git a/source/fileInfo.go b/source/fileInfo.go
index 1d2226994..44d08e620 100644
--- a/source/fileInfo.go
+++ b/source/fileInfo.go
@@ -61,7 +61,7 @@ func (fi *File) Extension() string {
 func (fi *File) Ext() string { return fi.p().Ext() }
 
 // Lang returns a file's language (e.g. "sv").
-// Deprecated: use .Page.Language.Lang instead.
+// Deprecated: Use .Page.Language.Lang instead.
 func (fi *File) Lang() string {
 	hugo.Deprecate(".Page.File.Lang", "Use .Page.Language.Lang instead.", "v0.123.0")
 	return fi.fim.Meta().Lang
diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
index c90ababd2..fa78cd00c 100644
--- a/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
+++ b/tpl/tplimpl/embedded/templates/_default/_markup/render-image.html
@@ -5,7 +5,7 @@
     {{- $src = .RelPermalink -}}
   {{- end -}}
 {{- end -}}
-{{- $attributes := dict "alt" .Text "src" $src "title" .Title -}}
+{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" .Title) -}}
 <img
   {{- range $k, $v := $attributes -}}
     {{- if $v -}}


Test patch:
diff --git a/common/hugo/hugo_integration_test.go b/common/hugo/hugo_integration_test.go
new file mode 100644
index 000000000..77dbb5c91
--- /dev/null
+++ b/common/hugo/hugo_integration_test.go
@@ -0,0 +1,77 @@
+// Copyright 2024 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugo_test
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/gohugoio/hugo/hugolib"
+)
+
+func TestIsMultilingualAndIsMultihost(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+defaultContentLanguageInSubdir = true
+[languages.de]
+baseURL = 'https://de.example.org/'
+[languages.en]
+baseURL = 'https://en.example.org/'
+-- content/_index.md --
+---
+title: home
+---
+-- layouts/index.html --
+multilingual={{ hugo.IsMultilingual }}
+multihost={{ hugo.IsMultihost }}
+  `
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/de/index.html",
+		"multilingual=true",
+		"multihost=true",
+	)
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=true",
+		"multihost=true",
+	)
+
+	files = strings.ReplaceAll(files, "baseURL = 'https://de.example.org/'", "")
+	files = strings.ReplaceAll(files, "baseURL = 'https://en.example.org/'", "")
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/de/index.html",
+		"multilingual=true",
+		"multihost=false",
+	)
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=true",
+		"multihost=false",
+	)
+
+	files = strings.ReplaceAll(files, "[languages.de]", "")
+	files = strings.ReplaceAll(files, "[languages.en]", "")
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=false",
+		"multihost=false",
+	)
+}
diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go
index e252dffbe..6e8b2620e 100644
--- a/common/hugo/hugo_test.go
+++ b/common/hugo/hugo_test.go
@@ -65,9 +65,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
 }
 
 type testConfig struct {
-	environment string
-	running     bool
-	workingDir  string
+	environment  string
+	running      bool
+	workingDir   string
+	multihost    bool
+	multilingual bool
 }
 
 func (c testConfig) Environment() string {
@@ -81,3 +83,11 @@ func (c testConfig) Running() bool {
 func (c testConfig) WorkingDir() string {
 	return c.workingDir
 }
+
+func (c testConfig) IsMultihost() bool {
+	return c.multihost
+}
+
+func (c testConfig) IsMultilingual() bool {
+	return c.multilingual
+}
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index 7914dda45..22beea655 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -508,12 +508,12 @@ func TestLoadConfigFromThemeDir(t *testing.T) {
 theme = "test-theme"
 
 [params]
-m1 = "mv1"	
+m1 = "mv1"
 `
 
 	themeConfig := `
 [params]
-t1 = "tv1"	
+t1 = "tv1"
 t2 = "tv2"
 `
 
@@ -885,9 +885,9 @@ ThisIsAParam: {{ site.Params.thisIsAParam }}
 	).BuildE()
 
 	b.Assert(err, qt.IsNil)
-	b.AssertFileContent("public/index.html", `		
+	b.AssertFileContent("public/index.html", `
 MyParam: enParamValue
-ThisIsAParam: thisIsAParamValue	
+ThisIsAParam: thisIsAParamValue
 `)
 }
 
@@ -919,7 +919,7 @@ title: "My Swedish Section"
 -- layouts/index.html --
 LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Language.LanguageCode }}|
 {{ range $i, $e := (slice site .Site) }}
-{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultiLingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
+{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultilingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
 {{ end }}
 
 
@@ -939,9 +939,9 @@ LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Lang
 	b.AssertFileContent("public/index.html", `
 AllPages: 4|
 Sections: true|
-Param: enParamValue	
-Param: enParamValue	
-IsMultiLingual: true
+Param: enParamValue
+Param: enParamValue
+IsMultilingual: true
 LanguageCode: true|en-US|
 `)
 
@@ -1062,7 +1062,7 @@ Home
 	).BuildE()
 
 	b.Assert(err, qt.IsNil)
-	b.AssertFileContent("public/index.html", `		
+	b.AssertFileContent("public/index.html", `
 Home
 `)
 
@@ -1095,7 +1095,7 @@ HTML.
 HTACCESS.
 
 
-	
+
 `
 	b := Test(t, files)
 
@@ -1111,7 +1111,7 @@ languageCode = "en-US"
 -- layouts/index.html --
 LanguageCode: {{ .Site.LanguageCode }}|{{ site.Language.LanguageCode }}|
 
-	
+
 `
 	b := Test(t, files)
 
@@ -1137,7 +1137,7 @@ suffixes = ["bar"]
 -- layouts/index.html --
 Home.
 
-	
+
 `
 	b := Test(t, files)
 
@@ -1164,8 +1164,8 @@ func TestConfigMiscPanics(t *testing.T) {
 params:
 -- layouts/index.html --
 Foo: {{ site.Params.foo }}|
-	
-		
+
+
 	`
 		b := Test(t, files)
 
@@ -1188,8 +1188,8 @@ defaultContentLanguage = "en"
 	weight = 1
 -- layouts/index.html --
 Foo: {{ site.Params.foo }}|
-	
-		
+
+
 	`
 		b, err := NewIntegrationTestBuilder(
 			IntegrationTestConfig{
@@ -1215,8 +1215,8 @@ languageCode = "en"
 languageName = "English"
 weight = 1
 
-	
-		
+
+
 	`
 		b, err := NewIntegrationTestBuilder(
 			IntegrationTestConfig{
@@ -1241,7 +1241,7 @@ contentDir = "mycontent"
 -- layouts/index.html --
 Home.
 
-	
+
 `
 	b := Test(t, files)
 
@@ -1343,7 +1343,7 @@ disabled = true
 -- layouts/index.html --
 Home.
 
-	
+
 `
 	b := Test(t, files)
 
@@ -1438,7 +1438,7 @@ home = ["html"]
 -- hugo.toml --
 baseURL = "https://example.com"
 disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "section"]
-		
+
 		`
 
 		runVariant(t, files, nil)
diff --git a/hugolib/content_map_test.go b/hugolib/content_map_test.go
index e043d9363..85e2c6056 100644
--- a/hugolib/content_map_test.go
+++ b/hugolib/content_map_test.go
@@ -326,3 +326,35 @@ R: {{ with $r }}{{ .Content }}{{ end }}|Len: {{ len $bundle.Resources }}|$
 		b.AssertFileContent("public/index.html", "R: Data 1.txt|", "Len: 1|")
 	}
 }
+
+func TestBundleResourcesNoPublishedIssue12198(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['home','rss','sitemap','taxonomy','term']
+-- content/s1/p1.md --
+---
+title: p1
+---
+-- content/s1/foo.txt --
+foo.txt
+-- content/s1/p1.txt --
+p1.txt
+-- content/s1/p1-foo.txt --
+p1-foo.txt
+-- layouts/_default/list.html --
+{{.Title }}|
+-- layouts/_default/single.html --
+{{.Title }}|
+	`
+
+	b := Test(t, files)
+	b.Build()
+
+	b.AssertFileExists("public/s1/index.html", true)
+	b.AssertFileExists("public/s1/foo.txt", true)
+	b.AssertFileExists("public/s1/p1.txt", true)     // failing test
+	b.AssertFileExists("public/s1/p1-foo.txt", true) // failing test
+	b.AssertFileExists("public/s1/p1/index.html", true)
+}
diff --git a/hugolib/filesystems/basefs_test.go b/hugolib/filesystems/basefs_test.go
index 10b4a4cb2..e39709386 100644
--- a/hugolib/filesystems/basefs_test.go
+++ b/hugolib/filesystems/basefs_test.go
@@ -315,7 +315,7 @@ func TestStaticFs(t *testing.T) {
 	checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
 }
 
-func TestStaticFsMultiHost(t *testing.T) {
+func TestStaticFsMultihost(t *testing.T) {
 	c := qt.New(t)
 	v := config.New()
 	workDir := "mywork"
@@ -537,7 +537,7 @@ SCSS Match: {{ with resources.Match "**.scss" }}{{ . | len }}|{{ range .}}{{ .Re
 
 	b := hugolib.Test(t, files)
 
-	b.AssertFileContent("public/index.html", `	
+	b.AssertFileContent("public/index.html", `
 SCSS: /scss/app.scss|body { color: blue; }|
 SCSS Match: 2|
 `)
diff --git a/hugolib/hugo_sites_multihost_test.go b/hugolib/hugo_sites_multihost_test.go
index 4b779ee92..937166146 100644
--- a/hugolib/hugo_sites_multihost_test.go
+++ b/hugolib/hugo_sites_multihost_test.go
@@ -66,12 +66,12 @@ robots|{{ site.Language.Lang }}
 404|{{ site.Language.Lang }}
 
 
-	
+
 `
 
 	b := Test(t, files)
 
-	b.Assert(b.H.Conf.IsMultiLingual(), qt.Equals, true)
+	b.Assert(b.H.Conf.IsMultilingual(), qt.Equals, true)
 	b.Assert(b.H.Conf.IsMultihost(), qt.Equals, true)
 
 	// helpers.PrintFs(b.H.Fs.PublishDir, "", os.Stdout)
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 9a904ee46..b6236ca5f 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -578,7 +578,7 @@ date: 2012-01-12
 	b.Assert(s.getPageOldVersion("/with-index-no-date").Date().IsZero(), qt.Equals, true)
 	checkDate(s.getPageOldVersion("/with-index-date"), 2018)
 
-	b.Assert(s.Site().LastChange().Year(), qt.Equals, 2018)
+	b.Assert(s.Site().Lastmod().Year(), qt.Equals, 2018)
 }
 
 func TestCreateNewPage(t *testing.T) {
@@ -773,7 +773,7 @@ func TestSummaryManualSplit(t *testing.T) {
 title: Simple
 ---
 This is **summary**.
-<!--more--> 
+<!--more-->
 This is **content**.
 -- layouts/_default/single.html --
 Summary: {{ .Summary }}|Truncated: {{ .Truncated }}|
diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go
index 5a48df3b7..bfdfd8dfd 100644
--- a/hugolib/taxonomy_test.go
+++ b/hugolib/taxonomy_test.go
@@ -911,3 +911,62 @@ title: tag-a-title-override
 
 	b.AssertFileContent("public/tags/a/index.html", "Tag: tag-a-title-override|")
 }
+
+func TestTaxonomyLookupIssue12193(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap']
+[taxonomies]
+author = 'authors'
+-- layouts/_default/list.html --
+{{ .Title }}|
+-- layouts/_default/author.terms.html --
+layouts/_default/author.terms.html
+-- content/authors/_index.md --
+---
+title: Authors Page
+---
+`
+
+	b := Test(t, files)
+
+	b.AssertFileExists("public/index.html", true)
+	b.AssertFileExists("public/authors/index.html", true)
+	b.AssertFileContent("public/authors/index.html", "layouts/_default/author.terms.html") // failing test
+}
+
+func TestTaxonomyNestedEmptySectionsIssue12188(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['rss','sitemap']
+defaultContentLanguage = 'en'
+defaultContentLanguageInSubdir = true
+[languages.en]
+weight = 1
+[languages.ja]
+weight = 2
+[taxonomies]
+'s1/category' = 's1/category'
+-- layouts/_default/single.html --
+{{ .Title }}|
+-- layouts/_default/list.html --
+{{ .Title }}|
+-- content/s1/p1.en.md --
+---
+title: p1
+---
+`
+
+	b := Test(t, files)
+
+	b.AssertFileExists("public/en/s1/index.html", true)
+	b.AssertFileExists("public/en/s1/p1/index.html", true)
+	b.AssertFileExists("public/en/s1/category/index.html", true)
+
+	b.AssertFileExists("public/ja/s1/index.html", false) // failing test
+	b.AssertFileExists("public/ja/s1/category/index.html", true)
+}
diff --git a/markup/goldmark/toc_integration_test.go b/markup/goldmark/toc_integration_test.go
index f58f91f1c..3b48dac6c 100644
--- a/markup/goldmark/toc_integration_test.go
+++ b/markup/goldmark/toc_integration_test.go
@@ -253,14 +253,12 @@ title: p7 (emoji)
 `)
 
 	// strikethrough
-	// TODO failing test: Issue #8087
-	// 	b.AssertFileContent("public/p6/index.html", `
-	// <li><a href="#ome-deleted-text">Some <del>deleted</del> text</a></li>
-	// 	`)
+	b.AssertFileContent("public/p6/index.html", `
+<li><a href="#some-deleted-text">Some <del>deleted</del> text</a></li>
+`)
 
 	// emoji
-	// TODO failing test: Issue #12022
-	// 	b.AssertFileContent("public/p7/index.html", `
-	// <li><a href="#a-snake-emoji">A &#x1f40d; emoji</a></li>
-	// 		`)
+	b.AssertFileContent("public/p7/index.html", `
+<li><a href="#a-snake-emoji">A &#x1f40d; emoji</a></li>
+`)
 }
diff --git a/resources/page/page_matcher_test.go b/resources/page/page_matcher_test.go
index e659eb3b5..dfb479d5e 100644
--- a/resources/page/page_matcher_test.go
+++ b/resources/page/page_matcher_test.go
@@ -157,9 +157,11 @@ func TestDecodeCascadeConfig(t *testing.T) {
 }
 
 type testConfig struct {
-	environment string
-	running     bool
-	workingDir  string
+	environment  string
+	running      bool
+	workingDir   string
+	multihost    bool
+	multilingual bool
 }
 
 func (c testConfig) Environment() string {
@@ -174,6 +176,14 @@ func (c testConfig) WorkingDir() string {
 	return c.workingDir
 }
 
+func (c testConfig) IsMultihost() bool {
+	return c.multihost
+}
+
+func (c testConfig) IsMultilingual() bool {
+	return c.multilingual
+}
+
 func TestIsGlobWithExtension(t *testing.T) {
 	c := qt.New(t)
 
diff --git a/resources/page/pagemeta/pagemeta_integration_test.go b/resources/page/pagemeta/pagemeta_integration_test.go
new file mode 100644
index 000000000..4d195b7f0
--- /dev/null
+++ b/resources/page/pagemeta/pagemeta_integration_test.go
@@ -0,0 +1,45 @@
+// Copyright 2024 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package pagemeta_test
+
+import (
+	"testing"
+
+	"github.com/gohugoio/hugo/hugolib"
+)
+
+func TestLastModEq(t *testing.T) {
+	files := `
+-- hugo.toml --
+timeZone = "Europe/London"
+-- content/p1.md --
+---
+title: p1
+date: 2024-03-13T06:00:00
+---
+-- layouts/_default/single.html --
+Date: {{ .Date }}
+Lastmod: {{ .Lastmod }}
+Eq: {{ eq .Date .Lastmod }}
+
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/p1/index.html", `
+Date: 2024-03-13 06:00:00 &#43;0000 GMT
+Lastmod: 2024-03-13 06:00:00 &#43;0000 GMT
+Eq: true
+`)
+}
diff --git a/tpl/resources/resources_integration_test.go b/tpl/resources/resources_integration_test.go
index d72b13507..6bc872bca 100644
--- a/tpl/resources/resources_integration_test.go
+++ b/tpl/resources/resources_integration_test.go
@@ -143,3 +143,98 @@ ByType: /401K%20Prospectus.txt|http://example.com/401K%20Prospectus.txt|
 
 		`)
 }
+
+func TestGlobalResourcesNotPublishedRegressionIssue12190(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- assets/a.txt --
+I am a.txt
+-- assets/b.txt --
+I am b.txt
+-- layouts/index.html --
+Home.
+{{ with resources.ByType "text" }}
+  {{ with .Get "a.txt" }}
+    {{ .Publish }}
+  {{ end }}
+  {{ with .GetMatch "*b*" }}
+    {{ .Publish }}
+  {{ end }}
+{{ end }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileExists("public/a.txt", true) // failing test
+	b.AssertFileExists("public/b.txt", true) // failing test
+}
+
+func TestGlobalResourcesNotPublishedRegressionIssue12214(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- assets/files/a.txt --
+I am a.txt
+-- assets/files/b.txt --
+I am b.txt
+-- assets/files/c.txt --
+I am c.txt
+-- assets/files/C.txt --
+I am C.txt
+-- layouts/index.html --
+Home.
+{{ with resources.ByType "text" }}
+  {{ with .Get "files/a.txt" }}
+    {{ .Publish }}
+	files/a.txt: {{ .Name }}
+  {{ end }}
+  {{ with .Get "/files/a.txt" }}
+	/files/a.txt: {{ .Name }}
+  {{ end }}
+  {{ with .GetMatch "files/*b*" }}
+    {{ .Publish }}
+	files/*b*: {{ .Name }}
+  {{ end }}
+  {{ with .GetMatch "files/C*" }}
+    {{ .Publish }}
+    files/C*: {{ .Name }}
+  {{ end }}
+  {{ with .GetMatch "files/c*" }}
+	{{ .Publish }}
+	files/c*: {{ .Name }}
+  {{ end }}
+  {{ with .GetMatch "/files/c*" }}
+    /files/c*: {{ .Name }}
+  {{ end }}
+  {{ with .Match "files/C*" }}
+	match files/C*: {{ len . }}|
+  {{ end }}
+  {{ with .Match "/files/C*" }}
+  match /files/C*: {{ len . }}|
+{{ end }}
+{{ end }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html", `
+files/a.txt: /files/a.txt
+# There are both C.txt and c.txt in the assets, but the Glob matching is case insensitive, so GetMatch returns the first.
+files/C*: /files/C.txt
+files/c*: /files/C.txt
+files/*b*: /files/b.txt
+/files/c*: /files/C.txt
+/files/a.txt: /files/a.txt
+match files/C*: 2|
+match /files/C*: 2|
+	`)
+
+	b.AssertFileContent("public/files/a.txt", "I am a.txt")
+	b.AssertFileContent("public/files/b.txt", "I am b.txt")
+	b.AssertFileContent("public/files/C.txt", "I am C.txt")
+}
diff --git a/tpl/tplimpl/render_hook_integration_test.go b/tpl/tplimpl/render_hook_integration_test.go
index 607e06059..07c24108d 100644
--- a/tpl/tplimpl/render_hook_integration_test.go
+++ b/tpl/tplimpl/render_hook_integration_test.go
@@ -14,6 +14,7 @@
 package tplimpl_test
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/gohugoio/hugo/hugolib"
@@ -127,3 +128,32 @@ title: s1/p3
 		`<a href="/s1/p2/b.txt">600</a>`,
 	)
 }
+
+// Issue 12203
+func TestEmbeddedImageRenderHookMarkdownAttributes(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[markup.goldmark.parser]
+wrapStandAloneImageWithinParagraph = false
+[markup.goldmark.parser.attribute]
+block = false
+[markup.goldmark.renderHooks.image]
+enableDefault = true
+-- content/_index.md --
+![alt](a.jpg)
+{.foo #bar}
+-- layouts/index.html --
+{{ .Content }}
+`
+
+	b := hugolib.Test(t, files)
+	b.AssertFileContent("public/index.html", `<img alt="alt" src="a.jpg">`)
+
+	files = strings.Replace(files, "block = false", "block = true", -1)
+
+	b = hugolib.Test(t, files)
+	b.AssertFileContent("public/index.html", `<img alt="alt" class="foo" id="bar" src="a.jpg">`)
+}