Super Meta Template Picker (SMTP) handles super-templates, superclases, subclasses in frontmatter

Hello,
this is the fourth iteration of Meta Template Picker, I am giving it a new name SMTP, where “S” is for super. We use super-templates based on dictionaries instead of the vanilla templates with only one class.

This templater template does:

  1. Creates a new note based on tens of templates that were generated from dictionaries
  2. Creates a note and automatically moves it to a designated folder in its own dictionary
  3. Creates a minimal note if the user presses Escape, or selects from the suggester.

Here is the code. This has to be copied to your templates folder.

<%* 
/*  Load all super templates
	1. Read all super templates from folder set below
	2. Load all variables from the super-templates into a super array 
	3. Each member of the new array will contain two arrays, "common" and "main" which are defined in each super template. "common" contains properties in common to one class; "main" is an array that contains properties that share a class.
	4. The purpose of the super-templates is reducing the number of templater files and make it easier to maintain
*/
const templatesFolder = ['Extras/Templates/super templates'];
const test = false;
const templateFiles = []   // names of super template files
for (const folder of templatesFolder) {
  const files = (await app.vault.adapter.list(folder))?.files || []
  files.sort((a, b) => a.localeCompare(b)) // alphabetically
  templateFiles.push(...files)
}
if (!templateFiles) return   // no super template found
const templates = templateFiles.map(path => {
	// read base names of super-templates
	const file = app.vault.getAbstractFileByPath(path)
	return file.basename
})
const superTemplates = [];   // array will contain all super-templates
for (item in templates) {    // iterate through each super-template
	superTemplates.push(await tp.file.include(`[[${templates[item]}]]`))
} // super template variables loaded in super array
let title = tp.file.title;   // grab current title
if (title.startsWith("Untitled")) {
	/*
		Create a note through a nested suggester of templates.
		Two options here:
		1. A new note prompt to type in the name of the note. This title may or may not contain a prefix. Call "newNoteFromPrefix"
		2. More templates options where the classes and prefixes in the super-templates are exposed. Call "newNoteThroughSelectors"
	*/
	const options = ["New Note", "More Templates"];
	const choice = await tp.system.suggester(options, options)
	switch(choice) {
		case "New Note":
			title = await tp.system.prompt("Note Name")
			if (!title) break    // aborted
			tR += await newNoteFromPrefix(superTemplates)  // new note
			break;
		case "More Templates":    // show a templater suggester
			tR += await newNoteFromUntitled(superTemplates);	
			break;
		default:   // nothing selected
			tR += await tp.file.include("[[Default Minimal Template]]");
	}
} else {   // Create a note if it has a title and a prefix       
	tR += await newNoteFromPrefix(superTemplates)
}



/**
	CREATING A NEW NOTE THAT IS AN EXISTING LINK WITH A PREFIX
	The user clicked on a link-title typed in a note. Process according to prefix of link-title in a note. This assumes we are starting with a note not named "Untitled" because the note is receiving its title from the link
 * @param {array} sT - an array with all super-templates
 * return {string} frontmatter
 */
async function newNoteFromPrefix(sT) {
	let choice;
	let chosen = findPrefixInSuperArray(sT);
	let cleanTop;
	let cleanBot;
	let dataview;
	let folder; 
	if (chosen) {
		console.log("Found prefix in title: ", title)
		const common = chosen.common;
		const main = chosen.main;
		let properties = cleanFrontmatter(main.fields)
		if (main.exclude) {   // we have properties in "exclude"
			cleanTop = cleanRemovedExcluded(common[0].fields, main.exclude)
			cleanBot = cleanRemovedExcluded(common[1].fields, main.exclude)
		} else {   // no exclusions defined in "exclude"
			cleanTop = cleanFrontmatter(common[0].fields)
			cleanBot = cleanFrontmatter(common[1].fields)
		}   // done with excluding properties
		dataview = main.dataview   // add dataview function if exists
			? addTableBelowFrontmatter(main.dataview)
			: ''
		cleanBot = replace_uuid(cleanBot);
		title = extractTitle(title);  // get rid of the prefix
		await tp.file.rename(title);
		folder = main.folder ? main.folder + "/" : "/" 
		await tp.file.move(folder + title);   // move to class sub-folder
		if (test) tR += cleanTop + properties + cleanBot   // frontmatter
		return addFrontmatter(cleanTop + properties + cleanBot) 
				+ dataview
	} else {
		// no prefix provided, create a minimal note
		console.log("No prefix in title. Add default properties to: ", title)
		setTimeout(() => {tp.file.rename(title)}, 350)
		return await tp.file.include("[[Default Minimal Template]]");
		
	}
}

/*
 *  CREATING A NEW NOTE USING A NESTED SUGGESTER
 * The user is creating a note from scratch by pressing Ctrl N. By default, the name of the new note is “Untitled”. We start by  checking if the note being created is named “Untitled”. Otherwise, it means the note candidate is a link which contains the title plus a prefix. The prefix indicates the folder or tags as destinations.
 * 
 * @param {array} superTemplate - an array of super templates
 * return {string} frontmatter
 * 
 */
async function newNoteFromUntitled(sT) {
	let result = await newNoteThroughSelectors(sT)
	if (result == 0) {  // Escape or Enter pressed
		new Notice("Escape or Enter pressed")
		title = "Untitled-" + get_uuid(false)
		return await tp.file.include("[[Default Minimal Template]]");
	}
	return result
}

/**
 * Nested suggester for all super-templates.
 * @param {array} superTemplate - an array of super templates
 * return {string} frontmatter
 */
async function newNoteThroughSelectors(superTemplate) {
	const classes = getSuperClasses(superTemplate);
	const chosen = await tp.system.suggester(classes, superTemplate)
	if (!chosen) return 0
	const common = chosen.common;   // load common properties
	const main = chosen.main;       // load core propertiees
	// wait a bit for the suggester to return the array
	await new Promise((resolve, reject) => setTimeout(resolve, 300));
	let mainNames = getValuesToListGivenKey(main, "name") // main 
	const selected = await tp.system.suggester(mainNames, main)
	if (!selected) return 0;
	let topProperties = getTopProperties(common)   // read top properties
	let botProperties = getBottomProperties(common)  // bottom props
	if (selected.exclude) {   // there is a list of exclusions
		topProperties = removeExcludedKeys(topProperties, selected.exclude)
		botProperties = removeExcludedKeys(botProperties, selected.exclude)
		botProperties = replace_uuid(botProperties);
	}   // properties in exclude removed
	let dataview = selected.dataview   // add dataview function if exists
			? addTableBelowFrontmatter(selected.dataview)
			: ''
	let coreProperties = cleanFrontmatter(selected.fields)
	return addFrontmatter(topProperties + coreProperties 
		+ botProperties) + dataview
}

/**
 * Get the name of the classes for all super-templates. 
 * The class name in in the array "common"
 * @param {array} sT - array of objects
 * return {array} name of classes
 */
function getSuperClasses(sT) {
	let result = [];
	sT.forEach(i => {
		result.push(i.common[2].class)
	})
	return result;
}

/**
 * Make an uuid to time stamp each new note.  If true, will return frontmatter as "uuid: '20240220103600'"; if false, the string 20240220103600
 * @param {boolean} withProperty 
 * return {string} an uuid string or the uuid property
 */
function get_uuid(withProperty) {
	return withProperty 
			? "uuid: " + "'" 
				+  tp.file.creation_date("YYYYMMDDHHmmss")  + "'"
			: tp.file.creation_date("YYYYMMDDHHmmss")
}

/**
 * Replace the uuid in current properties
 * @param {string} a YAML like string
 * return {string} a string with an updated uuid
 */
function replace_uuid(txt) {
	return txt + get_uuid(true)
}

/**
 * Return an array of values for a given dictionary and key. Important for providing the prefixes that validate an entry
 * @param {array} arr - array of objects
 * @param {string} key - Title / filename of the new note
 * return {array}
 */
function getValuesToListGivenKey(arr, key) {
	let result = [];
	arr.forEach(item => {
		let dict = item;
		let value = dict[key];
		// console.log(`${key}: ${value}`);
		result.push(value)
	})
	return result;
}

/**
 * Remove blank lines, spaces and tabs at the start of frontmatter caused by the indentation in the dictionary
 * @param {string} yml - a YAML like text
 * return {string} without blank lines
**/
function cleanFrontmatter(yml) {
	const regex = /^(?=\n)$|^\s*|s*$|\n\n+/gm   // line breaks, spaces
	yml = yml.split('\n')
		.map(function(line) {
			return line.replace(regex, "")})
		.filter(function(x) {return x}).join("\n");
	return yml + "\n";   // adding blank line so no mixed with next
}

/**
 * Get the common properties, top or bottom depending of the index
 * @param {array} arr - the common array from a super-template
 * @param {integer} idx - a number 0 or 1. 0 for top; 1 for bottom
 * return {array} common properties
 */
function getCommonProperties(arr, idx) {
	return arr[idx].fields
}

/**
 * Get the common top properties
 * @param {array} arr - the common array from a super-template
 * return {string}
 */
function getTopProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 0))
}

/**
 * Get the common bottom properties
 * @param {array} arr - the common array from a super-template
 * retrun {string}
 */
function getBottomProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 1))
}

/**
 * Print the frontmatter to the note
 * @param {string} yml - text with properties
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "---\n"
}

/**
 * Read the field prefix from each dictionary in the array
 * @param {array} arr - array of objects
 * return {array} list of all prefixes in super-templates
 */
function getPrefixValuesToList(arr) {
	const key = "prefix";
	return getValuesToListGivenKey(arr, key)
}

/**
 * Find the prefix in the note title in the super-templates. It will iterate through each array in the super array and will stop when finding a match.
 * @param {array} superArr - array of super-templates
 * return {array} matched dictionary with super-template variables
 */
function findPrefixInSuperArray(superArr) {
	console.log(superArr)
	let choice;
	let main;
	let common;
	superArr.forEach(item => {
		item.main.map(d => {
			if (test) console.log(d.prefix);
			if (title.startsWith(d.prefix)) {
				choice = item;
				main = d
				 }
		})
	})
	return !common && !main 
		? undefined
		: { common: choice.common, main: main };
}

/**
 * Clean the YAML string and remove the excluded properties
 * @param {string} txt - a string of properties or fields
 * @param {array} exclude - a list with the properties to exclude
 * return {string} a clean text with some properties removed
 */
function cleanRemovedExcluded(txt, exclude) {
	return cleanFrontmatter(removeExcludedKeys(txt, exclude));
}

/**
 * Remove properties specified in the array exclude because some classes do not need all the common properties.
 * @param {string} txt - original fields
 * @param {array} exclude - list of exclusions given by "main"
 * return {string} removed properties
 */
function removeExcludedKeys(txt, exclude) {
	removeList = exclude.map(p => p+":")
	var expStr = removeList.join("|");
	return txt
		.replace(new RegExp(expStr, 'gi'), ' ')
		.replace(/\s{2,}/g, '\n')  // remove void lines after exclusion
}

/**
 * Add dashes to properties to form the YAML frontmatter
 * @param {string} yml - a string of properties or fields
 * return {string}  frontmatter
 */
function addFrontmatter(yml) {
	return "---\n" + yml + "\n---\n"
}

/**
 * Print YAML frontmatter after adding dashes to properties
 * @param {string} yml - a string of properties or fields
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "\n---\n"
}

/**
 * Extract the title after the prefix. Date regex to detect a date in the title. We don't want to split it at yyyy- or mm-
 * @param {string} title - a note title with a prefix
 * return {string} note title
 */
function extractTitle(title) {
	let result;
	let count = (title.match(/-/g) || []).length;
	let dateRegex = /(\d{4})([-])(\d{2})([-])(\d{2})/;
	
	switch (count) {
		case 0:      // title has no dash
			result = title.trim()
			break
		case 1:      // title has one dash
			result = title.split("-").slice(1)[0].trim()
			break
		default:     // more than one dash
			// title could be a date "2024-02-21", or
			// of the form "prefix-Battery-powered-device"
			result = dateRegex.test(title) && count == 2
				? title.trim() 
				: title.split("-").slice(1).join("-").trim()
	}
	return result;
}

/**
 * Insert a Yaml like string along the dataview query
 * @param {string} fun - a function as a string
 * return {string} text with a dataview block including the resolved function with DQL code
 */
function addTableBelowFrontmatter(fun) {
	return "\n\n```dataview\n" + cleanFrontmatter(eval(fun))
	+ "```"
}

/**
 * Returns a dataview table showing notes where their properties up and related contain a link to the title of the current note. There could be as many as these DV functions as needed. But they have to be entered as strings in their respective dictionary.
 * @param {void} 
 * return {string} the Dataview Query as text 
 */
function getTableUpRelated() {
	let yml = `
	TABLE up, related
	FROM ""
	WHERE up = link(this.file.name)
	   OR contains(related, link(this.file.name))
	SORT file.name ASC
	`
	return yml
}
-%>

The super-templates could be copied to a folder under templates as well.
This is how the super-templates would look like. I am sharing three.
The Atlas super-template takes care of creating the core organization of your Obsidian vault. In some way I have been inspired by @nickmilo on this part. No vaults can easily grow or maintained without core notes. This stresses the point that links are way more important than tags. At least in Obsidian.

Atlas Super Template

<%*
/** 
 * List of dictionaries/objects for each subclass are defined here. This the central place where to control all the properties.
  
 * common {array} contains common properties to all sub-classes. Contains three unnamed dictionaries. [0] contains the common properties at the top; [1] common properties at the bottom; [2] name of the main class.
 * main {array} contains attributes and frontmatter properties for each subclass. This array contains a flexible or dynamic number of dictionaries. As many as the user requires. Each dictionary correspond to a subclass. The mandatory keys are: name, prefix, folder, and fields. The other keys (exclude, dataview) are optional.
  
 * name:      name of the dictionary   
 * prefix:    prefix in note title
 * folder:    where the note is going to be moved
 * dataview:  a function that will return a Dataview table below the frontmatter
 * exclude:   an array of properties to exclude
 * fields:    Frontmatter properties

 */
const common = [
  { name: "CommonTop",
	prefix: "top",
	description: "Common properties at the top of frontmatter",
	fields: `
	    aliases: 
		type: 
		archetype: 
	  `
  }, 
  
  { name: "CommonBottom",
	prefix: "bot",
    description: "Common properties at the bottom of frontmatter",
	fields: `
		status: 
	    rating: 	
		summary: 
		up: 
		related: 
		uuid: 
		source: 
	  `
	},
	
	{ 
	  name: "config",
	  class: "Atlas",
	  template: "Atlas Super Template"
	}
]

const main = [
  { name: "Collection",
	prefix: "collect",
	exclude: ["source"],
	dataview: "getTableUpRelated()",
	folder: "Atlas/Collections",
    fields: `
		tags: map/collection
		archetype: "[[Collections]]"
    `
  },

  { name: "Company",
    prefix: "comp",
    exclude: ["rating", "source"],
    folder: "Atlas/Companies",
    dataview: "getTableUpRelated()",
    fields: `
	    industry: 
	    country: 
	    address: 
	    website: 
	    linkedin: 
		tags: map/company
		archetype: "[[Companies]]"
    `
  },
     
  { name: "Concept",
    prefix: "concept",
    exclude: ["source"],
    folder: "Atlas/Concepts",
    dataview: "getTableUpRelated()",
    fields: `
		tags: map/concept
		archetype: "[[Concepts]]"
    `
  },
  
  { name: "Discipline",
    prefix: "disc",
    exclude: ["rating", "source"],
    folder: "Atlas/Disciplines",
    fields: `
		tags: map/discipline
		archetype: "[[Disciplines]]"
    `
  },

  { name: "Event",
    prefix: "eve",
    folder: "Calendar/Events",
    fields: `
	    type: "[[event]]"
	    when: 
		tags: map/events
		archetype: "[[Calendar]]"
    `
  }, 

  { name: "Industry",
    prefix: "ind",
    exclude: ["rating", "source"],
    dataview: "getTableUpRelated()",
    folder: "Atlas/Industries",
    fields: `
		tags: map/industry
		archetype: "[[Industries]]"
    `
  },

  { name: "Location",
    prefix: "loc",
    exclude: ["status", "rating", "source"],
    dataview: "getTableUpRelated()", 
    folder: "Atlas/Locations",
    fields: `
	    country: 
	    state: 
	    region: 
		tags: map/location
		archetype: "[[Locations]]"
    `
  },

  { name: "Media",
    prefix: "med",
    exclude: ["up", "related", "rating", "source"],
    folder: "Atlas/Media",
    fields: `
		tags: map/media
		archetype: "[[Media]]"
    `
  },

  { name: "Object",
    prefix: "obj",
    exclude: ["status", "up", "related", "source"],
    folder: "Atlas/Objects",
    fields: `
		tags: map/object
		archetype: "[[Objects]]"
    `
  },

  { name: "People",
    prefix: "people",
    exclude: ["status", "rating", "source"],
    dataview: "getTableUpRelated()",
    folder: "Atlas/People",
    fields: `
	    known_for: 
	    company: 
	    BU:
	    position: 
	    role: 
	    reports_to: 
	    email: 
	    address: 
	    phone: 
	    city: 
	    linkedin: 
	    github: 
		tags: map/person
		archetype: "[[People]]"
    `
  },

  { name: "Private",
    prefix: "priv",
    exclude: ["rating", "summary", "status", "source"],
    folder: "Spaces/Private",
    fields: `
		tags: private
		archetype: "[[Private]]"  
    `
  },

  { name: "Rating",
    prefix: "rating",
    exclude: ["rating", "status", "up", "related", "source"],
    folder: "Atlas/Ratings",
    fields: `
		tags: map/rating
		archetype: "[[Ratings]]"  
    `
  },

  { name: "Realm",
    prefix: "realm",
    exclude: ["rating", "up", "related", "source"],
    folder: "Atlas/Realms",
    fields: `
		tags: map/realm
		archetype: "[[Realms]]"
    `
  },

  { name: "Status",
    prefix: "status",
    exclude: ["rating", "status", "up", "related", "source"],
    folder: "Atlas/Status",
    fields: `
		tags: map/status
		archetype: "[[Status]]"
    `
  },

  { name: "Type",
    prefix: "type",
    exclude: ["rating", "status", "up", "related", "source"],
    folder: "Atlas/Types",
    fields: `
		tags: map/type
		archetype: "[[Types]]"
    `
  },
]

let  atlasArray = []
atlasArray.common = common;
atlasArray.main = main;
return atlasArray;  // pass the named arrays to the master template

-%>

Stuff Super Template

This is another super-template with one common class and several dictionaries that carry the core properties for each sub-template.

<%*
// Define my document types
const common = [
  { 
	name: "CommonTop",
	prefix: "top",
	fields: `
	    aliases: 
		type: 
		archetype: "[[My Stuff]]"
	  `
  },

  { 
	name: "CommonBottom",
	prefix: "bot",
	fields: `
		summary:  
	    purpose: 
	    rating: 
	    source: 
	  `
  },  

  { 
	 name: "config",
	 class: "Stuff",
	 template: "Stuff Super Template"
 }
]

const main = [
  { name: "My Articles",
	prefix: "myart",
	folder: "My Stuff",
    fields: `
	    published: 
	    journal: 
	    repository: 
	    partition: 
	    folder: 
	    up: "[[My Articles]]"
	    related: 
	    status: 
	    tags: content/article
	    `
  },

  { name: "My Code",
	prefix: "mycod",
	folder: "My Stuff",
	exclude: ["source"],
    fields: `
		gist_url: 
		partition: 
		folder: 
		language: 
		up: "[[My Code]]"
		related: 
		status: active
		tags: content/code
    `
  }, 
  
    { name: "My Comments",
	  prefix: "mycom",
	  folder: "My Stuff",
      fields: `
	    prefix: "mycom",
	    published: 
	    journal: 
	    up: "[[My Comments]]"
	    related: 
	    status: 
	    tags: content/comment	    
    `
  },
  
  { name: "My Computers",
	prefix: "mycomp",
	folder: "My Stuff",
    fields: `
		purchased: 
		brand: 
		cost: 
		CPU: 
		RAM:  
		OS: 
		use: 
		assigned: 
		location: "[[At Home]]"
		up: "[[Equipment]]"
		related:  
		status: unknown
		tags: digital/assets
    `
  }, 
  
  { name: "My Devices",
	prefix: "mydev",
	folder: "My Stuff",
    fields: `
		purchased: 
		brand: 
		cost: 
		use: 
		location: "[[At Home]]"
		up: "[[My Devices]]"
		related: 
		status: unknown
		tags: digital/assets
    `
  },

  { name: "My Contributions",
	prefix: "mycontrib",
	folder: "My Stuff",
    fields: `
	    published: 
	    journal: 
	    up: "[[My Contributions]]"
	    related: 
	    status: 
	    tags: content/contributions
    `
  },

  { name: "My Packages",
	prefix: "mypkg",
	folder: "My Stuff",
    fields: `
	    published: 
	    repository: 
	    partition: 
	    folder: 
	    OS: 
	    language: 
	    up: "[[My Packages]]"
	    related: 
	    status: 
	    tags: code/packages
    `
  },

  { name: "My Partitions",
	prefix: "mypart",
	folder: "My Stuff",
    fields: `
	    label: 
	    name: 
	    OS: 
	    size: 
	    up: "[[My Partitions]]"
	    related: 
	    status: 
	    tags: system/partitions
    `
  },

  { name: "My Portable Disks",
	prefix: "myporta",
	folder: "My Stuff",
    fields: `
		purchased: 
		brand: 
		cost: 
		use: 
		size: 
		location: "[[At Home]]"
		up: "[[My Portable Disks]]"
		related: 
		status: unknown
		tags: digital/assets
    `
  },
  
  { name: "My Portfolio",
	prefix: "myportf",
	folder: "My Stuff",
    fields: `
	    published: 
	    language: 
	    partition:
	    folder: 
	    up: "[[My Portfolio]]"
	    related: 
	    status: unknown
	    tags: content/portfolio
    `
  },
  
  { name: "My Repositories",
	prefix: "myrepos",
	folder: "My Stuff",
    fields: `
	    created: 
	    url:  
	    branch: 
	    partition: "[[2560x]]"
	    folder: 
	    language: 
	    up: "[[Programming]]"
	    related: 
	    status: unknown
	    is_synced: false
	    tags: content/code
	 `
	},

{ name: "My Transcripts",
  prefix: "mytransc",
  folder: "My Stuff",
  fields: `
		published: 
		journal: 
		url:
		partition: 
		folder: 
		up: "[[My Transcripts]]"
		related: 
		status: unknown
		tags: content/transcript
  `
  },

{ name: "My Virtual Machines",
  prefix: "myvm",
  folder: "My Stuff",
  fields: `
		created: 
		last_used: 
		brand: 
		OS: 
		size: 
		RAM: 
		network: 
		partition: 
		folder: 
		up: "[[My Virtual Machines]]"
		related: 
		status: unknown
		tags: digital/virtual
  `
},
]

let obj = [];
obj.common = common;
obj.main = main;
return obj;
-%>

The idea of Meta Template Picker comes from Bryan Jenks and Pamela Wang. Their ideas was instrumental in order to come to this super-templates. Special thanks to @holroy and @gino_m for their help, contributions and ideas. From @AlanG I got the inspiration for a better code. I am not sure if I achieve that goal though.

EDIT 1

I have modified the code above to allow the inclusion of dataview functions with each of the sub-classes. The function has to be entered as string in the dictionary, otherwise they will try to immediately execute requiring the function to be present in each super-template. I found that is better that the function is executed from a central location, the master template, where all the functions should live. The function is executed inside the master template with eval().

Before the function was entered as

datatable: getTableUpRelated(),

Now, should be like this:

dataview: "getTableUpRelated()",

An updated version of the master template, streamlined, better comments and shorter:

<%* 
/*  Load all super templates
	1. Read all super templates from custom folder set below
	2. Load all variables from the super-templates into a super array 
	3. Each member of the new array will contain two arrays, "common" and "main". "common" contains properties in common to one superclass; "main" is an array of objects or dictionaries that contains subclasses of properties which share a superclass.
	4. The purpose of the super-templates is reducing the number of templater files and make it easier to maintain from one central location.
*/
const templatesFolder = ['Extras/Templates/super templates'];
const test = false;
const templateFiles = []   // names of super template files
for (const folder of templatesFolder) {
  const files = (await app.vault.adapter.list(folder))?.files || []
  files.sort((a, b) => a.localeCompare(b)) // alphabetically
  templateFiles.push(...files) }
if (!templateFiles) return   // no super template found
const templates = templateFiles.map(path => { // read base names
	const file = app.vault.getAbstractFileByPath(path)
	return file.basename })
const superTemplates = [];   // array will contain all super-templates
for (item in templates) {    // iterate through each super-template
	superTemplates.push(await tp.file.include(`[[${templates[item]}]]`))
} // super template variables loaded in super array
let title = tp.file.title;   // grab current title
if (title.startsWith("Untitled")) {
	/** Create a note through multiple suggesters. Two options here:
		1. A new note prompt to type in the name of the note. This title may or may not contain a prefix. Call "newNoteFromPrefix"
		2. More templates options where the subclasses in the super-templates are exposed. Call "newNoteThruSelectors"
	 */
	const options = ["New Note", "More Templates"];
	const choice = await tp.system.suggester(options, options)
	switch(choice) {
		case "New Note":
			title = await tp.system.prompt("Note Name")
			if (title) {
				const output = await newNoteFromPrefix(superTemplates) 
				if (output) {
					tR += output;
					break; }
			}
			tR += await runDefaultMinimal()
			break;
		case "More Templates":    // show a templater suggester
			const output = await newNoteThruSelectors(superTemplates);	
			if (output) {
				tR += output;
				break; } 
			tR += await runDefaultMinimal();
			break;
		default:   // nothing selected
			tR += await runDefaultMinimal();
	}
} else {   
	/**
		Note to create is a link that has a prefix and a title; 
		name is not "Untitled"
	 */
	tR += await newNoteFromPrefix(superTemplates)
}

/**
    Run the Minimal Default template for the cases when user presses Escape, Enter, or cancels a selection
 */
async function runDefaultMinimal() {
	await new Promise((resolve, reject) => setTimeout(resolve, 100));
	return await tp.file.include("[[Default Minimal Template]]");
}

/**
 * CREATING A NEW NOTE THAT IS AN EXISTING LINK WITH A PREFIX
   The user clicked on a link-title typed in a note. Process according to prefix of link-title in a note. This assumes we are starting with a note not named "Untitled" because the note is receiving its title from the link.
 * @param {array} sT - an array with all super-templates
 * return {string} frontmatter
 */
async function newNoteFromPrefix(sT) {
	const foundClass = findPrefixInSuperArray(sT);
	if (foundClass) {
		console.log("Found prefix in title: ", title)
		const subCommon = foundClass.common;
		const subClass  = foundClass.main;
		title = extractTitle(title);  // get rid of the prefix
		await tp.file.rename(title);
		return processFrontmatter(subCommon, subClass);
	} else { // prefix not found in super-array
		console.log("No prefix in title: ", title)
		setTimeout(() => { tp.file.rename(title) }, 300)
		return await tp.file.include("[[Default Minimal Template]]");
	}  
}

/**
 * CREATING A NEW NOTE USING A NESTED SUGGESTER
 * Nested suggester for all super-templates. The user is creating a note from scratch by pressing Ctrl N. By default, the name of the new note is “Untitled”. 
 * @param {array} sT - an array with all super-templates
 * return {string} frontmatter
 */
async function newNoteThruSelectors(sT) {
	const supClasses = getSuperClasses(sT);
	const SClass = await tp.system.suggester(supClasses, sT)
	if (!SClass) return
	// wait a bit for the suggester to return the array
	await new Promise((resolve, reject) => setTimeout(resolve, 300));
	const classes = getValuesToListGivenKey(SClass.main, "name") 
	const subClass = await tp.system.suggester(classes, SClass.main)
	if (!subClass) return;             // nothing selected
	const subCommon = SClass.common;   // load common properties
	return processFrontmatter(subCommon, subClass);
}

/**
 * Build the frontmatter from the selected arrays and dictionaries
 * @param {array} subCommon - array for subclass common properties
 * @param {array} subClass - array of subclass main properties
 * return {string} frontmatter
*/
async function processFrontmatter(subCommon, subClass) {
	const properties = cleanFrontmatter(subClass.fields)
	let topProps = getTopProperties(subCommon)    // read top properties
	let botProps = getBottomProperties(subCommon) // bottom props
	if (subClass.exclude) {   // there is a list of exclusions
		topProps = removeExcludedKeys(topProps, subClass.exclude)
		botProps = removeExcludedKeys(botProps, subClass.exclude)
	}   // properties in exclude removed
	botProps = replace_uuid(botProps);
	const dataview = subClass.dataview   // add function if exists
			? addTableBelowFrontmatter(subClass.dataview)
			: ''
	const folder = subClass.folder ? subClass.folder + "/" : "/"; 
	await tp.file.move(folder + title);   // move to class sub-folder
	return addFrontmatter(topProps + properties + botProps) + dataview	
}

/**
 * Get the name of the super classes for all super-templates. 
 * The class name in in the array "common"
 * @param {array} sT - array of super template objects
 * return {array} name of super classes
 */
function getSuperClasses(sT) {
	let result = [];
	sT.forEach(i => {
		result.push(i.common[2].class) })
	return result;
}

/**
 * Make an uuid to time stamp each new note.  If true, return frontmatter as "uuid: '20240220103600'"; if false, only the string 20240220103600
 * @param {boolean} withProperty 
 * return {string} an uuid string or the uuid property
 */
function get_uuid(withProperty) {
	return withProperty 
			? "uuid: " + "'" 
				+  tp.file.creation_date("YYYYMMDDHHmmss")  + "'"
			: tp.file.creation_date("YYYYMMDDHHmmss")
}

/**
 * Replace the updated uuid in the current properties
 * @param {string} a YAML like string
 * return {string} a string with an updated uuid
 */
function replace_uuid(txt) {
	return txt + get_uuid(true)
}

/**
 * Return an array of values for a given dictionary and key. Important for providing the names and prefixes of property subclasses and validate an entry
 * @param {array} arr - array of objects in a superclass 
 * @param {string} key - a key in a dictionary
 * return {array} an array of values corresponding to a key
 */
function getValuesToListGivenKey(arr, key) {
	let result = [];
	arr.forEach(item => {
		let dict = item;
		let value = dict[key];
		// console.log(`${key}: ${value}`);
		result.push(value) })
	return result;
}

/**
 * Remove blank lines, spaces and tabs at the start of frontmatter caused by the indentations
 * @param {string} yml - a YAML like text
 * return {string} without blank lines
**/
function cleanFrontmatter(yml) {
	const regex = /^(?=\n)$|^\s*|s*$|\n\n+/gm   // line breaks, spaces
	yml = yml.split('\n')
		.map(function(line) {
			return line.replace(regex, "")})
		.filter(function(x) {return x}).join("\n");
	return yml + "\n";   // adding blank line so no mixed with next
}

/**
 * Get the common properties, top or bottom depending of the index
 * @param {array} arr - the common array from a super-template
 * @param {integer} idx - a number 0 or 1. 0 for top; 1 for bottom
 * return {array} common properties
 */
function getCommonProperties(arr, idx) {
	return arr[idx].fields
}

/**
 * Get the common top properties
 * @param {array} arr - the common array from a super-template
 * return {string} a string with the top common properties 
 */
function getTopProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 0))
}

/**
 * Get the common bottom properties
 * @param {array} arr - the common array from a super-template
 * return {string} a string with the bottom common properties 
 */
function getBottomProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 1))
}

/**
 * Print the frontmatter to the note
 * @param {string} yml - text with properties
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "---\n"
}

/**
 * Read the field prefix from each dictionary in the array
 * @param {array} arr - array of objects
 * return {array} list of all prefixes in super-templates
 */
function getPrefixValuesToList(arr) {
	const key = "prefix";
	return getValuesToListGivenKey(arr, key)
}

/**
 * Find the prefix that was entered in the note title through all the super-templates. It will iterate through each array in the main super array and will stop when finding a prefix match.
 * @param {array} superArr - array of super-templates
 * return {object} matched dictionary 
 */
function findPrefixInSuperArray(superArr) {
	console.log(superArr)
	let choice;
	let main;
	let common;
	superArr.forEach(item => {
		item.main.map(d => {
			if (test) console.log(d.prefix);
			if (title.startsWith(d.prefix)) {
				choice = item;
				main = d
			 }
		})
	})
	return !common && !main 
		? undefined
		: { common: choice.common, main: main };
}

/**
 * Clean the YAML string and remove the excluded properties
 * @param {string} txt - a string of properties or fields
 * @param {array} exclude - a list with the properties to exclude
 * return {string} a clean text with some properties removed
 */
function cleanRemovedExcluded(txt, exclude) {
	return cleanFrontmatter(removeExcludedKeys(txt, exclude));
}

/**
 * Remove properties specified in the array exclude because some classes do not need all the common properties.
 * @param {string} txt - original fields
 * @param {array} exclude - list of exclusions given by "main"
 * return {string} removed properties
 */
function removeExcludedKeys(txt, exclude) {
	removeList = exclude.map(p => p+":")
	var expStr = removeList.join("|");
	return txt
		.replace(new RegExp(expStr, 'gi'), ' ')
		.replace(/\s{2,}/g, '\n')  // remove void lines after exclusion
}

/**
 * Add dashes to properties to form the YAML frontmatter
 * @param {string} yml - a string of properties or fields
 * return {string}  frontmatter
 */
function addFrontmatter(yml) {
	return "---\n" + yml + "\n---\n"
}

/**
 * Print YAML frontmatter after adding dashes to properties
 * @param {string} yml - a string of properties or fields
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "\n---\n"
}

/**
 * Extract the title after the prefix. Remember that the dash is the delimiter for "prefix-title". Date regex to detect if there is a date in the title. We don't want to split it at yyyy- or mm-. Additionally, title could be a date "2024-02-21", or of the form "prefix-Battery-powered-device"
 * @param {string} title - note title with a prefix and extra dashes
 * return {string} just the note title
 */
function extractTitle(title) {
	let result;
	let count = (title.match(/-/g) || []).length;
	let dateRegex = /(\d{4})([-])(\d{2})([-])(\d{2})/;
	
	switch (count) {
		case 0:      // title has no dash, no prefix
			result = title.trim()
			break
		case 1:      // title has one dash, means prefix
			result = title.split("-").slice(1)[0].trim()
			break
		default:     // more than one dash
			result = dateRegex.test(title) && count == 2
				? title.trim() 
				: title.split("-").slice(1).join("-").trim()
	}
	return result;
}

/**
 * Insert a Yaml like string along the dataview query
 * @param {string} fun - a function as a string
 * return {string} text with a dataview block including the resolved function with DQL code
 */
function addTableBelowFrontmatter(fun) {
	return "\n\n```dataview\n" + cleanFrontmatter(eval(fun))
	+ "```"
}

/**
 * Returns a dataview table showing notes where their properties up and related contain a link to the title of the current note. There could be as many as these DV functions as needed. But they have to be entered as strings in their respective dictionary.
 * @param {void} 
 * return {string} the Dataview Query as text 
 */
function getTableUpRelated() {
	return `
	TABLE up, related
	FROM ""
	WHERE up = link(this.file.name)
	   OR contains(related, link(this.file.name))
	SORT file.name ASC
	`
}
-%>

Hi, you made it! this is great!

After I copy the js and templates to my vault, it seems not work very well. when I use the prefix to create a new note, it keeps the prefix in notes name and pops an error message.


would you please have a look at this, and give me some instruction to how to make it work?

Thanks.

In Templater you have to add the main template as a folder template pointing at root. See picture:

Also, the super templates have to be located somewhere under your templates folder:

When you press Ctrl+N, or create a new note, it will automatically invoke the main template that you set up pointing at your vault root folder.

Thank you for your quick reply. I did as your instructions, and no luck.

Here I attached the test vault I used.

templater test.zip (163.4 KB)

Will you please check and help? Thanks

Ok. I will take a look.

Hi @FrancisHugo,
I got it working. The problem is the setting of the super-templates. You forgot to point at a sub-folder of the templater folder.

# yours
const templatesFolder = ['Obsidian/02-Templater];
# fixed
const templatesFolder = ['Obsidian/02-Templater/Super Templater'];

That’s why you receive the error about nesting templates because it is iterating over and over without finding the super-templates to load. I will later add a error trapping in there.

Here it is a screenshot of your test vault with a new event note working as expected:

and the selector:

Hi there,
I have added a function to check for the correct location of the super-templates folder. Now, it will send a message if the folder is not located under the templater folder in settings.

<%* 
/*  Load all super templates
	1. Read all super templates from custom subfolder set below under templater folder location in settings
	2. Load all variables from the super-templates into a super array inside this master template 
	3. Each member of the new array will contain two arrays, "common" and "main". "common" contains properties in common to a superclass; "main" is an array of objects or dictionaries that contains subclasses of properties which share a superclass. It is one super-template per superclass and many subclasses per superclass
	4. The purpose of the super-templates is reducing the number of templater files and make it easier to maintain them from one central location.
*/
const StemplatesFolder = ['Extras/Templates/super templates'];
const test = false;
const templateFiles = []   // names of super template files
if (!isSupertemplatesDirValid(StemplatesFolder)) {
	new Notice("Invalid!\nPlace super-templates subfolder under the templater folder location");
	return
}
for (const folder of StemplatesFolder) {
  const files = (await app.vault.adapter.list(folder))?.files || []
  files.sort((a, b) => a.localeCompare(b)) // alphabetically
  templateFiles.push(...files) }
if (!templateFiles) return   // no super template found
const templates = templateFiles.map(path => { // read base names
	const file = app.vault.getAbstractFileByPath(path)
	return file.basename })
const superTemplates = [];   // array will contain all super-templates
for (item in templates) {    // iterate through each super-template
	superTemplates.push(await tp.file.include(`[[${templates[item]}]]`))
} // super template variables loaded in super array
let title = tp.file.title;   // grab current title
if (title.startsWith("Untitled")) {
	/** Create a note through multiple suggesters. Two options here:
		1. A new note prompt to type in the name of the note. This title may or may not contain a prefix. Call "newNoteFromPrefix"
		2. More templates options where the subclasses in the super-templates are exposed. Call "newNoteThruSelectors"
	 */
	const options = ["New Note", "More Templates"];
	const choice = await tp.system.suggester(options, options)
	switch(choice) {
		case "New Note":
			title = await tp.system.prompt("Note Name")
			if (title) {
				const output = await newNoteFromPrefix(superTemplates) 
				if (output) {
					tR += output;
					break; }
			}
			tR += await runDefaultMinimal()  // returned undefined
			break;
		case "More Templates":    // show a templater suggester
			const output = await newNoteThruSelectors(superTemplates);	
			if (output) {
				tR += output;
				break; } 
			tR += await runDefaultMinimal();  // returned undefined
			break;
		default:   // nothing selected
			tR += await runDefaultMinimal();
	}
} else {   
	/** Note to create is a link that has a prefix and a title; 
	    default name is not "Untitled"
	 */
	tR += await newNoteFromPrefix(superTemplates)
}

/**
    Run the Minimal Default template for the cases when user presses Escape, Enter, or cancels a selection
 */
async function runDefaultMinimal() {
	await new Promise((resolve, reject) => setTimeout(resolve, 100));
	return await tp.file.include("[[Default Minimal Template]]");
}

/**
 * CREATING A NEW NOTE THAT IS AN EXISTING LINK WITH A PREFIX
   The user clicked on a link-title typed in a note. Process according to prefix of link-title in a note. This assumes we are starting with a note not named "Untitled" because the note is receiving its title from the link.
 * @param {array} sT - an array with all super-templates
 * return {string} frontmatter
 */
async function newNoteFromPrefix(sT) {
	const foundClass = findPrefixInSuperArray(sT);
	if (foundClass) {
		console.log("Found prefix in title: ", title)
		const subCommon = foundClass.common;
		const subClass  = foundClass.main;
		title = extractTitle(title);  // get rid of the prefix
		await tp.file.rename(title);
		return processFrontmatter(subCommon, subClass);
	} else { // prefix not found in super-array
		console.log("No prefix in title: ", title)
		setTimeout(() => { tp.file.rename(title) }, 300)
		return await tp.file.include("[[Default Minimal Template]]");
	}  
}

/**
 * CREATING A NEW NOTE USING A NESTED SUGGESTER
 * Nested suggester for all super-templates. The user is creating a note from scratch by pressing Ctrl N. By default, the name of the new note is “Untitled”. 
 * @param {array} sT - an array with all super-templates
 * return {string} frontmatter
 */
async function newNoteThruSelectors(sT) {
	const supClasses = getSuperClasses(sT);
	const SClass = await tp.system.suggester(supClasses, sT)
	if (!SClass) return
	// wait a bit for the suggester to return the array
	await new Promise((resolve, reject) => setTimeout(resolve, 300));
	const classes = getValuesToListGivenKey(SClass.main, "name") 
	const subClass = await tp.system.suggester(classes, SClass.main)
	if (!subClass) return;             // nothing selected
	const subCommon = SClass.common;   // load common properties
	return processFrontmatter(subCommon, subClass);
}

/**
 * Build the frontmatter from the selected arrays and dictionaries
 * @param {array} subCommon - array for subclass common properties
 * @param {array} subClass - array of subclass main properties
 * return {string} frontmatter
*/
async function processFrontmatter(subCommon, subClass) {
	const properties = cleanFrontmatter(subClass.fields)
	let topProps = getTopProperties(subCommon)    // read top properties
	let botProps = getBottomProperties(subCommon) // bottom props
	if (subClass.exclude) {   // there is a list of exclusions
		topProps = removeExcludedKeys(topProps, subClass.exclude)
		botProps = removeExcludedKeys(botProps, subClass.exclude)
	}   // properties in exclude removed
	botProps = replace_uuid(botProps);
	const dataview = subClass.dataview   // add function if exists
			? addTableBelowFrontmatter(subClass.dataview)
			: ''
	const folder = subClass.folder ? subClass.folder + "/" : "/"; 
	await tp.file.move(folder + title);   // move to class sub-folder
	return addFrontmatter(topProps + properties + botProps) + dataview	
}

/**
 * Get the name of the super classes for all super-templates. 
 * The class name in in the array "common"
 * @param {array} sT - array of super template objects
 * return {array} name of super classes
 */
function getSuperClasses(sT) {
	let result = [];
	sT.forEach(i => {
		result.push(i.common[2].class) })
	return result;
}

/**
 * Make an uuid to time stamp each new note.  If true, return frontmatter as "uuid: '20240220103600'"; if false, only the string 20240220103600
 * @param {boolean} withProperty 
 * return {string} an uuid string or the uuid property
 */
function get_uuid(withProperty) {
	return withProperty 
			? "uuid: " + "'" 
				+  tp.file.creation_date("YYYYMMDDHHmmss")  + "'"
			: tp.file.creation_date("YYYYMMDDHHmmss")
}

/**
 * Replace the updated uuid in the current properties
 * @param {string} a YAML like string
 * return {string} a string with an updated uuid
 */
function replace_uuid(txt) {
	return txt + get_uuid(true)
}

/**
 * Return an array of values for a given dictionary and key. Important for providing the names and prefixes of property subclasses and validate an entry
 * @param {array} arr - array of objects in a superclass 
 * @param {string} key - a key in a dictionary
 * return {array} an array of values corresponding to a key
 */
function getValuesToListGivenKey(arr, key) {
	let result = [];
	arr.forEach(item => {
		let dict = item;
		let value = dict[key];
		// console.log(`${key}: ${value}`);
		result.push(value) })
	return result;
}

/**
 * Remove blank lines, spaces and tabs at the start of frontmatter caused by the indentations
 * @param {string} yml - a YAML like text
 * return {string} without blank lines
**/
function cleanFrontmatter(yml) {
	const regex = /^(?=\n)$|^\s*|s*$|\n\n+/gm   // line breaks, spaces
	yml = yml.split('\n')
		.map(function(line) {
			return line.replace(regex, "")})
		.filter(function(x) {return x}).join("\n");
	return yml + "\n";   // adding blank line so no mixed with next
}

/**
 * Get the common properties, top or bottom depending of the index
 * @param {array} arr - the common array from a super-template
 * @param {integer} idx - a number 0 or 1. 0 for top; 1 for bottom
 * return {array} common properties
 */
function getCommonProperties(arr, idx) {
	return arr[idx].fields
}

/**
 * Get the common top properties
 * @param {array} arr - the common array from a super-template
 * return {string} a string with the top common properties 
 */
function getTopProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 0))
}

/**
 * Get the common bottom properties
 * @param {array} arr - the common array from a super-template
 * return {string} a string with the bottom common properties 
 */
function getBottomProperties(arr) {
	return cleanFrontmatter(getCommonProperties(arr, 1))
}

/**
 * Print the frontmatter to the note
 * @param {string} yml - text with properties
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "---\n"
}

/**
 * Read the field prefix from each dictionary in the array
 * @param {array} arr - array of objects
 * return {array} list of all prefixes in super-templates
 */
function getPrefixValuesToList(arr) {
	const key = "prefix";
	return getValuesToListGivenKey(arr, key)
}

/**
 * Find the prefix that was entered in the note title through all the super-templates. It will iterate through each array in the main super array and will stop when finding a prefix match.
 * @param {array} superArr - array of super-templates
 * return {object} matched dictionary 
 */
function findPrefixInSuperArray(superArr) {
	console.log(superArr)
	let choice;
	let main;
	let common;
	superArr.forEach(item => {
		item.main.map(d => {
			if (test) console.log(d.prefix);
			if (title.startsWith(d.prefix)) {
				choice = item;
				main = d
			 }
		})
	})
	return !common && !main 
		? undefined
		: { common: choice.common, main: main };
}

/**
 * Clean the YAML string and remove the excluded properties
 * @param {string} txt - a string of properties or fields
 * @param {array} exclude - a list with the properties to exclude
 * return {string} a clean text with some properties removed
 */
function cleanRemovedExcluded(txt, exclude) {
	return cleanFrontmatter(removeExcludedKeys(txt, exclude));
}

/**
 * Remove properties specified in the array exclude because some classes do not need all the common properties. We do not use the flags "g" and "i" to make the search less greedy.
 * @param {string} txt - original fields
 * @param {array} exclude - list of exclusions given by "main"
 * @return {string} removed properties
 */
function removeWhitespace(txt) {
	return txt
		.filter(function(entry) { 
			return entry.trim() != ''; 
		});
}
function removeExcludedKeys(str, exclude) {
	let strA = str.split("\n")    // convert to array
    strA = removeWhitespace(strA) // remove white spaces
	exclude.forEach(ex => {
		let regex = RegExp("\\b" + ex + ".*","gi")
		strA = strA.map(p => p.replace(regex, '')) });
	return strA.join("\n") + "\n"
}

/**
 * Add dashes to properties to form the YAML frontmatter
 * @param {string} yml - a string of properties or fields
 * return {string}  frontmatter
 */
function addFrontmatter(yml) {
	return "---\n" + yml + "\n---\n"
}

/**
 * Print YAML frontmatter after adding dashes to properties
 * @param {string} yml - a string of properties or fields
 * return {void}
 */
function printFrontmatter(yml) {
	tR += "---\n"
	tR += yml
	tR += "\n---\n"
}

/**
 * Extract the title after the prefix. Remember that the dash is the delimiter for "prefix-title". Date regex to detect if there is a date in the title. We don't want to split it at yyyy- or mm-. Additionally, title could be a date "2024-02-21", or of the form "prefix-Battery-powered-device"
 * @param {string} title - note title with a prefix and extra dashes
 * return {string} just the note title
 */
function extractTitle(title) {
	let result;
	let count = (title.match(/-/g) || []).length;
	let dateRegex = /(\d{4})([-])(\d{2})([-])(\d{2})/;
	
	switch (count) {
		case 0:      // title has no dash, no prefix
			result = title.trim()
			break
		case 1:      // title has one dash, means prefix
			result = title.split("-").slice(1)[0].trim()
			break
		default:     // more than one dash
			result = dateRegex.test(title) && count == 2
				? title.trim() 
				: title.split("-").slice(1).join("-").trim()
	}
	return result;
}

/**
 * Insert a Yaml like string along the dataview query
 * @param {string} fun - a function as a string
 * return {string} text with a dataview block including the resolved function with DQL code
 */
function addTableBelowFrontmatter(fun) {
	return "\n\n```dataview\n" + cleanFrontmatter(eval(fun))
	+ "```"
}

/**
 * Returns a dataview table showing notes where their properties up and related contain a link to the title of the current note. There could be as many as these DV functions as needed. But they have to be entered as strings in their respective dictionary.
 * @param {void} 
 * return {string} the Dataview Query as text 
 */
function getTableUpRelated() {
	return `
	TABLE up, related
	FROM ""
	WHERE up = link(this.file.name)
	   OR contains(related, link(this.file.name))
	   AND !this.file.name
	SORT file.name ASC
	`
}

/**
 * Checks for the correct location of the super-templates folder.
 * It will return `true` if the super-templates folder exists under the templater folder location that set in your configuration. `false` otherwise.
 * @param {string} StemplatesFolder - super-templates folder name
 * @return {boolean} 
 */
function isSupertemplatesDirValid(StemplatesFolder) {
	const tp_folder_children = tp.config.template_file.parent.children
	const tp_subfolders = tp_folder_children
		.filter(file => file.extension != 'md')
		.map(f => f.path)
	return StemplatesFolder.some(r => tp_subfolders.includes(r))
}
-%>

@msfz751 ,

Thanks for your time and helps.

It works now. like a magic, it works smoothly.

I had another question now, for the superTemplates, like in Atlas Super Template, is it possible to call another template to put some text in it below the frontmatter?

What I mean is:

for example, in the class disc, is it possible to put some text, better to call another template (I had already have such template notes made)?


  { name: "Discipline",
    prefix: "disc",
    exclude: ["rating", "source"],
    folder: "Atlas/Disciplines",
    fields: `
		tags: map/discipline
		archetype: "[[Disciplines]]"
    `
** ADD some text so that can be passed to notes, even better to call another template here, so cam be more easy to use **

  },

Thanks again, and kindly looking forward your reply.

if possible, I would like to put all fields to another template notes. So for the superTemplate, it is just to judge which template should be adapted base on the prefix.

It is a good idea. Let me play with it. Definitely, it will make SMTP more flexible. It would be very similar to adding a dataview block to the class you choose.

That would take a little bit more time but it is doable.
The central idea of the super-templates was to reduce the number of template files because I found them difficult to maintain, homogenize, and standardize.