mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-14 20:00:39 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f668a5cc41 | ||
|
|
098f5164e7 | ||
|
|
432f878bb6 | ||
|
|
aa54dad36e | ||
|
|
c6ff1f7911 | ||
|
|
11355cbf09 | ||
|
|
7ccd0cb1e5 | ||
|
|
4326c223f3 | ||
|
|
78b6e35fbf | ||
|
|
e275368cb3 | ||
|
|
ca2b16a583 | ||
|
|
86d0529a5e | ||
|
|
400b93c88d |
@@ -10,21 +10,37 @@
|
||||
[](https://david-dm.org/harttle/liquidjs?type=dev)
|
||||
[](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
|
||||
|
||||
Vendored
+53
-38
@@ -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';
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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'
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user