1.0.1 • Published 8 months ago

block-inheritance-templating v1.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

block-inheritance-templating

block based template rendering engine

Features

  • block based template inheritance (blocks can be overwritten or reused)
  • translation hooks (integrates with nearly all translation apis)
  • caching hooks (integrates with nearly all caching apis)
  • livecycle hooks (easy to add new features, e.g. css collecting)
  • extendable (all kinds of objects and functions can be passed into the template)

Advantages

  • small (~1.7kb minified)
  • fast
  • dependency free
  • all features are documented and covered by tests
  • pure javascript templates (no extra parsing overhead)
  • dual use: frontend (browser) and backend (node)
  • can be used with javascript template literals
  • allows a logic-less template style or full-fledged template scripting

install

npm install block-inheritance-templating

hello world

(async () => {
    const { render } = await import('block-inheritance-templating')

    render({
        parent: null,
        block: {
            main: () => "hello world"
        }
    }).then(content => console.log(content))
})()

the template

Every object that has a parent and a block property is a template.

The parent property is null or points to another template. As the parent can have a parent template of its own, every template is the beginning of a template (inheritance) chain. If a lookup in a template fails the lookup is repeated in the parent, then in the parents parent and so on. Therefor the template chain should not create a circle.

The block property is an object of template blocks...

const someTemplate = {
    parent: null,
    block: {
        main: (vars, local, fnc) => `...`,
        someBlock: (vars, local, fnc) => `...`,
        anotherBlock: (vars, local, fnc) => `...`,
    },
}

or a function returning template blocks.

const someTemplate = {
    parent: null,
    block: name => {
        switch (name) {
            case 'main': return (vars, local, fnc) => `...`
            case 'someBlock': return (vars, local, fnc) => `...`
            case `anotherBlock`: return (vars, local, fnc) => `...`
            default: return null
        }
    },
}

a template block

A template block is an async function rooted in the block property of the template object. Its name is the property name. There are three arguments to this function vars, local and fnc supplied by the rendering engine.

A template block has to return a string value.

the vars argument

The vars argument is a function by which all variables can be accessed that have been passed to the render call or are rooted in the vars property of the template chain.

const variable = vars('someVariable')

the fnc argument

The fnc object holds user-land functions passed to the render call and the three (async) rendering functions block, ìterate and render.

- fnc.block (blockName, localVars)
- fnc.iterate (blockName, data, separator='')
- fnc.render (tpl, variables, lng, fnc={}, entrypoint='main')

The block function renders a block. It has to be a block of the same template chain identified by the blocks name. As second argument local variables can be passed. If the same variable exists in the current vars function, it will be overwritten by the passed variable.

const renderedBlock = await fnc.block('someBlock', { someVar: 42, anotherVar: 'Hello World' })

The iterate function iterates block function calls over a data array. The result will be joined via the passed separator.

const renderedBlocks = await fnc.iterate('someBlock', [/*...*/], '<hr/>')

The render function is the render function. By it one can render other template chains or the same template chain with other arguments.

const renderedTemplate = await fnc.render(template, vars, lang, fnc, entrypoint)

the local argument

The local argument holds some special functionality:

- local.parent()
- local.lang
- local.index
- local.vars(name)
- local.trans(item, category=null)
- local.block(blockName, localVars)
- local.iterate(bockName, data, separator='')

local.parent() looks for the same block in the templates inheritance chain and renders it with the same arguments.

const renderedParentBlock = await local.parent()

local.lang holds the current language in which the template is rendered.

const currentLang = local.lang

local.index holds the current iteration index or null.

const loopIndexIsEven = (local.index % 2 === 0)

local.vars is like the vars argument but holds the variables explicitly passed to the last block call only.

const localVariable = local.vars('someLocalVariable')

local.trans translates an item in the current language via the special function trans().

const translatedString = local.trans('hello', 'snippet.words')

local.block is the same as fnc.block without access to the global scope. If the local vars are omitted the current scope is used. Template vars remain always accessible as fallback.

local.iterate is the same as fnc.interate without access to the global scope. Template vars remain always accessible as fallback.

special functions

Some functions have a special meaning to the template engine. They can be passed by the fnc object or be rooted in the fnc property of the template inheritance chain.

- trans (item, category, lang, blockName, template, vars, local, fnc) => ...
- onError (e,            lang, blockName, template, vars, local, fnc) => ...
- getCache (             lang, blockName, template, vars, local, fnc) => ...
- setCache (result,      lang, blockName, template, vars, local, fnc) => ...
- preRender (            lang, blockName, template, vars, local, fnc) => ...
- postRender (           lang, blockName, template, vars, local, fnc) => ...
- preCall (              lang, blockName, template, vars, local, fnc) => ...
- postCall (             lang, blockName, template, vars, local, fnc) => ...

trans

trans() is the translation function it can be accessed in the template via local.trans(item, category).

onError

onError() will be called if rendering or one of the other special functions throws an error. If onError() does not throw an error the result is used instead of the rendered.

If no onError() function is found the error will be rethrown.

getCache, setCache

getCache() and setCache() can be used to cache rendered blocks.

preRender, postRender

preRender() will be called pre and postRender() post rendering. Both will not be called if the cache hits.

preCall, postCall

preCall() will be called pre and postCall() post rendering. Both will be called with or without a cache hit.

how to render a template

const { render } = await import('block-inheritance-templating')

or

import { render } from 'block-inheritance-templating'

then

const vars = {}
const lang = 'en'
const fnc = {}
const entrypoint = 'main'

const output = await render(template, vars, lang, fnc, entrypoint)

Basic Example

const { render } = await import('block-inheritance-templating')

const parentTemplate = {
    parent: null,
    vars: {
        content: '<p>some content</p>',
    },
    block: {
        main: async (vars, local, fnc) => {
            return `<!doctype html>
<html lang="${ await local.trans('lang') }">
<head>
    <title>${ vars('title') }</title>
    ${ await fnc.block('head') }
</head>
<body>
    ${ await fnc.block('nav') }
    <h1>${ vars('title') }</h1>
    ${ await fnc.block('content') }
</body>
</html>`
        },
        head: async function() {
            return `<script>let that, be, empty</script>`
        },
    },
}

const renderedTemplate = {
    parent: parentTemplate,
    block: {
        nav: async (vars, local, fnc) => {
            return `
    <nav>${ await fnc.iterate('navItemBlock', vars('navItems')) }
    </nav>`
        },
        navItemBlock: async (vars, local) => {
            return `
        <a href=${ vars('href') }>(${ local.index }) ${ vars('content') }</a>`
        },
        content: (vars) => vars('content'),
    },
}

const vars = { 
    title: 'Hello World', 
    navItems: [
        { href: 'https://hello.com', content: 'hugs to you' },
        { href: 'https://world.com', content: 'global issues' },
    ],
}

const lang = 'en'

const fnc = {
    trans: function(name, category, lang) {
        switch (name) {
            case 'lang': return lang
            default: return name
        }
    }
}

const output = await render(renderedTemplate, vars, lang, fnc);

// c o n s o l e . l o g (output) gives:
//
// <!doctype html>
// <html lang="en">
// <head>
//     <title>Hello World</title>
//     <script>let that, be, empty</script>
// </head>
// <body>
//     
//     <nav>
//         <a href=https://hello.com>(0) hugs to you</a>
//         <a href=https://world.com>(1) global issues</a>
//     </nav>
//     <h1>Hello World</h1>
//     <p>some content</p>
// </body>
// </html>
1.0.1

8 months ago

1.0.0

8 months ago