Skip to content

cho45/micro-template.js

Repository files navigation

micro-template.js

Demo page (Try in your browser!)

https://github.com/cho45/micro-template.js

micro-template is a template engine for JavaScript which is similar to embed.js.

This is inspired by John Resig's template but has more efficient features:

  • Better error messages: shows line numbers in runtime errors
  • Supports source maps: debugging is easier in Chrome, including for syntax errors
  • Well tested: works on Node.js
  • Escapes by default: all output is escaped for security

SYNOPSIS

on HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>micro-template.js test</title>
</head>
<body>
  <script type="application/x-template" id="tmpl1">
    <div class='foobar' id="aaa">
      <% if (isFoo) { %>
      <%= foobar %>
      <% } else { %>
      <%= foobaz %>
      <% } %>

      <%=raw html %>
    </div>
  </script>
  <script type="module">
    import { extended as template } from 'https://cdn.jsdelivr.net/npm/micro-template@2.1.2/lib/micro-template.js';
    console.log('micro-template.js loaded', template);
    console.log('Template example:', template('tmpl1', { isFoo: true, foobar: "a", foobaz: "b" }));
    let html = template('tmpl1', {
        isFoo : true,
        foobar : 'foobar!!',
        foobaz : 'foobaz!!',
        html : '<marquee>Helloooo</marquee>'
    });
    console.log(html);
  </script>
</body>
</html>

on node.js:

npm install micro-template
import fs from 'node:fs';
import { template } from 'micro-template';
template.get = (id) => fs.readFileSync(`tmpl/${id}.tmpl`, 'utf-8');

const result = template('part1', {
  foo : 'bar',
  baz : 'piyo'
});
console.log(result)

SYNTAX

  • <% … %>: normal script part
  • <%= … %>: escaped html output part
  • <%=raw …%>: unescaped (almost dangerous) html output part

DESCRIPTION

template(id or source , data | array )

If the first argument of template matches /^[\w\-]+$/, it is treated as id of template. In this case, use document.getElementById(id).innerHTML to get source.

Otherwise, the first argument is treated as source directly.

If the second argument is an Array, it is treated as a list of property names for the data object. In this case, the template function will be compiled with these property names as its local variables, and the compiled function itself will be returned (not executed). This allows you to precompile a template for repeated use with the same set of variable names.

If the second argument is an Object, the template will be rendered immediately using the object's properties as local variables.

CUSTOM get FUNCTION

By default, micro-template uses document.getElementById(id).innerHTML to get the template source from the id.

To override this behavior, you can set a function to template.get. For example, if your template files are in the tmpl/ directory:

import { template } from 'micro-template';
template.get = function (id) { return require('fs').readFileSync('tmpl/' + id + '.tmpl', 'utf-8') };

DEFINE DATA VARIABLE SCOPE

micro-template always expands data variables as local variables in the template function. The template API only supports two arguments: the template source/id and the data object (or an array of property names for precompilation). All keys of the data object are available as local variables in the template code.

If the second argument is an Array, it is treated as a list of property names for the data object. In this case, the template function will be compiled with these property names as its local variables, and the function itself will be returned (not executed). This allows you to precompile a template for repeated use with the same set of variable names.

For example:

const render = template('aaa <% foo %> bbb', ['foo']);
const result = render({ foo: 'bar' });
// result: 'aaa bar bbb'

You can access all properties of the data object directly as variables inside the template.

Note: The previous API that allowed calling template(tmpl) to return a function is removed. Always use the two-argument form: template(tmpl, data) or template(tmpl, [prop1, prop2, ...]).

EXTENDED FEATURES

This package also provides extended function which includes include and wrapper function. Of course, this feature can be used on browsers by just copying and pasting lib/micro-template.js.

include(name)

import { extended as template } from 'micro-template';

template('view1', { foo : 'bar' });
<!-- view1 -->
aaa
<% include('view2') %>
bbb
<!-- view2 -->
Hello, World!

wrapper(name, func)

import { extended as template } from 'micro-template';

template('view1', { foo : 'bar' });
<!-- view1 -->
<% wrapper('wrapper', function () { %>
<h1>Hello, World!</h1>
<% }) %>
<!-- wrapper -->
<!DOCTYPE html>
<title><%= foo %></title>
<body><%=raw content %></body>

BENCHMARK

online: benchmark on perf.link

node:

  • node misc/benchmark.js
> node --expose-gc ./misc/benchmark.js

 1 = 1 / 2 = 2 / 3 = Fizz / 4 = 4 / 5 = Buzz / 6 = Fizz / 7 = 7 / 8 = 8 / 9 = Fizz / 10 = Buzz / 11 = 11 / 12 = Fizz / 13 = 13 / 14 = 14 / 15 = FizzBuzz / 16 = 16 / 17 = 17 / 18 = Fizz / 19 = 19 / 20 = Buzz / 21 = Fizz / 22 = 22 / 23 = 23 / 24 = Fizz / 25 = Buzz / 26 = 26 / 27 = Fizz / 28 = 28 / 29 = 29 / 30 = FizzBuzz / 
clk: ~3.05 GHz
cpu: Apple M1
runtime: node 20.10.0 (arm64-darwin)

benchmark                         avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------------- -------------------------------
micro-template                      20.72 µs/iter  19.75 µs  █                   
                           (18.83 µs … 232.63 µs)  35.50 µs  █                   
                          ( 11.37 kb … 429.21 kb) 108.69 kb ▄█▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

micro-template pre compiled         19.64 µs/iter  19.65 µs       █              
                            (19.48 µs … 19.94 µs)  19.83 µs ▅  ▅▅ █▅▅▅▅▅        ▅
                          (  1.92 kb …   1.96 kb)   1.93 kb █▁▁██▁██████▁▁▁▁▁▁▁▁█

micro-template (not cached)        174.13 µs/iter 172.08 µs ▆█                   
                          (164.50 µs … 627.62 µs) 325.83 µs ██                   
                          (  4.38 kb … 639.55 kb) 140.32 kb ██▄▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

micro-template (template.variable)  21.01 µs/iter  20.13 µs  █                   
                           (19.25 µs … 245.96 µs)  32.79 µs  █                   
                          ( 12.90 kb … 356.45 kb) 108.75 kb ██▆▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

ejs.compile (pre compiled)         438.36 µs/iter 443.67 µs   ▄█▄                
                          (418.00 µs … 665.54 µs) 489.46 µs   ███▂               
                          ( 10.37 kb … 798.84 kb)  99.42 kb ▁▁██████▇▅▅▃▃▂▂▂▁▂▁▁▁

ejs.render                         457.18 µs/iter 458.37 µs   █                 
                          (439.75 µs … 659.54 µs) 520.92 µs                     
                          (  5.91 kb … 842.41 kb) 141.02 kb ▃▄██▇▆▄▄▃▃▂▂▂▂▂▁▁▁▁▁▁

John Resig's tmpl                  240.02 µs/iter 240.67 µs                      
                          (228.29 µs … 436.46 µs) 277.63 µs   ▅                
                          ( 14.08 kb … 438.89 kb) 126.56 kb ▃▅███▆▄▃▃▂▃▂▂▂▂▁▁▁▁▁▁

                                   ┌                                            ┐
                    micro-template ┤ 20.72 s
       micro-template pre compiled ┤ 19.64 s
       micro-template (not cached) ┤■■■■■■■■■■s ■ 174.13 µ
micro-template (template.variable) ┤ 21.01 s
        ejs.compile (pre compiled) ┤■■■■■■■■■■■■■■■■■■■■ s■■■■■■■■■■■■ 438.36 µ
                        ejs.render ┤■■■■■■■■■■■■■■■■■■■■■s ■■■■■■■■■■■■ 457.18 µ
                 John Resig's tmpl ┤■■■■■■■■■■■■ s■■■■ 240.02 µ
                                   └                                            ┘

summary
  micro-template pre compiled
   1.06x faster than micro-template
   1.07x faster than micro-template (template.variable)
   8.87x faster than micro-template (not cached)
   12.22x faster than John Resig's tmpl
   22.32x faster than ejs.compile (pre compiled)
   23.28x faster than ejs.render

micro-template-serialize

micro-template-serialize is a CLI tool for precompiling multiple template files into a single ESM (ECMAScript Module) JavaScript file. This is especially useful for environments where dynamic function generation (such as with new Function()) is not allowed, or for delivering precompiled templates to browsers or serverless environments.

Why use it?

  • Security: Avoids the use of new Function() at runtime, which is often restricted in secure or serverless environments.
  • Performance: Templates are precompiled, so rendering is fast and does not require parsing or compiling templates at runtime.
  • Convenience: Bundles multiple templates into a single importable module.

Usage

micro-template-serialize <input1.tmpl> <input2.tmpl> ... --output <output.js> [--root <dir>]
  • <input1.tmpl> ... : List of template files to serialize.
  • --output <output.js> : Output JavaScript file (required).
  • --root <dir> : Root directory for template IDs (default: current directory).

Example

micro-template-serialize test/data-test1.tmpl test/data-fizzbuzz.tmpl --output templates.js --root test

This will generate a file named templates.js in the current directory. You can then import the generated module in your JavaScript code:

import { extended as template } from './templates.js';
const result = template('main', { foo: 'world', baz: 'baz!' });
console.log('render result:', result);
  • If a template does not contain a <!--meta.keys=[...]--> comment, a warning will be shown.
  • The output file is an ESM module (use import to load it).

LICENSE

MIT: https://cho45.github.io/mit-license

About

template engine for JavaScript which is similar to embed.js.

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 5