Help needed with JS for Templater

Hi everyone,

I want to improve my workflow for processing new content.

When I read a new article in Obsidian, I want to create highlight notes instantly, right where I encounter these highlights in the article.

I want to use Templater to do the following:

  1. Create a new note from a template.
  2. Give the new note a title. The title should be a concatenation of the original filename of the article + “_H” + i. Since I will likely encounter more than one highlight in the article, I want to append numbered suffixes. The first highlight gets the suffix “_H1”, the second highlight gets the suffix “_H2”, and so on. This is why I am using a while-loop and the “i” counter.
  3. Insert a link to the newly created file.

This is what I have tried, but I am not sure how to debug it, so I am not sure what the error is. I apologize for that, but I hope my description and code attempt are clear enough to get help from you.

Thank you in advance!

<%* //create a highlight-note from the highlight-note template (!!! not the TEMPLATER-template)

const template = tp.file.find_tfile("highlight-note template")

let i = 1
while (tp.file.exists(tp.file.path(true) + tp.file.title + "_H" + i) == true) {

const filename = tp.file.path(true) + "/" + tp.file.title + "_H" + i

const folder = app.vault.getAbstractFileByPath(tp.file.path(true))

await tp.file.create_new(template, filename, false, folder)

//create a link to the newly created journal note
tR += "![[" + filename + "]]"; %>

The function tp.file.path(true) returns not just the path to the current note; it includes the note’s file name. Say you’re running your script on a note file titled report that’s contained in FolderA of the vault. Then tp.file.path(true) will give you the value FolderA/

For your script to work as intended, you need to get rid of this file name. Maybe something like the following:

const template = tp.file.find_tfile('highlight-note template');

const path = tp.file.path(true);
const slash = path.lastIndexOf('/');
let folder = '';
if (slash > 0) folder = path.slice(0,splash+1);

let i = 1;
while(tp.file.exists(`${folder}${tp.file.title}_H${i}`)) { i++; }

let filename = `${tp.file.title}_H${i}`;

await tp.file.create_new(template, filename, false, folder);

tR += `![[${filename}]]`;

Dear Ninjineer,

Thank you very much for providing the corrected code and explanation! I tried running it, but after a while, Obsidian stopped responding and I received the error message shown in the attached image. I had to close Obsidian using the task manager. It appears that the code may have entered an endless loop. Could this be the issue?


Also, could you please add some comments explaining why you used the slash variable? Based on your explanation, it seems that the function tp.file.path(true) returns the entire filename including the path. In that case, wouldn’t it be simpler to just use the return string of the tp.file.path(true) function as the filename variable?

Thank you again for your help!

Best regard,

First off, there’s a typo in the following line of my code:

if (slash > 0) folder = path.slice(0,splash+1);

I inadvertently typed splash when I meant slash

As for the use of slash, here’s more detailed explanation:

//If you're working with note `report-today` in folder `reports` which is in 
//the `projects` folder which is the root level of the vault:

const path = tp.file.path(true);
//Returns the note file's path relative to the vault root. It includes the 
//file's `.md` extension
console.log(`path = '${path}'`);
//path = 'projects/reports/'

const slash = path.lastIndexOf('/');
//This command looks for the first `/` character starting from the end of the 
//string and returns its position in the string. To the right of this location 
//is the note's file name. To the left is relative path within the vault where
//where that note is kept. `lastIndexOf` will return -1 if the search string 
//is not found in the source string, which here means the note file is located
//in the vault's root folder.
console.log(`slash = ${slash}`);
//slash = 16

let folder = '';
//Empty string here represents the root folder of the vault;

if (slash > 0) folder = path.slice(0,slash+1);
//If a `/` was found, chop off the file name portion
console.log(`folder = '${folder}'`);
//folder = 'projects/reports/'

I noticed that you said you used task manager to force quit Obsidian, which makes me think you’re using a Windows computer. If that’s the case, it’s possible that searching for a forward slash is the wrong character, since Windows uses back slashes to separate folders. I don’t have a way to test this, but tp.file.path() may not normalize this separator.

In this case the alternative described below might serve you better.

Since really it’s really the file extension that is reason you cannot use tp.file.path(true) directly, as an alternate you could do the following to just lop off the extension:

const path = tp.file.path(true);
const dot = path.lastIndexOf('.');
const srcfile = path.slice(0,dot);
console.log(`srcfile = '${srcfile}'`);
//srcfile = 'projects/reports/report-today'

Or if you want to assume the extension will always be .md, you can go even more succinct:

const srcfile = tp.file.path(true).slice(0,-3);

An infinite loop is certainly possible since you have a while statement. It would mean that tp.file.exists is returning true constantly. To confirm there’s a problem with this loop, I’d start by commenting out the while line and confirming that it will make the _H1 file correctly. Once that was done, I’d add back the while but I’d include a console.log() statement inside the loop that includes same content being passed to the tp.file.exists() function. This might clue you into why it’s never getting a false.

I just realized why my alternate of just eliminating the file extension won’t work so well. You still need the folder value to pass to the tp.file.create function.

You’re really going to have to figure out how your system represents paths as returned from tp.file.path so you can get to just the containing folder.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.