A template language akin to Jinja, Twig, Liquid, etc.
NOT (currently) intended for production!
Work in progress
Install dependencies (Node.js 8.6+ required):
npm install
The public API is still a work in progress. A cli is not (yet?) available, and no constructor options are implemented. That said, much functionality is present and can be used from a script or Node REPL:
const Crackle = require('./');
const env = new Crackle();
// Parse a template:
const ast = env.parse('{{ animal }}');
// Render a parsed template:
env.render(ast, { animal: 'toad' });
// Parse and render in a single step:
env.parseAndRender('{{ animal }}', { animal: 'toad' });
- Safe: Since Crackle is intended to be user-facing, the primary goal is safety. Degenerate inputs, whether malicious or otherwise, should never be able to degrade the running service.
- Familiar: Syntax choices should look familiar for most programmers.
NOTE: None of the syntax or semantics are finalized. This is a playground to try out various configurations.
Crackle is made up of the following language constructs:
- Statements, denoted by
{%
and%}
by default - Expressions, denoted by
{{
and}}
by default
Expressions output content, while statements are used for control flow.
"Hello world"
, 'Hello world'
Strings are denoted with single or double quotes. A backslash can be used to escape a quote character in a string. Strings can span multiple lines.
1
, 3.14
Ints and floats differ by the presense of a decimal point.
true
, false
, null
Boolean literals and null
are available for completeness, but usually aren't
used directly in templates.
Literals for arrays and objects are not available.
Crackle supports the following data types:
- String
- Int
- Float (including NaN)
- Boolean
- Null
- Array
- Object
{{ myVariableName }}
TBD. Nothing yet available.
NOTE: All comparisons are non-strict. For example, 1 == true
resolves to
true
.
==
!=
>
<
>=
<=
&&
||
!
(grouped-expression)
|
: Apply a filter.
: Member access, e.g.,author.name
Filters are value modifiers. They may be used in any expression.
{{ "hello world" | upcase }}
Available filters:
capitalize
: Capitalize the first letter of a stringdate
: Accepts a format argument to format a date (or the string "now") using strftime syntaxdefault
: Accepts a default value argument to fall back to for falsy valuesdowncase
: Lowercase all letters in a stringtitlecase
: Titlecase a stringupcase
: Uppercase all letters in a string
TBD. Currently no extra treatment is made, which may result in undesirable whitespace. Trimming will be offered, but it is still undecided what should be default behavior and what should be offered via syntax or filter(s).
TBD. Current thought is for HTML-escaping to be offered by default for variables, and for literals to be considered escaped already. Intent is for this behavior to be configurable (including non-html escape mechanisms). Filter(s) may or may not be defined for escaping as well.
The only way to escape Crackle delimiters currently is to specify the delimiters within expressions using string literals, i.e.,
This is an escaped delimiter: {{ "{{ }}" }}
Raw blocks may be defined in the future.
Loop over arrays and strings:
<ul>
{% for tag in tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
Note that for
loops are constrained for safety reasons (50 iterations by
default). Loops that hit the limit silently ignore the remaining items. This
behavior may change in the future, but the intent is for strict and lax
variants to be offered. A global counter will eventually be implemented as
well, so that nested for
loops cannot become problematic.
Loops will likely have a special loop context variable available in the future, so that traits such as first-iteration, loop index, and so on can be referenced. This is not yet defined.
Conditionally output content:
{% if score >= 90 %}
A
{% elif score >= 80 && score < 90 %}
B
{% elif score >= 70 && score < 80 %}
C
{% elif score >= 60 && score < 70 %}
D
{% else %}
F
{% endif %}
No good reason. I couldn't think of a good name and my daughter was eating a Crunch bar.
MIT