moosebench · Node benchmark

moose‑stash vs mustache.js

Results from npm run moosebench — the same templating jobs, plus the drop-in compatibility API (Writer, Context, Scanner, the config argument, function partials, cache control), each rendered by both engines to confirm identical output, then timed on Node. Green past the 1× line means moose-stash is faster.

Node v22.22.2 · 4-vCPU cloud

Throughput

—

faster on 16 of 17 comparable jobs

Drop-in

100%

replacement for mustache.js†

Peak speed

—

on the deepest job

Moose-only

—

does two things mustache.js can't‡

Lines of code — packaged script, excluding comments (moose-stash.js vs mustache.js)

moose-stash646 LoC
mustache.js 4.2463 LoC

646 vs 463 lines of code, measured excluding comments — moose-stash does more (inheritance, dynamic partial names, render-callback lambdas), zero deps.

Job
Parity
moose µs
mustache µs
Speed

* All render timings — the workload jobs above and the test suite below — were measured on Node.js v22.22.2 (V8) on a 4-vCPU cloud container (Intel Xeon @ 2.10 GHz), each figure the fastest of 8 timed windows. Absolute microseconds are hardware-specific (≈1.5–2× slower than desktop Apple Silicon); the ratios are the signal. Run-to-run swing is ≈10–15% on shared-tenant vCPUs, so anything within ~0.1× of parity is a tie — big-list-2000 (≈1.0×) is such a tie.

† Drop-in means byte-for-byte parity on the 17 common, hard, and compat-surface jobs above — real templates (an HTML table, a page layout, a transactional email, deeply-nested sections, …) each rendered by both engines and compared character-for-character, plus the same public API (render, parse, escape, tags, clearCache, templateCache, Writer, Context, Scanner). Their templates and data are moose-stash's own moosebench workload.

‡ The two moose-only jobs are template inheritance ({{<parent}} / {{$block}}) and section lambdas that use the spec's render callback — neither is implemented by mustache.js 4.2.

Interpreter, not a compiler. moose-stash and mustache.js both parse a template to a token tree and walk it per render — no eval, no new Function. Compiled engines (hogan.js, wontache, mote, …) emit JavaScript and eval it, which V8 then JITs, so they render faster than either interpreter. moose-stash is the faster interpreter, and — unlike the compilers — it runs under a strict Content-Security-Policy (script-src without 'unsafe-eval') that forbids new Function.

Correctness

Identical output, broader coverage

Two example corpora: the official Mustache spec, and mustache.js's own test suite. moose-stash reproduces every mustache.js fixture "byte-for-byte", and passes spec modules mustache.js doesn't implement.

Spec conformance

190/194 · +4 declined*

mustache.js: 142 / 194

Test suite

62/62

mustache.js's test suite — "byte-for-byte" identical

Mustache spec — by modulemoose 190/194 · mustache 142/194
Module
Coverage · green = moose, gold = mustache.js
passing

* The 4 declined are the spec's lambda re-render cases (lambdas row above). moose-stash never re-parses a lambda's return value as a template — doing so would let user text returned from a lambda become live tags like {{secret}} or {{> admin}}, a server-side template-injection surface — so it emits the safe literal output by design instead of expanding the returned tags. They are declines, not failures: the engine still produces correct, safe text. (mustache.js 4.2 doesn't implement these either — it passes 2 of the 10 lambda cases.)

mustache.js test suite62 / 62 identical · moose faster on 60 / 62, median 1.29×