Nesting shortcodes

I’m trying to use shortcodes, nested.

For instance, I have an aside shortcode and an abbr shortcode, and use them like this:

{{< aside >}}
The {{< abbr "GUI" >}} is *fantastic*.
{{< /aside >}}

Hoping to get this result:

<aside>
The <abbr>GUI</abbr> is <strong>fantastic</strong>.
</aside>

However, I get this result:

<aside>
The <!-- raw HTML omitted -->GUI<!-- raw HTML omitted --> is <strong>fantastic</strong>.
</aside>

How can I allow a shortcode to be passed content, that it’ll parse as Markdown, allowing shortcodes to be used? Essentially, treating that content just like top-level content is treated in a Markdown page.

Sources

Here’s aside.html:

<aside>
	({{ $.Page.RenderString .Inner }})
</aside>

And abbr.html:

{{- /**/ -}}
<abbr>{{ .Get 0 }}</abbr>
{{- /**/ -}}

Alternatives tried

Of note, I do not wish to disable safe HTML rendering.

I’ve already tried printing .Inner directly, instead of passing it through $.Page.RenderString, but that has the opposite problem to the current solution: it properly renders the abbr shortcode, but lets the raw Markdown through.

I found this old thread that seems to indicate there is no solution, but that seems very surprising, and the thread is four years old: Shortcode - nested shortcode, multiple inner - #10 by PaulMuaddib

That example does not contain any Markdown.

Haha, oops, indeed! I just edited the example to include Markdown. I’ve also aded the second paragraph in “Alternatives tried” at the bottom.

The only way to handle this is to set:

[markup.goldmark.renderer]
unsafe = true

But it’s not unsafe if you control the content.

Thank you for the answer. I’ve been resisting enabling unsafe mode because of two reasons: I like the idea of keeping custom HTML out of the Markdown content; and I really want HTML comments (that I leave in my Markdown content) to be stripped out of the final output.

The former is something I should reconsider, maybe, but the latter—comment stripping—doesn’t seem to be possible with Goldmark. Should I write a shortcode that prints nothing, and switch my comments over to that?

But that is exactly what you are doing when you pass a string containing Markdown and HTML through the Markdown renderer (Goldmark) when calling the .Page.RenderString method.

With this project configuration:

[markup.goldmark.renderer]
unsafe = true

And this Markdown:

This is **bold** text.

<!-- a comment -->

This is _emphasized_ text.

The published file looks like this:

<p>This is <strong>bold</strong> text.</p>
<p>This is <em>emphasized</em> text.</p>

…If you minify the output when you build your site:

hugo --minify

Which you should be doing in production anyway.

Thank you for the answer. Sorry for the wall of text below. TL;DR: things are good now, I have a mostly-satisfactory solution.

But that is exactly what you are doing when you pass a string containing Markdown and HTML through the Markdown renderer (Goldmark) when calling the .Page.RenderString method.

What I meant was, I like the idea of keeping custom HTML out of my Markdown files, out of my Markdown content. It feels like it’ll make it easier to move it all to a different site generator, or a different website, or theme, etc, if that ever comes up. I might be over-indexing on that, though; and I can try to enforce that separation even with unsafe rendering enabled, after all.

(going on a tangent here, but) I disagree! I really enjoy, when browsing the Web, being able to read the raw source of sites I visit. A site with human-legible HTML automatically feels much more like a labor of love to me; it’s welcoming and open, reminds me of the handcrafted values of the older Web. There’s obviously good reasons for minification—though the effect might be smaller than people assume—but in my case, I’ve gone the other way, and added tidying of the source as part of the build process!

(while auto-formatting is not as good as having the source be nice in the first place, it’s hard to get indentation and spacing all correct with a site generator, so, that’s the choice I made here)

Anyway, from your answer I have a working solution. It took a bit of extra effort (detailed below for the curious) because of other factors, but that’ll do. Thank you!

I still strongly feel that Hugo should enable nesting shortcodes. As someone who’s admittedly not familiar with the Hugo source, and has a tenuous grasp on its mental model, it’s very surprising that something that feels so essential isn’t supported.

The final solution (pretty personal)

Adding the --minify build argument breaks prettier, which I use to tidy the final source, as part of the build process, so I have to go another way. (Hugo’s minify removes the auto-closing slash from <link/> elements, which prettier doesn’t seem to accept)

I ended up stripping comments as a post-process, using strip-comments. This runs the risk or removing comments that are part of the content (e.g. in a code block), but that should be fine (I could just use HTML entities to make these comments escape stripping).

This needs to run on all output files: not just the HTML, but also the XML feed.

Do you really read the source or do you look at the sites in your browser developer tools? The latter seems more private and useful to me.

I do read the source on occasion, yes, but the parsed DOM tree is indeed much more practical. The pleasure of shipping/reading legible HTML really a vibes thing!

And the parsed DOM tree is easily legible, regardless of --minify. Perfectly indented and all that…

So, why even bother about HTML if most of it is generated by Hugo which likes to add white space all over…

Thank you for your curiosity. Maybe I wasn’t clear enough: I’m doing making my output HTML nice because that makes me feel good! That’s the reason.

It’s a very subjective, human thing. If you don’t feel the same way about it, I don’t think you’ll succeed into reasoning your way into an understanding, sadly, though I’m happy to help if you do want to keep trying.