Templater icon indicating copy to clipboard operation
Templater copied to clipboard

feat: support merging frontmatter with `tp.file.include`

Open AndyEveritt opened this issue 3 months ago • 6 comments

This adds support for merging template frontmatter when using tp.file.include

eg:

Root

---
some_frontmatter:
root_func: <% tp.date.now() %>
not_overwritten: 1
overwritten: 1
---
<%- tp.file.include("[[Sub]]") %>

top content

Sub

---
sub: 2
not_overwritten:
overwritten: 2
---

sub content
<% tp.file.include("[[Sub 2]]") %>

Sub 2

---
sub_func: <% tp.date.now() %>
---
sub 2 content

The expected output for this template is

---
some_frontmatter:
root_func: 2025-09-24
not_overwritten: 1
overwritten: 2
sub_func: 2025-09-24
sub: 2
---
sub content
sub 2 content

top content

This also works with tp.file.create_new() and files created with Bases

AndyEveritt avatar Sep 24 '25 11:09 AndyEveritt

For details on how this feature works:

  • The frontmatter for the template is maintained separately from the contents of the template by adding a new field to RunningConfig called frontmatter
  • Parsing of <% tp... %> blocks is executed
    • Any tp.file.include has it's frontmatter processed, merged into the running_config.frontmatter, and removed from the included content
  • Once parsing of the root template has finished, any frontmatter is extracted
    • It is done at this point so tp functions like tp.date.now() are still parsed correctly
  • The running_config.frontmatter is merged into the root frontmatter and running_config.frontmatter is updated with the result
    • It is done this way so included templates overwrite the parent if they have overlapping property keys
  • The running_config.frontmatter is now a parsed & merged frontmatter for the root and all included templates
  • Depending on how templater was initiated the following happens:
    • create_new_note_from_template(): this is from tp.file.create_new() or the Obsidian command Templater: Create new note from template, the output_content is updated to have the merged frontmatter
    • append_template_to_active_file(): the merged template frontmatter is merged into any existing file frontmatter
    • write_template_to_file: frontmatter is merged with existing file frontmatter. Existing file frontmatter will typically only exist if the file was created by a Base

AndyEveritt avatar Sep 24 '25 12:09 AndyEveritt

Btw I think there is a fundamental issue with the config system in Templater. When you have nested calls such as a tp.file.create_new() inside a template included with tp.file.include(), the config for create_new overwrites the config for include so even after the create_new has finished processing, the include config is uses the create_new config.

This essentially means that config.target_file, config.active_file and the new config.frontmatter are now pointing to the wrong thing which will cause various weird issues.

In this case it means if you have a template which is includes another template that creates a file, the frontmatter of the template which creates the file won't be merged into the frontmatter of the root template.

AndyEveritt avatar Sep 24 '25 16:09 AndyEveritt

I've fixed the config issue by creating a new instance of the modules in functions_object each time the Templater.parse_template() is called. Before a singleton was used for each InternalModule which was leading to issues as the configs were getting overwritten

AndyEveritt avatar Sep 24 '25 17:09 AndyEveritt

@Zachatoo have you had a chance to review this?

AndyEveritt avatar Oct 14 '25 15:10 AndyEveritt

Sorry I have not! I'll need to do some testing with this, hopefully I'll have time this weekend

Zachatoo avatar Oct 17 '25 04:10 Zachatoo

Any chance this PR can be merged since it has no conflicts ?

Just tested and it works flawlessly. Thanks @AndyEveritt

Fred-Vatin avatar Dec 11 '25 06:12 Fred-Vatin