Can I call extend or export functions inside a custom Leaf tag?

Hi. Can I call #extend or #export inbuilt tags inside my own custom Leaf tag? Or is it possible to load a leaf template from a string instead of a file?

What I am trying to achieve is to be able to load outer (layout) HTML conditionally without creating additional files. So that my route handler can either return a fragment of the page or the whole page depending on a certain condition without a need for an extra template. Basically, I am experimenting with htmx and creating some helpers for it. :slight_smile:

Thanks and sorry for a messy post.

I don't think you can, those tags are treated as special cases in Leaf so I don't think the behaviour is exposed to tags. Worth playing around though!

You should be able to load templates as a string, there are some examples in the tests.

As for doing conditional stuff like that, generally it doesn't play well with Leaf as it's designed to be a daily basic templating language. You would probably be better off having a function to work out the template name and pass in the correct template you want for that use case

Thanks for a suggestion. In the end, conditionally returning dynamically generated templates worked well. Meta templating for template files. :slight_smile:

For future references for those who are interested – I managed to implement a custom LeafSource struct such as:

struct HtmxPageLeafSource: LeafSource { // implementation ... }

and then registered it in configure.swift:

// Copying the default init for sources
app.leaf.sources = .singleSource(NIOLeafFiles(fileio: app.fileio, limits: .default, sandboxDirectory: app.leaf.configuration.rootDirectory, viewDirectory: app.leaf.configuration.rootDirectory))
// Adding a custom source
try app.leaf.sources.register(source: "htmx", using: HtmxPageLeafSource(), searchable: true)

Now, the file method of the LeafSource protocol would check for a special pattern in the template name and interpolate it to generate a custom template. However, such a template must not exist in the filesystem because this custom LeafSource is the last one in the search path as defined in the LeafSources.

So, as an example, my file method checks if the template name starts with "--page/" and if it does, it treats the remainder of the template name as a parameter and returns something like this (as a buffer, of course):

let result =
            #extend("index-base"): #export("body"): #extend("\(String(remainder))") #endexport #endextend

Where the remainder is the non-zero length remainder after dropping "--page/" part of the template name.

In all other case it will return an error which will make the leaf work as it always does.

1 Like