mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-29 07:05:13 -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
|
## Async Support
|
||||||
|
|
||||||
harttle/shopify-liquid do NOT support async rendering, this is by design.
|
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/
|
[nunjucks]: http://mozilla.github.io/nunjucks/
|
||||||
[liquid-node]: https://github.com/sirlantis/liquid-node
|
[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 '';
|
return '';
|
||||||
},
|
},
|
||||||
|
set: function(k, v){
|
||||||
|
this.context[this.context.length-1][k] = v;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
push: function(ctx) {
|
push: function(ctx) {
|
||||||
return this.context.push(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 context = require('./context');
|
||||||
const tokenizer = require('./tokenizer.js');
|
const tokenizer = require('./tokenizer.js');
|
||||||
const render = require('./render.js');
|
const Render = require('./render.js');
|
||||||
const lexical = require('./lexical.js');
|
const lexical = require('./lexical.js');
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@@ -11,15 +11,11 @@ const tagsPath = path.join(__dirname, "tags");
|
|||||||
const filtersPath = path.join(__dirname, "filters");
|
const filtersPath = path.join(__dirname, "filters");
|
||||||
|
|
||||||
var _engine = {
|
var _engine = {
|
||||||
render: function(html, ctx) {
|
|
||||||
return this.doRender(tokenizer.parse(html), context.factory(ctx));
|
|
||||||
},
|
|
||||||
lexical,
|
|
||||||
registerFilter : function(name, filter){
|
registerFilter : function(name, filter){
|
||||||
return this.filter.register(name, filter);
|
return this.filter.register(name, filter);
|
||||||
},
|
},
|
||||||
registerTag : function(name, tag){
|
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);
|
var engine = Object.create(_engine);
|
||||||
engine.tag = Tag();
|
engine.tag = Tag();
|
||||||
engine.filter = Filter();
|
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){
|
fs.readdirSync(tagsPath).map(function(f){
|
||||||
var match = /^(\w+)\.js$/.exec(f);
|
var match = /^(\w+)\.js$/.exec(f);
|
||||||
if(!match) return;
|
if(!match) return;
|
||||||
engine.tag.register(match[1], require("./tags/" + f));
|
require("./tags/" + f)(engine);
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.readdirSync(filtersPath).map(function(f){
|
fs.readdirSync(filtersPath).map(function(f){
|
||||||
var match = /^(\w+)\.js$/.exec(f);
|
var match = /^(\w+)\.js$/.exec(f);
|
||||||
if(!match) return;
|
if(!match) return;
|
||||||
engine.filter.register(match[1], require("./filters/" + f));
|
require("./filters/" + f)(engine);
|
||||||
});
|
});
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory.lexical = lexical;
|
||||||
|
|
||||||
module.exports = factory;
|
module.exports = factory;
|
||||||
|
|||||||
@@ -1,44 +1,49 @@
|
|||||||
module.exports = (Filter, Tag) =>
|
module.exports = function(Filter, Tag) {
|
||||||
function render(tokens, ctx) {
|
function render(tokens, ctx) {
|
||||||
var html = '';
|
var html = '';
|
||||||
for (var i = 0; i < tokens.length; i++) {
|
while (tokens.length) {
|
||||||
var token = tokens.shift();
|
var token = tokens.shift();
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case 'html':
|
case 'html':
|
||||||
html += token.value;
|
html += token.value;
|
||||||
break;
|
break;
|
||||||
case 'output':
|
case 'output':
|
||||||
html += renderOutput(token);
|
html += evaluate(token.value, ctx);
|
||||||
break;
|
break;
|
||||||
case 'tag':
|
case 'tag':
|
||||||
html += renderTag(token);
|
html += renderTag(token, tokens, ctx);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`unexpected type: ${token.type}`);
|
throw new Error(`unexpected type: ${token.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html;
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
function renderOutput(token) {
|
function evaluate(str, ctx) {
|
||||||
var filters = token.value.split('|');
|
var filters = str.split('|');
|
||||||
var val = ctx.get(filters.shift());
|
var val = ctx.get(filters.shift());
|
||||||
return filters
|
return filters
|
||||||
.map(str => Filter.parse(str))
|
.map(str => Filter.parse(str))
|
||||||
.reduce((v, filter) => filter.render(v, ctx), val);
|
.reduce((v, filter) => filter.render(v, ctx), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTag(token) {
|
function renderTag(token, tokens, ctx) {
|
||||||
var tag = Tag.parse(token.value),
|
var tag = Tag.parse(token.value),
|
||||||
subTokens = [];
|
subTokens = [];
|
||||||
if (tag.needClose) {
|
if (tag.needClose) {
|
||||||
var curToken, endToken = 'end' + tag.name;
|
var curToken, endToken = 'end' + tag.name;
|
||||||
while ((curToken = tokens.shift()).value !== endToken) {
|
while ((curToken = tokens.shift()).value !== endToken) {
|
||||||
subTokens.push(curToken);
|
subTokens.push(curToken);
|
||||||
}
|
}
|
||||||
if (curToken.value !== endToken) {
|
if (curToken.value !== endToken) {
|
||||||
throw new Error(`${token.value} not closed`);
|
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 = {
|
var _tagInstance = {
|
||||||
render: function(tokens, ctx) {
|
render: function(tokens, ctx) {
|
||||||
var obj = hash(this.markup, 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() {
|
describe('context', function() {
|
||||||
var ctx;
|
var ctx;
|
||||||
before(function(){
|
beforeEach(function(){
|
||||||
ctx = context.factory({
|
ctx = context.factory({
|
||||||
foo: 'bar',
|
foo: 'bar',
|
||||||
bar: ['a', {b: [1, 2]}]
|
bar: ['a', {b: [1, 2]}]
|
||||||
@@ -23,6 +23,11 @@ describe('context', function() {
|
|||||||
ctx.get('foo').should.equal('bar');
|
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() {
|
it('should get desendent property', function() {
|
||||||
ctx.get('bar[0]').should.equal('a');
|
ctx.get('bar[0]').should.equal('a');
|
||||||
ctx.get('bar[1].b').should.deep.equal([1, 2]);
|
ctx.get('bar[1].b').should.deep.equal([1, 2]);
|
||||||
@@ -37,6 +42,7 @@ describe('context', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pop context', function() {
|
it('should pop context', function() {
|
||||||
|
ctx.push({foo: 'foo', foo1: 'foo1'});
|
||||||
ctx.pop();
|
ctx.pop();
|
||||||
expect(ctx.get('foo')).to.equal('bar');
|
expect(ctx.get('foo')).to.equal('bar');
|
||||||
expect(ctx.get('foo1')).to.equal('');
|
expect(ctx.get('foo1')).to.equal('');
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ chai.use(sinonChai);
|
|||||||
var tag = require('../tag.js')();
|
var tag = require('../tag.js')();
|
||||||
var context = require('../context.js');
|
var context = require('../context.js');
|
||||||
var filter = require('../filter')();
|
var filter = require('../filter')();
|
||||||
var render = require('../render.js')(filter, tag);
|
var render = require('../render.js')(filter, tag).render;
|
||||||
|
|
||||||
describe('render', function() {
|
describe('render', function() {
|
||||||
var ctx, htmlToken, tagToken, filterToken;
|
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