tag: layout, block

This commit is contained in:
harttle
2016-06-24 10:44:57 +08:00
parent 8fbbec089f
commit 3695eb2d6e
8 changed files with 111 additions and 7 deletions
+28 -1
View File
@@ -49,7 +49,9 @@ var html = engine.renderFile("hello.html", {name: 'alice'});
`cache` default to `false`, `extname` default to `.liquid`, `root` default to `""`.
## Include
## Includes and Layouts
### Includes
```
// file: color.liquid
@@ -70,6 +72,30 @@ color: 'red' shape: 'circle'
color: 'yellow' shape: 'square'
```
### Layouts
```
// file: default-layout.html
Header
{% block content %}My default content{% endblock %}
Footer
// file: page.html
{% layout "default-layout" %}
{% block content %}My page content{% endblock %}
```
The output of `page.html`:
```
Header
My page content
Footer
```
* It's possible to define multiple blocks.
* block name is optional when there's only one block.
## Extension
Register Filters:
@@ -131,6 +157,7 @@ Tag | Document | Source | Test
`raw` | [Document](https://help.shopify.com/themes/liquid/tags/theme-tags#raw) | [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/raw.js) | [Test][tt]
`comment` | [Document](https://help.shopify.com/themes/liquid/tags/theme-tags#comment) | [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/comment.js) | [Test][tt]
`include` | [Document](https://help.shopify.com/themes/liquid/tags/theme-tags#include) | [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/include.js) | [Test][tt]
`layout, block` | [Document](http://docs.mixture.io/templates/) | [Source](https://github.com/harttle/shopify-liquid/blob/master/tags/layout.js) | [Test][tt]
## Filters
+1 -1
View File
@@ -58,7 +58,7 @@ var _engine = {
filepath += this.options.extname;
}
var tpl = this.options.cache && this.cache[filepath] ||
this.parse(fs.readFileSync(filepath));
this.parse(fs.readFileSync(filepath, 'utf8'));
return this.options.cache ? (this.cache[filepath] = tpl) : tpl;
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "shopify-liquid",
"version": "1.1.4",
"version": "1.1.5",
"description": "A Shopify Liquid Implementation in Node.js",
"main": "index.js",
"scripts": {
-1
View File
@@ -8,7 +8,6 @@ var scope = {
var v = _.get(this.scopes[i], str);
if (v !== undefined) return v;
}
return '';
},
set: function(k, v) {
_.set(this.scopes[this.scopes.length - 1], k, v);
+51
View File
@@ -0,0 +1,51 @@
var Liquid = require('..');
var lexical = Liquid.lexical;
var withRE = new RegExp(`with\\s+(${lexical.value.source})`);
module.exports = function(liquid) {
liquid.registerTag('layout', {
parse: function(token, remainTokens){
var match = lexical.value.exec(token.args);
if(!match) error(`illegal token ${token.raw}`, token);
this.layout = match[0];
this.tpls = liquid.parser.parse(remainTokens);
},
render: function(scope, hash) {
var layout = Liquid.evalValue(this.layout, scope);
var tpl = liquid.handleCache(layout);
scope.push({});
liquid.renderer.renderTemplates(this.tpls, scope);
var html = liquid.renderer.renderTemplates(tpl, scope);
scope.pop();
return html;
}
});
liquid.registerTag('block', {
parse: function(token, remainTokens){
var match = /\w+/.exec(token.args);
this.block = match ? match[0] : '';
this.tpls = [];
var p, stream = liquid.parser.parseStream(remainTokens)
.on('tag:endblock', token => stream.stop())
.on('template', tpl => this.tpls.push(tpl))
.on('end', x => {
throw new Error(`tag ${token.raw} not closed`);
});
stream.start();
},
render: function(scope, hash){
var html = scope.get(`_liquid.blocks.${this.block}`);
if(html === undefined){
html = liquid.renderer.renderTemplates(this.tpls, scope);
}
scope.set(`_liquid.blocks.${this.block}`, html);
return html;
}
});
};
+1 -1
View File
@@ -40,7 +40,7 @@ describe('expression', function() {
expect(evalExp('one<=two', scope)).to.equal(true);
expect(evalExp('x contains "x"', scope)).to.equal(false);
expect(evalExp('x contains "X"', scope)).to.equal(true);
expect(evalExp('x contains z', scope)).to.equal(true);
expect(evalExp('x contains z', scope)).to.equal(false);
expect(evalExp('"<=" == "<="', scope)).to.equal(true);
});
+1 -1
View File
@@ -44,7 +44,7 @@ describe('scope', function() {
scope.push({foo: 'foo', foo1: 'foo1'});
scope.pop();
expect(scope.get('foo')).to.equal('bar');
expect(scope.get('foo1')).to.equal('');
expect(scope.get('foo1')).to.equal(undefined);
expect(scope.get('bar[1].b[1]')).to.equal(2);
});
});
+28 -1
View File
@@ -31,6 +31,9 @@ describe('tags', function() {
emptyArray: []
};
mock({
'/default-layout.html': 'foo{% block %}Default{% endblock %}foo',
'/multi-blocks-layout.html': 'foo{% block "a"%}{% endblock %}{% block b%}{%endblock%}foo',
'/multi-blocks.html': '{% layout "multi-blocks-layout" %}{%block a%}aaa{%endblock%},{%block b%};{%block c%}ccc{%endblock%};{%endblock%}',
'/files/foo.html': 'foo',
'/current.html': 'bar{% include "foo.html" %}bar',
'/relative.html': 'bar{% include "../files/foo.html" %}bar',
@@ -202,6 +205,11 @@ describe('tags', function() {
test(src, dst);
});
it('should throw when tablerow not closed', function() {
src = '{% tablerow i in (1..0) cols:2 %}{{ i }}';
testThrow(src, /tag .* not closed/);
});
it('should support tablerow with range', function() {
src = '{% tablerow i in (1..5) cols:2 %}{{ i }}{% endtablerow %}';
dst = '<table>' +
@@ -257,5 +265,24 @@ describe('tags', function() {
var dst = 'color:red, shape:rect';
expect(liquid.renderFile(filepath, ctx)).to.equal(dst);
});
it('should throw when block not closed', function() {
src = '{% layout "default-layout" %}{%block%}bar';
testThrow(src, /tag {%block%} not closed/);
});
it('should support layout', function() {
src = '{% layout "default-layout" %}{%block%}bar{%endblock%}';
test(src, 'foobarfoo');
});
it('should support layout: multiple blocks', function() {
src = '{% layout "multi-blocks-layout" %}' +
'{%block a%}bara{%endblock%}' +
'{%block b%}barb{%endblock%}';
test(src, 'foobarabarbfoo');
});
it('should support layout: nested', function() {
src = '{% layout "multi-blocks" %}{% block a%}A{%endblock%}{%block c%}C{%endblock%}';
test(src, 'fooA;C;foo');
src = '{% layout "multi-blocks" %}{%block c%}C{%endblock%}';
test(src, 'fooaaa;C;foo');
});
});