renderTemplate syscall is including templates tag

Hi there, I’m new to SilverBullet and am looking to use it for D&D player notes.

I have a page for my campaign, and am trying to create a command to automatically instantiate a Location template at a path relative to my campaign page. The idea is that a “New Location” button will be clicked on the campaign page which will run the command.

Here’s my current command:

  silverbullet.registerCommand({name: "New D&D Campaign Location", hide: false}, async () => {
    const name = await editor.getCurrentPage()
    const tmpl = await space.readPage("Custom/Templates/Location")
    const rendered = await template.renderTemplate(tmpl, {}, { page: tempPageMeta, config })
    const path = `${name}/Locations/New Location`
    
    await space.writePage(path, rendered)
    editor.navigate(path)
})

When I run this command, the page is created in the correct location, but the template frontmatter is not transformed and is instead placed in full. Example:

---
tags:
- template
frontmatter:
  tags:
  - location
  dateCreated: "2024-09-18"
---

# Location

Test Location

This is my template:

---
tags:
- template
frontmatter:
  tags:
  - location
  dateCreated: "{{today}}"
---

# Location

Test Location

I feel as though the way I’m (trying) to do this is very hacky. If anyone has any suggestions, I’d love to hear them.

Thanks :slight_smile:

I’m trying to achieve something similar and you got me started on the process.

Unfortunately, after some testing and some browsing in the SilverBullet code base. It seems to be by design that the exposed syscall to render a template keeps the full frontmatter from the template instead of keeping only the content from the “child” frontmatter.

As you can see here, a renderTemplate function (different one, unavailable to space-script) uses the template.renderTemplate syscall. However, it does some other operations to extract the frontmatter “child” object before rendering the template using the syscall function.

@zef Would it be possible to expose a different template rendering syscall that process the frontmatter in the same way as Page: From Template does? Manually doing that processing in space-script would work but it seems counterproductive as the required functions already exist in the code base. I’d love to contribute but I’m not proficient in java script unfortunately.

EDIT: I just saw that space-lua seems to be progressing well. So maybe it is something to be included in the Lua API instead?

The code below is quite ugly and done with some help from chatGPT but I think it gets you what you were looking for.

    silverbullet.registerCommand({name: "New D&D Campaign Location", hide: false}, async () => {

    const name = await editor.getCurrentPage()
    const rawTemplate = await space.readPage("Library/Personal/Location")
    let {frontmatter, templateBody} = splitTemplate(rawTemplate)
    frontmatter = await renderFrontmatter(frontmatter)
    if (frontmatter) {
      templateBody = '---\n' + frontmatter + '---\n' + templateBody
    }
    
    const rendered = await template.renderTemplate(templateBody, {}, {})

      
    const path = `${name}/Locations/New Location`
    await space.writePage(path, rendered)
    editor.navigate(path)
})

function splitTemplate(rawTemplate) {
  // Regex pattern to extract the frontmatter (between '---' markers)
  const frontmatterPattern = /^---\n([\s\S]*?)\n---/;
  const frontmatterMatch = rawTemplate.match(frontmatterPattern);

  let frontmatter = null;
  let templateBody = rawTemplate;
  
  // If frontmatter found, split template
  if (frontmatterMatch) {
    frontmatter = frontmatterMatch[1]
    templateBody = rawTemplate.slice(frontmatterMatch[0].length).trim()
  }

  return {
    frontmatter: frontmatter || '',
    templateBody: templateBody
  };
}

async function renderFrontmatter(frontmatter) {
    const frontyaml = await YAML.parse(frontmatter)
  
  if (frontyaml && frontyaml.frontmatter) {
    const frontmatterYaml = await YAML.stringify(frontyaml.frontmatter)
    return frontmatterYaml;
    }
    return null;  
}