Handlebars - the next generation
Language specification

The Mustache Statement

A mustache statement defines a placeholder in the output. The placeholder is replaced by a value that is resolved from the input object via a PathExpression. In the most simple form, the PathExpression is the name of a property in the input object.

More complex expressions are possible and described in a later chapter (TODO).

Mustaches
MustacheStatement[Unescaped] ::
MustacheStart[?Unescaped] MustacheContents MustacheEnd[?Unescaped]
MustacheStart[Unescaped] ::
[+Unescaped]{{{
[~Unescaped]{{
MustacheEnd[Unescaped] ::
[+Unescaped]}}}
[~Unescaped]}}
MustacheContents ::
WhitespaceControlopt Whitespaceopt PathExpression MustacheParametersopt Whitespaceopt WhitespaceControlopt
WhitespaceControl :: ~
Whitespace ::
WhitespaceChar Whitespaceopt
WhitespaceChar :: one of
<SPACE> <TAB> <LF> <CR>

Html escaped Mustache Statement

When using {{ and }}, the resolved value is HTML-escaped using the following substitutions:

Character    Substitute
    &           &amp;
    <           &lt;
    >           &gt;
    "           &quot:
    '           &#x27;
    `           &#x60;
    =           &#x3D;

05-mustache-statement/html-escaped-mustache.hb-spec.json
A simple mustache with html-escaped output
Template:
{{ value }}
Input / Output
Input
{ "value": "abc & < > \" ' ` = 123" }
Output
abc &amp; &lt; &gt; &quot; &#x27; &#x60; &#x3D; 123
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "value",
        "data": false,
        "depth": 0,
        "parts": ["value"],
        "loc": {
          "start": { "line": 1, "column": 3 },
          "end": { "line": 1, "column": 8 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 11 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 11 }
  }
}

Unscaped Mustache Statement

When using {{{ and }}}, the resolved value copied to the output as-is.

05-mustache-statement/unescaped-mustache.hb-spec.json
A simple mustache with unescaped output
Template:
{{{ value }}}
Input / Output
Input
{ "value": "abc & < > \" ' ` = 123" }
Output
abc & < > " ' ` = 123
AST
{
  "type": "Program",
  "body": [
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "value",
        "data": false,
        "depth": 0,
        "parts": ["value"],
        "loc": {
          "start": { "line": 1, "column": 4 },
          "end": { "line": 1, "column": 9 }
        }
      },
      "strip": { "open": false, "close": false },
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 13 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 13 }
  }
}

White space control

A ”~” at the beginning or at the end of a mustache statements declares that white space before (or respectively after) the mustache must be removed from the output.

White spec control is done by the parser. The value property of adjacent ContentStatement-nodes is trimmed accordingly, the original property is preserved.

05-mustache-statement/white-space-control-escaped.hb-spec.json
White space should be removed when control characters are in place
Template:
|- {{~name}} | {{name~}} -|- {{~name~}} -|
Input / Output
Input
{ "name": "joe" }
Output
|-joe | joe-|-joe-|
AST
{
  "type": "Program",
  "body": [
    {
      "type": "ContentStatement",
      "value": "|-",
      "original": "|- ",
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 3 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 6 },
          "end": { "line": 1, "column": 10 }
        }
      },
      "strip": { "open": true, "close": false },
      "loc": {
        "start": { "line": 1, "column": 3 },
        "end": { "line": 1, "column": 12 }
      }
    },
    {
      "type": "ContentStatement",
      "value": " | ",
      "original": " | ",
      "loc": {
        "start": { "line": 1, "column": 12 },
        "end": { "line": 1, "column": 15 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 17 },
          "end": { "line": 1, "column": 21 }
        }
      },
      "strip": { "open": false, "close": true },
      "loc": {
        "start": { "line": 1, "column": 15 },
        "end": { "line": 1, "column": 24 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|-",
      "original": " -|- ",
      "loc": {
        "start": { "line": 1, "column": 24 },
        "end": { "line": 1, "column": 29 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 32 },
          "end": { "line": 1, "column": 36 }
        }
      },
      "strip": { "open": true, "close": true },
      "loc": {
        "start": { "line": 1, "column": 29 },
        "end": { "line": 1, "column": 39 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|",
      "original": " -|",
      "loc": {
        "start": { "line": 1, "column": 39 },
        "end": { "line": 1, "column": 42 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 42 }
  }
}

05-mustache-statement/white-space-control-unescaped.hb-spec.json
White space should be removed when control characters are in place
Template:
|- {{{~name}}} | {{{name~}}} -|- {{{~name~}}} -|
Input / Output
Input
{ "name": "joe" }
Output
|-joe | joe-|-joe-|
AST
{
  "type": "Program",
  "body": [
    {
      "type": "ContentStatement",
      "value": "|-",
      "original": "|- ",
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 3 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 7 },
          "end": { "line": 1, "column": 11 }
        }
      },
      "strip": { "open": true, "close": false },
      "loc": {
        "start": { "line": 1, "column": 3 },
        "end": { "line": 1, "column": 14 }
      }
    },
    {
      "type": "ContentStatement",
      "value": " | ",
      "original": " | ",
      "loc": {
        "start": { "line": 1, "column": 14 },
        "end": { "line": 1, "column": 17 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 20 },
          "end": { "line": 1, "column": 24 }
        }
      },
      "strip": { "open": false, "close": true },
      "loc": {
        "start": { "line": 1, "column": 17 },
        "end": { "line": 1, "column": 28 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|-",
      "original": " -|- ",
      "loc": {
        "start": { "line": 1, "column": 28 },
        "end": { "line": 1, "column": 33 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 1, "column": 37 },
          "end": { "line": 1, "column": 41 }
        }
      },
      "strip": { "open": true, "close": true },
      "loc": {
        "start": { "line": 1, "column": 33 },
        "end": { "line": 1, "column": 45 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|",
      "original": " -|",
      "loc": {
        "start": { "line": 1, "column": 45 },
        "end": { "line": 1, "column": 48 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 1, "column": 48 }
  }
}

Nodes with empty value property are preserved to allow tools to recreate the exact template from an AST.

Ignored whitespace

Whitespace around the PathExpression is ignored

05-mustache-statement/white-space-ignored-escaped.hb-spec.json
White space around path expressions is ignored (escaped mustache)
Template:
|- {{~  	
name  	
}} | {{  	
name  	
~}} -|- {{~  	
name  	
~}} -|
Input / Output
Input
{ "name": "joe" }
Output
|-joe | joe-|-joe-|
AST
{
  "type": "Program",
  "body": [
    {
      "type": "ContentStatement",
      "value": "|-",
      "original": "|- ",
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 3 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 2, "column": 0 },
          "end": { "line": 2, "column": 4 }
        }
      },
      "strip": { "open": true, "close": false },
      "loc": {
        "start": { "line": 1, "column": 3 },
        "end": { "line": 3, "column": 2 }
      }
    },
    {
      "type": "ContentStatement",
      "value": " | ",
      "original": " | ",
      "loc": {
        "start": { "line": 3, "column": 2 },
        "end": { "line": 3, "column": 5 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 4, "column": 0 },
          "end": { "line": 4, "column": 4 }
        }
      },
      "strip": { "open": false, "close": true },
      "loc": {
        "start": { "line": 3, "column": 5 },
        "end": { "line": 5, "column": 3 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|-",
      "original": " -|- ",
      "loc": {
        "start": { "line": 5, "column": 3 },
        "end": { "line": 5, "column": 8 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": true,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 6, "column": 0 },
          "end": { "line": 6, "column": 4 }
        }
      },
      "strip": { "open": true, "close": true },
      "loc": {
        "start": { "line": 5, "column": 8 },
        "end": { "line": 7, "column": 3 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|",
      "original": " -|",
      "loc": {
        "start": { "line": 7, "column": 3 },
        "end": { "line": 7, "column": 6 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 7, "column": 6 }
  }
}

05-mustache-statement/white-space-ignored-unescaped.hb-spec.json
White space around path expressions is ignored (unescaped mustache)
Template:
|- {{{~  	
name  	
}}} | {{{  	
name  	
~}}} -|- {{{~  	
name	
  ~}}} -|
Input / Output
Input
{ "name": "joe" }
Output
|-joe | joe-|-joe-|
AST
{
  "type": "Program",
  "body": [
    {
      "type": "ContentStatement",
      "value": "|-",
      "original": "|- ",
      "loc": {
        "start": { "line": 1, "column": 0 },
        "end": { "line": 1, "column": 3 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 2, "column": 0 },
          "end": { "line": 2, "column": 4 }
        }
      },
      "strip": { "open": true, "close": false },
      "loc": {
        "start": { "line": 1, "column": 3 },
        "end": { "line": 3, "column": 3 }
      }
    },
    {
      "type": "ContentStatement",
      "value": " | ",
      "original": " | ",
      "loc": {
        "start": { "line": 3, "column": 3 },
        "end": { "line": 3, "column": 6 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 4, "column": 0 },
          "end": { "line": 4, "column": 4 }
        }
      },
      "strip": { "open": false, "close": true },
      "loc": {
        "start": { "line": 3, "column": 6 },
        "end": { "line": 5, "column": 4 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|-",
      "original": " -|- ",
      "loc": {
        "start": { "line": 5, "column": 4 },
        "end": { "line": 5, "column": 9 }
      }
    },
    {
      "type": "MustacheStatement",
      "escaped": false,
      "params": [],
      "path": {
        "type": "PathExpression",
        "original": "name",
        "data": false,
        "depth": 0,
        "parts": ["name"],
        "loc": {
          "start": { "line": 6, "column": 0 },
          "end": { "line": 6, "column": 4 }
        }
      },
      "strip": { "open": true, "close": true },
      "loc": {
        "start": { "line": 5, "column": 9 },
        "end": { "line": 7, "column": 6 }
      }
    },
    {
      "type": "ContentStatement",
      "value": "-|",
      "original": " -|",
      "loc": {
        "start": { "line": 7, "column": 6 },
        "end": { "line": 7, "column": 9 }
      }
    }
  ],
  "strip": {},
  "loc": {
    "start": { "line": 1, "column": 0 },
    "end": { "line": 7, "column": 9 }
  }
}