Compare commits

..
13 Commits
Author SHA1 Message Date
Jun Yang f668a5cc41 3.1.2 2018-04-07 19:49:46 +08:00
Jun Yang 098f5164e7 fix: linting 2018-04-07 19:48:46 +08:00
Jun Yang 432f878bb6 fix: support comma-separated arguments for static partials, fixes #62 2018-04-07 19:45:57 +08:00
Cory MawhorterandJun Yang aa54dad36e fix dupe name bug 2018-04-07 19:36:50 +08:00
Jun Yang c6ff1f7911 3.1.1 2018-03-05 01:34:00 +08:00
Jun Yang 11355cbf09 refactor: remove makefile 2018-03-05 01:31:12 +08:00
Jun Yang 7ccd0cb1e5 fix: branch coverage 2018-03-05 01:20:43 +08:00
Jun Yang 4326c223f3 fix #61: static filename not parsed for subdirectories 2018-03-05 00:58:26 +08:00
Jun Yang 78b6e35fbf fix: file path asserts for windows 2018-03-03 20:01:24 +08:00
Jun YangandGitHub e275368cb3 Update README.md 2018-01-17 11:01:58 +08:00
Jun YangandGitHub ca2b16a583 Update README.md 2018-01-17 10:48:05 +08:00
Jun YangandGitHub 86d0529a5e Update README.md 2018-01-17 10:47:11 +08:00
Jun YangandGitHub 400b93c88d Update README.md 2018-01-17 10:44:11 +08:00
14 changed files with 280 additions and 136 deletions
+54 -38
View File
@@ -10,21 +10,37 @@
[![David Dev](https://img.shields.io/david/dev/harttle/liquidjs.svg)](https://david-dm.org/harttle/liquidjs?type=dev)
[![DUB](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/harttle/liquidjs/blob/master/LICENSE)
Visit our website: <http://harttle.github.io/liquidjs/>
This is a liquid implementation for both Node.js and browsers. Website: <http://harttle.github.io/liquidjs/>, Live Demo: <https://jsfiddle.net/6u40xbzs/>
## Features
**Features**
* Support both Node.js and browsers. Here's a demo: <https://jsfiddle.net/6u40xbzs/>
* Fully compatible to [shopify][shopify/liquid], with all [tags][tags] and [filters][filters] implemented
* Support layout(extend) and include syntax
* In pure JavaScript with [any-promise][any-promise] as the only one dependency
API Reference:
**Differences**
* Builtin Tags: <https://github.com/harttle/liquidjs/wiki/Builtin-Tags>
* Builtin Filters: <https://github.com/harttle/liquidjs/wiki/Builtin-Filters>
* Operators: <https://github.com/harttle/liquidjs/wiki/Operators>
* Whitespace Control: <https://github.com/harttle/liquidjs/wiki/Whitespace-Control>
Though being compatible with [Ruby Liquid](https://github.com/shopify/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), which means layout/partial name can be an variable in liquidjs. See [#51](https://github.com/harttle/liquidjs/issues/51).
* Truthy and Falsy. All values except `undefined`, `null`, `false` are truthy, whereas in Ruby Liquid all except `nil` and `false` are truthy. See [#26](https://github.com/harttle/liquidjs/pull/26).
* Number Rendering. Since JavaScript do not distinguish `float` and `integer`, we cannot either convert between them nor render regarding their type. See [#59](https://github.com/harttle/liquidjs/issues/59).
## TOC
* Usage
* [Render from String](#render-from-string)
* [Render from File](#render-from-file)
* [Use with Express.js](#use-with-expressjs)
* [Use in Browser](#use-in-browser)
* [Include Partials](#include-partials)
* [Layout Templates (Extends)](#layout-templates-extends)
* API Spec
* [Constructor Options](#options)
* [Register Filters](#register-filters), [Builtin Filters](https://github.com/harttle/liquidjs/wiki/Builtin-Filters)
* [Register Tags](#register-tags), [Builtin Tags](https://github.com/harttle/liquidjs/wiki/Builtin-Tags)
* [Operators](https://github.com/harttle/liquidjs/wiki/Operators)
* [Whitespace Control](https://github.com/harttle/liquidjs/wiki/Whitespace-Control)
## Render from String
@@ -74,36 +90,6 @@ engine
.then(console.log) // outputs "Alice"
```
## Options
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 to `false`.
* `dynamicPartials`: if set, treat `<filepath>` parameter in `{%include filepath %}`, `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`.
* `strict_filters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`.
* `strict_variables` is used to enable strict variable derivation.
If set to `false`, undefined variables will be rendered as empty string.
Otherwise, undefined variables will cause an exception. Defaults to `false`.
* `trim_tag_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`.
* `trim_tag_left` is similiar to `trim_tag_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `trim_value_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`.
* `trim_value_left` is similiar to `trim_value_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`.
## Use with Express.js
```javascript
@@ -176,6 +162,36 @@ Footer
* It's possible to define multiple blocks.
* block name is optional when there's only one block.
## Options
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 to `false`.
* `dynamicPartials`: if set, treat `<filepath>` parameter in `{%include filepath %}`, `{%layout filepath%}` as a variable, otherwise as a literal value. Defaults to `true`.
* `strict_filters` is used to enable strict filter existence. If set to `false`, undefined filters will be rendered as empty string. Otherwise, undefined filters will cause an exception. Defaults to `false`.
* `strict_variables` is used to enable strict variable derivation.
If set to `false`, undefined variables will be rendered as empty string.
Otherwise, undefined variables will cause an exception. Defaults to `false`.
* `trim_tag_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`.
* `trim_tag_left` is similiar to `trim_tag_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `trim_value_right` is used to strip blank characters (including ` `, `\t`, and `\r`) from the right of values (`{{ }}`) until `\n` (inclusive). Defaults to `false`.
* `trim_value_left` is similiar to `trim_value_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
* `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`.
## Register Filters
```javascript
View File
+53 -38
View File
@@ -632,7 +632,9 @@ module.exports = function (options) {
while (match = valueRE.exec(argList.trim())) {
var v = match[0];
var re = new RegExp(v + '\\s*:', 'g');
re.test(match.input) ? args.push('\'' + v + '\'') : args.push(v);
var keyMatch = re.exec(match.input);
var currentMatchIsKey = keyMatch && keyMatch.index === match.index;
currentMatchIsKey ? args.push('\'' + v + '\'') : args.push(v);
}
this.name = name;
@@ -2164,41 +2166,41 @@ var candidatesRE = new RegExp(lexical.value.source, 'g');
var assert = require('../src/util/assert.js');
module.exports = function (liquid) {
liquid.registerTag('cycle', {
liquid.registerTag('cycle', {
parse: function parse(tagToken, remainTokens) {
var match = groupRE.exec(tagToken.args);
assert(match, 'illegal tag: ' + tagToken.raw);
parse: function parse(tagToken, remainTokens) {
var match = groupRE.exec(tagToken.args);
assert(match, 'illegal tag: ' + tagToken.raw);
this.group = match[1] || '';
var candidates = match[2];
this.group = match[1] || '';
var candidates = match[2];
this.candidates = [];
this.candidates = [];
while (match = candidatesRE.exec(candidates)) {
this.candidates.push(match[0]);
}
assert(this.candidates.length, 'empty candidates: ' + tagToken.raw);
},
while (match = candidatesRE.exec(candidates)) {
this.candidates.push(match[0]);
}
assert(this.candidates.length, 'empty candidates: ' + tagToken.raw);
},
render: function render(scope, hash) {
var group = Liquid.evalValue(this.group, scope);
var fingerprint = 'cycle:' + group + ':' + this.candidates.join(',');
render: function render(scope, hash) {
var group = Liquid.evalValue(this.group, scope);
var fingerprint = 'cycle:' + group + ':' + this.candidates.join(',');
var groups = scope.opts.groups = scope.opts.groups || {};
var idx = groups[fingerprint];
var groups = scope.opts.groups = scope.opts.groups || {};
var idx = groups[fingerprint];
if (idx === undefined) {
idx = groups[fingerprint] = 0;
}
if (idx === undefined) {
idx = groups[fingerprint] = 0;
}
var candidate = this.candidates[idx];
idx = (idx + 1) % this.candidates.length;
groups[fingerprint] = idx;
var candidate = this.candidates[idx];
idx = (idx + 1) % this.candidates.length;
groups[fingerprint] = idx;
return Promise.resolve(Liquid.evalValue(candidate, scope));
}
});
return Promise.resolve(Liquid.evalValue(candidate, scope));
}
});
};
},{"..":2,"../src/util/assert.js":18,"any-promise":3}],31:[function(require,module,exports){
@@ -2391,14 +2393,21 @@ module.exports = function (liquid) {
var Liquid = require('..');
var lexical = Liquid.lexical;
var withRE = new RegExp('with\\s+(' + lexical.value.source + ')');
var staticFileRE = /[^\s,]+/;
var assert = require('../src/util/assert.js');
module.exports = function (liquid) {
liquid.registerTag('include', {
parse: function parse(token) {
var match = lexical.value.exec(token.args);
assert(match, 'illegal token ' + token.raw);
this.value = match[0];
var match = staticFileRE.exec(token.args);
if (match) {
this.staticValue = match[0];
}
match = lexical.value.exec(token.args);
if (match) {
this.value = match[0];
}
match = withRE.exec(token.args);
if (match) {
@@ -2406,10 +2415,8 @@ module.exports = function (liquid) {
}
},
render: function render(scope, hash) {
var filepath = this.value;
if (scope.opts.dynamicPartials) {
filepath = Liquid.evalValue(this.value, scope);
}
var filepath = scope.opts.dynamicPartials ? Liquid.evalValue(this.value, scope) : this.staticValue;
assert(filepath, 'cannot include with empty filename');
var originBlocks = scope.opts.blocks;
var originBlockMode = scope.opts.blockMode;
@@ -2481,6 +2488,7 @@ var Liquid = require('..');
var Promise = require('any-promise');
var lexical = Liquid.lexical;
var assert = require('../src/util/assert.js');
var staticFileRE = /\S+/;
/*
* blockMode:
@@ -2491,14 +2499,21 @@ var assert = require('../src/util/assert.js');
module.exports = function (liquid) {
liquid.registerTag('layout', {
parse: function parse(token, remainTokens) {
var match = lexical.value.exec(token.args);
assert(match, 'illegal token ' + token.raw);
var match = staticFileRE.exec(token.args);
if (match) {
this.staticLayout = match[0];
}
match = lexical.value.exec(token.args);
if (match) {
this.layout = match[0];
}
this.layout = match[0];
this.tpls = liquid.parser.parse(remainTokens);
},
render: function render(scope, hash) {
var layout = scope.opts.dynamicPartials ? Liquid.evalValue(this.layout, scope) : this.layout;
var layout = scope.opts.dynamicPartials ? Liquid.evalValue(this.layout, scope) : this.staticLayout;
assert(layout, 'cannot apply layout with empty filename');
// render the remaining tokens immediately
scope.opts.blockMode = 'store';
+1 -1
View File
File diff suppressed because one or more lines are too long
-18
View File
@@ -1,18 +0,0 @@
MINIFY = ./node_modules/.bin/uglifyjs
BROWSERIFY = ./node_modules/.bin/browserify
.PHONY: dist default clean
default:
dist:
[ -d dist/ ] || mkdir dist/
$(BROWSERIFY) index.js -s Liquid \
--ignore path \
-t [ babelify --global true --presets [ es2015 ] ] \
> dist/liquid.js
$(MINIFY) dist/liquid.js --compress warnings=false --mangle --output dist/liquid.min.js
ls -lh dist/
clean:
rm -rf dist/
+7 -4
View File
@@ -1,14 +1,16 @@
{
"name": "liquidjs",
"version": "3.1.0",
"version": "3.1.2",
"description": "Liquid template engine by pure JavaScript: compatible to shopify, easy to extend.",
"main": "index.js",
"scripts": {
"lint": "eslint .",
"test": "mocha --recursive",
"coverage": "NODE_ENV=test istanbul cover --report html ./node_modules/mocha/bin/_mocha -- -R spec --recursive",
"lcov": "NODE_ENV=test istanbul cover --report lcovonly ./node_modules/mocha/bin/_mocha -- -R spec --recursive",
"dist": "make dist",
"coverage": "cross-env NODE_ENV=test istanbul cover --report html ./node_modules/mocha/bin/_mocha -- -R spec --recursive",
"lcov": "cross-env NODE_ENV=test istanbul cover --report lcovonly ./node_modules/mocha/bin/_mocha -- -R spec --recursive",
"dist": "npm run browserify && npm run uglify",
"browserify": "browserify index.js -s Liquid --ignore path -t [ babelify --global true --presets [ es2015 ] ] > dist/liquid.js",
"uglify": "uglifyjs dist/liquid.js --compress warnings=false --mangle --output dist/liquid.min.js",
"preversion": "npm run lint && npm test",
"version": "npm run dist && git add -A dist",
"postversion": "git push && git push --tags"
@@ -48,6 +50,7 @@
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.0.0",
"cross-env": "^5.1.3",
"eslint": "^4.8.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.7.0",
+3 -1
View File
@@ -36,7 +36,9 @@ module.exports = function (options) {
while ((match = valueRE.exec(argList.trim()))) {
var v = match[0]
var re = new RegExp(`${v}\\s*:`, 'g')
re.test(match.input) ? args.push(`'${v}'`) : args.push(v)
var keyMatch = re.exec(match.input)
var currentMatchIsKey = keyMatch && keyMatch.index === match.index
currentMatchIsKey ? args.push(`'${v}'`) : args.push(v)
}
this.name = name
+14 -7
View File
@@ -1,14 +1,21 @@
const Liquid = require('..')
const lexical = Liquid.lexical
const withRE = new RegExp(`with\\s+(${lexical.value.source})`)
const staticFileRE = /[^\s,]+/
const assert = require('../src/util/assert.js')
module.exports = function (liquid) {
liquid.registerTag('include', {
parse: function (token) {
var match = lexical.value.exec(token.args)
assert(match, `illegal token ${token.raw}`)
this.value = match[0]
var match = staticFileRE.exec(token.args)
if (match) {
this.staticValue = match[0]
}
match = lexical.value.exec(token.args)
if (match) {
this.value = match[0]
}
match = withRE.exec(token.args)
if (match) {
@@ -16,10 +23,10 @@ module.exports = function (liquid) {
}
},
render: function (scope, hash) {
var filepath = this.value
if (scope.opts.dynamicPartials) {
filepath = Liquid.evalValue(this.value, scope)
}
var filepath = scope.opts.dynamicPartials
? Liquid.evalValue(this.value, scope)
: this.staticValue
assert(filepath, `cannot include with empty filename`)
var originBlocks = scope.opts.blocks
var originBlockMode = scope.opts.blockMode
+12 -4
View File
@@ -2,6 +2,7 @@ const Liquid = require('..')
const Promise = require('any-promise')
const lexical = Liquid.lexical
const assert = require('../src/util/assert.js')
const staticFileRE = /\S+/
/*
* blockMode:
@@ -12,14 +13,21 @@ const assert = require('../src/util/assert.js')
module.exports = function (liquid) {
liquid.registerTag('layout', {
parse: function (token, remainTokens) {
var match = lexical.value.exec(token.args)
assert(match, `illegal token ${token.raw}`)
var match = staticFileRE.exec(token.args)
if (match) {
this.staticLayout = match[0]
}
match = lexical.value.exec(token.args)
if (match) {
this.layout = match[0]
}
this.layout = match[0]
this.tpls = liquid.parser.parse(remainTokens)
},
render: function (scope, hash) {
var layout = scope.opts.dynamicPartials ? Liquid.evalValue(this.layout, scope) : this.layout
var layout = scope.opts.dynamicPartials ? Liquid.evalValue(this.layout, scope) : this.staticLayout
assert(layout, `cannot apply layout with empty filename`)
// render the remaining tokens immediately
scope.opts.blockMode = 'store'
+35
View File
@@ -54,4 +54,39 @@ describe('filter', function () {
filter.construct('foo: 33').render('foo', scope)
expect(spy).to.have.been.calledWith('foo', 33)
})
it('should support arguments as named key/values', function () {
filter.register('foo', x => x)
var f = filter.construct('foo: key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support arguments as named key/values with inline literals', function () {
filter.register('foo', x => x)
var f = filter.construct('foo: "test0", key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ '"test0"', '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support arguments as named key/values with inline values', function () {
filter.register('foo', x => x)
var f = filter.construct('foo: test0, key1: "literal1", key2: value2')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal([ 'test0', '\'key1\'', '"literal1"', '\'key2\'', 'value2' ])
})
it('should support argument values named same as keys', function () {
filter.register('foo', x => x)
var f = filter.construct('foo: a: a')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal(['\'a\'', 'a'])
})
it('should support argument literals named same as keys', function () {
filter.register('foo', x => x)
var f = filter.construct('foo: a: "a"')
expect(f.name).to.equal('foo')
expect(f.args).to.deep.equal(['\'a\'', '"a"'])
})
})
+2 -1
View File
@@ -394,6 +394,7 @@ describe('filters', function () {
liquid.registerFilter('obj_test', function () {
return Array.prototype.slice.call(arguments).join(',')
})
it('should support object', () => test('{{ "a" | obj_test: k1: "v1", k2: "v2" }}', 'a,k1,v1,k2,v2'))
it('should support object', () => test(`{{ "a" | obj_test: k1: "v1", k2: foo }}`, 'a,k1,v1,k2,bar'))
it('should support mixed object', () => test(`{{ "a" | obj_test: "something", k1: "v1", k2: foo }}`, 'a,something,k1,v1,k2,bar'))
})
})
+54 -12
View File
@@ -24,13 +24,23 @@ describe('tags/include', function () {
.eventually.equal('barfoobar')
})
it('should throw when illegal', function () {
it('should throw when not specified', function () {
mock({
'/illegal.html': '{%include%}'
'/parent.html': '{%include%}'
})
return liquid.renderFile('/illegal.html').catch(function (e) {
expect(e.name).to.equal('ParseError')
expect(e.message).to.match(/illegal token {%include%}/)
return liquid.renderFile('/parent.html').catch(function (e) {
expect(e.name).to.equal('RenderError')
expect(e.message).to.match(/cannot include with empty filename/)
})
})
it('should throw when not exist', function () {
mock({
'/parent.html': '{%include not-exist%}'
})
return liquid.renderFile('/parent.html').catch(function (e) {
expect(e.name).to.equal('RenderError')
expect(e.message).to.match(/cannot include with empty filename/)
})
})
@@ -89,13 +99,45 @@ describe('tags/include', function () {
.eventually.equal('This is a person <p>Joe Shmoe<br/>City: Dallas</p>')
})
it('should support static filename', function () {
var staticLiquid = new Liquid({dynamicPartials: false, root: '/'})
mock({
'/with.html': 'X{% include color.html shape: "rect" %}Y',
'/color.html': 'shape:{{shape}}'
describe('static partial', function () {
it('should support filename with extention', function () {
mock({
'/parent.html': 'X{% include child.html color:"red" %}Y',
'/child.html': 'child with {{color}}'
})
var staticLiquid = new Liquid({dynamicPartials: false, root: '/'})
return expect(staticLiquid.renderFile('parent.html')).to
.eventually.equal('Xchild with redY')
})
it('should support parent paths', function () {
mock({
'/parent.html': 'X{% include bar/./../foo/child.html %}Y',
'/foo/child.html': 'child'
})
var staticLiquid = new Liquid({dynamicPartials: false, root: '/'})
return expect(staticLiquid.renderFile('parent.html')).to
.eventually.equal('XchildY')
})
it('should support subpaths', function () {
mock({
'/parent.html': 'X{% include foo/child.html %}Y',
'/foo/child.html': 'child'
})
var staticLiquid = new Liquid({dynamicPartials: false, root: '/'})
return expect(staticLiquid.renderFile('parent.html')).to
.eventually.equal('XchildY')
})
it('should support comma separated arguments', function () {
mock({
'/parent.html': 'X{% include child.html, color:"red" %}Y',
'/child.html': 'child with {{color}}'
})
var staticLiquid = new Liquid({dynamicPartials: false, root: '/'})
return expect(staticLiquid.renderFile('parent.html')).to
.eventually.equal('Xchild with redY')
})
return expect(staticLiquid.renderFile('with.html')).to
.eventually.equal('Xshape:rectY')
})
})
+39 -7
View File
@@ -24,6 +24,15 @@ describe('tags/layout', function () {
return expect(liquid.parseAndRender(src)).to
.be.rejectedWith(/tag {%block%} not closed/)
})
it('should throw when filename not specified', function () {
mock({
'/parent.html': '{%layout%}'
})
return liquid.renderFile('/parent.html').catch(function (e) {
expect(e.name).to.equal('RenderError')
expect(e.message).to.match(/cannot apply layout with empty filename/)
})
})
describe('anonymous block', function () {
it('should handle anonymous block', function () {
mock({
@@ -96,13 +105,36 @@ describe('tags/layout', function () {
return expect(liquid.renderFile('/main.html')).to
.eventually.equal('blackredA')
})
it('should support static filename', function () {
mock({
'/parent.html': '{{color}}{%block%}{%endblock%}',
'/main.html': '{% layout parent.html color:"black"%}{%block%}A{%endblock%}'
describe('static partial', function () {
it('should support filename with extention', function () {
mock({
'/parent.html': '{{color}}{%block%}{%endblock%}',
'/main.html': '{% layout parent.html color:"black"%}{%block%}A{%endblock%}'
})
var staticLiquid = Liquid({ root: '/', dynamicPartials: false })
return expect(staticLiquid.renderFile('/main.html')).to
.eventually.equal('blackA')
})
it('should support parent paths', function () {
mock({
'/foo/parent.html': '{{color}}{%block%}{%endblock%}',
'/main.html': '{% layout bar/../foo/parent.html color:"black"%}{%block%}A{%endblock%}'
})
var staticLiquid = Liquid({ root: '/', dynamicPartials: false })
return expect(staticLiquid.renderFile('/main.html')).to
.eventually.equal('blackA')
})
it('should support subpaths', function () {
mock({
'/foo/parent.html': '{{color}}{%block%}{%endblock%}',
'/main.html': '{% layout foo/parent.html color:"black"%}{%block%}A{%endblock%}'
})
var staticLiquid = Liquid({ root: '/', dynamicPartials: false })
return expect(staticLiquid.renderFile('/main.html')).to
.eventually.equal('blackA')
})
var staticLiquid = Liquid({ root: '/', dynamicPartials: false })
return expect(staticLiquid.renderFile('/main.html')).to
.eventually.equal('blackA')
})
})
+6 -5
View File
@@ -1,6 +1,7 @@
const chai = require('chai')
const expect = chai.expect
const mock = require('mock-fs')
const path = require('path')
chai.use(require('chai-as-promised'))
var engine = require('../..')()
@@ -87,7 +88,7 @@ describe('error', function () {
.then(function (err) {
mock.restore()
expect(err.name).to.equal('TokenizationError')
expect(err.file).to.equal('/foo.html')
expect(err.file).to.equal(path.resolve('/foo.html'))
})
})
})
@@ -195,7 +196,7 @@ describe('error', function () {
.then(function (err) {
console.log(err.message)
console.log(err.stack)
expect(err.message).to.equal('intended render error, file:/throwing-tag.html, line:4')
expect(err.message).to.equal(`intended render error, file:${path.resolve('/throwing-tag.html')}, line:4`)
expect(err.stack).to.contain(message.join('\n'))
expect(err.name).to.equal('RenderError')
})
@@ -218,7 +219,7 @@ describe('error', function () {
return expect(engine.parseAndRender(html)).to.eventually
.be.rejected
.then(function (err) {
expect(err.message).to.equal('intended render error, file:/throwing-tag.html, line:4')
expect(err.message).to.equal(`intended render error, file:${path.resolve('/throwing-tag.html')}, line:4`)
expect(err.stack).to.contain(message.join('\n'))
expect(err.name).to.equal('RenderError')
})
@@ -260,7 +261,7 @@ describe('error', function () {
.then(function (err) {
mock.restore()
expect(err.name).to.equal('RenderError')
expect(err.file).to.equal('/foo.html')
expect(err.file).to.equal(path.resolve('/foo.html'))
})
})
})
@@ -383,7 +384,7 @@ describe('error', function () {
.then(function (err) {
mock.restore()
expect(err.name).to.equal('ParseError')
expect(err.file).to.equal('/foo.html')
expect(err.file).to.equal(path.resolve('/foo.html'))
})
})
})