This is a liquid implementation for both Node.js and browsers. Website: http://harttle.github.io/liquidjs/, Live Demo: https://jsfiddle.net/6u40xbzs/
Features
- Fully compatible to shopify, with all tags and filters implemented
- Support layout(extend) and include syntax
- In pure JavaScript with Promise-based API
Differences
Though being compatible with Ruby Liquid is one of our priorities, there're still certain differences. You may need some configuration to get it compatible in these senarios:
- Dynamic file locating (enabled by default), which means layout/partial name can be an variable in liquidjs. See #51.
- Truthy and Falsy. All values except
undefined
,null
,false
are truthy, whereas in Ruby Liquid all exceptnil
andfalse
are truthy. See #26. - Number Rendering. Since JavaScript do not distinguish
float
andinteger
, we cannot either convert between them nor render regarding to their type. See #59.
- Usage
- API Spec
Install as Node.js dependency:
# You'll need a promise-polyfill for Node.js < 4
npm install --save liquidjs
Parse and Render:
var Liquid = require('liquidjs');
var engine = Liquid();
engine
.parseAndRender('{{name | capitalize}}', {name: 'alice'})
.then(console.log);
// outputs 'Alice'
Caching templates:
var tpl = engine.parse('{{name | capitalize}}');
engine
.render(tpl, {name: 'alice'})
.then(console.log);
// outputs 'Alice'
var engine = Liquid({
root: path.resolve(__dirname, 'views/'), // dirs to lookup layouts/includes
extname: '.liquid' // the extname used for layouts/includes, defaults ""
});
engine.renderFile("hello.liquid", {name: 'alice'})
.then(console.log) // outputs "Alice"
// which is equivalent to:
engine
.renderFile("hello", {name: 'alice'})
.then(console.log) // outputs "Alice"
// register liquid engine
app.engine('liquid', engine.express());
app.set('views', './views'); // specify the views directory
app.set('view engine', 'liquid'); // set to default
Here's an Express demo. When used with Express.js,
Express views
will be included when looking up
partials(includes and layouts).
You can get a dist file for browsers from
- Releases page for liquidjs, or
- unpkg.com: https://unpkg.com/liquidjs/dist/liquid.min.js
Here's the demo:
- JSFiddle: https://jsfiddle.net/6u40xbzs/
- Demo directory: /demo/browser/.
Note: For IE and Android UC browser, you will need a Promise polyfill.
// file: color.liquid
color: '{{ color }}' shape: '{{ shape }}'
// file: theme.liquid
{% assign shape = 'circle' %}
{% include 'color' %}
{% include 'color' with 'red' %}
{% include 'color', color: 'yellow', shape: 'square' %}
The output will be:
color: '' shape: 'circle'
color: 'red' shape: 'circle'
color: 'yellow' shape: 'square'
// file: default-layout.liquid
Header
{% block content %}My default content{% endblock %}
Footer
// file: page.liquid
{% layout "default-layout" %}
{% block content %}My page content{% endblock %}
The output of page.liquid
:
Header
My page content
Footer
- It's possible to define multiple blocks.
- block name is optional when there's only one block.
The full list of options for Liquid()
is listed as following:
-
root
is a directory or an array of directories to resolve layouts and includes, as well as the filename passed in when calling.renderFile()
. If an array, the files are looked up in the order they occur in the array. Defaults to["."]
-
extname
is used to lookup the template file when filepath doesn't include an extension name. Eg: setting to".html"
will allow including file by basename. Defaults to""
. -
cache
indicates whether or not to cache resolved templates. Defaults tofalse
. -
dynamicPartials
: if set, treat<filepath>
parameter in{%include filepath %}
,{%layout filepath%}
as a variable, otherwise as a literal value. Defaults totrue
. -
strict_filters
is used to enable strict filter existence. If set tofalse
, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults tofalse
. -
strict_variables
is used to enable strict variable derivation. If set tofalse
, undefined variables will be rendered as empty string. Otherwise, undefined variables will cause an exception. Defaults tofalse
. -
trim_tag_right
is used to strip blank characters (including\t
, and\r
) from the right of tags ({% %}
) until\n
(inclusive). Defaults tofalse
. -
trim_tag_left
is similiar totrim_tag_right
, whereas the\n
is exclusive. Defaults tofalse
. See Whitespace Control for details. -
trim_value_right
is used to strip blank characters (including\t
, and\r
) from the right of values ({{ }}
) until\n
(inclusive). Defaults tofalse
. -
trim_value_left
is similiar totrim_value_right
, whereas the\n
is exclusive. Defaults tofalse
. See Whitespace Control for details. -
greedy
is used to specify whethertrim_left
/trim_right
is greedy. When set totrue
, all consecutive blank characters including\n
will be trimed regardless of line breaks. Defaults totrue
.
// Usage: {{ name | uppper }}
engine.registerFilter('upper', v => v.toUpperCase())
Filter arguments will be passed to the registered filter function, for example:
// Usage: {{ 1 | add: 2, 3 }}
engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
See existing filter implementations here: https://github.com/harttle/liquidjs/blob/master/filters.js
// Usage: {% upper name%}
engine.registerTag('upper', {
parse: function(tagToken, remainTokens) {
this.str = tagToken.args; // name
},
render: function(scope, hash) {
var str = Liquid.evalValue(this.str, scope); // 'alice'
return Promise.resolve(str.toUpperCase()); // 'Alice'
}
});
parse
: Read tokens fromremainTokens
until your end token.render
: Combine scope data with your parsed tokens into HTML string.
See existing tag implementations here: https://github.com/harttle/liquidjs/blob/master/tags/