mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
case/when
This commit is contained in:
@@ -16,7 +16,7 @@ shall be implemented.
|
||||
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/)
|
||||
- [x] [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/)
|
||||
|
||||
@@ -12,7 +12,7 @@ var _filterInstance = {
|
||||
module.exports = function() {
|
||||
var filters = {};
|
||||
|
||||
function parse(str) {
|
||||
function construct(str) {
|
||||
var match = lexical.patterns.filterLine.exec(str.trim());
|
||||
if (!match) {
|
||||
throw new Error('illegal filter: ' + str);
|
||||
@@ -43,6 +43,6 @@ module.exports = function() {
|
||||
}
|
||||
|
||||
return {
|
||||
parse, register, clear
|
||||
construct, register, clear
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,11 +24,12 @@ function factory(){
|
||||
engine.tag = Tag();
|
||||
engine.filter = Filter();
|
||||
|
||||
var render = Render(engine.filter, engine.tag);
|
||||
var renderer = Render(engine.filter, engine.tag);
|
||||
engine.evaluate = renderer.evaluate;
|
||||
engine.renderTokens = renderer.render;
|
||||
engine.render = function(html, ctx) {
|
||||
return render.render(tokenizer.parse(html), context.factory(ctx));
|
||||
return engine.renderTokens(tokenizer.parse(html), context.factory(ctx));
|
||||
},
|
||||
engine.evaluate = render.evaluate;
|
||||
|
||||
fs.readdirSync(tagsPath).map(function(f){
|
||||
var match = /^(\w+)\.js$/.exec(f);
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ var rangeLine = new RegExp(`^(?:${range.source})$`);
|
||||
var filterLine = new RegExp(`^(?:${filter.source})$`);
|
||||
|
||||
exports.patterns = {
|
||||
quoted, number, bool, range, literal, hash, filter, identifier,
|
||||
quoted, number, bool, range, literal, hash, filter, identifier, value,
|
||||
quotedLine, numberLine, boolLine, rangeLine, literalLine, filterLine
|
||||
};
|
||||
|
||||
|
||||
@@ -21,22 +21,23 @@ module.exports = function(Filter, Tag) {
|
||||
}
|
||||
|
||||
function evaluate(str, ctx) {
|
||||
if(!ctx) throw new Error('ctx is needed to evaluate');
|
||||
var filters = str.split('|');
|
||||
var val = ctx.get(filters.shift());
|
||||
return filters
|
||||
.map(str => Filter.parse(str))
|
||||
.map(str => Filter.construct(str))
|
||||
.reduce((v, filter) => filter.render(v, ctx), val);
|
||||
}
|
||||
|
||||
function renderTag(token, tokens, ctx) {
|
||||
var tag = Tag.parse(token.value),
|
||||
var tag = Tag.construct(token),
|
||||
subTokens = [];
|
||||
if (tag.needClose) {
|
||||
var curToken, endToken = 'end' + tag.name;
|
||||
while ((curToken = tokens.shift()).value !== endToken) {
|
||||
while ((curToken = tokens.shift()) && curToken.value !== endToken) {
|
||||
subTokens.push(curToken);
|
||||
}
|
||||
if (curToken.value !== endToken) {
|
||||
if (!curToken) {
|
||||
throw new Error(`${token.value} not closed`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,22 +30,15 @@ module.exports = function() {
|
||||
tags[name] = tag;
|
||||
}
|
||||
|
||||
function parse(str) {
|
||||
var match = lexical.patterns.identifier.exec(str.trim());
|
||||
if (!match) throw new Error('illegal tag: ' + str);
|
||||
|
||||
var tagInstance = factory(match[0], str);
|
||||
return tagInstance;
|
||||
}
|
||||
|
||||
function factory(name, markup) {
|
||||
var tag = tags[name];
|
||||
if (!tag) throw new Error(`tag ${name} not found`);
|
||||
function construct(token) {
|
||||
var tag = tags[token.name];
|
||||
if (!tag) throw new Error(`tag ${token.name} not found`);
|
||||
|
||||
var instance = Object.create(_tagInstance);
|
||||
instance.name = name;
|
||||
instance.markup = markup;
|
||||
instance.name = token.name;
|
||||
instance.markup = token.value;
|
||||
instance.tag = tag;
|
||||
instance.needClose = tag.needClose;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -54,6 +47,6 @@ module.exports = function() {
|
||||
}
|
||||
|
||||
return {
|
||||
parse, register, hash, clear
|
||||
construct, register, hash, clear
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
var Liquid = require('..');
|
||||
var patterns = Liquid.lexical.patterns;
|
||||
var caseRE = new RegExp(`^\\s*case\\s+(${patterns.value.source})`);
|
||||
var whenRE = new RegExp(`^\\s*when\\s+(${patterns.value.source})`);
|
||||
|
||||
module.exports = function(liquid) {
|
||||
|
||||
liquid.registerTag('case', {
|
||||
needClose: true,
|
||||
render: function(tokens, ctx, markup, hash) {
|
||||
var match = markup.match(caseRE);
|
||||
var cond = liquid.evaluate(match[1], ctx);
|
||||
|
||||
var partialTokens = [],
|
||||
matching = false;
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if (token.type === 'tag' && token.name === 'when') {
|
||||
if (matching) break;
|
||||
match = token.value.match(whenRE);
|
||||
if(!match) continue;
|
||||
|
||||
var val = liquid.evaluate(match[1], ctx);
|
||||
if (val === cond) matching = true;
|
||||
} else if (token.type === 'tag' && token.name === 'else') {
|
||||
if (matching) break;
|
||||
else matching = true;
|
||||
} else if (matching) partialTokens.push(token);
|
||||
}
|
||||
|
||||
return liquid.renderTokens(partialTokens, ctx);
|
||||
}
|
||||
});
|
||||
};
|
||||
+3
-3
@@ -16,19 +16,19 @@ describe('filter', function() {
|
||||
});
|
||||
it('should throw when not registered', function() {
|
||||
expect(function() {
|
||||
filter.parse('foo');
|
||||
filter.construct('foo');
|
||||
}).to.throw(/filter foo not found/);
|
||||
});
|
||||
|
||||
it('should register a simple filter', function(){
|
||||
filter.register('foo', x => x.toUpperCase());
|
||||
expect(filter.parse('foo').render('foo', ctx)).to.equal('FOO');
|
||||
expect(filter.construct('foo').render('foo', ctx)).to.equal('FOO');
|
||||
});
|
||||
|
||||
it('should call filter with corrct arguments', function(){
|
||||
var spy = sinon.spy();
|
||||
filter.register('foo', spy);
|
||||
filter.parse('foo: 33').render('foo', ctx);
|
||||
filter.construct('foo: 33').render('foo', ctx);
|
||||
expect(spy).to.have.been.calledWith('foo', 33);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ describe('render', function() {
|
||||
});
|
||||
tagToken = {
|
||||
type: 'tag',
|
||||
value: 'foo bar:x foo:"FOO" num:2.3'
|
||||
value: 'foo bar:x foo:"FOO" num:2.3',
|
||||
name: 'foo'
|
||||
};
|
||||
htmlToken = {
|
||||
type: 'html',
|
||||
|
||||
+16
-4
@@ -9,7 +9,7 @@ var context = require('../context.js');
|
||||
|
||||
describe('tag', function() {
|
||||
var ctx;
|
||||
before(function(){
|
||||
before(function() {
|
||||
ctx = context.factory({
|
||||
foo: 'bar',
|
||||
arr: [2, 1]
|
||||
@@ -19,7 +19,11 @@ describe('tag', function() {
|
||||
|
||||
it('should throw when not registered', function() {
|
||||
expect(function() {
|
||||
tag.parse('foo');
|
||||
tag.construct({
|
||||
type: 'tag',
|
||||
value: 'foo',
|
||||
name: 'foo'
|
||||
});
|
||||
}).to.throw(/tag foo not found/);
|
||||
});
|
||||
|
||||
@@ -44,7 +48,11 @@ describe('tag', function() {
|
||||
tag.register('foo', {
|
||||
render: spy
|
||||
});
|
||||
tag.parse('foo').render(tokens, ctx);
|
||||
tag.construct({
|
||||
type: 'tag',
|
||||
value: 'foo',
|
||||
name: 'foo'
|
||||
}).render(tokens, ctx);
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
@@ -54,7 +62,11 @@ describe('tag', function() {
|
||||
tag.register('foo', {
|
||||
render: spy
|
||||
});
|
||||
var t = tag.parse('foo aa:foo bb: arr[0] cc: 2.3');
|
||||
var t = tag.construct({
|
||||
type: 'tag',
|
||||
value: 'foo aa:foo bb: arr[0] cc: 2.3',
|
||||
name: 'foo'
|
||||
});
|
||||
t.render(tokens, ctx);
|
||||
expect(spy).to.have.been.calledWithMatch(tokens, ctx, 'foo', {
|
||||
aa: 'bar',
|
||||
|
||||
+19
-4
@@ -4,16 +4,31 @@ const expect = chai.expect;
|
||||
var liquid = require('..')();
|
||||
|
||||
var ctx = {
|
||||
empty: '',
|
||||
foo: 'bar',
|
||||
arr: [-2, 'a']
|
||||
};
|
||||
|
||||
describe('tags', function() {
|
||||
it('should support assign', function() {
|
||||
test('{% assign foo="bar"%}{{foo}}', 'bar');
|
||||
expect(liquid.render('{% assign foo="bar"%}{{foo}}', ctx)).to.equal('bar');
|
||||
});
|
||||
it('should support case', function() {
|
||||
expect(function() {
|
||||
liquid.render('{% case "foo"%}');
|
||||
}).to.throw(/case "foo" not closed/);
|
||||
expect(liquid.render('{% case "foo"%}' +
|
||||
'{% when "foo" %}foo{% when "bar"%}bar' +
|
||||
'{%endcase%}', ctx)).to.equal('foo');
|
||||
expect(liquid.render('{% case empty %}' +
|
||||
'{% when "foo" %}foo{% when ""%}bar' +
|
||||
'{%endcase%}')).to.equal('bar');
|
||||
expect(liquid.render('{% case false %}' +
|
||||
'{% when "foo" %}foo{% when ""%}bar' +
|
||||
'{%endcase%}')).to.equal('');
|
||||
expect(liquid.render('{% case "a" %}' +
|
||||
'{% when "b" %}b{% when "c"%}c{%else %}d' +
|
||||
'{%endcase%}')).to.equal('d');
|
||||
});
|
||||
});
|
||||
|
||||
function test(src, dst){
|
||||
expect(liquid.render(src, ctx)).to.equal(dst);
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,3 +1,4 @@
|
||||
const lexical = require('./lexical.js');
|
||||
|
||||
function parse(html){
|
||||
var tokens = [];
|
||||
@@ -12,21 +13,21 @@ function parse(html){
|
||||
htmlFragment = html.slice(idx, result.index);
|
||||
tokens.push({
|
||||
type: 'html',
|
||||
raw: htmlFragment,
|
||||
value: htmlFragment
|
||||
});
|
||||
}
|
||||
if(result[1]){
|
||||
var match = result[1].trim().match(lexical.patterns.identifier);
|
||||
if (!match) throw new Error('illegal tag: ' + result[1]);
|
||||
tokens.push({
|
||||
type: 'tag',
|
||||
raw: result[1],
|
||||
name: match[0],
|
||||
value: result[2].trim()
|
||||
});
|
||||
}
|
||||
else{
|
||||
tokens.push({
|
||||
type: 'output',
|
||||
raw: result[3],
|
||||
value: result[4].trim()
|
||||
});
|
||||
}
|
||||
@@ -37,7 +38,6 @@ function parse(html){
|
||||
htmlFragment = html.slice(idx, html.length);
|
||||
tokens.push({
|
||||
type: 'html',
|
||||
raw: htmlFragment,
|
||||
value: htmlFragment
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user