npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mustachio

v1.0.1

Published

A pull streaming Mustache engine

Downloads

235

Readme

Mustachio is a pull streaming implementation of the Mustache templating language. It is fully compliant with version 1.1 of the Mustache spec.

Build Status

Getting started

const mustachio = require('mustachio');

const mu = mustachio.resolver();

mu("demo", { name: "World" }).stream().pipe(process.stdout);

And then, in a directory templates, create the file demo.mustache:

Hello, {{name}}!

Running this program outputs:

Hello, World!

If you want to use this together with Express, see mustachio-middleware.

Why Mustachio?

Mustachio is streaming, whereas other templating engines usually are blocking. Traditionally, when serving a web request, the control flow is a sequence of three discrete steps:

  1. Input: Collect all the requisite data from the database, filesystem and any other sources
  2. Rendering: Pass the data (sometimes called "view") and (compiled) template to the templating engine to render the full response
  3. Output: Send the rendered response to the client

In this model, no output can happen before the rendering is finished, and no rendering or output can happen before all the data has been collected. This is often good enough! However, a streaming model offers greater flexibility, can give better performance and it can be a better fit in an otherwise asynchronous environment such as node.js.

In Mustachio these three steps happen interleaved, in a streaming fashion. This means rendering can proceed as soon as the relevant data becomes available. Consequently, Mustachio will be able to respond immediately to many requests instead of waiting until the last bit of data trickles in. It also means flow control works, so rendering and gathering of input data can be suspended when the client can not keep up. This frees up resources for handling other requests.

The examples directory contains examples that highlight different qualities of the streaming model:

  • large-response demonstrates flow control
  • file-browser demonstrates rendering with data that asynchronously becomes available
  • postgres-query-stream demonstrates rendering with data from a PostgreSQL database, stream reading from the database
  • react shows how to explicitly flush the render buffer before blocking for a large or slow operation

API

const mustachio = require('mustachio');

Using a template string

const template = mustachio.string("Hello, {{name}}\n");
const rendering = template.render({ name: "World" });

Stream render:

rendering.stream().pipe(process.stdout);

Render to string:

rendering.string().then(result => console.log(result));

Using templates from the file system

const resolver = mustachio.resolver();

const rendering = resolver("template-name", { name: "World" });

This rendering object has stream() and string() methods just like the rendering object above that we got from the template string.

The resolver uses the template ID you give in to locate a file in the file system, under the directory templates in your project directory. It looks for files with file extensions .mustache, .mu, .mu.html and .html in that order. The same mechanism is used to resolve partial includes.

When using the resolver, the compiled template is cached in memory and reused when rendering the same template again. Additionally, a file system watcher is set up to invalidate the cache whenever the template file is edited.

Customizing partials resolving

You can customize the base directory for templates and the file name suffixes used for resolving partials by passing a configuration object to the resolver:

const resolver = mustachio.resolver({
  root: path.join(__dirname, "my-templates"),
  suffixes: [ ".my.oh.my" ]
});

To get other behaviour and even resolve partials from other sources than the file system (such as the database, or HTTP calls), pass a custom partials resolver object:

const resolver = mustachio.resolver({
  partialsResolver: new CustomPartialsResolver()
});

See partials for details.

When using a template string, partials will by default not be resolved. To enable partials resolving for such templates, pass a partials resolver to the render function:

template.render(data, partialsResolver);

Explicitly flushing the render buffer

As a special concern, it is possible to explicitly flush the render buffer during rendering. Mustachio offers two ways of doing this:

  • by using the {{_flush}} tag in the template. Note that if your data object includes data named _flush it overrides the flush function
  • call stream.flush() in the code. It returns a Promise, and when this resolves, the render buffer has been flushed

See the React example for more about both methods.

The data object

These are the types of values you can put in the data object: Fundamental types, objects, arrays, functions, generator functions, promises, text streams and object mode streams.

The power of Mustachio comes from combining these building blocks. It works perfectly well to specify a function that returns a promise which resolves to a generator that yields functions which ... etc, etc. See the file-browser example for an effective use of this.

Fundamental types

{
  "number": 5.25,
  "desc": "is a number.",
  "true-bool": true
}

{{number}} {{desc}} {{#true-bool}}Yes!{{/true-bool}} {{true-bool}}5.25 is a number. Yes! true

Objects

{
  "a": {
    "b": 5,
    "c": {
      "d": 6
    }
  }
}

{{#a}}{{b}}, {{c.d}}{{/a}}5, 6

Arrays

{ "a": [1, 2, 3] }

{{#a}}({{.}}){{/a}}(1)(2)(3).

Functions

{
  "a": () => 5
}

{{a}}5

Functions get called with the containing object as the this argument:

{
  "a": 5,
  "b": function () { return this.a * 2; }
}

{{b}}10

Generator functions

Generator functions will be treated as arrays:

{
  "a": function* () {
    yield 0;
    yield "is smaller than";
    yield 1;
  }
}

{{#a}}({{.}}){{/a}}(0)(is smaller than)(1)

Promises

{
  "a": new Promise((resolve, reject) => {
    // Any asynchronous operation
    require('fs').stat(__dirname, (stat, err) => {
      if (err) reject(err);
      else resolve(stat);
    });
  })
}

{{#a.isDirectory}}A directory!{{/a.isDirectory}}A directory!

The file-browser example demonstrates use of promises as data.

Text streams

A text stream can be interpolated, like a string:

{
  "cmdline": require('fs').createReadStream('/proc/self/cmdline', 'utf8')
}

You ran: {{cmdline}}You ran: node

Note that streams must have an encoding set, so they output text rather than binary data.

Object mode streams

An object mode stream can be iterated over, like an array:

{
  "objects": () => {
    const objects = [ 1, 2, 3 ];
    return new (require('stream')).Readable({
      objectMode: true,
      read() {
        this.push(objects.length ? objects.shift() : null);
      }
    });
  }
}

{{#objects}}({{.}}){{/objects}}(1)(2)(3)

The postgres-query-stream example demonstrates use of object mode streams.