Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I get all partials includes of a template? #682

Open
PedroUrday opened this issue Oct 30, 2018 · 1 comment
Open

How can I get all partials includes of a template? #682

PedroUrday opened this issue Oct 30, 2018 · 1 comment

Comments

@PedroUrday
Copy link

Question:

How can I get all partials includes of a template? the includes not its contents. For example: for any partial include: {{>aTemplate}} i should get aTemplate

New Feature:

If it is not posible, one feature can be a function that return all partial includes as an array of string.

Why:

To fetch, by ajax requests, only the needed partials of a template.

@advanced-media
Copy link

advanced-media commented Sep 24, 2020

TL;DR use method Mustache.parse(template) on the templates (partials = templates)

  1. To get the names of partials of a (root-)template you can use the return value of method Mustache.parse(template).
    It contains an Array of Mustache related tokens. You have to filter out the partials (">" is the partials identifier at idx 0, the name is on idx 1).
  2. To get the the names of the subsequent/nested partials you have to treat the content of all partials from step 1. as templates. Caching partials (name and content) and having this relation consistent (no logic for various/different content for same partial name) is essential!
  3. repeat step 1. and 2. recursively to find new partials in partials

As mentioned in the README.md:

... so recursive partials are possible. Just avoid infinite loops.

For nodejs I use reduce(), filter(), map(), ... to have some fun with Arrays and Object.fromEntries() to build my partials object, to finally Mustache.render(template, data, partials).
Here is an example for filesystem based templates. This can probably be improved with more or newer JS syntax improvements. And you can of cause shrink this partialsFromTemplatesRecursiveReducer to a one-liner, using less constants, combining filters, moving functions to Closures, etc., I leave them there to explain the workflow

const { readFileSync, readdirSync } = require('fs')
const { resolve } = require('path')
const Mustache = require('mustache')
const getTemplate = path => {
    try{
        return readFileSync(path).toString()
    } catch(e) {
        console.warn(e.message)
        // console.trace()
        return ''
    }
}
const getPartialTpl = partialName => getTemplate(resolve(partialsRoot, partialName + '.freemarker.html'))
const partialsFromTemplatesRecursiveReducer = (partials, template) => {
    const templateToken = Mustache.parse(template)
    const filterPartials = token => token[0] === '>'
    const mapTokenName = token => token[1]
    const filterUnique = (element, index, array) => array.indexOf(element) === index
    const filterNew = partialName => !Object.keys(partials).includes(partialName)
    const mapPartialTpl = partialName => [partialName, getPartialTpl(partialName)]
    const newPartialNames = templateToken
        .filter(filterPartials)
        .map(mapTokenName)
        .filter(filterUnique)
        .filter(filterNew)
    const newPartials = newPartialNames
        .map(mapPartialTpl)
    Object.assign(partials, Object.fromEntries(newPartials))
    const newTemplates = newPartials.map(entry => entry[1]).filter(Boolean)
    return Object.assign(partials, newTemplates.reduce(partialsFromTemplatesRecursiveReducer, partials))
}
const shouldAllPartialsBeLoaded = true
const partials = shouldAllPartialsBeLoaded
    // simply load all partials (loadtime, Memory ?)
    ? Object.fromEntries(readdirSync(partialsRoot)
        .map(file => file.replace(/.html$/,''))
        .map(name => [name, getPartialTpl(name)]))
    // or use Mustache.parse() to only load used partials
    : [masterTemplate, pageTemplate].reduce(partialsFromTemplatesRecursiveReducer, {})

const pageContent = Mustache.render(pageTemplate, data, partials)

Note: the expensive part in the reducer is the mapPartialTpl mapping (template content is received by template name), so clever filtering can reduce iterations. Same applies to the recursive call.

The effort to figure out the used partials in the (root-)template(s) competes with simply loading all partials in front. The question you have to answer is "shouldAllPartialsBeLoaded?"
This depends on the use case, if you know the available partials, or if you facing other issues.
Its also a balance between existing, but not-used/orphaned partial templates (files) vs. not-existing/not-filled but used partials (names)

In my case, I'm bulk-rendering a huge number of templates for various languages and varying layouts (same templates/partials, different data) with the same instance of Mustache, finally storing all rendered results as templates for another template engine (e.g. Apache freemarker or Apache velocity). Until now I had no real reason to not load all partials (in my case).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants