latest version on npm author: Julian Gonggrijp license text code hosted on GitLab changelog issue tracker on GitLab pipeline status

Wontache

Compact, spec-compliant Mustache implementation with extras

Wontache is a fast, precompiling implementation of the Mustache templating language for JavaScript, written in just a few hundred lines of literate CoffeeScript. It fully implements version 1.3.0 of the Mustache specification, including the optional modules, as well as some extra features which are listed further down below. An up-to-date reference of the Mustache templating language is available over here. You can try out the Wontache engine in its full glory, right from your browser, in the playground.

Compared to:

The above claims are based on extensive benchmarks and rendering comparisons. There is no user-friendly presentation of the comparison yet, but if you wish, you can inspect the comparison source code. Of course, the best way to determine how Wontache performs in your application is to try it.

Quickstart


    import mustache from 'wontache';

    const template = '{{#people}}Hello {{name}}!\n{{/people}}';
    const data = {
        people: [
            {name: 'Alice'},
            {name: 'Bob'},
        ],
    };

    const templateFunction = mustache(template);
    const output = templateFunction(data);
    // Hello Alice!
    // Hello Bob!

    // You can also do a double call for a quick one-off:
    output = mustache(template)(data);
    

Development status

The 0.x release series is intended for evaluation. Wontache already completely implements the latest Mustache specification and is fully tested. Templates that compile in Wontache 0.x are expected to also compile in the 1.x release series and should mostly produce the same output. However, the interface has not fully stabilized yet. Among other things, this means that you might need to recompile your templates when Wontache is updated.

Most of the current development work is focused on adding production-friendly conveniences, such as TypeScript and Flow type declarations, a command line tool and integrations for various code bundling tools. More extensive documentation is planned as well. You can track progress towards the official launch over here.

Extras

You can track planned extras over here.

Companion libraries and tools

Integrations for more bundling and task automation tools are planned, as well as other companion libraries and tools.

Build variants

Wontache has several build variants. If you use the package in Node.js or bundle it with a Node.js-aware tool such as Rollup, WebPack or Browserify, the right build variant will be selected automatically. In other situations, such as loading from a CDN, using an AMD loader or usage in deno, you may need to select a build variant explicitly. The following build variants are of interest outside of Node.js:

Underscore dependency

Wontache depends on Underscore for some utility functions. This enables us to write shorter, more maintainable, more portable and higher quality code than if the library was dependency-free. We encourage everyone to embrace Underscore (and other libraries) for these same reasons.

If you are concerned about dependency size, you can use the module.js build variant in order to enable treeshaking. This is the module entry in the package.json, so bundlers like Rollup and WebPack will find it automatically. The subset of Underscore that Wontache depends on is only about 190 lines of ES3, including blank lines and comments.

Alternatively, the package includes a customUnderscore subdirectory with an index.js that lists all of our direct and indirect Underscore dependencies. You can use this in order to compose your own custom Underscore.

Wontache should also work with Lodash, although this is untested.

Manual

Compilation

The default and only export is a function that we call mustache by convention. It takes a Mustache template string as first argument and returns the compiled template as a function:


    import mustache from 'wontache';

    var template = 'template with {{variable}}';
    var compiled = mustache(template);
    

The Rollup plugin and the webpack loader will generate the above code automatically and wrap it in a module, so you can import the compiled template directly from a standalone template file:


    {{! template.mustache }}
    template with {{variable}}
    

    import compiled from './template.mustache';
    

Rendering

The compiled template function takes the input data as its first argument. This can be any JavaScript value. Values to interpolate in the template are taken from the data. The template function returns a string with the final result of the template, given the data.


    compiled({variable: 'flair'});
    // 'template with flair'
    

Partials

The Partial and Parent tags let you render and interpolate a template inside another template. If your templates contain either of those tags, you have to do some administration so that compiled templates are able to find each other by name. This administration takes the form of an object, where each key is the name of a template that may be embedded. The corresponding value may be either a template string or a template function (if you set a template string, it will be compiled on first use).


    // The administration.
    var namedTemplates = {
        link: '<a href="{{&url}}">{{title}}</a>'
    };

    // A template that will need the above administration.
    var template = '<ul>{{#.}}<li>{{>link}}{{/.}}</ul>';
    var linkList = mustache(template);

    // The data.
    var links = [{
        url: 'https://jgonggrijp.gitlab.io/wontache/',
        title: 'Wontache home page'
    }];
    

There are two possible ways to make the partial administration available to a template function. The most hygienic way is to pass an object with a partials property as the second argument in the call to the template. This approach is strongly recommended for library authors, because it ensures that the partials provided to your template are completely isolated from the partials that other libraries or the application may be using.


    linkList(links, {partials: namedTemplates});
    // '<ul><li><a href="https://jgonggrijp.gitlab.io/wontache/">Wontache home page</a></ul>'
    

If desired, you can write a wrapper function that always passes the same partial administration to a given template function.


    function render(templateFunc, data) {
        return templateFunc(data, {partials: namedTemplates});
    }

    render(linkList, links);
    // Same output as above ('<ul><li><a href="...</a></ul>').
    

The most convenient way is to assign your administration to mustache.partials. Template functions automatically fall back to this if you don’t pass a set of partials explicitly, so it can be “set and forget”. This approach is intended for application authors. When using it, do keep in mind that mustache.partials is easy to compromise, by overwriting either one of its keys or the property as a whole. In principle, a sloppy library author could do this as well. You may even want to dedicate a test to ensuring that mustache.partials is complete.


    Object.assign(mustache.partials, namedTemplates);
    // Could also use Underscore's _.extend for compatibility.

    linkList(links);
    // Same output again ('<ul><li><a href="...</a></ul>').
    

Changing the delimiters

The {{ and }} default delimiters were chosen for a low probability of conflict with other computer languages. However, a conflict is still possible, for example when the template is meant to generate LaTeX code, or when you are writing a template that includes example Mustache template code that should be rendered verbatim. In such cases, you can change the delimiters.

The normal way to change the delimiters is by including a Set Delimiter tag in the template text. This is recommended in most cases, because this ensures your templates are portable to other Mustache implementations. It also gives you the freedom to switch delimiters halfway through a template, even multiple times if necessary.


    var template = '{{=< >=}} template with <variable>';
    var compiled = mustache(template);
    

However, you may have a large application with many templates. If there is a specific, alternative set of delimiters that you are consistently using in each of them, you probably don’t want to start each template with the same Set Delimiter tag. For this purpose, you can pass the alternative delimiters as a second argument to mustache instead.


    var template = 'template with <variable>';
    var compiled = mustache(template, ['<', '>']);
    

If you are also calling mustache in many places, you can wrap the function so you don’t have to explicitly pass the delimiters every time:


    var myMustache = template => mustache(template, ['<', '>']);

    // Equivalent, using Underscore:

    import _, { partial } from 'underscore';

    var myMustache = partial(mustache, _, ['<', '>']);

    // usage in either case:

    var compiled = myMustache('template with <variable>');
    

Precompilation

Template compilation is a costly operation. Precompilation is an optimization that lets you do the compilation ahead of time.

A common situation where you might want to use precompilation, is in a client side web application. The application will respond faster if it does not need to compile its templates before rendering them. Precompilation lets you do the compilation already before the application code is sent to the client, for example in a serverside process that bundles the code.

Keep in mind that the precompiled template code is larger than the original template text. You save startup time at the receiving end, at the expense of transferring more data.

The easiest way to use precompilation, is to set precompile: true when using the Rollup plugin or the webpack loader. However, you can also tap directly in the underlying mechanisms if you need a custom solution.

As described above, when you compile a template the “regular” way, you obtain a function that will render the template. This function has a source property, which is a string that encodes a JavaScript object. That object contains the end result of compilation. It can be passed to mustache instead of the original template text, in order to recreate the compiled template function. In other words, if you build a string precompiled as follows,


    var precompiled = "import mustache from 'wontache';\n" +
    "export default mustache(" + compiled.source + ");";
    

and you write precompiled to a file, you have created a new JavaScript module that exports compiled. No compilation needs to be done in that module; all that work was already done when you created compiled the first time, before writing the module code to disk.

compiled.source does not include the outer mustache() function call in order to give you freedom. For example, you could name the mustache import differently, or you could write JavaScript code that has the precompiled objects in an array rather than passing them directly to mustache.

If writing JavaScript modules is exactly what you want to do, however, you do not need to build the strings yourself. Instead, you can use wrapModule, as described in the following section.

wrapModule

The Rollup plugin and integrations for other build tools all do roughly the same thing: take a Mustache template and wrap it as a JavaScript module. The common logic is contained in the wrapModule function, which is the default and only export of the auxiliary module wontache/wrap-module.

wrapModule takes the raw template string as the first argument and an object with options as an optional second argument. It returns a string with the JavaScript module code. The following options are available.


    import wrapModule from 'wontache/wrap-module';

    const wrappedModule = wrapModule(template, {precompile: true});
    // import mustache from 'wontache';
    // export default mustache(...);
    

Decompilation

Since templates sometimes need to be recompiled, there is a “backdoor” of sorts to reconstruct the original template text from the compiled function. While this is mostly an implementation detail, it might occasionally be useful for debugging purposes.


    compiled(null, {decompile: true});
    // 'template with {{variable}}'
    

Credits

The name “Wontache” was suggested by my dear friend Arie de Bruin.