mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 12:50:38 -07:00
assign tag, abs filter
This commit is contained in:
@@ -1,3 +1,85 @@
|
||||
# shopify-liquid
|
||||
|
||||
[](https://www.npmjs.org/package/shopify-liquid)
|
||||
[](https://travis-ci.org/harttle/shopify-liquid)
|
||||
[](https://coveralls.io/github/harttle/shopify-liquid?branch=master)
|
||||
[](https://david-dm.org/harttle/shopify-liquid)
|
||||
|
||||
[Liquid][shopify-liquid] implementation for Node.js.
|
||||
All tags and filters listed in [Shopify Documentation][shopify-liquid]
|
||||
shall be implemented.
|
||||
|
||||
> Shopify liquid is used by [Jekyll][jekyll] and [Github Pages][gh].
|
||||
|
||||
## Tags
|
||||
|
||||
Documentation: <https://shopify.github.io/liquid/basics/introduction/#tags>
|
||||
|
||||
- [x] [assign](https://shopify.github.io/liquid/tags/control-flow/)
|
||||
- [ ] [case/when](https://shopify.github.io/liquid/tags/control-flow/)
|
||||
- [ ] [if](https://shopify.github.io/liquid/tags/control-flow/)
|
||||
- [ ] [unless](https://shopify.github.io/liquid/tags/control-flow/)
|
||||
- [ ] [elsif/else](https://shopify.github.io/liquid/tags/control-flow/)
|
||||
- [ ] [for](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [break](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [continue](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [for: limit,offset,range,reversed)](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [cycle](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [cycle: group)](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [tablerow](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [tablerow: cols,limit,offset,range](https://shopify.github.io/liquid/tags/iteration/)
|
||||
- [ ] [assign](https://shopify.github.io/liquid/tags/variable/)
|
||||
- [ ] [capture](https://shopify.github.io/liquid/tags/variable/)
|
||||
- [ ] [increment](https://shopify.github.io/liquid/tags/variable/)
|
||||
- [ ] [decrement](https://shopify.github.io/liquid/tags/variable/)
|
||||
|
||||
|
||||
## Filters
|
||||
|
||||
Documentation: <https://shopify.github.io/liquid/basics/introduction/#filters>
|
||||
|
||||
- [x] [abs](https://shopify.github.io/liquid/filters/abs/)
|
||||
- [ ] [append](https://shopify.github.io/liquid/filters/append)
|
||||
- [ ] [capitalize](https://shopify.github.io/liquid/filters/capitalize)
|
||||
- [ ] [ceil](https://shopify.github.io/liquid/filters/ceil)
|
||||
- [ ] [date](https://shopify.github.io/liquid/filters/date)
|
||||
- [ ] [default](https://shopify.github.io/liquid/filters/default)
|
||||
- [ ] [divided_by](https://shopify.github.io/liquid/filters/divided_by)
|
||||
- [ ] [downcase](https://shopify.github.io/liquid/filters/downcase)
|
||||
- [ ] [escape](https://shopify.github.io/liquid/filters/escape)
|
||||
- [ ] [escape_once](https://shopify.github.io/liquid/filters/escape_once)
|
||||
- [ ] [first](https://shopify.github.io/liquid/filters/first)
|
||||
- [ ] [floor](https://shopify.github.io/liquid/filters/floor)
|
||||
- [ ] [join](https://shopify.github.io/liquid/filters/join)
|
||||
- [ ] [last](https://shopify.github.io/liquid/filters/last)
|
||||
- [ ] [lstrip](https://shopify.github.io/liquid/filters/lstrip)
|
||||
- [ ] [map](https://shopify.github.io/liquid/filters/map)
|
||||
- [ ] [minus](https://shopify.github.io/liquid/filters/minus)
|
||||
- [ ] [modulo](https://shopify.github.io/liquid/filters/modulo)
|
||||
- [ ] [newline_to_br](https://shopify.github.io/liquid/filters/newline_to_br)
|
||||
- [ ] [plus](https://shopify.github.io/liquid/filters/plus)
|
||||
- [ ] [prepend](https://shopify.github.io/liquid/filters/prepend)
|
||||
- [ ] [remove](https://shopify.github.io/liquid/filters/remove)
|
||||
- [ ] [remove_first](https://shopify.github.io/liquid/filters/remove_first)
|
||||
- [ ] [replace](https://shopify.github.io/liquid/filters/replace)
|
||||
- [ ] [replace_first](https://shopify.github.io/liquid/filters/replace_first)
|
||||
- [ ] [reverse](https://shopify.github.io/liquid/filters/reverse)
|
||||
- [ ] [round](https://shopify.github.io/liquid/filters/round)
|
||||
- [ ] [rstrip](https://shopify.github.io/liquid/filters/rstrip)
|
||||
- [ ] [size](https://shopify.github.io/liquid/filters/size)
|
||||
- [ ] [slice](https://shopify.github.io/liquid/filters/slice)
|
||||
- [ ] [sort](https://shopify.github.io/liquid/filters/sort)
|
||||
- [ ] [split](https://shopify.github.io/liquid/filters/split)
|
||||
- [ ] [strip](https://shopify.github.io/liquid/filters/strip)
|
||||
- [ ] [strip_html](https://shopify.github.io/liquid/filters/strip_html)
|
||||
- [ ] [strip_newlines](https://shopify.github.io/liquid/filters/strip_newlines)
|
||||
- [ ] [times](https://shopify.github.io/liquid/filters/times)
|
||||
- [ ] [truncate](https://shopify.github.io/liquid/filters/truncate)
|
||||
- [ ] [truncatewords](https://shopify.github.io/liquid/filters/truncatewords)
|
||||
- [ ] [uniq](https://shopify.github.io/liquid/filters/uniq)
|
||||
- [ ] [upcase](https://shopify.github.io/liquid/filters/upcase)
|
||||
- [ ] [url_encode](https://shopify.github.io/liquid/filters/url_encode)
|
||||
|
||||
## Async Support
|
||||
|
||||
harttle/shopify-liquid do NOT support async rendering, this is by design.
|
||||
@@ -12,3 +94,6 @@ For template-driven projects, checkout these Liquid-like engines:
|
||||
|
||||
[nunjucks]: http://mozilla.github.io/nunjucks/
|
||||
[liquid-node]: https://github.com/sirlantis/liquid-node
|
||||
[shopify-liquid]: https://shopify.github.io/liquid/
|
||||
[jekyll]: http://jekyllrb.com/
|
||||
[gh]: https://pages.github.com/
|
||||
|
||||
@@ -18,6 +18,10 @@ var context = {
|
||||
}
|
||||
return '';
|
||||
},
|
||||
set: function(k, v){
|
||||
this.context[this.context.length-1][k] = v;
|
||||
return this;
|
||||
},
|
||||
push: function(ctx) {
|
||||
return this.context.push(ctx);
|
||||
},
|
||||
|
||||
+3
-1
@@ -1 +1,3 @@
|
||||
module.exports = v => Math.abs(v);
|
||||
module.exports = function(liquid) {
|
||||
liquid.registerFilter('abs', v => Math.abs(v));
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const context = require('./context');
|
||||
const tokenizer = require('./tokenizer.js');
|
||||
const render = require('./render.js');
|
||||
const Render = require('./render.js');
|
||||
const lexical = require('./lexical.js');
|
||||
const path = require("path");
|
||||
const fs = require('fs');
|
||||
@@ -11,15 +11,11 @@ const tagsPath = path.join(__dirname, "tags");
|
||||
const filtersPath = path.join(__dirname, "filters");
|
||||
|
||||
var _engine = {
|
||||
render: function(html, ctx) {
|
||||
return this.doRender(tokenizer.parse(html), context.factory(ctx));
|
||||
},
|
||||
lexical,
|
||||
registerFilter : function(name, filter){
|
||||
return this.filter.register(name, filter);
|
||||
},
|
||||
registerTag : function(name, tag){
|
||||
return this.filter.register(name, tag);
|
||||
return this.tag.register(name, tag);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,20 +23,27 @@ function factory(){
|
||||
var engine = Object.create(_engine);
|
||||
engine.tag = Tag();
|
||||
engine.filter = Filter();
|
||||
engine.doRender = render(engine.filter, engine.tag);
|
||||
|
||||
var render = Render(engine.filter, engine.tag);
|
||||
engine.render = function(html, ctx) {
|
||||
return render.render(tokenizer.parse(html), context.factory(ctx));
|
||||
},
|
||||
engine.evaluate = render.evaluate;
|
||||
|
||||
fs.readdirSync(tagsPath).map(function(f){
|
||||
var match = /^(\w+)\.js$/.exec(f);
|
||||
if(!match) return;
|
||||
engine.tag.register(match[1], require("./tags/" + f));
|
||||
require("./tags/" + f)(engine);
|
||||
});
|
||||
|
||||
fs.readdirSync(filtersPath).map(function(f){
|
||||
var match = /^(\w+)\.js$/.exec(f);
|
||||
if(!match) return;
|
||||
engine.filter.register(match[1], require("./filters/" + f));
|
||||
require("./filters/" + f)(engine);
|
||||
});
|
||||
return engine;
|
||||
}
|
||||
|
||||
factory.lexical = lexical;
|
||||
|
||||
module.exports = factory;
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
module.exports = (Filter, Tag) =>
|
||||
module.exports = function(Filter, Tag) {
|
||||
function render(tokens, ctx) {
|
||||
var html = '';
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
while (tokens.length) {
|
||||
var token = tokens.shift();
|
||||
switch (token.type) {
|
||||
case 'html':
|
||||
html += token.value;
|
||||
break;
|
||||
case 'output':
|
||||
html += renderOutput(token);
|
||||
html += evaluate(token.value, ctx);
|
||||
break;
|
||||
case 'tag':
|
||||
html += renderTag(token);
|
||||
html += renderTag(token, tokens, ctx);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`unexpected type: ${token.type}`);
|
||||
}
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderOutput(token) {
|
||||
var filters = token.value.split('|');
|
||||
var val = ctx.get(filters.shift());
|
||||
return filters
|
||||
.map(str => Filter.parse(str))
|
||||
.reduce((v, filter) => filter.render(v, ctx), val);
|
||||
}
|
||||
function evaluate(str, ctx) {
|
||||
var filters = str.split('|');
|
||||
var val = ctx.get(filters.shift());
|
||||
return filters
|
||||
.map(str => Filter.parse(str))
|
||||
.reduce((v, filter) => filter.render(v, ctx), val);
|
||||
}
|
||||
|
||||
function renderTag(token) {
|
||||
var tag = Tag.parse(token.value),
|
||||
subTokens = [];
|
||||
if (tag.needClose) {
|
||||
var curToken, endToken = 'end' + tag.name;
|
||||
while ((curToken = tokens.shift()).value !== endToken) {
|
||||
subTokens.push(curToken);
|
||||
}
|
||||
if (curToken.value !== endToken) {
|
||||
throw new Error(`${token.value} not closed`);
|
||||
}
|
||||
function renderTag(token, tokens, ctx) {
|
||||
var tag = Tag.parse(token.value),
|
||||
subTokens = [];
|
||||
if (tag.needClose) {
|
||||
var curToken, endToken = 'end' + tag.name;
|
||||
while ((curToken = tokens.shift()).value !== endToken) {
|
||||
subTokens.push(curToken);
|
||||
}
|
||||
if (curToken.value !== endToken) {
|
||||
throw new Error(`${token.value} not closed`);
|
||||
}
|
||||
return tag.render(subTokens, ctx);
|
||||
}
|
||||
return tag.render(subTokens, ctx);
|
||||
}
|
||||
|
||||
return {
|
||||
render, evaluate, renderTag
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ const lexical = require('./lexical.js');
|
||||
var _tagInstance = {
|
||||
render: function(tokens, ctx) {
|
||||
var obj = hash(this.markup, ctx);
|
||||
return this.tag.render(tokens, ctx, this.markup, obj);
|
||||
var res = this.tag.render(tokens, ctx, this.markup, obj);
|
||||
return res === undefined ? '' : res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
var Liquid = require('..');
|
||||
var patterns = Liquid.lexical.patterns;
|
||||
var re = new RegExp(`(${patterns.identifier.source})\\s*=(.*)`);
|
||||
|
||||
module.exports = function(liquid) {
|
||||
|
||||
liquid.registerTag('assign', {
|
||||
render: function(tokens, ctx, markup, hash) {
|
||||
var match = markup.match(re);
|
||||
var k = match[1], v = match[2];
|
||||
ctx.set(k, liquid.evaluate(v, ctx));
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
+7
-1
@@ -6,7 +6,7 @@ var context = require('../context.js');
|
||||
|
||||
describe('context', function() {
|
||||
var ctx;
|
||||
before(function(){
|
||||
beforeEach(function(){
|
||||
ctx = context.factory({
|
||||
foo: 'bar',
|
||||
bar: ['a', {b: [1, 2]}]
|
||||
@@ -23,6 +23,11 @@ describe('context', function() {
|
||||
ctx.get('foo').should.equal('bar');
|
||||
});
|
||||
|
||||
it('should set property', function() {
|
||||
ctx.set('foo', 'FOO');
|
||||
ctx.get('foo').should.equal('FOO');
|
||||
});
|
||||
|
||||
it('should get desendent property', function() {
|
||||
ctx.get('bar[0]').should.equal('a');
|
||||
ctx.get('bar[1].b').should.deep.equal([1, 2]);
|
||||
@@ -37,6 +42,7 @@ describe('context', function() {
|
||||
});
|
||||
|
||||
it('should pop context', function() {
|
||||
ctx.push({foo: 'foo', foo1: 'foo1'});
|
||||
ctx.pop();
|
||||
expect(ctx.get('foo')).to.equal('bar');
|
||||
expect(ctx.get('foo1')).to.equal('');
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ chai.use(sinonChai);
|
||||
var tag = require('../tag.js')();
|
||||
var context = require('../context.js');
|
||||
var filter = require('../filter')();
|
||||
var render = require('../render.js')(filter, tag);
|
||||
var render = require('../render.js')(filter, tag).render;
|
||||
|
||||
describe('render', function() {
|
||||
var ctx, htmlToken, tagToken, filterToken;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const chai = require("chai");
|
||||
const expect = chai.expect;
|
||||
|
||||
var liquid = require('..')();
|
||||
|
||||
var ctx = {
|
||||
foo: 'bar',
|
||||
arr: [-2, 'a']
|
||||
};
|
||||
|
||||
describe('tags', function() {
|
||||
it('should support assign', function() {
|
||||
test('{% assign foo="bar"%}{{foo}}', 'bar');
|
||||
});
|
||||
});
|
||||
|
||||
function test(src, dst){
|
||||
expect(liquid.render(src, ctx)).to.equal(dst);
|
||||
}
|
||||
Reference in New Issue
Block a user