hugo icon indicating copy to clipboard operation
hugo copied to clipboard

Add .HasCodeBlock

Open writeonlycode opened this issue 1 year ago • 3 comments

Hugo already has a .HasShortCode that returns true if a given shortcode was used in the page. I't useful when, for example, we want to include some CSS or JS in the head to handle the content of the shortcode. It would be nice to have a .HasCodeBlock as well, for when we need to include some CSS or JS in the head to handle the content of particular code-blocks.

For example, mermaid diagrams are typically implemented with code blocks like this:

```mermaid
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
\```

It would be nice to be able to add something like this in the head:

{{ if .HasCodeBlock "mermaid" }}
...
{{ end }}

It's already possible to do something similar by setting a variable in the codeblock hook, and then adding the JS in the footer or something. But there is currently no way to add it in the head, before the {{ .Content }} is called. I think it would be nice to have this option, as it's typically recommended by the packages to add things in the head of the page. And it's nice to keep all the resources in the same place in the head of the page. (Without using some variable in the front matter like hasMermaid = true...)

writeonlycode avatar Mar 06 '23 03:03 writeonlycode

I agree, but it needs to wait a little, and I'll spend a few lines to explain why:

  • We do lazy rendering of .Content, mostly to avoid doing the work if it's never used.
  • We have some methods that depends on .Content (e.g. .WordCount) which leads to a rather clumsy init setup. Also, the root Page API has become rather big and is difficult to document (we're working on a auto generated reference).
  • This situation became even more visible in the recent Hugo edition where we added .Fragments (where you now can do .Fragments.Identifiers.Contains "my-page-heading") and split the parsing and rendering of the content (calling just .Fragments is very effective).
  • I have propsed that we add an RenderContent that returns a struct. With .Fragments in mind, we should probably also have a ParseContent method. When I'm getting around to wrapping up my million pages branch that should also allow us to pass a context (cache) identifiers to these, e.g. RenderContent "home".
  • With that done, it should be much easier to do .ParseContent.CodeBlocks.Identifiers.Contains, .ParseContent.ShortCodes.Identifiers.Contains, .ParseContent.Fragments.Identifiers.Contains etc. and get a much simpler and easier to understand API.

bep avatar Mar 06 '23 08:03 bep

But there is currently no way to add it in the head, before the {{ .Content }} is called.

You can force content to be rendered anywhere with something like:

<head>
  ...
  {{ $noop := .WordCount }}
  {{ if .Store.Get "hasMermaid" }}
    ...
  {{ end  }}
  ...
</head>

Any of these will force content rendering:

Content
FuzzyWordCount
Len
Plain
PlainWords
ReadingTime
Summary (regardless of how you define it)
Truncated
WordCount

jmooring avatar Nov 26 '23 21:11 jmooring

Oh, that's a nice workaround. Thanks!

writeonlycode avatar Nov 27 '23 09:11 writeonlycode