Include-file shortcode context issues

#1

We defined the following shortcode to include Markdown (MD) files from other MD files. The main incentive is code reuse for longer snippets that also include shortcode calls; (for simple short text snippets we use shortcodes):

{{- with .Site.GetPage (.Get 0) -}}
    {{- .Content | markdownify -}}
{{- end -}}

This works except for the following issue: some of the shortcode calls in our include files use page Scratch context variables that are set with a partial, so the same shortcode call might translate differently depending from where it’s called. We mainly use this to derive the version of the active page, so we can refer to matching versioned pages or data variables without specifying the version in the shortcode call. The problem is that the context for the include-file shortcode calls is set according to the context of the include file (which is unversioned) instead of the context of the “calling” page.

I tried the following code, to print the content outside of the include-page context in the with clause, but this didn’t help because it seems the content is processed before the printf call:

{{ $content := "" -}}
{{- with .Site.GetPage (.Get 0) -}}
  {{ $content = .Content -}}
{{- end -}}
{{- printf "%s" $content | markdownify -}}

I also tried, to get the content of the included page without using with, so as not to apply this context, but this failed (perhaps because the include files don’t have metadata and aren’t “real” pages).

I thought that using readFile instead of GetPage might help, but I found that with readFile the shortcode calls in the included file aren’t processed.

I also tried setting in the include shortcode a Scratch context variable for the include file (e.g., set it to the context of the “calling” page), so I can and then edit the relevant shortcodes to check for this context variable. But I found that this context variable wasn’t recognized in the shortcodes that were called from the include file.

Include-file shortcode for including files with shortcode calls
Shortcode processing order
#2

Since Hugo 0.52. there is a new feature called Inline Shortcodes that enables the use of template functions and partials from within content files. It can also be used to call other shortcodes and it might solve the context issues you describe above.

Also I would think that it would be best if you provided a test repository with your use case.

As stated in the Requesting Help Topic (big pink banner at the top of this forum) users need to provide the source code of a project because the entire context needs to be seen.

If the project in question cannot be shared, I recommend that you create a minimal repo with sample content that illustrates the problem you describe.

#3

Thanks @onedrawingperday. I think I recall seeing something about the inline shortcodes in the Hugo release notes, but I hadn’t had time to check it yet.

I can’t share my entire repo but I can share relevant code. I didn’t share it yet because the context-related shortcodes are a bit complicated and I wanted to simplify the discourse question as much as possible. I’ll try to find the time to create a simplified version to demonstrate the issue.

#4

UPDATE: The context-sensitive shortcode uses that are at the core of this issue are all related to automatic detection of the version of the active page: we have a partial that automatically detects the version of the active page from the URL and sets Scratch context version variables that we then use from other partials and shortcodes. The incentive is that we have <section>/vX.Y/ and <section>/latest-release/ versioned-content directories on our site, and I wanted to support relative cross references to pages in the same version (but not necessarily in the same section) and using version-specific data variables without specifying the version in the related shortcode call. This way, when a new version is released, for example, I can just duplicate the current latest-release directories to new version directories without having to change the version number in all the shortcode calls (and it also simplifies comparing files in different version directories). (Our shortcodes also have the option of specifying a fixed version where relevant.)

The problem that I encountered with the include shortcode is that I was including a file from an unversioned content/_includes directory, and the context version variables for the shortcode calls in the included files were always calculated according to the context of included file and not the including file, and I wasn’t able to override this.

My current solution is to use version subdirectories in the includes directory, as we do in all the versioned section directories (and which actually makes sense for our site design). I modified the include shortcode so that by default it automatically calculates the includes subdirectory according to the version of the including page that called the include shortcode (using the same partial that we use in our xref shortcode etc.). By default, non-versioned content uses the latest-release include file, but this can be overridden via the shortcode params, and it’s also possible to set a hardcoded include-file path in the shortcode call.

I also configure a default includes root directory in config.toml (and a variable for internal infra tests):

[Params]
...
  ## Included-Files Root Content Directory
  includes_dir = "_includes"
  # Infra-Tests Included-Files Root Content Directory
  test_includes_dir = "_test-includes"

This is my final include.html shortcode. (It also supports optional filter and filterval params for filtering out content using our custom filter site params).

{{- if or (not (.Get "filter")) (eq (.Get "filterval") (index $.Site.Params.Filters (.Get "filter"))) -}}
  {{- partial "func/product/set-ctx-product-version.html" (dict "ctx" .) -}}
  
  {{- $includes_dir := .Get "d" -}}
  {{- if eq $includes_dir "" -}}
    {{- $vdir := cond (eq (.Get "vdir") "none") "" (printf "/%s" ((.Get "vdir") | default (.Scratch.Get "ctxProductVersionDir"))) -}}

    {{- $includes_dir = printf "%s%s" (cond (eq (.Get "t") "tests") .Site.Params.test_includes_dir .Site.Params.includes_dir) $vdir -}}
  {{- end -}}
  {{- $include_file := printf "%s/%s" $includes_dir (.Get "f") -}}

  {{- with .Site.GetPage $include_file -}}
    {{- .Content | markdownify -}}
  {{- end -}}
{{- end -}}