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 🙏

© 2026 – Pkg Stats / Ryan Hefner

moose-stash

v1.0.1

Published

A tiny, fast, logic-less {{mustache}} template engine. Zero dependencies. Safe against lambda template injection.

Readme

moose-stash

A tiny, fast, logic-less Mustache template engine for JavaScript and TypeScript — zero dependencies, safe against lambda template injection, and a drop-in for mustache.js.

  • Small. ~400 lines, no runtime dependencies. UMD, ESM, and minified builds.
  • Fast. Beats mustache on every workload measured — up to 1.6×.
  • Complete. The whole Mustache spec plus template inheritance and dynamic partial names, which mustache doesn't have.
  • Safe by default. A lambda's return value is always literal text and is never re-parsed as a template — closing the server-side template-injection surface.
  • Drop-in. The same public surface as mustache.js — render (with the config argument), parse, escape, tags, clearCache, templateCache, and the Writer, Context, and Scanner classes — and it reproduces all 62 of mustache.js's own fixtures byte for byte. Swap require("mustache") for require("moose-stash").
  • Typed. Written in TypeScript; type declarations ship with the package.

vs. mustache.js

| | moose-stash | mustache.js 4.2 | |---|---|---| | Mustache spec cases | 190 / 194 (+4 declined for safety) | 142 / 194 | | mustache.js fixtures | 62 / 62 | 62 / 62 | | Template inheritance ({{<parent}} / {{$block}}) | ✅ | ❌ | | Dynamic partial names ({{>*name}}) | ✅ | ❌ | | Render-callback section lambdas | ✅ | ❌ | | Lambda injection safe by default | ✅ | by omission | | Throughput | 1.0–1.6× | 1× | | Runtime dependencies | 0 | 0 |

The 4 declined spec cases are the lambda re-render cases — see Security.

Install

npm install moose-stash

Usage

import MooseStash from "moose-stash";      // default import
import { render } from "moose-stash";      // …or named
const MooseStash = require("moose-stash"); // …or CommonJS

render("Hello, {{name}}!", { name: "world" });
// → "Hello, world!"

// sections, inverted sections, dotted names
render("{{#user}}{{name}} <{{contact.email}}>{{/user}}{{^user}}nobody{{/user}}",
  { user: { name: "Ann", contact: { email: "[email protected]" } } });
// → "Ann <[email protected]>"

// lists
render("{{#items}}- {{label}}\n{{/items}}", { items: [{ label: "a" }, { label: "b" }] });
// → "- a\n- b\n"

// partials: a name → source map
render("{{> header}} {{body}}", { title: "Hi", body: "…" }, { header: "[{{title}}]" });
// → "[Hi] …"

API

The surface mirrors mustache.js, so existing code works unchanged:

render(template: string, view: unknown,
       partials?: Record<string, string> | ((name: string) => string | undefined),
       config?: [string, string] | { tags?: [string, string]; escape?: (s: unknown) => string }
): string

parse(template: string, tags?: [string, string]): Token[]  // parse + prime the cache
clearCache(): void                                          // empty the parse cache

escape: (s: unknown) => string   // mutable — the default HTML escape
tags: [string, string]           // mutable — the default delimiters, ["{{", "}}"]
templateCache                    // set to undefined to disable caching
version: string
name: string

class Writer   // an isolated engine with its own template cache
class Context  // a view + parent chain: new Context(view).push(child), .lookup(name)
class Scanner  // an incremental scanner: .scan(re), .scanUntil(re), .eos(), .tail
// the 4th `config` argument: custom delimiters and/or a custom escape
render("Hi <%name%>", { name: "Ann" }, {}, { tags: ["<%", "%>"] });   // → "Hi Ann"
render("{{x}}", { x: "a&b" }, {}, { escape: (s) => String(s) });       // → "a&b"

// partials as a resolver function
render("{{> row}}", view, (name) => loadPartial(name));

// an isolated writer with its own cache
const w = new Writer();
w.render("{{greeting}}", { greeting: "hi" });

{{name}} HTML-escapes its value; {{{name}}} and {{&name}} emit it raw. A value may be a function: {{fn}} renders fn(); {{#fn}}…{{/fn}} calls fn(rawText, render) and the render callback re-renders template-author text on demand.

Like mustache.js, a malformed template throws: an unterminated tag throws Unclosed tag at N, and a non-string template throws a TypeError.

Features

Everything Mustache — interpolation, sections, inverted sections, comments, partials, set delimiters ({{=<% %>=}}), dotted names, and lambdas — plus:

  • Template inheritance — {{<parent}} with named {{$block}} overrides, including correct block reindentation.
  • Dynamic partial names — {{>*name}}, resolving the partial's name from the data.

Security

Mustache lets a lambda's return value be re-parsed as a new template. That powers i18n/filters, but it's a server-side template-injection surface: user text returned from a lambda becomes live tags — {{secret}}, {{> admin}} — that can read the context or pull in partials.

moose-stash never re-parses a lambda's return. It is always literal text. There is no mode or flag that re-opens the surface.

render("{{leak}}", { secret: "hunter2", leak: () => "{{secret}}" });
// → "{{secret}}"   (literal — the secret is not read)

A section lambda can still render template-author text through its explicit render callback — that's the lambda's deliberate act on trusted source, not an automatic re-parse of an untrusted return. The cost of this guarantee is the spec's 4 lambda re-render cases, declined by design.

Defense in depth still applies: prefer {{ }} (escaped) over {{{ }}} (raw), don't let users supply templates or register lambdas/partials, and keep secrets off the view.

CLI

moose-stash <view.json> <template.mustache> [output]   # -p <partial> to add partials
moose-stash --version

The view may be a JSON file, a .js/.cjs module (so it can hold functions), or - for JSON on stdin.

Builds & wrappers

moose-stash.js (UMD, global MooseStash) · moose-stash.mjs (ESM) · moose-stash.min.js (minified). Adapters for jQuery, MooTools, Dojo, YUI3, and qooxdoo live under wrappers/.

License

MIT.