npm package: @funcstache/stache-stream
Description: A [Mustache](https://mustache.github.io/) template renderer for JavaScript focused on returning results as quickly as possible.
Latest version: 0.3.2

# Streaming Mustache Template Rendering

A [Mustache](https://mustache.github.io/) template renderer for JavaScript focused on returning
results as quickly as possible.

This library renders a Mustache template as it's read, rather than loading the entire template into
memory before processing. The values of tags in the Mustache template are rendered as soon as
possible and written to the output stream.

stache-stream is intended to be used as part of a server-side rendering platform that generates
HTML, whose goal is to provide a superior Web user experience on all kinds of devices - especially
devices with lower memory and CPU capabilities.

> [!TIP]
> There are two other packages with higher level abstractions for streaming mustache rendering.
>
> - The "funcstache" package is a library that renders a complete mustache template from a collection
>   of components in a file system. See the [funcstache
>   docs](https://codeberg.org/rlmcneary2/funcstache/src/branch/main/libs/funcstache).
> - Koa middleware for serving rendered HTML. See the [koa-middleware
>   docs](https://codeberg.org/rlmcneary2/funcstache/src/branch/main/libs/koa-middleware).

## Overview

The library is constructed to maximize asynchronous processing of a template and streaming a result
as quickly as possible.

- A [ReadableStream](https://nodejs.org/api/webstreams.html#class-readablestream) created from a
  Mustache template, for example, a string or a file.
- Context that prefers the use of async functions (lambdas) to provide the data rendered into the
  template.
- A [WritableStream](https://nodejs.org/api/webstreams.html#class-writablestream) that will receive
  the rendered template output.

This functionality is wrapped in a
[StacheTransformStream](src/lib/stache-stream/StacheStream.ts) that implements a
NodeJS [TransformStream](https://nodejs.org/api/webstreams.html#class-transformstream).

The components that comprise a `StacheTransformStream` instance process a Mustache Template in this
order:

```text
ReadableStream -> (Parse → Tokenize → Queue → Template) → WritableStream
```

## Use

When using a `StacheTransformStream` you will need to provide a Mustache template as the source
`ReadableStream` and options that include the `ContextProvider` to be used to render data into the
template.

The following example includes a template with two variables that are replaced. The output
illustrates how chunks are written to the output as they become available.

```ts
// Mustache template in the ReadableStream:
// "Dear {{title}} {{name}},\n\nYou may already be a winner!"

const stacheTransformStream = new StacheTransformStream({
  contextProvider: {
    context: { name: "Alice Brown" },
    getContextValue: async (tag) => (tag.key === "title" ? "Doctor" : ""),
  },
});

for await (const chunk of readable.pipeThrough(stacheTransformStream)) {
  await writable.getWriter().write(chunk);
}

// Writable stream output chunks:
// "Dear "
// "Doctor"
// " "
// "Alice Brown"
// ",\n\nYou may already be a winner!"
```

### Asynchronously render a partial's replacement

A more complex example renders a partial tag by reading the partial's replacement -&nbsp;another
mustache template&nbsp;- from a file. The replacement template has a variable for the current date
which named `newsItemDate` which is replaced with a value from newsItem's parent's context.

```ts
// Mustache template in the ReadableStream:
// "<article>Latest news: {{ >newsItem }}</article>"

// `newsItem` template:
// "<p>As of {{newsItemDate}} - in the news...</p>"

const stacheTransformStream = new StacheTransformStream({
  contextProvider: {
    context: {
      newsItemDate: new Date().toString(),
      newsItem: async (tag: Tag): Promise<ReadableStream> => {
        const fileName = "latest-news-item.mustache";
        const file = fs.createReadStream(fileName, { encoding: "utf-8" });
        return ReadableStream.from(file);
      },
    },
  },
});

for await (const chunk of readable.pipeThrough(stacheTransformStream)) {
  await writable.getWriter().write(chunk);
}
```

### Improve web page performance

The previous example is trivial and a little silly, but being able to render a template containing
HTML and write it to an output stream as soon as possible can have significant performance
improvements for display in a web browser. Consider the following pseudo-code for a Mustache
template that outputs a complete HTML file.

```html
<!-- HTML with embedded Mustache tags. -->

<html>
  <head>
    <!-- load CSS files and other assets -->
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <!--
      The content for the page at this route is generated dynamically, maybe it's a table with data
      that varies depending on the search params...
    -->
    <section>{{>aboveTheFoldContent}}</section>
    <section>{{>content}}</section>
  </body>
</html>
```

When rendered using stache-stream all the content up to the `{{>content}}` partial tag is sent to
the browser immediately. Browsers will parse streamed response data as it arrives and where possible
will begin to render the web page. In the case of a head tag with CSS and other assets the browser
will begin to download those assets in the background as soon as they are parsed. This is a main
goal of stache-stream to improve the experience in a browser by allowing web pages to load and
render more quickly.

We can also break the page content into separate blocks - for example, content that appears "above
the fold" will be transformed on the server and sent down as soon as that is complete, without
waiting for the rest of the content to be rendered. THis can be especially helpful when display data
is fetched from storage.