feat: support merging frontmatter with `tp.file.include`
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
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
RunningConfigcalledfrontmatter - Parsing of
<% tp... %>blocks is executed- Any
tp.file.includehas it's frontmatter processed, merged into therunning_config.frontmatter, and removed from the included content
- Any
- Once parsing of the root template has finished, any frontmatter is extracted
- It is done at this point so
tpfunctions liketp.date.now()are still parsed correctly
- It is done at this point so
- The running_config.frontmatter is merged into the root frontmatter and
running_config.frontmatteris updated with the result- It is done this way so included templates overwrite the parent if they have overlapping property keys
- The
running_config.frontmatteris 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 fromtp.file.create_new()or the Obsidian commandTemplater: Create new note from template, theoutput_contentis 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
-
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.
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
@Zachatoo have you had a chance to review this?
Sorry I have not! I'll need to do some testing with this, hopefully I'll have time this weekend
Any chance this PR can be merged since it has no conflicts ?
Just tested and it works flawlessly. Thanks @AndyEveritt