Repository: gohugoio/hugo
Pull request #13188 (2024-12-23 08:48:22)

build(deps): bump github.com/evanw/esbuild from 0.24.0 to 0.24.2
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.24.0 to 0.24.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/evanw/esbuild/releases">github.com/evanw/esbuild's releases</a>.</em></p>
<blockquote>
<h2>v0.24.2</h2>
<ul>
<li>
<p>Fix regression with <code>--define</code> and <code>import.meta</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4010">#4010</a>, <a href="https://redirect.github.com/evanw/esbuild/issues/4012">#4012</a>, <a href="https://redirect.github.com/evanw/esbuild/pull/4013">#4013</a>)</p>
<p>The previous change in version 0.24.1 to use a more expression-like parser for <code>define</code> values to allow quoted property names introduced a regression that removed the ability to use <code>--define:import.meta=...</code>. Even though <code>import</code> is normally a keyword that can't be used as an identifier, ES modules special-case the <code>import.meta</code> expression to behave like an identifier anyway. This change fixes the regression.</p>
<p>This fix was contributed by <a href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a>.</p>
</li>
</ul>
<h2>v0.24.1</h2>
<ul>
<li>
<p>Allow <code>es2024</code> as a target in <code>tsconfig.json</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4004">#4004</a>)</p>
<p>TypeScript recently <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/#support-for---target-es2024-and---lib-es2024">added <code>es2024</code></a> as a compilation target, so esbuild now supports this in the <code>target</code> field of <code>tsconfig.json</code> files, such as in the following configuration file:</p>
<pre lang="json"><code>{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2024&quot;
  }
}
</code></pre>
<p>As a reminder, the only thing that esbuild uses this field for is determining whether or not to use legacy TypeScript behavior for class fields. You can read more in <a href="https://esbuild.github.io/content-types/#tsconfig-json">the documentation</a>.</p>
<p>This fix was contributed by <a href="https://github.com/billyjanitsch"><code>@​billyjanitsch</code></a>.</p>
</li>
<li>
<p>Allow automatic semicolon insertion after <code>get</code>/<code>set</code></p>
<p>This change fixes a grammar bug in the parser that incorrectly treated the following code as a syntax error:</p>
<pre lang="ts"><code>class Foo {
  get
  *x() {}
  set
  *y() {}
}
</code></pre>
<p>The above code will be considered valid starting with this release. This change to esbuild follows a <a href="https://redirect.github.com/microsoft/TypeScript/pull/60225">similar change to TypeScript</a> which will allow this syntax starting with TypeScript 5.7.</p>
</li>
<li>
<p>Allow quoted property names in <code>--define</code> and <code>--pure</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4008">#4008</a>)</p>
<p>The <code>define</code> and <code>pure</code> API options now accept identifier expressions containing quoted property names. Previously all identifiers in the identifier expression had to be bare identifiers. This change now makes <code>--define</code> and <code>--pure</code> consistent with <code>--global-name</code>, which already supported quoted property names. For example, the following is now possible:</p>
<pre lang="js"><code>// The following code now transforms to &quot;return true;\n&quot;
console.log(esbuild.transformSync(
  `return process.env['SOME-TEST-VAR']`,
  { define: { 'process.env[&quot;SOME-TEST-VAR&quot;]': 'true' } },
))
</code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/evanw/esbuild/blob/main/CHANGELOG.md">github.com/evanw/esbuild's changelog</a>.</em></p>
<blockquote>
<h2>0.24.2</h2>
<ul>
<li>
<p>Fix regression with <code>--define</code> and <code>import.meta</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4010">#4010</a>, <a href="https://redirect.github.com/evanw/esbuild/issues/4012">#4012</a>, <a href="https://redirect.github.com/evanw/esbuild/pull/4013">#4013</a>)</p>
<p>The previous change in version 0.24.1 to use a more expression-like parser for <code>define</code> values to allow quoted property names introduced a regression that removed the ability to use <code>--define:import.meta=...</code>. Even though <code>import</code> is normally a keyword that can't be used as an identifier, ES modules special-case the <code>import.meta</code> expression to behave like an identifier anyway. This change fixes the regression.</p>
<p>This fix was contributed by <a href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a>.</p>
</li>
</ul>
<h2>0.24.1</h2>
<ul>
<li>
<p>Allow <code>es2024</code> as a target in <code>tsconfig.json</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4004">#4004</a>)</p>
<p>TypeScript recently <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/#support-for---target-es2024-and---lib-es2024">added <code>es2024</code></a> as a compilation target, so esbuild now supports this in the <code>target</code> field of <code>tsconfig.json</code> files, such as in the following configuration file:</p>
<pre lang="json"><code>{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2024&quot;
  }
}
</code></pre>
<p>As a reminder, the only thing that esbuild uses this field for is determining whether or not to use legacy TypeScript behavior for class fields. You can read more in <a href="https://esbuild.github.io/content-types/#tsconfig-json">the documentation</a>.</p>
<p>This fix was contributed by <a href="https://github.com/billyjanitsch"><code>@​billyjanitsch</code></a>.</p>
</li>
<li>
<p>Allow automatic semicolon insertion after <code>get</code>/<code>set</code></p>
<p>This change fixes a grammar bug in the parser that incorrectly treated the following code as a syntax error:</p>
<pre lang="ts"><code>class Foo {
  get
  *x() {}
  set
  *y() {}
}
</code></pre>
<p>The above code will be considered valid starting with this release. This change to esbuild follows a <a href="https://redirect.github.com/microsoft/TypeScript/pull/60225">similar change to TypeScript</a> which will allow this syntax starting with TypeScript 5.7.</p>
</li>
<li>
<p>Allow quoted property names in <code>--define</code> and <code>--pure</code> (<a href="https://redirect.github.com/evanw/esbuild/issues/4008">#4008</a>)</p>
<p>The <code>define</code> and <code>pure</code> API options now accept identifier expressions containing quoted property names. Previously all identifiers in the identifier expression had to be bare identifiers. This change now makes <code>--define</code> and <code>--pure</code> consistent with <code>--global-name</code>, which already supported quoted property names. For example, the following is now possible:</p>
<pre lang="js"><code>// The following code now transforms to &quot;return true;\n&quot;
console.log(esbuild.transformSync(
  `return process.env['SOME-TEST-VAR']`,
  { define: { 'process.env[&quot;SOME-TEST-VAR&quot;]': 'true' } },
</code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/evanw/esbuild/commit/745abd9f0c06f73ca40fbe198546a9bc36c23b81"><code>745abd9</code></a> publish 0.24.2 to npm</li>
<li><a href="https://github.com/evanw/esbuild/commit/79fd0b0ccc911a8e1571b83f25deec1b18d0ed10"><code>79fd0b0</code></a> skip nulls in source map finalization (<a href="https://redirect.github.com/evanw/esbuild/issues/4011">#4011</a>)</li>
<li><a href="https://github.com/evanw/esbuild/commit/4b9322f723ce72b4d5fee6dc48d2a5e2c2e2d3bb"><code>4b9322f</code></a> source map: avoid null entry for 0-length parts</li>
<li><a href="https://github.com/evanw/esbuild/commit/199a0d38e4e4191e970f2a0a25e50e5c7ae36464"><code>199a0d3</code></a> close <a href="https://redirect.github.com/evanw/esbuild/issues/4013">#4013</a>: credit to <a href="https://github.com/sapphi-red"><code>@​sapphi-red</code></a> for the fix</li>
<li><a href="https://github.com/evanw/esbuild/commit/947f99fb085024ff711055d776b3982a75383d51"><code>947f99f</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/4010">#4010</a>, fix <a href="https://redirect.github.com/evanw/esbuild/issues/4012">#4012</a>: <code>import.meta</code> regression</li>
<li><a href="https://github.com/evanw/esbuild/commit/de9598f42dc3ffc395e3fd3672a4804f6b4e5c09"><code>de9598f</code></a> publish 0.24.1 to npm</li>
<li><a href="https://github.com/evanw/esbuild/commit/15d56ca7d2196839c1d13a15fc214d6e81169e30"><code>15d56ca</code></a> emit null source mappings for empty chunk content</li>
<li><a href="https://github.com/evanw/esbuild/commit/8d98f6f6e663f7ecc9f0496edbd8bb8314b0333a"><code>8d98f6f</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/3985">#3985</a>: <code>entryPoint</code> metadata for <code>copy</code> loader</li>
<li><a href="https://github.com/evanw/esbuild/commit/0db1b828bf69fa353f17e65837f2114d94b9e2c3"><code>0db1b82</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/3998">#3998</a>: avoid <code>outbase</code> in identifier names</li>
<li><a href="https://github.com/evanw/esbuild/commit/723647263fcd0e4095e25a569e1d0a38f382768a"><code>7236472</code></a> close <a href="https://redirect.github.com/evanw/esbuild/issues/3974">#3974</a>: add support for netbsd on arm64</li>
<li>Additional commits viewable in <a href="https://github.com/evanw/esbuild/compare/v0.24.0...v0.24.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/evanw/esbuild&package-manager=go_modules&previous-version=0.24.0&new-version=0.24.2)](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/README.md b/README.md
index 7dfbca7c1..913130b5d 100644
--- a/README.md
+++ b/README.md
@@ -66,8 +66,6 @@ See the [features] section of the documentation for a comprehensive summary of H
 <p float="left">
   <a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
 &nbsp;&nbsp;&nbsp;
-  <a href="https://route4me.com/" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg" width="200" alt="Route Planning & Route Optimization Software"></a>
-  &nbsp;&nbsp;&nbsp;
   <a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
 </p>
 
@@ -103,6 +101,7 @@ Build the extended edition:
 ```text
 CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
 ```
+
 ## Star History
 
 [![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline)
@@ -156,7 +155,7 @@ github.com/bep/clocks="v0.5.0"
 github.com/bep/debounce="v1.2.0"
 github.com/bep/gitmap="v1.6.0"
 github.com/bep/goat="v0.5.0"
-github.com/bep/godartsass/v2="v2.3.0"
+github.com/bep/godartsass/v2="v2.3.2"
 github.com/bep/golibsass="v1.2.0"
 github.com/bep/gowebp="v0.3.0"
 github.com/bep/imagemeta="v0.8.3"
@@ -191,7 +190,7 @@ github.com/gohugoio/locales="v0.14.0"
 github.com/gohugoio/localescompressed="v1.0.1"
 github.com/google/go-cmp="v0.6.0"
 github.com/gorilla/websocket="v1.5.3"
-github.com/hairyhenderson/go-codeowners="v0.6.1"
+github.com/hairyhenderson/go-codeowners="v0.7.0"
 github.com/hashicorp/golang-lru/v2="v2.0.7"
 github.com/invopop/yaml="v0.2.0"
 github.com/jdkato/prose="v1.2.1"
@@ -220,30 +219,32 @@ github.com/russross/blackfriday/v2="v2.1.0"
 github.com/sass/dart-sass/compiler="1.81.0"
 github.com/sass/dart-sass/implementation="1.81.0"
 github.com/sass/dart-sass/protocol="3.1.0"
+github.com/sass/libsass="3.6.6"
 github.com/spf13/afero="v1.11.0"
-github.com/spf13/cast="v1.7.0"
+github.com/spf13/cast="v1.7.1"
 github.com/spf13/cobra="v1.8.1"
 github.com/spf13/fsync="v0.10.1"
 github.com/spf13/pflag="v1.0.5"
-github.com/tdewolff/minify/v2="v2.21.1"
-github.com/tdewolff/parse/v2="v2.7.18"
-github.com/tetratelabs/wazero="v1.8.1"
+github.com/tdewolff/minify/v2="v2.20.37"
+github.com/tdewolff/parse/v2="v2.7.15"
+github.com/tetratelabs/wazero="v1.8.2"
+github.com/webmproject/libwebp="v1.3.2"
 github.com/yuin/goldmark-emoji="v1.0.4"
 github.com/yuin/goldmark="v1.7.8"
 go.uber.org/automaxprocs="v1.5.3"
-golang.org/x/crypto="v0.29.0"
+golang.org/x/crypto="v0.31.0"
 golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326"
 golang.org/x/image="v0.22.0"
 golang.org/x/mod="v0.22.0"
-golang.org/x/net="v0.31.0"
-golang.org/x/sync="v0.9.0"
-golang.org/x/sys="v0.27.0"
-golang.org/x/text="v0.20.0"
-golang.org/x/tools="v0.27.0"
-google.golang.org/protobuf="v1.35.1"
+golang.org/x/net="v0.33.0"
+golang.org/x/sync="v0.10.0"
+golang.org/x/sys="v0.28.0"
+golang.org/x/text="v0.21.0"
+golang.org/x/tools="v0.28.0"
+google.golang.org/protobuf="v1.35.2"
 gopkg.in/yaml.v2="v2.4.0"
 gopkg.in/yaml.v3="v3.0.1"
-howett.net/plist="v1.0.0"
+rsc.io/qr="v0.2.0"
 software.sslmate.com/src/go-pkcs12="v0.2.0"
 ```
 </details>
diff --git a/commands/gen.go b/commands/gen.go
index 83b4d637c..67aa7a896 100644
--- a/commands/gen.go
+++ b/commands/gen.go
@@ -75,7 +75,9 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
 					return err
 				}
 				formatter := html.New(html.WithAllClasses(true))
-				formatter.WriteCSS(os.Stdout, style)
+				w := os.Stdout
+				fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
+				formatter.WriteCSS(w, style)
 				return nil
 			},
 			withc: func(cmd *cobra.Command, r *rootCommand) {
diff --git a/common/hashing/hashing.go b/common/hashing/hashing.go
index 70aa74ecd..18ec82623 100644
--- a/common/hashing/hashing.go
+++ b/common/hashing/hashing.go
@@ -38,6 +38,19 @@ func XXHashFromReader(r io.Reader) (uint64, int64, error) {
 	return h.Sum64(), size, nil
 }
 
+// XxHashFromReaderHexEncoded calculates the xxHash for the given reader
+// and returns the hash as a hex encoded string.
+func XxHashFromReaderHexEncoded(r io.Reader) (string, error) {
+	h := getXxHashReadFrom()
+	defer putXxHashReadFrom(h)
+	_, err := io.Copy(h, r)
+	if err != nil {
+		return "", err
+	}
+	hash := h.Sum(nil)
+	return hex.EncodeToString(hash), nil
+}
+
 // XXHashFromString calculates the xxHash for the given string.
 func XXHashFromString(s string) (uint64, error) {
 	h := xxhash.New()
@@ -70,6 +83,13 @@ func HashString(vs ...any) string {
 	return strconv.FormatUint(hash, 10)
 }
 
+// HashStringHex returns a hash from the given elements as a hex encoded string.
+// See HashString for more information.
+func HashStringHex(vs ...any) string {
+	hash := HashUint64(vs...)
+	return strconv.FormatUint(hash, 16)
+}
+
 var hashOptsPool = sync.Pool{
 	New: func() any {
 		return &hashstructure.HashOptions{
diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go
index c8a48823e..38b198656 100644
--- a/common/herrors/file_error.go
+++ b/common/herrors/file_error.go
@@ -258,8 +258,27 @@ func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
 	return f, realFilename, nil
 }
 
-// Cause returns the underlying error or itself if it does not implement Unwrap.
+// Cause returns the underlying error, that is,
+// it unwraps errors until it finds one that does not implement
+// the Unwrap method.
+// For a shallow variant, see Unwrap.
 func Cause(err error) error {
+	type unwrapper interface {
+		Unwrap() error
+	}
+
+	for err != nil {
+		cause, ok := err.(unwrapper)
+		if !ok {
+			break
+		}
+		err = cause.Unwrap()
+	}
+	return err
+}
+
+// Unwrap returns the underlying error or itself if it does not implement Unwrap.
+func Unwrap(err error) error {
 	if u := errors.Unwrap(err); u != nil {
 		return u
 	}
@@ -267,7 +286,7 @@ func Cause(err error) error {
 }
 
 func extractFileTypePos(err error) (string, text.Position) {
-	err = Cause(err)
+	err = Unwrap(err)
 
 	var fileType string
 
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index be4480f45..4d65d5f2b 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -734,7 +734,7 @@ func (c *Configs) Validate(logger loggers.Logger) error {
 
 // transientErr returns the last transient error found during config compilation.
 func (c *Configs) transientErr() error {
-	for _, l := range c.LanguageConfigSlice {
+	for _, l := range c.LanguageConfigMap {
 		if l.C.transientErr != nil {
 			return l.C.transientErr
 		}
@@ -751,7 +751,6 @@ func (c *Configs) Init() error {
 	var languages langs.Languages
 	defaultContentLanguage := c.Base.DefaultContentLanguage
 	for k, v := range c.LanguageConfigMap {
-		c.LanguageConfigSlice = append(c.LanguageConfigSlice, v)
 		languageConf := v.Languages[k]
 		language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf)
 		if err != nil {
diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md
index 47e4f94ed..afe84e79b 100644
--- a/docs/content/en/content-management/shortcodes.md
+++ b/docs/content/en/content-management/shortcodes.md
@@ -121,7 +121,7 @@ Hugo renders this to:
 </details>
 ```
 
-The details shortcode accepts these named arguments:
+The `details` shortcode accepts these named arguments:
 
 summary
 : (`string`) The content of the child `summary` element rendered from Markdown to HTML. Default is `Details`.
@@ -333,6 +333,106 @@ Access nested values by [chaining] the [identifiers]:
 {{</* param my.nested.param */>}}
 ```
 
+### qr
+
+{{% note %}}
+To override Hugo's embedded `qr` shortcode, copy the [source code] to a file with the same name in the layouts/shortcodes directory.
+
+[source code]: {{% eturl qr %}}
+{{% /note %}}
+
+The `qr` shortcode encodes the given text into a [QR code] using the specified options and renders the resulting image.
+
+[QR code]: https://en.wikipedia.org/wiki/QR_code
+
+Use the self-closing syntax to pass the text as an argument:
+
+```text
+{{</* qr text="https://gohugo.io" /*/>}}
+```
+
+Or insert the text between the opening and closing tags:
+
+```text
+{{</* qr */>}}
+https://gohugo.io
+{{</* /qr */>}}
+```
+
+Both of the above produce this image:
+
+{{< qr text="https://gohugo.io" class="qrcode" />}}
+
+To create a QR code for a phone number:
+
+```text
+{{</* qr text="tel:+12065550101" /*/>}}
+```
+
+{{< qr text="tel:+12065550101" class="qrcode" />}}
+
+To create a QR code containing contact information in the [vCard] format:
+
+[vCard]: https://en.wikipedia.org/wiki/VCard
+
+```text
+{{</* qr level="low" scale=2 alt="QR code of vCard for John Smith" */>}}
+BEGIN:VCARD
+VERSION:2.1
+N;CHARSET=UTF-8:Smith;John;R.;Dr.;PhD
+FN;CHARSET=UTF-8:Dr. John R. Smith, PhD.
+ORG;CHARSET=UTF-8:ABC Widgets
+TITLE;CHARSET=UTF-8:Vice President Engineering
+TEL;TYPE=WORK:+12065550101
+EMAIL;TYPE=WORK:jsmith@example.org
+END:VCARD
+{{</* /qr */>}}
+```
+
+{{< qr level="low" scale=2 alt="QR code of vCard for John Smith" class="qrcode" >}}
+BEGIN:VCARD
+VERSION:2.1
+N;CHARSET=UTF-8:Smith;John;R.;Dr.;PhD
+FN;CHARSET=UTF-8:Dr. John R. Smith, PhD.
+ORG;CHARSET=UTF-8:ABC Widgets
+TITLE;CHARSET=UTF-8:Vice President Engineering
+TEL;TYPE=WORK:+12065550101
+EMAIL;TYPE=WORK:jsmith@example.org
+END:VCARD
+{{< /qr >}}
+
+Internally this shortcode calls the `images.QR` function. Please read the [related documentation] for implementation details and guidance.
+
+[related documentation]: /functions/images/qr/
+
+The `qr` shortcode accepts these named arguments:
+
+text
+: (`string`) The text to encode, falling back to the text between the opening and closing shortcode tags.
+
+level
+: (`string`) The error correction level to use when encoding the text, one of `low`, `medium`, `quartile`, or `high`. Default is `medium`.
+
+scale
+: (`int`) The number of image pixels per QR code module. Must be greater than or equal to 2. Default is `4`.
+
+targetDir
+: (`string`) The subdirectory within the [`publishDir`] where Hugo will place the generated image.
+
+[`publishDir`]: /getting-started/configuration/#publishdir
+
+alt
+: (`string`) The `alt` attribute of the `img` element.
+
+class
+: (`string`) The `class` attribute of the `img` element.
+
+id
+: (`string`) The `id` attribute of the `img` element.
+
+title
+: (`string`) The `title` attribute of the `img` element.
+
 ### ref
 
 {{% note %}}
diff --git a/docs/content/en/functions/images/QR.md b/docs/content/en/functions/images/QR.md
new file mode 100644
index 000000000..c1b8fb465
--- /dev/null
+++ b/docs/content/en/functions/images/QR.md
@@ -0,0 +1,115 @@
+---
+title: images.QR
+description: Encodes the given text into a QR code using the specified options, returning an image resource.
+keywords: []
+action:
+  aliases: []
+  related: []
+  returnType: images.ImageResource
+  signatures: ['images.QR OPTIONS']
+toc: true
+math: true
+---
+
+{{< new-in 0.141.0 >}}
+
+The `images.QR` function encodes the given text into a [QR code] using the specified options, returning an image resource. The size of the generated image depends on three factors:
+
+- Data length: Longer text necessitates a larger image to accommodate the increased information density.
+- Error correction level: Higher error correction levels enhance the QR code's resistance to damage, but this typically results in a slightly larger image size to maintain readability.
+- Pixels per module: The number of image pixels assigned to each individual module (the smallest unit of the QR code) directly impacts the overall image size. A higher pixel count per module leads to a larger, higher-resolution image.
+
+Although the default option values are sufficient for most applications, you should test the rendered QR code both on-screen and in print.
+
+[QR code]: https://en.wikipedia.org/wiki/QR_code
+
+## Options
+
+text
+: (`string`) The text to encode.
+
+level
+: (`string`) The error correction level to use when encoding the text, one of `low`, `medium`, `quartile`, or `high`. Default is `medium`.
+
+Error correction level|Redundancy
+:--|:--|:--
+low|20%
+medium|38%
+quartile|55%
+high|65%
+
+scale
+: (`int`) The number of image pixels per QR code module. Must be greater than or equal to `2`. Default is `4`.
+
+targetDir
+: (`string`) The subdirectory within the [`publishDir`] where Hugo will place the generated image. Use Unix-style slashes (`/`) to separarate path segments. If empty or not provided, the image is placed directly in the `publishDir` root. Hugo automatically creates the necessary subdirectories if they don't exist.
+
+[`publishDir`]: /getting-started/configuration/#publishdir
+
+## Examples
+
+To create a QR code using the default values for `level` and `scale`:
+
+```go-html-template
+{{ $opts := dict "text" "https://gohugo.io" }}
+{{ with images.QR $opts }}
+  <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
+{{ end }}
+```
+
+{{< qr text="https://gohugo.io" class="qrcode" />}}
+
+Specify `level`, `scale`, and `targetDir` as needed to achieve the desired result:
+
+```go-html-template
+{{ $opts := dict 
+  "text" "https://gohugo.io"
+  "level" "high" 
+  "scale" 3
+  "targetDir" "codes"
+}}
+{{ with images.QR $opts }}
+  <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
+{{ end }}
+```
+
+{{< qr text="https://gohugo.io" level="high" scale=3 targetDir="codes" class="qrcode" />}}
+
+## Scale
+
+As you decrease the size of a QR code, the maximum distance at which it can be reliably scanned by a device also decreases.
+
+In the example above, we set the `scale` to `2`, resulting in a QR code where each module consists of 2x2 pixels. While this might be sufficient for on-screen display, it's likely to be problematic when printed at 600 dpi.
+
+\[ \frac{2\:px}{module} \times \frac{1\:inch}{600\:px} \times \frac{25.4\:mm}{1\:inch} = \frac{0.085\:mm}{module} \]
+
+This module size is half of the commonly recommended minimum of 0.170 mm.\
+If the QR code will be printed, use the default `scale` value of `4` pixels per module.
+
+Avoid using Hugo's image processing methods to resize QR codes. Resizing can introduce blurring due to anti-aliasing when a QR code module occupies a fractional number of pixels.
+
+{{% note %}}
+Always test the rendered QR code both on-screen and in print.
+{{% /note %}}
+
+## Shortcode
+
+Call the `qr` shortcode to insert a QR code into your content.
+
+Use the self-closing syntax to pass the text as an argument:
+
+```text
+{{</* qr text="https://gohugo.io" /*/>}}
+```
+
+Or insert the text between the opening and closing tags:
+
+```text
+{{</* qr */>}}
+https://gohugo.io
+{{</* /qr */>}}
+```
+
+The `qr` shortcode accepts several arguments including `level` and `scale`. See the [related documentation] for details.
+
+[related documentation]: /content-management/shortcodes/#qr
diff --git a/docs/data/embedded_template_urls.toml b/docs/data/embedded_template_urls.toml
index b7247f272..359105e60 100644
--- a/docs/data/embedded_template_urls.toml
+++ b/docs/data/embedded_template_urls.toml
@@ -10,18 +10,18 @@
 'google_analytics' = 'google_analytics.html'
 'opengraph' = 'opengraph.html'
 'pagination' = 'pagination.html'
-'schema' = 'schema.html'
-'twitter_cards' = 'twitter_cards.html'
-
 'robots' = '_default/robots.txt'
 'rss' = '_default/rss.xml'
+'schema' = 'schema.html'
 'sitemap' = '_default/sitemap.xml'
 'sitemapindex' = '_default/sitemapindex.xml'
+'twitter_cards' = 'twitter_cards.html'
 
 # Render hooks
+'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html'
 'render-image' = '_default/_markup/render-image.html'
 'render-link' = '_default/_markup/render-link.html'
-'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html'
+'render-table' = '_default/_markup/render-table.html'
 
 # Shortcodes
 'comment' = 'shortcodes/comment.html'
@@ -31,6 +31,7 @@
 'highlight' = 'shortcodes/highlight.html'
 'instagram' = 'shortcodes/instagram.html'
 'param' = 'shortcodes/param.html'
+'qr' = 'shortcodes/qr.html'
 'ref' = 'shortcodes/ref.html'
 'relref' = 'shortcodes/relref.html'
 'twitter' = 'shortcodes/twitter.html'
diff --git a/go.mod b/go.mod
index d5a4cd6d2..fe628920e 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/gohugoio/hugo
 
 require (
 	github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
-	github.com/alecthomas/chroma/v2 v2.14.0
+	github.com/alecthomas/chroma/v2 v2.15.0
 	github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
 	github.com/aws/aws-sdk-go-v2 v1.32.4
 	github.com/aws/aws-sdk-go-v2/service/cloudfront v1.41.0
@@ -26,7 +26,7 @@ require (
 	github.com/cli/safeexec v1.0.1
 	github.com/disintegration/gift v1.2.1
 	github.com/dustin/go-humanize v1.0.1
-	github.com/evanw/esbuild v0.24.0
+	github.com/evanw/esbuild v0.24.2
 	github.com/fatih/color v1.18.0
 	github.com/fortytw2/leaktest v1.3.0
 	github.com/frankban/quicktest v1.14.6
@@ -77,7 +77,7 @@ require (
 	golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
 	golang.org/x/image v0.22.0
 	golang.org/x/mod v0.22.0
-	golang.org/x/net v0.32.0
+	golang.org/x/net v0.33.0
 	golang.org/x/sync v0.10.0
 	golang.org/x/text v0.21.0
 	golang.org/x/tools v0.28.0
@@ -119,7 +119,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
 	github.com/aws/smithy-go v1.22.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
-	github.com/dlclark/regexp2 v1.11.0 // indirect
+	github.com/dlclark/regexp2 v1.11.4 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
@@ -165,6 +165,7 @@ require (
 	google.golang.org/protobuf v1.35.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	howett.net/plist v1.0.0 // indirect
+	rsc.io/qr v0.2.0 // indirect
 	software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
 )
 
diff --git a/go.sum b/go.sum
index 3f39a7a46..bbbf7e0c7 100644
--- a/go.sum
+++ b/go.sum
@@ -71,10 +71,10 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
-github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
-github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
-github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
+github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
 github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
 github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
@@ -176,8 +176,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
 github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
-github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
-github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
+github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -186,8 +186,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
 github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanw/esbuild v0.24.0 h1:GZ78naTLp7FKr+K7eNuM/SLs5maeiHYRPsTg6kmdsSE=
-github.com/evanw/esbuild v0.24.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
+github.com/evanw/esbuild v0.24.2 h1:PQExybVBrjHjN6/JJiShRGIXh1hWVm6NepVnhZhrt0A=
+github.com/evanw/esbuild v0.24.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
 github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -588,8 +588,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
-golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -878,6 +878,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
 howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
+rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go
index 02e541a05..388493174 100644
--- a/hugofs/rootmapping_fs.go
+++ b/hugofs/rootmapping_fs.go
@@ -311,12 +311,13 @@ func (fs *RootMappingFs) Open(name string) (afero.File, error) {
 
 // Stat returns the os.FileInfo structure describing a given file.  If there is
 // an error, it will be of type *os.PathError.
+// If multiple roots are found, the last one will be used.
 func (fs *RootMappingFs) Stat(name string) (os.FileInfo, error) {
 	fis, err := fs.doStat(name)
 	if err != nil {
 		return nil, err
 	}
-	return fis[0], nil
+	return fis[len(fis)-1], nil
 }
 
 type ComponentPath struct {
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index eda6f769e..b369dfe13 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -1274,7 +1274,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 
 		po.renderState = 0
 		po.p.resourcesPublishInit = &sync.Once{}
-		if r == identity.FinderFoundOneOfMany {
+		if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatusHTMLFormat.Name {
 			// Will force a re-render even in fast render mode.
 			po.renderOnce = false
 		}
@@ -1310,6 +1310,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 				if !po.isRendered() {
 					continue
 				}
+
 				for _, id := range changes {
 					checkedCounter.Add(1)
 					if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition {
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index 02ecd5785..60fb8ecc0 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -343,6 +343,18 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
 
 	siteRenderContext := &siteRenderContext{cfg: config, multihost: h.Configs.IsMultihost}
 
+	renderErr := func(err error) error {
+		if err == nil {
+			return nil
+		}
+		if strings.Contains(err.Error(), "can't evaluate field Err in type resource.Resource") {
+			// In Hugo 0.141.0 we replaced the special error handling for resources.GetRemote
+			// with the more general try.
+			return fmt.Errorf("%s: Resource.Err was removed in Hugo v0.141.0 and replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/", err)
+		}
+		return err
+	}
+
 	i := 0
 	for _, s := range h.Sites {
 		segmentFilter := s.conf.C.SegmentFilter
@@ -390,7 +402,7 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
 							}
 						} else {
 							if err := s.render(siteRenderContext); err != nil {
-								return err
+								return renderErr(err)
 							}
 						}
 						loggers.TimeTrackf(ll, start, nil, "")
diff --git a/hugolib/page.go b/hugolib/page.go
index 83f0c6e25..365376737 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -185,10 +185,6 @@ func (p *pageState) isContentNodeBranch() bool {
 	return p.IsNode()
 }
 
-func (p *pageState) Err() resource.ResourceError {
-	return nil
-}
-
 // Eq returns whether the current page equals the given page.
 // This is what's invoked when doing `{{ if eq $page $otherPage }}`
 func (p *pageState) Eq(other any) bool {
diff --git a/hugoreleaser.env b/hugoreleaser.env
index c7adad805..0c40dd43e 100644
--- a/hugoreleaser.env
+++ b/hugoreleaser.env
@@ -1,7 +1,9 @@
 # Release env. 
 # These will be replaced by script before release.
-HUGORELEASER_TAG=v0.140.0
-HUGORELEASER_COMMITISH=3f35721fb2c75a1f7cc5a7a14400b66e73d4b06e
+HUGORELEASER_TAG=v0.140.2
+HUGORELEASER_COMMITISH=aae02ca612a02e085c08366a9c9279f4abb39d94
+
+
 
 
 
diff --git a/markup/highlight/config.go b/markup/highlight/config.go
index 62db6b237..b19e9ec1b 100644
--- a/markup/highlight/config.go
+++ b/markup/highlight/config.go
@@ -33,7 +33,6 @@ const (
 	lineNosKey     = "linenos"
 	hlLinesKey     = "hl_lines"
 	linosStartKey  = "linenostart"
-	noHlKey        = "nohl"
 )
 
 var DefaultConfig = Config{
@@ -45,19 +44,21 @@ var DefaultConfig = Config{
 	NoClasses:          true,
 	LineNumbersInTable: true,
 	TabWidth:           4,
+	WrapperClass:       "highlight",
 }
 
 type Config struct {
 	Style string
 
+	// Enable syntax highlighting of fenced code blocks.
 	CodeFences bool
 
+	// The class or classes to use for the outermost element of the highlighted code.
+	WrapperClass string
+
 	// Use inline CSS styles.
 	NoClasses bool
 
-	// No highlighting.
-	NoHl bool
-
 	// When set, line numbers will be printed.
 	LineNos            bool
 	LineNumbersInTable bool
@@ -229,8 +230,6 @@ func normalizeHighlightOptions(m map[string]any) {
 
 	for k, v := range m {
 		switch k {
-		case noHlKey:
-			m[noHlKey] = cast.ToBool(v)
 		case lineNosKey:
 			if v == "table" || v == "inline" {
 				m["lineNumbersInTable"] = v == "table"
diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go
index a284b5981..ee07fb9ad 100644
--- a/markup/highlight/highlight.go
+++ b/markup/highlight/highlight.go
@@ -168,7 +168,7 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
 		lexer = chromalexers.Get(lang)
 	}
 
-	if lexer == nil && (cfg.GuessSyntax && !cfg.NoHl) {
+	if lexer == nil && cfg.GuessSyntax {
 		lexer = lexers.Analyse(code)
 		if lexer == nil {
 			lexer = lexers.Fallback
@@ -202,7 +202,7 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
 	}
 
 	if !cfg.Hl_inline {
-		writeDivStart(w, attributes)
+		writeDivStart(w, attributes, cfg.WrapperClass)
 	}
 
 	options := cfg.toHTMLOptions()
@@ -303,8 +303,9 @@ func (s startEnd) End(code bool) string {
 	return s.end(code)
 }
 
-func writeDivStart(w hugio.FlexiWriter, attrs []attributes.Attribute) {
-	w.WriteString(`<div class="highlight`)
+func writeDivStart(w hugio.FlexiWriter, attrs []attributes.Attribute, wrapperClass string) {
+	w.WriteString(`<div class="`)
+	w.WriteString(wrapperClass)
 	if attrs != nil {
 		for _, attr := range attrs {
 			if attr.Name == "class" {
diff --git a/markup/internal/attributes/attributes.go b/markup/internal/attributes/attributes.go
index 9c147f766..b2d4a5ed6 100644
--- a/markup/internal/attributes/attributes.go
+++ b/markup/internal/attributes/attributes.go
@@ -36,7 +36,6 @@ var chromaHighlightProcessingAttributes = map[string]bool{
 	"lineNoStart":        true,
 	"lineNumbersInTable": true,
 	"noClasses":          true,
-	"nohl":               true,
 	"style":              true,
 	"tabWidth":           true,
 }
diff --git a/resources/errorResource.go b/resources/errorResource.go
deleted file mode 100644
index 582c54f6d..000000000
--- a/resources/errorResource.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2021 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 resources
-
-import (
-	"context"
-	"image"
-
-	"github.com/gohugoio/hugo/common/hugio"
-	"github.com/gohugoio/hugo/common/maps"
-	"github.com/gohugoio/hugo/media"
-	"github.com/gohugoio/hugo/resources/images"
-	"github.com/gohugoio/hugo/resources/images/exif"
-	"github.com/gohugoio/hugo/resources/resource"
-)
-
-var (
-	_ error = (*errorResource)(nil)
-	// Image covers all current Resource implementations.
-	_ images.ImageResource = (*errorResource)(nil)
-	// The list of user facing and exported interfaces in resource.go
-	// Note that if we're missing some interface here, the user will still
-	// get an error, but not as pretty.
-	_ resource.ContentResource         = (*errorResource)(nil)
-	_ resource.ReadSeekCloserResource  = (*errorResource)(nil)
-	_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
-	// Make sure it also fails when passed to a pipe function.
-	_ ResourceTransformer = (*errorResource)(nil)
-)
-
-// NewErrorResource wraps err in a Resource where all but the Err method will panic.
-func NewErrorResource(err resource.ResourceError) resource.Resource {
-	return &errorResource{ResourceError: err}
-}
-
-type errorResource struct {
-	resource.ResourceError
-}
-
-func (e *errorResource) Err() resource.ResourceError {
-	return e.ResourceError
-}
-
-func (e *errorResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Content(context.Context) (any, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) ResourceType() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) MediaType() media.Type {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Permalink() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) RelPermalink() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Name() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Title() string {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Params() maps.Params {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Data() any {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Height() int {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Width() int {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Process(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Crop(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Fill(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Fit(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Resize(spec string) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Filter(filters ...any) (images.ImageResource, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Exif() *exif.ExifInfo {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Colors() ([]images.Color, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) DecodeImage() (image.Image, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) Transform(...ResourceTransformation) (ResourceTransformer, error) {
-	panic(e.ResourceError)
-}
-
-func (e *errorResource) TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) {
-	panic(e.ResourceError)
-}
diff --git a/resources/page/page_nop.go b/resources/page/page_nop.go
index 5a03b1994..af9f2682d 100644
--- a/resources/page/page_nop.go
+++ b/resources/page/page_nop.go
@@ -61,10 +61,6 @@ type nopPage int
 
 var noOpPathInfo = media.DefaultPathParser.Parse(files.ComponentFolderContent, "no-op.md")
 
-func (p *nopPage) Err() resource.ResourceError {
-	return nil
-}
-
 func (p *nopPage) Aliases() []string {
 	return nil
 }
diff --git a/resources/resource.go b/resources/resource.go
index 7ab10b0ae..29b9e5ddd 100644
--- a/resources/resource.go
+++ b/resources/resource.go
@@ -224,9 +224,6 @@ type resourceCopier interface {
 
 // Copy copies r to the targetPath given.
 func Copy(r resource.Resource, targetPath string) resource.Resource {
-	if r.Err() != nil {
-		panic(fmt.Sprintf("Resource has an .Err: %s", r.Err()))
-	}
 	return r.(resourceCopier).cloneTo(targetPath)
 }
 
@@ -439,10 +436,6 @@ func (l *genericResource) Content(context.Context) (any, error) {
 	return hugio.ReadString(r)
 }
 
-func (r *genericResource) Err() resource.ResourceError {
-	return nil
-}
-
 func (l *genericResource) Data() any {
 	return l.sd.Data
 }
diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go
index b33750e80..585dfd150 100644
--- a/resources/resource/resourcetypes.go
+++ b/resources/resource/resourcetypes.go
@@ -43,6 +43,9 @@ type OriginProvider interface {
 
 // NewResourceError creates a new ResourceError.
 func NewResourceError(err error, data any) ResourceError {
+	if data == nil {
+		data = map[string]any{}
+	}
 	return &resourceError{
 		error: err,
 		data:  data,
@@ -65,13 +68,6 @@ type ResourceError interface {
 	ResourceDataProvider
 }
 
-// ErrProvider provides an Err.
-type ErrProvider interface {
-	// Err returns an error if this resource is in an error state.
-	// This will currently only be set for resources obtained from resources.GetRemote.
-	Err() ResourceError
-}
-
 // Resource represents a linkable resource, i.e. a content page, image etc.
 type Resource interface {
 	ResourceWithoutMeta
@@ -83,7 +79,6 @@ type ResourceWithoutMeta interface {
 	MediaTypeProvider
 	ResourceLinksProvider
 	ResourceDataProvider
-	ErrProvider
 }
 
 type ResourceWrapper interface {
diff --git a/resources/resource_cache.go b/resources/resource_cache.go
index a3ba9aa26..898cd4c31 100644
--- a/resources/resource_cache.go
+++ b/resources/resource_cache.go
@@ -36,6 +36,11 @@ func newResourceCache(rs *Spec, memCache *dynacache.Cache) *ResourceCache {
 			"/res1",
 			dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
 		),
+		cacheResourceFile: dynacache.GetOrCreatePartition[string, resource.Resource](
+			memCache,
+			"/res2",
+			dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
+		),
 		CacheResourceRemote: dynacache.GetOrCreatePartition[string, resource.Resource](
 			memCache,
 			"/resr",
@@ -58,6 +63,7 @@ type ResourceCache struct {
 	sync.RWMutex
 
 	cacheResource               *dynacache.Partition[string, resource.Resource]
+	cacheResourceFile           *dynacache.Partition[string, resource.Resource]
 	CacheResourceRemote         *dynacache.Partition[string, resource.Resource]
 	cacheResources              *dynacache.Partition[string, resource.Resources]
 	cacheResourceTransformation *dynacache.Partition[string, *resourceAdapterInner]
@@ -79,6 +85,12 @@ func (c *ResourceCache) GetOrCreate(key string, f func() (resource.Resource, err
 	})
 }
 
+func (c *ResourceCache) GetOrCreateFile(key string, f func() (resource.Resource, error)) (resource.Resource, error) {
+	return c.cacheResourceFile.GetOrCreate(key, func(key string) (resource.Resource, error) {
+		return f()
+	})
+}
+
 func (c *ResourceCache) GetOrCreateResources(key string, f func() (resource.Resources, error)) (resource.Resources, error) {
 	return c.cacheResources.GetOrCreate(key, func(key string) (resource.Resources, error) {
 		return f()
diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go
index 7dd26f4c0..1d78a2923 100644
--- a/resources/resource_factories/create/create.go
+++ b/resources/resource_factories/create/create.go
@@ -143,19 +143,7 @@ func (c *Client) Get(pathname string) (resource.Resource, error) {
 			return nil, err
 		}
 
-		meta := fi.(hugofs.FileMetaInfo).Meta()
-		pi := meta.PathInfo
-
-		return c.rs.NewResource(resources.ResourceSourceDescriptor{
-			LazyPublish: true,
-			OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
-				return c.rs.BaseFs.Assets.Fs.Open(filename)
-			},
-			Path:                 pi,
-			GroupIdentity:        pi,
-			TargetPath:           pathname,
-			SourceFilenameOrPath: meta.Filename,
-		})
+		return c.getOrCreateFileResource(fi.(hugofs.FileMetaInfo))
 	})
 }
 
@@ -181,6 +169,23 @@ func (c *Client) GetMatch(pattern string) (resource.Resource, error) {
 	return res[0], err
 }
 
+func (c *Client) getOrCreateFileResource(info hugofs.FileMetaInfo) (resource.Resource, error) {
+	meta := info.Meta()
+	return c.rs.ResourceCache.GetOrCreateFile(filepath.ToSlash(meta.Filename), func() (resource.Resource, error) {
+		return c.rs.NewResource(resources.ResourceSourceDescriptor{
+			LazyPublish: true,
+			OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
+				return meta.Open()
+			},
+			NameNormalized:       meta.PathInfo.Path(),
+			NameOriginal:         meta.PathInfo.Unnormalized().Path(),
+			GroupIdentity:        meta.PathInfo,
+			TargetPath:           meta.PathInfo.Unnormalized().Path(),
+			SourceFilenameOrPath: meta.Filename,
+		})
+	})
+}
+
 func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) bool, firstOnly bool) (resource.Resources, error) {
 	pattern = glob.NormalizePath(pattern)
 	partitions := glob.FilterGlobParts(strings.Split(pattern, "/"))
@@ -191,19 +196,7 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
 		var res resource.Resources
 
 		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()
-				},
-				NameNormalized:       meta.PathInfo.Path(),
-				NameOriginal:         meta.PathInfo.Unnormalized().Path(),
-				GroupIdentity:        meta.PathInfo,
-				TargetPath:           meta.PathInfo.Unnormalized().Path(),
-				SourceFilenameOrPath: meta.Filename,
-			})
+			r, err := c.getOrCreateFileResource(info)
 			if err != nil {
 				return true, err
 			}
@@ -225,22 +218,83 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
 	})
 }
 
-// FromString creates a new Resource from a string with the given relative target path.
-// TODO(bep) see #10912; we currently emit a warning for this config scenario.
-func (c *Client) FromString(targetPath, content string) (resource.Resource, error) {
-	targetPath = path.Clean(targetPath)
-	key := dynacache.CleanKey(targetPath) + hashing.MD5FromStringHexEncoded(content)
+type Options struct {
+	// The target path relative to the publish directory.
+	// Unix style path, i.e. "images/logo.png".
+	TargetPath string
+
+	// Whether the TargetPath has a hash in it which will change if the resource changes.
+	// If not, we will calculate a hash from the content.
+	TargetPathHasHash bool
+
+	// The content to create the Resource from.
+	CreateContent func() (func() (hugio.ReadSeekCloser, error), error)
+}
+
+// FromOpts creates a new Resource from the given Options.
+// Make sure to set optis.TargetPathHasHash if the TargetPath already contains a hash,
+// as this avoids the need to calculate it.
+// To create a new ReadSeekCloser from a string, use hugio.NewReadSeekerNoOpCloserFromString,
+// or hugio.NewReadSeekerNoOpCloserFromBytes for a byte slice.
+// See FromString.
+func (c *Client) FromOpts(opts Options) (resource.Resource, error) {
+	opts.TargetPath = path.Clean(opts.TargetPath)
+	var hash string
+	var newReadSeeker func() (hugio.ReadSeekCloser, error) = nil
+	if !opts.TargetPathHasHash {
+		var err error
+		newReadSeeker, err = opts.CreateContent()
+		if err != nil {
+			return nil, err
+		}
+		if err := func() error {
+			r, err := newReadSeeker()
+			if err != nil {
+				return err
+			}
+			defer r.Close()
+
+			hash, err = hashing.XxHashFromReaderHexEncoded(r)
+			if err != nil {
+				return err
+			}
+			return nil
+		}(); err != nil {
+			return nil, err
+		}
+	}
+
+	key := dynacache.CleanKey(opts.TargetPath) + hash
 	r, err := c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
+		if newReadSeeker == nil {
+			var err error
+			newReadSeeker, err = opts.CreateContent()
+			if err != nil {
+				return nil, err
+			}
+		}
 		return c.rs.NewResource(
 			resources.ResourceSourceDescriptor{
 				LazyPublish:   true,
 				GroupIdentity: identity.Anonymous, // All usage of this resource are tracked via its string content.
 				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
-					return hugio.NewReadSeekerNoOpCloserFromString(content), nil
+					return newReadSeeker()
 				},
-				TargetPath: targetPath,
+				TargetPath: opts.TargetPath,
 			})
 	})
 
 	return r, err
 }
+
+// FromString creates a new Resource from a string with the given relative target path.
+func (c *Client) FromString(targetPath, content string) (resource.Resource, error) {
+	return c.FromOpts(Options{
+		TargetPath: targetPath,
+		CreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {
+			return func() (hugio.ReadSeekCloser, error) {
+				return hugio.NewReadSeekerNoOpCloserFromString(content), nil
+			}, nil
+		},
+	})
+}
diff --git a/resources/transform.go b/resources/transform.go
index c5d240669..73f3b85d2 100644
--- a/resources/transform.go
+++ b/resources/transform.go
@@ -192,10 +192,6 @@ func (r *resourceAdapter) Content(ctx context.Context) (any, error) {
 	return r.target.Content(ctx)
 }
 
-func (r *resourceAdapter) Err() resource.ResourceError {
-	return nil
-}
-
 func (r *resourceAdapter) GetIdentity() identity.Identity {
 	return identity.FirstIdentity(r.target)
 }
diff --git a/tpl/images/images.go b/tpl/images/images.go
index 02ffb333f..106c2b280 100644
--- a/tpl/images/images.go
+++ b/tpl/images/images.go
@@ -16,11 +16,18 @@ package images
 
 import (
 	"errors"
+	"fmt"
 	"image"
+	"path"
 	"sync"
 
 	"github.com/bep/overlayfs"
+	"github.com/gohugoio/hugo/common/hashing"
+	"github.com/gohugoio/hugo/common/hugio"
 	"github.com/gohugoio/hugo/resources/images"
+	"github.com/gohugoio/hugo/resources/resource_factories/create"
+	"github.com/mitchellh/mapstructure"
+	"rsc.io/qr"
 
 	// Importing image codecs for image.DecodeConfig
 	_ "image/gif"
@@ -50,21 +57,22 @@ func New(d *deps.Deps) *Namespace {
 	}
 
 	return &Namespace{
-		readFileFs: readFileFs,
-		Filters:    &images.Filters{},
-		cache:      map[string]image.Config{},
-		deps:       d,
+		readFileFs:   readFileFs,
+		Filters:      &images.Filters{},
+		cache:        map[string]image.Config{},
+		deps:         d,
+		createClient: create.New(d.ResourceSpec),
 	}
 }
 
 // Namespace provides template functions for the "images" namespace.
 type Namespace struct {
 	*images.Filters
-	readFileFs afero.Fs
-	cacheMu    sync.RWMutex
-	cache      map[string]image.Config
-
-	deps *deps.Deps
+	readFileFs   afero.Fs
+	cacheMu      sync.RWMutex
+	cache        map[string]image.Config
+	deps         *deps.Deps
+	createClient *create.Client
 }
 
 // Config returns the image.Config for the specified path relative to the
@@ -117,3 +125,77 @@ func (ns *Namespace) Filter(args ...any) (images.ImageResource, error) {
 
 	return img.Filter(filtersv...)
 }
+
+var qrErrorCorrectionLevels = map[string]qr.Level{
+	"low":      qr.L,
+	"medium":   qr.M,
+	"quartile": qr.Q,
+	"high":     qr.H,
+}
+
+// QR encodes the given text into a QR code using the specified options,
+// returning an image resource.
+func (ns *Namespace) QR(options any) (images.ImageResource, error) {
+	const (
+		qrDefaultErrorCorrectionLevel = "medium"
+		qrDefaultScale                = 4
+	)
+
+	opts := struct {
+		Text      string // text to encode
+		Level     string // error correction level; one of low, medium, quartile, or high
+		Scale     int    // number of image pixels per QR code module
+		TargetDir string // target directory relative to publishDir
+	}{
+		Level: qrDefaultErrorCorrectionLevel,
+		Scale: qrDefaultScale,
+	}
+
+	err := mapstructure.WeakDecode(options, &opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if opts.Text == "" {
+		return nil, errors.New("cannot encode an empty string")
+	}
+
+	level, ok := qrErrorCorrectionLevels[opts.Level]
+	if !ok {
+		return nil, errors.New("error correction level must be one of low, medium, quartile, or high")
+	}
+
+	if opts.Scale < 2 {
+		return nil, errors.New("scale must be an integer greater than or equal to 2")
+	}
+
+	targetPath := path.Join(opts.TargetDir, fmt.Sprintf("qr_%s.png", hashing.HashStringHex(opts)))
+
+	r, err := ns.createClient.FromOpts(
+		create.Options{
+			TargetPath:        targetPath,
+			TargetPathHasHash: true,
+			CreateContent: func() (func() (hugio.ReadSeekCloser, error), error) {
+				code, err := qr.Encode(opts.Text, level)
+				if err != nil {
+					return nil, err
+				}
+				code.Scale = opts.Scale
+				png := code.PNG()
+				return func() (hugio.ReadSeekCloser, error) {
+					return hugio.NewReadSeekerNoOpCloserFromBytes(png), nil
+				}, nil
+			},
+		},
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	ir, ok := r.(images.ImageResource)
+	if !ok {
+		panic("bug: resource is not an image resource")
+	}
+
+	return ir, nil
+}
diff --git a/tpl/internal/go_templates/texttemplate/hugo_template.go b/tpl/internal/go_templates/texttemplate/hugo_template.go
index 12dbe0412..36962c444 100644
--- a/tpl/internal/go_templates/texttemplate/hugo_template.go
+++ b/tpl/internal/go_templates/texttemplate/hugo_template.go
@@ -15,9 +15,11 @@ package template
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"reflect"
 
+	"github.com/gohugoio/hugo/common/herrors"
 	"github.com/gohugoio/hugo/common/hreflect"
 
 	"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
@@ -255,10 +257,53 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
 	panic("not reached")
 }
 
+// newErrorWithCause creates a new error with the given cause.
+func newErrorWithCause(err error) *TryError {
+	return &TryError{Err: err, Cause: herrors.Cause(err)}
+}
+
+// TryError wraps an error with a cause.
+type TryError struct {
+	Err   error
+	Cause error
+}
+
+func (e *TryError) Error() string {
+	return e.Err.Error()
+}
+
+func (e *TryError) Unwrap() error {
+	return e.Err
+}
+
+// TryValue is what gets returned when using the "try" keyword.
+type TryValue struct {
+	// Value is the value returned by the function or method wrapped with "try".
+	// This will always be nil if Err is set.
+	Value any
+
+	// Err is the error returned by the function or method wrapped with "try".
+	// This will always be nil if Value is set.
+	Err *TryError
+}
+
 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
 // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
 // as the function itself.
-func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value {
+func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) (val reflect.Value) {
+	// Added for Hugo.
+	if name == "try" {
+		defer func() {
+			if r := recover(); r != nil {
+				// Cause: herrors.Cause(err)
+				if err, ok := r.(error); ok {
+					val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(err)})
+				} else {
+					val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(fmt.Errorf("%v", r))})
+				}
+			}
+		}()
+	}
 	if args != nil {
 		args = args[1:] // Zeroth arg is function name/node; not passed to function.
 	}
@@ -371,6 +416,11 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
 		s.helper.OnCalled(s.ctx, s.prep, name, argv, vv)
 	}
 
+	// Added for Hugo.
+	if name == "try" {
+		return reflect.ValueOf(TryValue{Value: vv.Interface()})
+	}
+
 	return vv
 }
 
diff --git a/tpl/partials/init.go b/tpl/partials/init.go
index e9d901bbf..6f7c0ffc1 100644
--- a/tpl/partials/init.go
+++ b/tpl/partials/init.go
@@ -38,7 +38,7 @@ func init() {
 			},
 		)
 
-		// TODO(bep) we need the return to be a valid identifier, but
+		// TODO(bep) we need the return to be a valid identifiers, but
 		// should consider another way of adding it.
 		ns.AddMethodMapping(func() string { return "" },
 			[]string{"return"},
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
index beace14e6..f28cc36fe 100644
--- a/tpl/resources/resources.go
+++ b/tpl/resources/resources.go
@@ -115,14 +115,10 @@ func (ns *Namespace) Get(filename any) resource.Resource {
 //
 // Note: This method does not return any error as a second return value,
 // for any error situations the error can be checked in .Err.
-func (ns *Namespace) GetRemote(args ...any) resource.Resource {
+func (ns *Namespace) GetRemote(args ...any) (resource.Resource, error) {
 	get := func(args ...any) (resource.Resource, error) {
-		if len(args) < 1 {
-			return nil, errors.New("must provide an URL")
-		}
-
-		if len(args) > 2 {
-			return nil, errors.New("must not provide more arguments than URL and options")
+		if len(args) < 1 || len(args) > 2 {
+			return nil, errors.New("must provide an URL and optionally an options map")
 		}
 
 		urlstr, err := cast.ToStringE(args[0])
@@ -146,12 +142,12 @@ func (ns *Namespace) GetRemote(args ...any) resource.Resource {
 	if err != nil {
 		switch v := err.(type) {
 		case *create.HTTPError:
-			return resources.NewErrorResource(resource.NewResourceError(v, v.Data))
+			return nil, resource.NewResourceError(v, v.Data)
 		default:
-			return resources.NewErrorResource(resource.NewResourceError(fmt.Errorf("error calling resources.GetRemote: %w", err), make(map[string]any)))
+			return nil, resource.NewResourceError(err, nil)
 		}
 	}
-	return r
+	return r, nil
 }
 
 // GetMatch finds the first Resource matching the given pattern, or nil if none found.
diff --git a/tpl/safe/init.go b/tpl/safe/init.go
index 3b498e6df..ea1469e80 100644
--- a/tpl/safe/init.go
+++ b/tpl/safe/init.go
@@ -70,6 +70,13 @@ func init() {
 			},
 		)
 
+		ns.AddMethodMapping(func(v any) (any, error) {
+			return v, nil
+		},
+			[]string{"try"},
+			[][2]string{},
+		)
+
 		return ns
 	}
 
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/qr.html b/tpl/tplimpl/embedded/templates/shortcodes/qr.html
new file mode 100644
index 000000000..cae7e5670
--- /dev/null
+++ b/tpl/tplimpl/embedded/templates/shortcodes/qr.html
@@ -0,0 +1,76 @@
+{{- /*
+Encodes the given text into a QR code using the specified options and renders the resulting image.
+
+@param {string} text The text to encode, falling back to the text between the opening and closing shortcode tags.
+@param {string} [level=medium] The error correction level to use when encoding the text, one of low, medium, quartile, or high.
+@param {int} [scale=4] The number of image pixels per QR code module. Must be greater than or equal to 2.
+@param {string} [targetDir] The subdirectory within the publishDir where Hugo will place the generated image.
+@param {string} [alt] The alt attribute of the img element.
+@param {string} [class] The class attribute of the img element.
+@param {string} [id] The id attribute of the img element.
+@param {string} [title] The title attribute of the img element.
+
+@returns {template.HTML}
+
+@examples
+
+  {{< qr text="https://gohugo.io" />}}
+
+  {{< qr >}}
+  https://gohugo.io"
+  {{< /qr >}}
+
+  {{< qr
+    text="https://gohugo.io"
+    level="high"
+    scale=4
+    targetDir="codes"
+    alt="QR code linking to https://gohugo.io"
+    class="my-class"
+    id="my-id"
+    title="My Title"
+  />}}
+
+*/}}
+
+{{- /* Constants. */}}
+{{- $validLevels := slice "low" "medium" "quartile" "high" }}
+{{- $minimumScale := 2 }}
+
+{{- /* Get arguments. */}}
+{{- $text := or (.Get "text") (strings.TrimSpace .Inner) "" }}
+{{- $level := or (.Get "level") "medium" }}
+{{- $scale := or (.Get "scale") 4 }}
+{{- $targetDir := or (.Get "targetDir") "" }}
+{{- $alt := or (.Get "alt") "" }}
+{{- $class := or (.Get "class") "" }}
+{{- $id := or (.Get "id") "" }}
+{{- $title := or (.Get "title") "" }}
+
+{{- /* Validate arguments. */}}
+{{- $errors := false}}
+{{- if not $text }}
+  {{- errorf "The %q shortcode requires a %q argument. See %s" .Name "text" .Position }}
+  {{- $errors = true }}
+{{- end }}
+{{- if not (in $validLevels $level) }}
+  {{- errorf "The %q argument passed to the %q shortcode must be one of %s. See %s" "level" .Name (delimit $validLevels ", " ", or ") .Position }}
+  {{- $errors = true }}
+{{- end }}
+{{- if or (lt $scale $minimumScale) (ne $scale (int $scale)) }}
+  {{- errorf "The %q argument passed to the %q shortcode must be an integer greater than or equal to %d. See %s" "scale" .Name $minimumScale .Position }}
+  {{- $errors = true }}
+{{- end }}
+
+{{- /* Render image. */}}
+{{- if not $errors }}
+  {{- $opts := dict "text" $text "level" $level "scale" $scale "targetDir" $targetDir }}
+  {{- with images.QR $opts -}}
+    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}"
+      {{- with $alt }} alt="{{ $alt }}" {{- end }}
+      {{- with $class }} class="{{ $class }}" {{- end }}
+      {{- with $id }} id="{{ $id }}" {{- end }}
+      {{- with $title }} title="{{ $title }}" {{- end -}}
+    >
+  {{- end }}
+{{- end -}}
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
index ba5a851ee..b88cf7ce0 100644
--- a/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
+++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter.html
@@ -17,13 +17,13 @@
   {{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
   {{- $query := querify "url" $url "dnt" .dnt -}}
   {{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
-  {{- with resources.GetRemote $request -}}
+  {{- with try (resources.GetRemote $request) -}}
     {{- with .Err -}}
       {{- errorf "%s" . -}}
-    {{- else -}}
+    {{- else with .Value -}}
       {{- (. | transform.Unmarshal).html | safeHTML -}}
-    {{- end -}}
-  {{- else -}}
+    {{- else -}}
     {{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
+    {{- end -}}
   {{- end -}}
 {{- end -}}
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
index 1f3b3c523..0fc8613b9 100644
--- a/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
+++ b/tpl/tplimpl/embedded/templates/shortcodes/twitter_simple.html
@@ -14,14 +14,14 @@
   {{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
   {{- $query := querify "url" $url "dnt" .dnt "omit_script" true -}}
   {{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
-  {{- with resources.GetRemote $request -}}
+  {{- with try (resources.GetRemote $request) -}}
     {{- with .Err -}}
       {{- errorf "%s" . -}}
-    {{- else -}}
+    {{- else with .Value -}}
       {{- (. | transform.Unmarshal).html | safeHTML -}}
+    {{- else -}}
+      {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
     {{- end -}}
-  {{- else -}}
-    {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
   {{- end -}}
 {{- end -}}
 
@@ -31,7 +31,16 @@
     {{- .Page.Scratch.Set "__h_simple_twitter_css" true -}}
     <style type="text/css">
       .twitter-tweet {
-        font: 14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
+        font:
+          14px/1.45 -apple-system,
+          BlinkMacSystemFont,
+          "Segoe UI",
+          Roboto,
+          Oxygen-Sans,
+          Ubuntu,
+          Cantarell,
+          "Helvetica Neue",
+          sans-serif;
         border-left: 4px solid #2b7bb9;
         padding-left: 1.5em;
         color: #555;
diff --git a/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html b/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html
index e262d3c3c..7f7940b80 100644
--- a/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html
+++ b/tpl/tplimpl/embedded/templates/shortcodes/vimeo_simple.html
@@ -23,10 +23,10 @@
   {{- $url := urls.JoinPath "https://vimeo.com" .id -}}
   {{- $query := querify "url" $url "dnt" $dnt -}}
   {{- $request := printf "https://vimeo.com/api/oembed.json?%s" $query -}}
-  {{- with resources.GetRemote $request -}}
+  {{- with try (resources.GetRemote $request) -}}
     {{- with .Err -}}
       {{- errorf "%s" . -}}
-    {{- else -}}
+    {{- else with .Value -}}
       {{- with . | transform.Unmarshal -}}
         {{- $class := printf "%s %s" "s_video_simple" "__h_video" -}}
         {{- with $.class -}}
@@ -45,8 +45,8 @@
           </a>
         </div>
       {{- end -}}
+    {{- else -}}
+      {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
     {{- end -}}
-  {{- else -}}
-    {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
   {{- end -}}
 {{- end -}}


Test patch:
diff --git a/config/allconfig/allconfig_integration_test.go b/config/allconfig/allconfig_integration_test.go
index 38e763b70..162a36cdb 100644
--- a/config/allconfig/allconfig_integration_test.go
+++ b/config/allconfig/allconfig_integration_test.go
@@ -175,3 +175,43 @@ func TestMapUglyURLs(t *testing.T) {
 	b.Assert(c.C.IsUglyURLSection("posts"), qt.IsTrue)
 	b.Assert(c.C.IsUglyURLSection("blog"), qt.IsFalse)
 }
+
+// Issue 13199
+func TestInvalidOutputFormat(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[outputs]
+home = ['html','foo']
+-- layouts/index.html --
+x
+`
+
+	b, err := hugolib.TestE(t, files)
+	b.Assert(err, qt.IsNotNil)
+	b.Assert(err.Error(), qt.Contains, `failed to create config: unknown output format "foo" for kind "home"`)
+}
+
+// Issue 13201
+func TestLanguageConfigSlice(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[languages.en]
+title = 'TITLE_EN'
+weight = 2
+[languages.de]
+title = 'TITLE_DE'
+weight = 1
+[languages.fr]
+title = 'TITLE_FR'
+weight = 3
+`
+
+	b := hugolib.Test(t, files)
+	b.Assert(b.H.Configs.LanguageConfigSlice[0].Title, qt.Equals, `TITLE_DE`)
+}
diff --git a/hugolib/404_test.go b/hugolib/404_test.go
index fef020905..78cf0e70c 100644
--- a/hugolib/404_test.go
+++ b/hugolib/404_test.go
@@ -14,6 +14,7 @@
 package hugolib
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -82,3 +83,33 @@ Page not found
 Base:
 Page not found`)
 }
+
+func Test404EditTemplate(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+baseURL = "http://example.com/"
+disableLiveReload = true
+[internal]
+fastRenderMode = true
+-- layouts/_default/baseof.html --
+Base: {{ block "main" . }}{{ end }}
+-- layouts/404.html --
+{{ define "main" }}
+Not found.
+{{ end }}
+	
+	`
+
+	b := TestRunning(t, files)
+
+	b.AssertFileContent("public/404.html", `Not found.`)
+
+	b.EditFiles("layouts/404.html", `Not found. Updated.`).Build()
+
+	fmt.Println("Rebuilding")
+	b.BuildPartial("/does-not-exist")
+
+	b.AssertFileContent("public/404.html", `Not found. Updated.`)
+}
diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go
index 0b17a8db0..669114c8a 100644
--- a/hugolib/resource_chain_test.go
+++ b/hugolib/resource_chain_test.go
@@ -67,11 +67,11 @@ FIT: {{ $fit.Name }}|{{ $fit.RelPermalink }}|{{ $fit.Width }}
 CSS integrity Data first: {{ $cssFingerprinted1.Data.Integrity }} {{ $cssFingerprinted1.RelPermalink }}
 CSS integrity Data last:  {{ $cssFingerprinted2.RelPermalink }} {{ $cssFingerprinted2.Data.Integrity }}
 
-{{ $failedImg := resources.GetRemote "%[1]s/fail.jpg" }}
+{{ $failedImg := try (resources.GetRemote "%[1]s/fail.jpg") }}
 {{ $rimg := resources.GetRemote "%[1]s/sunset.jpg" }}
 {{ $remotenotfound := resources.GetRemote "%[1]s/notfound.jpg" }}
 {{ $localnotfound := resources.Get "images/notfound.jpg" }}
-{{ $gopherprotocol := resources.GetRemote "gopher://example.org" }}
+{{ $gopherprotocol := try (resources.GetRemote "gopher://example.org") }}
 {{ $rfit := $rimg.Fit "200x200" }}
 {{ $rfit2 := $rfit.Fit "100x200" }}
 {{ $rimg = $rimg | fingerprint }}
@@ -79,10 +79,10 @@ SUNSET REMOTE: {{ $rimg.Name }}|{{ $rimg.RelPermalink }}|{{ $rimg.Width }}|{{ le
 FIT REMOTE: {{ $rfit.Name }}|{{ $rfit.RelPermalink }}|{{ $rfit.Width }}
 REMOTE NOT FOUND: {{ if $remotenotfound }}FAILED{{ else}}OK{{ end }}
 LOCAL NOT FOUND: {{ if $localnotfound }}FAILED{{ else}}OK{{ end }}
-PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ . | safeHTML }}{{ end }}
+PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ .Value | safeHTML }}{{ end }}
 PRINT PROTOCOL ERROR2: {{ with $gopherprotocol }}{{ .Err | safeHTML }}{{ end }}
-PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}Err: {{ .Err | safeHTML }}{{ with .Err }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}
-FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}|
+PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}{{ with .Err }}Err: {{ . | safeHTML }}{{ with .Cause }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}{{ end }}
+FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with .Cause }}{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}{{ end }}{{ end }}|
 `, ts.URL))
 
 	fs := b.Fs.Source
@@ -114,8 +114,8 @@ SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d
 FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200
 REMOTE NOT FOUND: OK
 LOCAL NOT FOUND: OK
-PRINT PROTOCOL ERROR DETAILS: Err: error calling resources.GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"||
-FAILED REMOTE ERROR DETAILS CONTENT: |failed to fetch remote resource from &#39;%[2]s/fail.jpg&#39;: Not Implemented|Body: { msg: failed }
+PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at <resources.GetRemote>: error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"|
+FAILED REMOTE ERROR DETAILS CONTENT: failed to fetch remote resource from &#39;%[2]s/fail.jpg&#39;: Not Implemented|Body: { msg: failed }
 |StatusCode: 501|ContentLength: 16|ContentType: text/plain; charset=utf-8|
 
 
diff --git a/markup/highlight/highlight_integration_test.go b/markup/highlight/highlight_integration_test.go
index d36bc820a..e6cc16d2e 100644
--- a/markup/highlight/highlight_integration_test.go
+++ b/markup/highlight/highlight_integration_test.go
@@ -103,3 +103,29 @@ xəx := 0
 		<span class="nx">xəx</span>
 	`)
 }
+
+func TestHighlightClass(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+[markup.highlight]
+noClasses = false
+wrapperClass = "highlight no-prose"
+-- content/_index.md --
+---
+title: home
+---
+§§§go
+xəx := 0
+§§§
+-- layouts/index.html --
+{{ .Content }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html", `
+		 <div class="highlight no-prose"><pre
+	`)
+}
diff --git a/resources/page/testhelpers_test.go b/resources/page/testhelpers_test.go
index 8a2d28e31..8e6dfb79a 100644
--- a/resources/page/testhelpers_test.go
+++ b/resources/page/testhelpers_test.go
@@ -111,10 +111,6 @@ type testPage struct {
 	sectionEntries []string
 }
 
-func (p *testPage) Err() resource.ResourceError {
-	return nil
-}
-
 func (p *testPage) Aliases() []string {
 	panic("testpage: not implemented")
 }
diff --git a/resources/resource_factories/create/create_integration_test.go b/resources/resource_factories/create/create_integration_test.go
index 17084574d..7b9c96e34 100644
--- a/resources/resource_factories/create/create_integration_test.go
+++ b/resources/resource_factories/create/create_integration_test.go
@@ -31,18 +31,17 @@ func TestGetRemoteHead(t *testing.T) {
   [security.http]
     methods = ['(?i)GET|POST|HEAD']
     urls = ['.*gohugo\.io.*']
-
 -- layouts/index.html --
 {{ $url := "https://gohugo.io/img/hugo.png" }}
 {{ $opts := dict "method" "head" }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
   {{ with .Err }}
     {{ errorf "Unable to get remote resource: %s" . }}
-  {{ else }}
+  {{ else with .Value }}
     Head Content: {{ .Content }}. Head Data: {{ .Data }}
-  {{ end }}
-{{ else }}
+  {{ else }}
   {{ errorf "Unable to get remote resource: %s" $url }}
+  {{ end }}
 {{ end }}
 `
 
@@ -90,14 +89,15 @@ mediaTypes = ['text/plain']
 -- layouts/_default/single.html --
 {{ $url := printf "%s%s" "URL" .RelPermalink}}
 {{ $opts := dict }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
   {{ with .Err }}
-    {{ errorf "Got Err: %s. Data: %v" . .Data }}
-  {{ else }}
+     {{ errorf "Got Err: %s" . }}
+	 {{ with .Cause }}{{ errorf "Data: %s" .Data }}{{ end }}
+  {{ else with .Value }}
     Content: {{ .Content }}
+  {{ else }}
+    {{ errorf "Unable to get remote resource: %s" $url }}
   {{ end }}
-{{ else }}
-  {{ errorf "Unable to get remote resource: %s" $url }}
 {{ end }}
 `
 
diff --git a/testscripts/commands/hugo__path-warnings_issue13164.txt b/testscripts/commands/hugo__path-warnings_issue13164.txt
new file mode 100644
index 000000000..1342c287a
--- /dev/null
+++ b/testscripts/commands/hugo__path-warnings_issue13164.txt
@@ -0,0 +1,15 @@
+hugo --printPathWarnings
+
+! stderr 'Duplicate target paths'
+
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- assets/foo.txt --
+foo
+-- layouts/index.html --
+A: {{ (resources.Get "foo.txt").RelPermalink }}
+B: {{ (resources.GetMatch "foo.txt").RelPermalink }}
+C: {{ (index (resources.Match "foo.txt") 0).RelPermalink }}
+D: {{ (index (resources.ByType "text") 0).RelPermalink }}
+-- layouts/unused/single.html --
+{{ .Title }}
diff --git a/tpl/images/images_integration_test.go b/tpl/images/images_integration_test.go
index 003422aed..0f89dbe09 100644
--- a/tpl/images/images_integration_test.go
+++ b/tpl/images/images_integration_test.go
@@ -14,8 +14,10 @@
 package images_test
 
 import (
+	"strings"
 	"testing"
 
+	qt "github.com/frankban/quicktest"
 	"github.com/gohugoio/hugo/hugolib"
 )
 
@@ -49,3 +51,52 @@ fileExists2 OK: true|
 imageConfig2 OK: 1|
 `)
 }
+
+func TestQR(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- layouts/index.html --
+{{- $text := "https://gohugo.io" }}
+{{- $optionMaps := slice
+    (dict "text" $text)
+    (dict "text" $text "level" "medium")
+    (dict "text" $text "level" "medium" "scale" 4)
+    (dict "text" $text "level" "low" "scale" 2)
+    (dict "text" $text "level" "medium" "scale" 3)
+    (dict "text" $text "level" "quartile" "scale" 5)
+    (dict "text" $text "level" "high" "scale" 6)
+    (dict "text" $text "level" "high" "scale" 6 "targetDir" "foo/bar")
+}}
+{{- range $k, $opts := $optionMaps }}
+    {{- with images.QR $opts }}
+<img data-id="{{ $k }}" data-img-hash="{{ .Content | hash.XxHash }}" data-level="{{ $opts.level }}" data-scale="{{ $opts.scale }}" data-targetDir="{{ $opts.targetDir }}" src="{{ .RelPermalink }}">
+    {{- end }}
+{{- end }}
+`
+
+	b := hugolib.Test(t, files)
+	b.AssertFileContent("public/index.html",
+		`<img data-id="0" data-img-hash="6ccacf8056c41475" data-level="" data-scale="" data-targetDir="" src="/qr_3601c357f288f47f.png">`,
+		`<img data-id="1" data-img-hash="6ccacf8056c41475" data-level="medium" data-scale="" data-targetDir="" src="/qr_3601c357f288f47f.png">`,
+		`<img data-id="2" data-img-hash="6ccacf8056c41475" data-level="medium" data-scale="4" data-targetDir="" src="/qr_3601c357f288f47f.png">`,
+		`<img data-id="3" data-img-hash="c29338c3d105b156" data-level="low" data-scale="2" data-targetDir="" src="/qr_232594637b3d9ac1.png">`,
+		`<img data-id="4" data-img-hash="8f7a639cea917b0e" data-level="medium" data-scale="3" data-targetDir="" src="/qr_5c02e7507f8e86e0.png">`,
+		`<img data-id="5" data-img-hash="2d15d6dcb861b5da" data-level="quartile" data-scale="5" data-targetDir="" src="/qr_c49dd961bcc47c06.png">`,
+		`<img data-id="6" data-img-hash="113c45f2c091bc4d" data-level="high" data-scale="6" data-targetDir="" src="/qr_17994d3244e3c686.png">`,
+		`<img data-id="7" data-img-hash="113c45f2c091bc4d" data-level="high" data-scale="6" data-targetDir="foo/bar" src="/foo/bar/qr_abd2f7b221eee6ea.png">`,
+	)
+
+	files = strings.ReplaceAll(files, "low", "foo")
+
+	b, err := hugolib.TestE(t, files)
+	b.Assert(err.Error(), qt.Contains, "error correction level must be one of low, medium, quartile, or high")
+
+	files = strings.ReplaceAll(files, "foo", "low")
+	files = strings.ReplaceAll(files, "https://gohugo.io", "")
+
+	b, err = hugolib.TestE(t, files)
+	b.Assert(err.Error(), qt.Contains, "cannot encode an empty string")
+}
diff --git a/tpl/templates/templates_integration_test.go b/tpl/templates/templates_integration_test.go
index 301f783a5..aa5540862 100644
--- a/tpl/templates/templates_integration_test.go
+++ b/tpl/templates/templates_integration_test.go
@@ -93,3 +93,37 @@ Home: true
 
 `)
 }
+
+func TestTry(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+-- layouts/index.html --
+Home.
+{{ $g :=  try ("hello = \"Hello Hugo\"" | transform.Unmarshal)   }}
+{{ with $g.Err }}
+Err1: {{ . }}
+{{ else }}
+Value1: {{ $g.Value.hello | safeHTML }}|
+{{ end }}
+{{ $g :=  try ("hello != \"Hello Hugo\"" | transform.Unmarshal)   }}
+{{ with $g.Err }}
+Err2: {{ . | safeHTML }}
+{{ else }}
+Value2: {{ $g.Value.hello | safeHTML }}|
+{{ end }}
+Try upper: {{ (try ("hello" | upper)).Value }}
+Try printf: {{ (try (printf "hello %s" "world")).Value }}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html",
+		"Value1: Hello Hugo|",
+		"Err2: template: index.html:",
+		"Try upper: HELLO",
+		"Try printf: hello world",
+	)
+}
diff --git a/tpl/tplimpl/tplimpl_integration_test.go b/tpl/tplimpl/tplimpl_integration_test.go
index dbadece4e..04abb5b31 100644
--- a/tpl/tplimpl/tplimpl_integration_test.go
+++ b/tpl/tplimpl/tplimpl_integration_test.go
@@ -698,3 +698,39 @@ Home!
 	b.BuildPartial("/mybundle1/")
 	b.AssertFileContent("public/mybundle1/index.html", "Baseof!!")
 }
+
+func TestQRShortcode(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- layouts/index.html --
+{{ .Content }}
+-- content/_index.md --
+---
+title: home
+---
+{{< qr
+	text="https://gohugo.io"
+	level="high"
+	scale=4
+	targetDir="codes"
+	alt="QR code linking to https://gohugo.io"
+	class="my-class"
+	id="my-id"
+	title="My Title"
+/>}}
+
+{{< qr >}}
+https://gohugo.io"
+{{< /qr >}}
+`
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html",
+		`<img src="/codes/qr_1ad588024620edf6.png" width="148" height="148" alt="QR code linking to https://gohugo.io" class="my-class" id="my-id" title="My Title">`,
+		`<img src="/qr_7307530a297f2cc9.png" width="132" height="132">`,
+	)
+}