A shopify compatible Liquid template engine in pure JavaScript.
Install via npm:
npm install --save liquidjs
Or include the UMD build:
<script src="//unpkg.com/liquidjs/dist/liquid.min.js"></script> <!--for production-->
<script src="//unpkg.com/liquidjs/dist/liquid.js"></script> <!--for development-->
You may need a Promise polyfill for Node.js < 4 and ES5 browsers like IE and Android UC.
- Get Started
- Demos
- Node.js: /demo/node/
- Browser: https://jsfiddle.net/6u40xbzs/, /demo/browser/.
- Express.js: /demo/express/
- TypeScript: /demo/typescript/
- React JS: /demo/reactjs/
- Advanced
- Contribute Guidelines
Just render a template string with a context:
var Liquid = require('liquidjs');
var engine = new Liquid();
engine
.parseAndRender('{{name | capitalize}}', {name: 'alice'})
.then(console.log); // outputs 'Alice'
Caching parsed templates:
var tpl = engine.parse('{{name | capitalize}}');
engine
.render(tpl, {name: 'alice'})
.then(console.log); // outputs 'Alice'
var engine = new Liquid({
root: path.resolve(__dirname, 'views/'), // root for layouts/includes lookup
extname: '.liquid' // used for layouts/includes, defaults ""
});
engine
.renderFile("hello", {name: 'alice'}) // will read and render `views/hello.liquid`
.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
views
in express.js will be included when looking up
partials(includes and layouts).
// 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
. -
strictFilters
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
. -
strictVariables
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
. -
trimTagRight
is used to strip blank characters (including\t
, and\r
) from the right of tags ({% %}
) until\n
(inclusive). Defaults tofalse
. -
trimTagLeft
is similiar totrimTagRight
, whereas the\n
is exclusive. Defaults tofalse
. See Whitespace Control for details. -
trimOutputRight
is used to strip blank characters (including\t
, and\r
) from the right of values ({{ }}
) until\n
(inclusive). Defaults tofalse
. -
trimOutputLeft
is similiar totrimOutputRight
, whereas the\n
is exclusive. Defaults tofalse
. See Whitespace Control for details. -
tagDelimiterLeft
andtagDelimiterRight
are used to override the delimiter for liquid tags. -
outputDelimiterLeft
andoutputDelimiterRight
are used to override the delimiter for liquid outputs. -
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/tree/master/src/builtin/filters
// Usage: {% upper name%}
engine.registerTag('upper', {
parse: function(tagToken, remainTokens) {
this.str = tagToken.args; // name
},
render: async function(scope, hash) {
var str = await Liquid.evalValue(this.str, scope); // 'alice'
return 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/tree/master/src/builtin/tags
A pack of tags or filters can be encapsulated into a plugin, which will be typically installed via npm.
engine.plugin(require('./some-plugin'));
// some-plugin.js
module.exports = function (Liquid) {
// here `this` refers to the engine instance
// `Liquid` provides facilities to implement tags and filters
this.registerFilter('foo', x => x);
}
Plugin List:
- To add your plugin, contact me or simply send a PR.
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), that means layout/partial names are treated as variables 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. - .to_liquid() is replaced by
.toLiquid()
- .to_s() is replaced by JavaScript
.toString()
This repo uses eslint to check code style, semantic-release to generate changelog and publish to npm and Github Releases.
- Code Style: https://github.com/standard/eslint-config-standard,
npm run lint
to check locally. - Commit Message: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits