mirror of
https://github.com/harttle/liquidjs.git
synced 2026-10-02 16:45:14 -07:00
filters: all
This commit is contained in:
@@ -53,37 +53,41 @@ Documentation: <https://shopify.github.io/liquid/basics/introduction/#filters>
|
|||||||
- [x] downcase [Document](https://shopify.github.io/liquid/filters/downcase) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] downcase [Document](https://shopify.github.io/liquid/filters/downcase) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [x] escape [Document](https://shopify.github.io/liquid/filters/escape) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] escape [Document](https://shopify.github.io/liquid/filters/escape) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [x] escape_once [Document](https://shopify.github.io/liquid/filters/escape_once) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] escape_once [Document](https://shopify.github.io/liquid/filters/escape_once) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] first [Document](https://shopify.github.io/liquid/filters/first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] first [Document](https://shopify.github.io/liquid/filters/first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] floor [Document](https://shopify.github.io/liquid/filters/floor) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] floor [Document](https://shopify.github.io/liquid/filters/floor) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] join [Document](https://shopify.github.io/liquid/filters/join) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] join [Document](https://shopify.github.io/liquid/filters/join) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] last [Document](https://shopify.github.io/liquid/filters/last) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] last [Document](https://shopify.github.io/liquid/filters/last) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] lstrip [Document](https://shopify.github.io/liquid/filters/lstrip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] lstrip [Document](https://shopify.github.io/liquid/filters/lstrip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] map [Document](https://shopify.github.io/liquid/filters/map) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] map [Document](https://shopify.github.io/liquid/filters/map) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] minus [Document](https://shopify.github.io/liquid/filters/minus) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] minus [Document](https://shopify.github.io/liquid/filters/minus) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] modulo [Document](https://shopify.github.io/liquid/filters/modulo) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] modulo [Document](https://shopify.github.io/liquid/filters/modulo) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] newline_to_br [Document](https://shopify.github.io/liquid/filters/newline_to_br) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] newline_to_br [Document](https://shopify.github.io/liquid/filters/newline_to_br) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] plus [Document](https://shopify.github.io/liquid/filters/plus) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] plus [Document](https://shopify.github.io/liquid/filters/plus) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] prepend [Document](https://shopify.github.io/liquid/filters/prepend) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] prepend [Document](https://shopify.github.io/liquid/filters/prepend) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] remove [Document](https://shopify.github.io/liquid/filters/remove) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] remove [Document](https://shopify.github.io/liquid/filters/remove) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] remove_first [Document](https://shopify.github.io/liquid/filters/remove_first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] remove_first [Document](https://shopify.github.io/liquid/filters/remove_first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] replace [Document](https://shopify.github.io/liquid/filters/replace) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] replace [Document](https://shopify.github.io/liquid/filters/replace) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] replace_first [Document](https://shopify.github.io/liquid/filters/replace_first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] replace_first [Document](https://shopify.github.io/liquid/filters/replace_first) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] reverse [Document](https://shopify.github.io/liquid/filters/reverse) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] reverse [Document](https://shopify.github.io/liquid/filters/reverse) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] round [Document](https://shopify.github.io/liquid/filters/round) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] round [Document](https://shopify.github.io/liquid/filters/round) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] rstrip [Document](https://shopify.github.io/liquid/filters/rstrip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] rstrip [Document](https://shopify.github.io/liquid/filters/rstrip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] size [Document](https://shopify.github.io/liquid/filters/size) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] size [Document](https://shopify.github.io/liquid/filters/size) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] slice [Document](https://shopify.github.io/liquid/filters/slice) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] slice [Document](https://shopify.github.io/liquid/filters/slice) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] sort [Document](https://shopify.github.io/liquid/filters/sort) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] sort [Document](https://shopify.github.io/liquid/filters/sort) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] split [Document](https://shopify.github.io/liquid/filters/split) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] split [Document](https://shopify.github.io/liquid/filters/split) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] strip [Document](https://shopify.github.io/liquid/filters/strip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] strip [Document](https://shopify.github.io/liquid/filters/strip) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] strip_html [Document](https://shopify.github.io/liquid/filters/strip_html) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] strip_html [Document](https://shopify.github.io/liquid/filters/strip_html) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] strip_newlines [Document](https://shopify.github.io/liquid/filters/strip_newlines) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] strip_newlines [Document](https://shopify.github.io/liquid/filters/strip_newlines) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] times [Document](https://shopify.github.io/liquid/filters/times) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] times [Document](https://shopify.github.io/liquid/filters/times) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] truncate [Document](https://shopify.github.io/liquid/filters/truncate) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] truncate [Document](https://shopify.github.io/liquid/filters/truncate) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] truncatewords [Document](https://shopify.github.io/liquid/filters/truncatewords) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] truncatewords [Document](https://shopify.github.io/liquid/filters/truncatewords) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] uniq [Document](https://shopify.github.io/liquid/filters/uniq) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] uniq [Document](https://shopify.github.io/liquid/filters/uniq) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] upcase [Document](https://shopify.github.io/liquid/filters/upcase) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] upcase [Document](https://shopify.github.io/liquid/filters/upcase) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
- [ ] url_encode [Document](https://shopify.github.io/liquid/filters/url_encode) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
- [x] url_encode [Document](https://shopify.github.io/liquid/filters/url_encode) [Source](https://github.com/harttle/shopify-liquid/blob/master/filters.js) [Test][ft]
|
||||||
|
|
||||||
|
## Differences from Shopify Liquid
|
||||||
|
|
||||||
|
* `url_encode` is based on `encodeURIComponent`, ` ` will be converted to `%20`.
|
||||||
|
|
||||||
## Async Support
|
## Async Support
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -5,12 +5,12 @@ const _ = require('lodash');
|
|||||||
|
|
||||||
function evalExp(exp, scope) {
|
function evalExp(exp, scope) {
|
||||||
if (!scope) throw new Error('unable to evalExp: scope undefined');
|
if (!scope) throw new Error('unable to evalExp: scope undefined');
|
||||||
var operatorREs = lexical.operators;
|
var operatorREs = lexical.operators,
|
||||||
|
match;
|
||||||
for (var i = 0; i < operatorREs.length; i++) {
|
for (var i = 0; i < operatorREs.length; i++) {
|
||||||
var operatorRE = operatorREs[i];
|
var operatorRE = operatorREs[i];
|
||||||
var expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`);
|
var expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`);
|
||||||
var match = exp.match(expRE);
|
if (match = exp.match(expRE)) {
|
||||||
if (match) {
|
|
||||||
var l = evalExp(match[1], scope);
|
var l = evalExp(match[1], scope);
|
||||||
var op = syntax.operators[match[2].trim()];
|
var op = syntax.operators[match[2].trim()];
|
||||||
var r = evalExp(match[3], scope);
|
var r = evalExp(match[3], scope);
|
||||||
|
|||||||
@@ -2,33 +2,42 @@ const lexical = require('./lexical.js');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const Exp = require('./expression.js');
|
const Exp = require('./expression.js');
|
||||||
|
|
||||||
var _filterInstance = {
|
var valueRE = new RegExp(`${lexical.value.source}`, 'g');
|
||||||
render: function(output, scope) {
|
|
||||||
var args = this.args.map(arg => Exp.evalValue(arg, scope));
|
|
||||||
args.unshift(output);
|
|
||||||
return this.filter.apply(null, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
var filters = {};
|
var filters = {};
|
||||||
|
|
||||||
|
var _filterInstance = {
|
||||||
|
render: function(output, scope) {
|
||||||
|
var args = this.args.map(arg => Exp.evalValue(arg, scope));
|
||||||
|
args.unshift(output);
|
||||||
|
return this.filter.apply(null, args);
|
||||||
|
},
|
||||||
|
parse: function(str) {
|
||||||
|
var match = lexical.filterLine.exec(str);
|
||||||
|
if (!match) throw new Error('illegal filter: ' + str);
|
||||||
|
|
||||||
|
var name = match[1], argList = match[2] || '', filter = filters[name];
|
||||||
|
if (typeof filter !== 'function'){
|
||||||
|
throw new Error(`filter "${name}" not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = [];
|
||||||
|
while(match = valueRE.exec(argList.trim())){
|
||||||
|
args.push(match[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.filter = filter;
|
||||||
|
this.args = args;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function construct(str) {
|
function construct(str) {
|
||||||
var match = lexical.filterLine.exec(str.trim());
|
|
||||||
if (!match) error('illegal filter: ' + str);
|
|
||||||
|
|
||||||
var k = match[1], v = match[2];
|
|
||||||
return factory(k, [v]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function factory(name, args) {
|
|
||||||
var filter = filters[name];
|
|
||||||
if (typeof filter !== 'function')
|
|
||||||
throw new Error(`filter ${name} not found`);
|
|
||||||
|
|
||||||
var instance = Object.create(_filterInstance);
|
var instance = Object.create(_filterInstance);
|
||||||
instance.args = args;
|
instance.parse(str);
|
||||||
instance.filter = filter;
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+59
-1
@@ -8,8 +8,66 @@ module.exports = function(liquid) {
|
|||||||
liquid.registerFilter('ceil', v => Math.ceil(v));
|
liquid.registerFilter('ceil', v => Math.ceil(v));
|
||||||
liquid.registerFilter('date', (v, arg) => strftime(arg, v));
|
liquid.registerFilter('date', (v, arg) => strftime(arg, v));
|
||||||
liquid.registerFilter('default', (v, arg) => arg || v);
|
liquid.registerFilter('default', (v, arg) => arg || v);
|
||||||
liquid.registerFilter('divided_by', (v, arg) => Math.floor(v/arg));
|
liquid.registerFilter('divided_by', (v, arg) => Math.floor(v / arg));
|
||||||
liquid.registerFilter('downcase', v => v.toLowerCase());
|
liquid.registerFilter('downcase', v => v.toLowerCase());
|
||||||
liquid.registerFilter('escape', v => _.escape(v));
|
liquid.registerFilter('escape', v => _.escape(v));
|
||||||
liquid.registerFilter('escape_once', v => _.escape(_.unescape(v)));
|
liquid.registerFilter('escape_once', v => _.escape(_.unescape(v)));
|
||||||
|
liquid.registerFilter('first', v => v[0]);
|
||||||
|
liquid.registerFilter('floor', v => Math.floor(v));
|
||||||
|
liquid.registerFilter('join', _.join);
|
||||||
|
liquid.registerFilter('last', v => v[v.length - 1]);
|
||||||
|
liquid.registerFilter('lstrip', v => _.trimStart(v));
|
||||||
|
liquid.registerFilter('map', (v, arg) => _.map(v, arg));
|
||||||
|
liquid.registerFilter('minus', bindFixed((v, arg) => v - arg));
|
||||||
|
liquid.registerFilter('modulo', bindFixed((v, arg) => v % arg));
|
||||||
|
liquid.registerFilter('newline_to_br', v => v.replace(/\n/g, '<br />'));
|
||||||
|
liquid.registerFilter('plus', bindFixed((v, arg) => v + arg));
|
||||||
|
liquid.registerFilter('prepend', (v, arg) => arg + v);
|
||||||
|
liquid.registerFilter('remove', (v, arg) => v.split(arg).join(''));
|
||||||
|
liquid.registerFilter('remove_first', (v, l) => v.replace(l, ''));
|
||||||
|
liquid.registerFilter('replace', (v, pattern, replacement) =>
|
||||||
|
_.replace(v, new RegExp(_.escapeRegExp(pattern), 'g'), replacement));
|
||||||
|
liquid.registerFilter('replace_first', _.replace);
|
||||||
|
liquid.registerFilter('reverse', _.reverse);
|
||||||
|
liquid.registerFilter('round', _.round);
|
||||||
|
liquid.registerFilter('rstrip', _.trimEnd);
|
||||||
|
liquid.registerFilter('size', _.size);
|
||||||
|
liquid.registerFilter('slice', (v, begin, length) =>
|
||||||
|
v.substr(begin, length === undefined ? 1 : length));
|
||||||
|
liquid.registerFilter('sort', _.sortBy);
|
||||||
|
liquid.registerFilter('split', _.split);
|
||||||
|
liquid.registerFilter('strip', _.trim);
|
||||||
|
liquid.registerFilter('strip_html', v => _.replace(v, /<\/?\s*\w+\s*\/?>/g, ''));
|
||||||
|
liquid.registerFilter('strip_newlines', v => _.replace(v, /\n/g, ''));
|
||||||
|
liquid.registerFilter('times', (v, arg) => v * arg);
|
||||||
|
liquid.registerFilter('truncate', (v, l, o) => _.truncate(v, {
|
||||||
|
length: l,
|
||||||
|
omission: o === undefined ? '...' : o
|
||||||
|
}));
|
||||||
|
liquid.registerFilter('truncatewords', (v, l, o) => {
|
||||||
|
if (o === undefined) o = '...';
|
||||||
|
var arr = v.split(' ');
|
||||||
|
var ret = arr.slice(0, l).join(' ');
|
||||||
|
if (arr.length > l) ret += o;
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
liquid.registerFilter('uniq', _.uniq);
|
||||||
|
liquid.registerFilter('upcase', _.toUpper);
|
||||||
|
liquid.registerFilter('url_encode', encodeURIComponent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getFixed(v) {
|
||||||
|
var p = (v + "").split(".");
|
||||||
|
return (p.length > 1) ? p[1].length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaxFixed(l, r) {
|
||||||
|
return Math.max(getFixed(l), getFixed(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindFixed(cb) {
|
||||||
|
return (l, r) => {
|
||||||
|
var f = getMaxFixed(l, r);
|
||||||
|
return cb(l, r).toFixed(f);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,21 +28,22 @@ function factory(){
|
|||||||
engine.filter = Filter();
|
engine.filter = Filter();
|
||||||
engine.tokenize = tokenizer.parse;
|
engine.tokenize = tokenizer.parse;
|
||||||
|
|
||||||
var template = Template(engine.tag);
|
engine.template = Template(engine.tag, engine.filter);
|
||||||
engine.parse = template.parse;
|
engine.parseStream = engine.template.parseStream;
|
||||||
engine.parseTag = template.parseTag;
|
|
||||||
engine.parseStream = template.parseStream;
|
|
||||||
|
|
||||||
var renderer = Render(engine.filter, engine.tag);
|
var renderer = Render(engine.filter, engine.tag);
|
||||||
engine.evalFilter = renderer.evalFilter;
|
|
||||||
engine.renderTemplates = renderer.renderTemplates;
|
engine.renderTemplates = renderer.renderTemplates;
|
||||||
|
|
||||||
engine.render = function(html, ctx) {
|
engine.render = function(html, ctx) {
|
||||||
var tokens = engine.tokenize(html);
|
var tokens = engine.tokenize(html);
|
||||||
var templates = engine.parse(tokens);
|
var templates = engine.template.parse(tokens);
|
||||||
engine.register = {};
|
engine.register = {};
|
||||||
return engine.renderTemplates(templates, scope.factory(ctx));
|
return engine.renderTemplates(templates, scope.factory(ctx));
|
||||||
},
|
};
|
||||||
|
engine.evalOutput = function(str, scope) {
|
||||||
|
var template = engine.template.parseOutput(str.trim());
|
||||||
|
return renderer.evalOutput(template, scope);
|
||||||
|
};
|
||||||
|
|
||||||
fs.readdirSync(tagsPath).map(function(f){
|
fs.readdirSync(tagsPath).map(function(f){
|
||||||
var match = /^(\w+)\.js$/.exec(f);
|
var match = /^(\w+)\.js$/.exec(f);
|
||||||
|
|||||||
+27
-18
@@ -1,33 +1,43 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
// quote related
|
||||||
var singleQuoted = /'[^']*'/;
|
var singleQuoted = /'[^']*'/;
|
||||||
var doubleQuoted = /"[^"]*"/;
|
var doubleQuoted = /"[^"]*"/;
|
||||||
|
var quoteBalanced = new RegExp(`(?:${singleQuoted.source}|${doubleQuoted.source}|[^'"])*`);
|
||||||
|
|
||||||
var number = /-?\d+\.?\d*|\.?\d+/;
|
var number = /(?:-?\d+\.?\d*|\.?\d+)/;
|
||||||
var bool = /true|false/i;
|
var bool = /true|false/i;
|
||||||
var identifier = /[a-zA-Z_$][a-zA-Z_$0-9]*/;
|
var identifier = /[a-zA-Z_$][a-zA-Z_$0-9]*/;
|
||||||
var subscript = /\[\d+\]/;
|
var subscript = /\[\d+\]/;
|
||||||
|
|
||||||
var quoted = new RegExp(`${singleQuoted.source}|${doubleQuoted.source}`);
|
var quoted = new RegExp(`(?:${singleQuoted.source}|${doubleQuoted.source})`);
|
||||||
var literal = new RegExp(`${quoted.source}|${bool.source}|${number.source}`, 'i');
|
var literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`, 'i');
|
||||||
var variable = new RegExp(`${identifier.source}(?:\\.${identifier.source}|${subscript.source})*`);
|
var variable = new RegExp(`${identifier.source}(?:\\.${identifier.source}|${subscript.source})*`);
|
||||||
var value = new RegExp(`${literal.source}|${variable.source}`, 'i');
|
|
||||||
var range = new RegExp(`\\((?:${value.source})\\.\\.(?:${value.source})\\)`);
|
// range related
|
||||||
var rangeCapture = new RegExp(`\\((${value.source})\\.\\.(${value.source})\\)`);
|
var rangeLimit = new RegExp(`(?:${variable.source}|${number.source})`);
|
||||||
var variableOrRange = new RegExp(`${variable.source}|${range.source}`);
|
var range = new RegExp(`\\(${rangeLimit.source}\\.\\.${rangeLimit.source}\\)`);
|
||||||
|
var rangeCapture = new RegExp(`\\((${rangeLimit.source})\\.\\.(${rangeLimit.source})\\)`);
|
||||||
|
|
||||||
|
var value = new RegExp(`(?:${literal.source}|${variable.source}|${range.source})`, 'i');
|
||||||
|
|
||||||
|
// hash related
|
||||||
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`, 'g');
|
var hash = new RegExp(`(?:${identifier.source})\\s*:\\s*(?:${value.source})`, 'g');
|
||||||
var hashCapture = new RegExp(`(${identifier.source})\\s*:\\s*(${value.source})`, 'g');
|
var hashCapture = new RegExp(`(${identifier.source})\\s*:\\s*(${value.source})`, 'g');
|
||||||
var filter = new RegExp(`(${identifier.source})(?:\\s*:\\s*(${value.source}))?`);
|
|
||||||
var quoteBalanced = new RegExp(`(?:${singleQuoted.source}|${doubleQuoted.source}|[^'"])*`);
|
|
||||||
|
|
||||||
var tagLine = new RegExp(`^\\s*(${identifier.source})\\s*(.*)\\s*$`);
|
var tagLine = new RegExp(`^\\s*(${identifier.source})\\s*(.*)\\s*$`);
|
||||||
var literalLine = new RegExp(`^(?:${literal.source})$`, 'i');
|
var literalLine = new RegExp(`^${literal.source}$`, 'i');
|
||||||
var variableLine = new RegExp(`^(?:${variable.source})$`);
|
var variableLine = new RegExp(`^${variable.source}$`);
|
||||||
var numberLine = new RegExp(`^(?:${number.source})$`);
|
var numberLine = new RegExp(`^${number.source}$`);
|
||||||
var boolLine = new RegExp(`^(?:${bool.source})$`, 'i');
|
var boolLine = new RegExp(`^${bool.source}$`, 'i');
|
||||||
var quotedLine = new RegExp(`^(?:${quoted.source})$`);
|
var quotedLine = new RegExp(`^${quoted.source}$`);
|
||||||
var rangeLine = new RegExp(`^(?:${rangeCapture.source})$`);
|
var rangeLine = new RegExp(`^${rangeCapture.source}$`);
|
||||||
var filterLine = new RegExp(`^(?:${filter.source})$`);
|
|
||||||
|
// filter related
|
||||||
|
var valueList = new RegExp(`${value.source}(\\s*,\\s*${value.source})*`);
|
||||||
|
var filter = new RegExp(`${identifier.source}(?:\\s*:\\s*${valueList.source})?`, 'g');
|
||||||
|
var filterCapture = new RegExp(`(${identifier.source})(?:\\s*:\\s*(${valueList.source}))?`);
|
||||||
|
var filterLine = new RegExp(`^${filterCapture.source}$`);
|
||||||
|
|
||||||
var operators = [
|
var operators = [
|
||||||
/\s+or\s+/,
|
/\s+or\s+/,
|
||||||
@@ -35,7 +45,6 @@ var operators = [
|
|||||||
/==|!=|<=|>=|<|>|\s+contains\s+/
|
/==|!=|<=|>=|<|>|\s+contains\s+/
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
function isLiteral(str) {
|
function isLiteral(str) {
|
||||||
return literalLine.test(str);
|
return literalLine.test(str);
|
||||||
}
|
}
|
||||||
@@ -64,7 +73,7 @@ function parseLiteral(str) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
quoted, number, bool, literal, filter,
|
quoted, number, bool, literal, filter,
|
||||||
hash, hashCapture,
|
hash, hashCapture,
|
||||||
range, rangeCapture, variableOrRange,
|
range, rangeCapture,
|
||||||
identifier, value, quoteBalanced, operators,
|
identifier, value, quoteBalanced, operators,
|
||||||
quotedLine, numberLine, boolLine, rangeLine, literalLine, filterLine, tagLine,
|
quotedLine, numberLine, boolLine, rangeLine, literalLine, filterLine, tagLine,
|
||||||
isLiteral, isVariable, parseLiteral, isRange
|
isLiteral, isVariable, parseLiteral, isRange
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "shopify-liquid",
|
"name": "shopify-liquid",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"description": "A Shopify Liquid Implementation in Node.js",
|
"description": "A Shopify Liquid Implementation in Node.js",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const lexical = require('./lexical.js');
|
|
||||||
const error = require('./error.js');
|
const error = require('./error.js');
|
||||||
const Exp = require('./expression.js');
|
const Exp = require('./expression.js');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
function stringify(val) {
|
function stringify(val) {
|
||||||
if (typeof val === 'string') return val;
|
if (typeof val === 'string') return val;
|
||||||
@@ -8,43 +8,51 @@ function stringify(val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function factory(Filter, Tag) {
|
function factory(Filter, Tag) {
|
||||||
|
|
||||||
function renderTemplates(templates, scope) {
|
function renderTemplates(templates, scope) {
|
||||||
if (!scope) throw new Error('unable to evalTemplates: scope undefined');
|
assert(scope, 'unable to evalTemplates: scope undefined');
|
||||||
var html = '';
|
var html = '', partial;
|
||||||
templates.some(template => {
|
templates.some(template => {
|
||||||
if (scope.get('forloop.skip')) return true;
|
if (scope.get('forloop.skip')) return true;
|
||||||
if (template.type === 'tag') {
|
switch (template.type) {
|
||||||
if (template.name === 'continue') {
|
case 'tag':
|
||||||
scope.set('forloop.skip', true);
|
partial = renderTag(template, scope, this.register);
|
||||||
return true;
|
if(partial === undefined) return true;
|
||||||
}
|
html += partial;
|
||||||
if (template.name === 'break') {
|
break;
|
||||||
scope.set('forloop.stop', true);
|
case 'html':
|
||||||
scope.set('forloop.skip', true);
|
html += template.value;
|
||||||
return true;
|
break;
|
||||||
}
|
case 'output':
|
||||||
html += template.render(scope, this.register);
|
var val = evalOutput(template, scope);
|
||||||
} else if (template.type === 'html') {
|
html += stringify(val);
|
||||||
html += template.value;
|
|
||||||
} else if (template.type === 'output') {
|
|
||||||
var val = evalFilter(template.value, scope);
|
|
||||||
html += stringify(val);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function evalFilter(str, scope) {
|
function renderTag(template, scope, register) {
|
||||||
if (!scope) throw new Error('unable to evalFilter: scope undefined');
|
if (template.name === 'continue') {
|
||||||
var filters = str.split('|');
|
scope.set('forloop.skip', true);
|
||||||
var val = Exp.evalValue(filters.shift(), scope);
|
return;
|
||||||
return filters
|
}
|
||||||
.map(str => Filter.construct(str))
|
if (template.name === 'break') {
|
||||||
|
scope.set('forloop.stop', true);
|
||||||
|
scope.set('forloop.skip', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return template.render(scope, register);
|
||||||
|
}
|
||||||
|
|
||||||
|
function evalOutput(template, scope) {
|
||||||
|
assert(scope, 'unable to evalOutput: scope undefined');
|
||||||
|
var val = Exp.evalExp(template.initial, scope);
|
||||||
|
return template.filters
|
||||||
.reduce((v, filter) => filter.render(v, scope), val);
|
.reduce((v, filter) => filter.render(v, scope), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
renderTemplates, evalFilter
|
renderTemplates, evalOutput, renderTag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ module.exports = function(liquid) {
|
|||||||
this.value = match[2];
|
this.value = match[2];
|
||||||
},
|
},
|
||||||
render: function(scope, hash) {
|
render: function(scope, hash) {
|
||||||
scope.set(this.key, Liquid.evalValue(this.value, scope));
|
scope.set(this.key, liquid.evalOutput(this.value, scope));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
var Liquid = require('..');
|
var Liquid = require('..');
|
||||||
var lexical = Liquid.lexical;
|
var lexical = Liquid.lexical;
|
||||||
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
|
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
|
||||||
`(${lexical.variableOrRange.source})` +
|
`(${lexical.value.source})` +
|
||||||
`(?:\\s+${lexical.hash.source})*` +
|
`(?:\\s+${lexical.hash.source})*` +
|
||||||
`(?:\\s+(reversed))?$`);
|
`(?:\\s+(reversed))?$`);
|
||||||
|
|
||||||
|
|||||||
+7
-11
@@ -1,7 +1,7 @@
|
|||||||
var Liquid = require('..');
|
var Liquid = require('..');
|
||||||
var lexical = Liquid.lexical;
|
var lexical = Liquid.lexical;
|
||||||
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
|
var re = new RegExp(`^(${lexical.identifier.source})\\s+in\\s+` +
|
||||||
`(${lexical.variableOrRange.source})` +
|
`(${lexical.value.source})` +
|
||||||
`(?:\\s+${lexical.hash.source})*$`);
|
`(?:\\s+${lexical.hash.source})*$`);
|
||||||
|
|
||||||
module.exports = function(liquid) {
|
module.exports = function(liquid) {
|
||||||
@@ -14,11 +14,9 @@ module.exports = function(liquid) {
|
|||||||
this.collection = match[2];
|
this.collection = match[2];
|
||||||
|
|
||||||
this.templates = [];
|
this.templates = [];
|
||||||
this.elseTemplates = [];
|
|
||||||
|
|
||||||
var p, stream = liquid.parseStream(remainTokens)
|
var p, stream = liquid.parseStream(remainTokens)
|
||||||
.onStart(x => p = this.templates)
|
.onStart(x => p = this.templates)
|
||||||
.onTag('else', token => p = this.elseTemplates)
|
|
||||||
.onTag('endtablerow', token => stream.stop())
|
.onTag('endtablerow', token => stream.stop())
|
||||||
.onTemplate(tpl => p.push(tpl))
|
.onTemplate(tpl => p.push(tpl))
|
||||||
.onEnd(x => {
|
.onEnd(x => {
|
||||||
@@ -29,10 +27,7 @@ module.exports = function(liquid) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function(scope, hash) {
|
render: function(scope, hash) {
|
||||||
var collection = Liquid.evalExp(this.collection, scope);
|
var collection = Liquid.evalExp(this.collection, scope) || [];
|
||||||
if (Liquid.isFalsy(collection)) {
|
|
||||||
return liquid.renderTemplates(this.elseTemplates, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = '<table>',
|
var html = '<table>',
|
||||||
ctx = {},
|
ctx = {},
|
||||||
@@ -40,12 +35,12 @@ module.exports = function(liquid) {
|
|||||||
var offset = hash.offset || 0;
|
var offset = hash.offset || 0;
|
||||||
var limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
var limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
||||||
|
|
||||||
var cols = hash.cols;
|
var cols = hash.cols, row, col;
|
||||||
if (!cols) throw new Error(`illegal cols: ${cols}`);
|
if (!cols) throw new Error(`illegal cols: ${cols}`);
|
||||||
|
|
||||||
collection.slice(offset, offset + limit).some((item, i) => {
|
collection.slice(offset, offset + limit).some((item, i) => {
|
||||||
var row = Math.floor(i / cols) + 1,
|
row = Math.floor(i / cols) + 1;
|
||||||
col = (i % cols) + 1;
|
col = (i % cols) + 1;
|
||||||
if(col === 1){
|
if(col === 1){
|
||||||
if(row !== 1){
|
if(row !== 1){
|
||||||
html += '</tr>';
|
html += '</tr>';
|
||||||
@@ -60,7 +55,8 @@ module.exports = function(liquid) {
|
|||||||
html += '</td>';
|
html += '</td>';
|
||||||
scope.pop(ctx);
|
scope.pop(ctx);
|
||||||
});
|
});
|
||||||
html += '</tr></table>';
|
if(row > 0) html += '</tr>';
|
||||||
|
html += '</table>';
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+42
-22
@@ -2,7 +2,7 @@ const lexical = require('./lexical.js');
|
|||||||
const error = require('./error.js');
|
const error = require('./error.js');
|
||||||
const ParseError = require('./error.js').ParseError;
|
const ParseError = require('./error.js').ParseError;
|
||||||
|
|
||||||
module.exports = function(Tag) {
|
module.exports = function(Tag, Filter) {
|
||||||
|
|
||||||
var stream = {
|
var stream = {
|
||||||
init: function(tokens) {
|
init: function(tokens) {
|
||||||
@@ -24,17 +24,11 @@ module.exports = function(Tag) {
|
|||||||
start: function() {
|
start: function() {
|
||||||
this.trigger('start');
|
this.trigger('start');
|
||||||
while (!this.stopRequested && (token = this.tokens.shift())) {
|
while (!this.stopRequested && (token = this.tokens.shift())) {
|
||||||
var template;
|
if (token.type == 'tag' &&
|
||||||
if (token.type == 'tag') {
|
this.trigger(`tag:${token.name}`, token)) {
|
||||||
if (this.trigger(`tag:${token.name}`, token)) continue;
|
continue;
|
||||||
if (token.name === 'continue' || token.name === 'break') {
|
|
||||||
template = token;
|
|
||||||
} else {
|
|
||||||
template = parseTag(token, this.tokens);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
template = token;
|
|
||||||
}
|
}
|
||||||
|
var template = parseToken(token, this.tokens);
|
||||||
this.trigger('template', template);
|
this.trigger('template', template);
|
||||||
}
|
}
|
||||||
if (!this.stopRequested) this.trigger('end');
|
if (!this.stopRequested) this.trigger('end');
|
||||||
@@ -59,32 +53,58 @@ module.exports = function(Tag) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function parse(tokens) {
|
function parse(tokens) {
|
||||||
var templates = [];
|
var token, templates = [];
|
||||||
var token;
|
|
||||||
while (token = tokens.shift()) {
|
while (token = tokens.shift()) {
|
||||||
if (token.type === 'tag') {
|
templates.push(parseToken(token, tokens));
|
||||||
var tagInstance = parseTag(token, tokens);
|
|
||||||
templates.push(tagInstance);
|
|
||||||
} else templates.push(token);
|
|
||||||
}
|
}
|
||||||
return templates;
|
return templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTag(token, tokens) {
|
function parseToken(token, tokens) {
|
||||||
try {
|
try {
|
||||||
return Tag.construct(token).parse(tokens);
|
switch (token.type) {
|
||||||
|
case 'tag':
|
||||||
|
return parseTag(token, tokens);
|
||||||
|
case 'output':
|
||||||
|
return parseOutput(token.value);
|
||||||
|
case 'html':
|
||||||
|
return token;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ParseError(e.message,
|
throw new ParseError(e.message, token.input, token.line, e.stack);
|
||||||
token.input, token.line, e.stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseTag(token, tokens) {
|
||||||
|
if (token.name === 'continue' || token.name === 'break') return token;
|
||||||
|
return Tag.construct(token).parse(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOutput(str) {
|
||||||
|
var match = lexical.value.exec(str);
|
||||||
|
if(!match) throw new Error(`illegal output string: ${str}`);
|
||||||
|
|
||||||
|
var initial = match[0];
|
||||||
|
str = str.substr(match.index + match[0].length);
|
||||||
|
|
||||||
|
var filters = [];
|
||||||
|
while(match = lexical.filter.exec(str)){
|
||||||
|
filters.push([match[0].trim()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'output',
|
||||||
|
initial: initial,
|
||||||
|
filters: filters.map(str => Filter.construct(str))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parseStream(tokens) {
|
function parseStream(tokens) {
|
||||||
var s = Object.create(stream);
|
var s = Object.create(stream);
|
||||||
return s.init(tokens);
|
return s.init(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
parse, parseTag, parseStream
|
parse, parseTag, parseStream, parseOutput
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ describe('error', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw ParseError when filter not exist', function() {
|
||||||
|
test(function(){
|
||||||
|
liquid.render('{{ a | xz }}', {});
|
||||||
|
}, function(err){
|
||||||
|
expect(err.name).to.equal('ParseError');
|
||||||
|
expect(err.message).to.equal('filter "xz" not found');
|
||||||
|
expect(err.input).to.equal('{{ a | xz }}');
|
||||||
|
expect(err.line).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
it('should throw ParseError when tag not exist', function() {
|
it('should throw ParseError when tag not exist', function() {
|
||||||
test(function(){
|
test(function(){
|
||||||
liquid.render('{% a %}', {});
|
liquid.render('{% a %}', {});
|
||||||
|
|||||||
+21
-3
@@ -17,12 +17,30 @@ describe('filter', function() {
|
|||||||
it('should throw when not registered', function() {
|
it('should throw when not registered', function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
filter.construct('foo');
|
filter.construct('foo');
|
||||||
}).to.throw(/filter foo not found/);
|
}).to.throw(/filter "foo" not found/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse argument syntax', function(){
|
||||||
|
filter.register('foo', x => x);
|
||||||
|
var f = filter.construct('foo: a, "b"');
|
||||||
|
|
||||||
|
expect(f.name).to.equal('foo');
|
||||||
|
expect(f.args).to.deep.equal(['a', '"b"']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should register a simple filter', function(){
|
it('should register a simple filter', function(){
|
||||||
filter.register('foo', x => x.toUpperCase());
|
filter.register('upcase', x => x.toUpperCase());
|
||||||
expect(filter.construct('foo').render('foo', scope)).to.equal('FOO');
|
expect(filter.construct('upcase').render('foo', scope)).to.equal('FOO');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register a argumented filter', function(){
|
||||||
|
filter.register('add', (a, b) => a + b);
|
||||||
|
expect(filter.construct('add: 2').render(3, scope)).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register a multi-argumented filter', function(){
|
||||||
|
filter.register('add', (a, b, c) => a + b + c);
|
||||||
|
expect(filter.construct('add: 2, "c"').render(3, scope)).to.equal("5c");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call filter with corrct arguments', function(){
|
it('should call filter with corrct arguments', function(){
|
||||||
|
|||||||
+208
-5
@@ -1,7 +1,8 @@
|
|||||||
const chai = require("chai");
|
const chai = require("chai");
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
var liquid = require('..')(), ctx;
|
var liquid = require('..')(),
|
||||||
|
ctx;
|
||||||
|
|
||||||
function test(src, dst) {
|
function test(src, dst) {
|
||||||
ctx = {
|
ctx = {
|
||||||
@@ -10,16 +11,21 @@ function test(src, dst) {
|
|||||||
arr: [-2, 'a'],
|
arr: [-2, 'a'],
|
||||||
obj: {
|
obj: {
|
||||||
foo: 'bar'
|
foo: 'bar'
|
||||||
}
|
},
|
||||||
|
posts: [{
|
||||||
|
category: 'foo'
|
||||||
|
}, {
|
||||||
|
category: 'bar'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
expect(liquid.render(src, ctx)).to.equal(dst);
|
expect(liquid.render(src, ctx)).to.equal(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('filters', function() {
|
describe('filters', function() {
|
||||||
it('should output object', function(){
|
it('should output object', function() {
|
||||||
test('{{obj}}', '{"foo":"bar"}');
|
test('{{obj}}', '{"foo":"bar"}');
|
||||||
});
|
});
|
||||||
it('should output array', function(){
|
it('should output array', function() {
|
||||||
test('{{arr}}', '[-2,"a"]');
|
test('{{arr}}', '[-2,"a"]');
|
||||||
});
|
});
|
||||||
it('should support abs', function() {
|
it('should support abs', function() {
|
||||||
@@ -67,9 +73,206 @@ describe('filters', function() {
|
|||||||
'Have you read 'James & the Giant Peach'?');
|
'Have you read 'James & the Giant Peach'?');
|
||||||
test('{{ "Tetsuro Takara" | escape }}', 'Tetsuro Takara');
|
test('{{ "Tetsuro Takara" | escape }}', 'Tetsuro Takara');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support escape_once', function() {
|
it('should support escape_once', function() {
|
||||||
test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3');
|
test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3');
|
||||||
test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3');
|
test('{{ "1 < 2 & 3" | escape_once }}', '1 < 2 & 3');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
it('should support split/first', function() {
|
||||||
|
src = '{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}' +
|
||||||
|
'{{ my_array | first }}';
|
||||||
|
test(src, 'apples');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support floor', function() {
|
||||||
|
test('{{ 1.2 | floor }}', '1');
|
||||||
|
test('{{ 2.0 | floor }}', '2');
|
||||||
|
test('{{ 183.357 | floor }}', '183');
|
||||||
|
test('{{ "3.5" | floor }}', '3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support join', function() {
|
||||||
|
src = '{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}' +
|
||||||
|
'{{ beatles | join: " and " }}';
|
||||||
|
test(src, 'John and Paul and George and Ringo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support split/last', function() {
|
||||||
|
src = '{% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}' +
|
||||||
|
'{{ my_array|last }}';
|
||||||
|
test(src, 'tiger');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support lstrip', function() {
|
||||||
|
src = '{{ " So much room for activities! " | lstrip }}';
|
||||||
|
test(src, 'So much room for activities! ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support map', function() {
|
||||||
|
test('{{posts | map: "category"}}', '["foo","bar"]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support minus', function() {
|
||||||
|
test('{{ 4 | minus: 2 }}', '2');
|
||||||
|
test('{{ 16 | minus: 4 }}', '12');
|
||||||
|
test('{{ 183.357 | minus: 12 }}', '171.357');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support modulo', function() {
|
||||||
|
test('{{ 3 | modulo: 2 }}', '1');
|
||||||
|
test('{{ 24 | modulo: 7 }}', '3');
|
||||||
|
test('{{ 183.357 | modulo: 12 }}', '3.357');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support string_with_newlines', function() {
|
||||||
|
src = '{% capture string_with_newlines %}\n' +
|
||||||
|
'Hello\n' +
|
||||||
|
'there\n' +
|
||||||
|
'{% endcapture %}' +
|
||||||
|
'{{ string_with_newlines | newline_to_br }}';
|
||||||
|
dst = '<br />' +
|
||||||
|
'Hello<br />' +
|
||||||
|
'there<br />';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support plus', function() {
|
||||||
|
test('{{ 4 | plus: 2 }}', '6');
|
||||||
|
test('{{ 16 | plus: 4 }}', '20');
|
||||||
|
test('{{ 183.357 | plus: 12 }}', '195.357');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support prepend', function() {
|
||||||
|
test('{% assign url = "liquidmarkup.com" %}' +
|
||||||
|
'{{ "/index.html" | prepend: url }}',
|
||||||
|
'liquidmarkup.com/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support remove', function() {
|
||||||
|
test('{{ "I strained to see the train through the rain" | remove: "rain" }}',
|
||||||
|
'I sted to see the t through the ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support remove_first', function() {
|
||||||
|
test('{{ "I strained to see the train through the rain" | remove_first: "rain" }}',
|
||||||
|
'I sted to see the train through the rain');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support replace', function() {
|
||||||
|
test('{{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}',
|
||||||
|
'Take your protein pills and put your helmet on');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support replace_first', function() {
|
||||||
|
test('{% assign my_string = "Take my protein pills and put my helmet on" %}\n' +
|
||||||
|
'{{ my_string | replace_first: "my", "your" }}',
|
||||||
|
'\nTake your protein pills and put my helmet on');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support reverse', function() {
|
||||||
|
test('{{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}',
|
||||||
|
'.moT rojaM ot lortnoc dnuorG');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support round', function() {
|
||||||
|
test('{{1.2|round}}', '1');
|
||||||
|
test('{{2.7|round}}', '3');
|
||||||
|
test('{{183.357|round: 2}}', '183.36');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support rstrip', function() {
|
||||||
|
test('{{ " So much room for activities! " | rstrip }}',
|
||||||
|
' So much room for activities!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support size', function() {
|
||||||
|
test('{{ "Ground control to Major Tom." | size }}', '28');
|
||||||
|
test('{% assign my_array = "apples, oranges, peaches, plums"' +
|
||||||
|
' | split: ", " %}{{ my_array | size }}',
|
||||||
|
'4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support slice', function() {
|
||||||
|
test('{{ "Liquid" | slice: 0 }}', 'L');
|
||||||
|
test('{{ "Liquid" | slice: 2 }}', 'q');
|
||||||
|
test('{{ "Liquid" | slice: 2, 5 }}', 'quid');
|
||||||
|
test('{{ "Liquid" | slice: -3, 2 }}', 'ui');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support sort', function() {
|
||||||
|
test('{% assign my_array = "zebra, octopus, giraffe, Sally Snake"' +
|
||||||
|
' | split: ", " %}' +
|
||||||
|
'{{ my_array | sort | join: ", " }}',
|
||||||
|
'Sally Snake, giraffe, octopus, zebra');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support split', function() {
|
||||||
|
test('{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}' +
|
||||||
|
'{% for member in beatles %}' +
|
||||||
|
'{{ member }} ' +
|
||||||
|
'{% endfor %}',
|
||||||
|
'John Paul George Ringo ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support strip', function() {
|
||||||
|
test('{{ " So much room for activities! " | strip }}',
|
||||||
|
'So much room for activities!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support strip_tml', function() {
|
||||||
|
test('{{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}',
|
||||||
|
'Have you read Ulysses?');
|
||||||
|
test('{{"<br/><br />< p ></p></ p >" | strip_html }}', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support strip_newlines', function() {
|
||||||
|
test('{% capture string_with_newlines %}\n' +
|
||||||
|
'Hello\nthere\n{% endcapture %}' +
|
||||||
|
'{{ string_with_newlines | strip_newlines }}',
|
||||||
|
'Hellothere');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support times', function() {
|
||||||
|
test('{{ 3 | times: 2 }}', '6');
|
||||||
|
test('{{ 24 | times: 7 }}', '168');
|
||||||
|
test('{{ 183.357 | times: 12 }}', '2200.284');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support truncate', function() {
|
||||||
|
test('{{ "Ground control to Major Tom." | truncate: 20 }}',
|
||||||
|
'Ground control to...');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncate: 80 }}',
|
||||||
|
'Ground control to Major Tom.');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncate: 25,", and so on" }}',
|
||||||
|
'Ground control, and so on');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncate: 20, "" }}',
|
||||||
|
'Ground control to Ma');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support truncatewords', function() {
|
||||||
|
test('{{ "Ground control to Major Tom." | truncatewords: 3 }}',
|
||||||
|
'Ground control to...');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncatewords: 8 }}',
|
||||||
|
'Ground control to Major Tom.');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncatewords: 3, "--" }}',
|
||||||
|
'Ground control to--');
|
||||||
|
test('{{ "Ground control to Major Tom." | truncatewords: 3, "" }}',
|
||||||
|
'Ground control to');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support uniq', function() {
|
||||||
|
test('{% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}' +
|
||||||
|
'{{ my_array | uniq | join: ", " }}',
|
||||||
|
'ants, bugs, bees');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support upcase', function() {
|
||||||
|
test('{{ "Parker Moore" | upcase }}', 'PARKER MOORE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support url_encode', function() {
|
||||||
|
test('{{ "[email protected]" | url_encode }}', 'john%40liquid.com');
|
||||||
|
test('{{ "Tetsuro Takara" | url_encode }}', 'Tetsuro%20Takara');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+44
-35
@@ -1,70 +1,79 @@
|
|||||||
var chai = require("chai");
|
var chai = require("chai");
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
var expect = chai.expect;
|
||||||
|
|
||||||
var identifier = require('../lexical.js');
|
var lexical = require('../lexical.js');
|
||||||
|
|
||||||
describe('identifier', function() {
|
describe('lexical', function() {
|
||||||
|
it('should test filter syntax', function(){
|
||||||
|
lexical.filterLine.test('abs').should.equal(true);
|
||||||
|
lexical.filterLine.test('plus:1').should.equal(true);
|
||||||
|
lexical.filterLine.test('replace: "a", b').should.equal(true);
|
||||||
|
lexical.filterLine.test('foo: a, "b"').should.equal(true);
|
||||||
|
lexical.filterLine.test('abs | another').should.equal(false);
|
||||||
|
lexical.filterLine.test('join: "," | another').should.equal(false);
|
||||||
|
});
|
||||||
it('should test boolean literal', function() {
|
it('should test boolean literal', function() {
|
||||||
identifier.isLiteral('true').should.equal(true);
|
lexical.isLiteral('true').should.equal(true);
|
||||||
identifier.isLiteral('TrUE').should.equal(true);
|
lexical.isLiteral('TrUE').should.equal(true);
|
||||||
identifier.isLiteral('false').should.equal(true);
|
lexical.isLiteral('false').should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should test number literal', function() {
|
it('should test number literal', function() {
|
||||||
identifier.isLiteral('2.3').should.equal(true);
|
lexical.isLiteral('2.3').should.equal(true);
|
||||||
identifier.isLiteral('.3').should.equal(true);
|
lexical.isLiteral('.3').should.equal(true);
|
||||||
identifier.isLiteral('-3.').should.equal(true);
|
lexical.isLiteral('-3.').should.equal(true);
|
||||||
identifier.isLiteral('23').should.equal(true);
|
lexical.isLiteral('23').should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should test range literal", function() {
|
it("should test range literal", function() {
|
||||||
identifier.isRange("(12..32)").should.equal(true);
|
lexical.isRange("(12..32)").should.equal(true);
|
||||||
identifier.isRange("(12..foo)").should.equal(true);
|
lexical.isRange("(12..foo)").should.equal(true);
|
||||||
identifier.isRange("(foo.bar..foo)").should.equal(true);
|
lexical.isRange("(foo.bar..foo)").should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should test string literal', function() {
|
it('should test string literal', function() {
|
||||||
identifier.isLiteral('""').should.equal(true);
|
lexical.isLiteral('""').should.equal(true);
|
||||||
identifier.isLiteral('"a\'b"').should.equal(true);
|
lexical.isLiteral('"a\'b"').should.equal(true);
|
||||||
identifier.isLiteral("''").should.equal(true);
|
lexical.isLiteral("''").should.equal(true);
|
||||||
identifier.isLiteral("'a bcd'").should.equal(true);
|
lexical.isLiteral("'a bcd'").should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should test variable", function() {
|
it("should test variable", function() {
|
||||||
identifier.isVariable("foo").should.equal(true);
|
lexical.isVariable("foo").should.equal(true);
|
||||||
identifier.isVariable("foo.bar.foo").should.equal(true);
|
lexical.isVariable("foo.bar.foo").should.equal(true);
|
||||||
identifier.isVariable("foo[0].b").should.equal(true);
|
lexical.isVariable("foo[0].b").should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should test none literal', function() {
|
it('should test none literal', function() {
|
||||||
identifier.isLiteral('2a').should.equal(false);
|
lexical.isLiteral('2a').should.equal(false);
|
||||||
identifier.isLiteral('"x').should.equal(false);
|
lexical.isLiteral('"x').should.equal(false);
|
||||||
identifier.isLiteral('a2').should.equal(false);
|
lexical.isLiteral('a2').should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should test none variable', function() {
|
it('should test none variable', function() {
|
||||||
identifier.isVariable("a.").should.equal(false);
|
lexical.isVariable("a.").should.equal(false);
|
||||||
identifier.isVariable(".b").should.equal(false);
|
lexical.isVariable(".b").should.equal(false);
|
||||||
identifier.isVariable(".").should.equal(false);
|
lexical.isVariable(".").should.equal(false);
|
||||||
identifier.isVariable("0a").should.equal(false);
|
lexical.isVariable("0a").should.equal(false);
|
||||||
identifier.isVariable("[0][12].bar[0]").should.equal(false);
|
lexical.isVariable("[0][12].bar[0]").should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse boolean literal', function() {
|
it('should parse boolean literal', function() {
|
||||||
identifier.parseLiteral('true').should.equal(true);
|
lexical.parseLiteral('true').should.equal(true);
|
||||||
identifier.parseLiteral('TrUE').should.equal(true);
|
lexical.parseLiteral('TrUE').should.equal(true);
|
||||||
identifier.parseLiteral('false').should.equal(false);
|
lexical.parseLiteral('false').should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse number literal', function() {
|
it('should parse number literal', function() {
|
||||||
identifier.parseLiteral('2.3').should.equal(2.3);
|
lexical.parseLiteral('2.3').should.equal(2.3);
|
||||||
identifier.parseLiteral('.32').should.equal(0.32);
|
lexical.parseLiteral('.32').should.equal(0.32);
|
||||||
identifier.parseLiteral('-23.').should.equal(-23);
|
lexical.parseLiteral('-23.').should.equal(-23);
|
||||||
identifier.parseLiteral('23').should.equal(23);
|
lexical.parseLiteral('23').should.equal(23);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse string literal', function() {
|
it('should parse string literal', function() {
|
||||||
identifier.parseLiteral('"ab\'c"').should.equal("ab\'c");
|
lexical.parseLiteral('"ab\'c"').should.equal("ab\'c");
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+20
-8
@@ -8,14 +8,14 @@ chai.use(sinonChai);
|
|||||||
var tag = require('../tag.js')();
|
var tag = require('../tag.js')();
|
||||||
var Scope = require('../scope.js');
|
var Scope = require('../scope.js');
|
||||||
var filter = require('../filter')();
|
var filter = require('../filter')();
|
||||||
var Render = require('../render.js')(filter, tag);
|
var Render = require('../render.js');
|
||||||
var render = Render.renderTemplates;
|
var Template = require('../template.js')(tag, filter);
|
||||||
var evalFilter = Render.evalFilter;
|
|
||||||
|
|
||||||
describe('render', function() {
|
describe('render', function() {
|
||||||
var scope;
|
var scope, render;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
render = Render(filter, tag);
|
||||||
scope = Scope.factory({
|
scope = Scope.factory({
|
||||||
foo: {
|
foo: {
|
||||||
bar: ['a', 2]
|
bar: ['a', 2]
|
||||||
@@ -25,8 +25,18 @@ describe('render', function() {
|
|||||||
tag.clear();
|
tag.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should stringify object', function() {
|
||||||
|
expect(Render.stringify({
|
||||||
|
foo: 'bar'
|
||||||
|
})).to.equal('{"foo":"bar"}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stringify object', function() {
|
||||||
|
expect(Render.stringify([1, 2, 3])).to.equal('[1,2,3]');
|
||||||
|
});
|
||||||
|
|
||||||
it('should render html', function() {
|
it('should render html', function() {
|
||||||
expect(render([{
|
expect(render.renderTemplates([{
|
||||||
type: 'html',
|
type: 'html',
|
||||||
value: '<p>'
|
value: '<p>'
|
||||||
}], scope)).to.equal('<p>');
|
}], scope)).to.equal('<p>');
|
||||||
@@ -37,14 +47,16 @@ describe('render', function() {
|
|||||||
var time = sinon.spy();
|
var time = sinon.spy();
|
||||||
filter.register('date', date);
|
filter.register('date', date);
|
||||||
filter.register('time', time);
|
filter.register('time', time);
|
||||||
evalFilter('foo.bar[0] | date: "b" | time:2', scope);
|
var tpl = Template.parseOutput('foo.bar[0] | date: "b" | time:2');
|
||||||
|
render.evalOutput(tpl, scope);
|
||||||
expect(date).to.have.been.calledWith('a', 'b');
|
expect(date).to.have.been.calledWith('a', 'b');
|
||||||
expect(time).to.have.been.calledWith('y', 2);
|
expect(time).to.have.been.calledWith('y', 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should eval filter', function() {
|
it('should eval output', function() {
|
||||||
filter.register('date', (l, r) => l + r);
|
filter.register('date', (l, r) => l + r);
|
||||||
filter.register('time', (l, r) => l + 3 * r);
|
filter.register('time', (l, r) => l + 3 * r);
|
||||||
expect(evalFilter('foo.bar[0] | date: "b" | time:2', scope)).to.equal('ab6');
|
var tpl = Template.parseOutput('foo.bar[0] | date: "b" | time:2');
|
||||||
|
expect(render.evalOutput(tpl, scope)).to.equal('ab6');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+9
-1
@@ -28,7 +28,9 @@ describe('tags', function() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
it('should support assign', function() {
|
it('should support assign', function() {
|
||||||
test('{% assign foo="bar"%}{{foo}}', 'bar');
|
test('{% assign foo="bar" %}{{foo}}', 'bar');
|
||||||
|
test('{% assign foo=(1..3) %}{{foo}}', '[1,2,3]');
|
||||||
|
test('{% assign foo="a b" | capitalize | split: " " | first %}{{foo}}', 'A');
|
||||||
});
|
});
|
||||||
it('should support case', function() {
|
it('should support case', function() {
|
||||||
testThrow('{% case "foo"%}', /{% case "foo"%} not closed/);
|
testThrow('{% case "foo"%}', /{% case "foo"%} not closed/);
|
||||||
@@ -146,6 +148,12 @@ describe('tags', function() {
|
|||||||
test(src, dst);
|
test(src, dst);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support empty tablerow', function() {
|
||||||
|
src = '{% tablerow i in (1..0) cols:2 %}{{ i }}{% endtablerow %}';
|
||||||
|
dst = '<table></table>';
|
||||||
|
test(src, dst);
|
||||||
|
});
|
||||||
|
|
||||||
it('should support tablerow with range', function() {
|
it('should support tablerow with range', function() {
|
||||||
src = '{% tablerow i in (1..5) cols:2 %}{{ i }}{% endtablerow %}';
|
src = '{% tablerow i in (1..5) cols:2 %}{{ i }}{% endtablerow %}';
|
||||||
dst = '<table>' +
|
dst = '<table>' +
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
const chai = require("chai");
|
||||||
|
const sinon = require("sinon");
|
||||||
|
const sinonChai = require("sinon-chai");
|
||||||
|
const should = chai.should();
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
chai.use(sinonChai);
|
||||||
|
|
||||||
|
var filter = require('../filter.js')();
|
||||||
|
var tag = require('../tag.js')();
|
||||||
|
var Template = require('../template.js');
|
||||||
|
|
||||||
|
describe('template', function() {
|
||||||
|
var scope, template, add = (l, r) => l + r;
|
||||||
|
|
||||||
|
beforeEach(function(){
|
||||||
|
filter.clear();
|
||||||
|
filter.register('add', add);
|
||||||
|
|
||||||
|
tag.clear();
|
||||||
|
template = Template(tag, filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output string', function() {
|
||||||
|
var tpl = template.parseOutput('foo');
|
||||||
|
expect(tpl.type).to.equal('output');
|
||||||
|
expect(tpl.initial).to.equal('foo');
|
||||||
|
expect(tpl.filters).to.deep.equal([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output string with a simple filter', function() {
|
||||||
|
var tpl = template.parseOutput('foo | add: 3, "foo"');
|
||||||
|
expect(tpl.initial).to.equal('foo');
|
||||||
|
expect(tpl.filters.length).to.equal(1);
|
||||||
|
expect(tpl.filters[0].filter).to.equal(add);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse output string with filters', function() {
|
||||||
|
var tpl = template.parseOutput('foo | add: "|" | add');
|
||||||
|
expect(tpl.initial).to.equal('foo');
|
||||||
|
expect(tpl.filters.length).to.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
+24
-16
@@ -12,6 +12,7 @@ function parse(html) {
|
|||||||
var _count = 0;
|
var _count = 0;
|
||||||
|
|
||||||
while ((result = syntax.exec(html)) !== null) {
|
while ((result = syntax.exec(html)) !== null) {
|
||||||
|
// passed html fragments
|
||||||
if (result.index > idx) {
|
if (result.index > idx) {
|
||||||
htmlFragment = html.slice(idx, result.index);
|
htmlFragment = html.slice(idx, result.index);
|
||||||
tokens.push({
|
tokens.push({
|
||||||
@@ -19,25 +20,29 @@ function parse(html) {
|
|||||||
value: htmlFragment
|
value: htmlFragment
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// tag appeared
|
||||||
if (result[1]) {
|
if (result[1]) {
|
||||||
token = factory('tag', 1, result);
|
token = factory('tag', 1, result);
|
||||||
|
|
||||||
var match = token.value.match(lexical.tagLine);
|
var match = token.value.match(lexical.tagLine);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new TokenizationError(`illegal tag: ${token.raw}`,
|
throw new TokenizationError(`illegal tag: ${token.raw}`,
|
||||||
token.input, token.line);
|
token.input, token.line);
|
||||||
}
|
}
|
||||||
|
|
||||||
token.name = match[1];
|
token.name = match[1];
|
||||||
token.args = match[2];
|
token.args = match[2];
|
||||||
|
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
} else {
|
}
|
||||||
|
// output
|
||||||
|
else {
|
||||||
token = factory('output', 3, result);
|
token = factory('output', 3, result);
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
}
|
}
|
||||||
idx = syntax.lastIndex;
|
idx = syntax.lastIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remaining html
|
||||||
if (html.length > idx) {
|
if (html.length > idx) {
|
||||||
htmlFragment = html.slice(idx, html.length);
|
htmlFragment = html.slice(idx, html.length);
|
||||||
tokens.push({
|
tokens.push({
|
||||||
@@ -48,25 +53,28 @@ function parse(html) {
|
|||||||
return tokens;
|
return tokens;
|
||||||
|
|
||||||
function factory(type, offset, match) {
|
function factory(type, offset, match) {
|
||||||
|
return {
|
||||||
var lines = match.input.slice(_idx + 1, match.index).split('\n');
|
|
||||||
var idx1 = match.input.lastIndexOf('\n', match.index);
|
|
||||||
var idx2 = match.input.indexOf('\n', match.index);
|
|
||||||
if(idx2 === -1) idx2 = match.input.length;
|
|
||||||
var input = match.input.slice(idx1 + 1, idx2);
|
|
||||||
_count += lines.length - 1;
|
|
||||||
_idx = match.index;
|
|
||||||
|
|
||||||
var token = {
|
|
||||||
type: type,
|
type: type,
|
||||||
raw: match[offset],
|
raw: match[offset],
|
||||||
value: match[offset + 1].trim(),
|
value: match[offset + 1].trim(),
|
||||||
line: _count + 1,
|
line: getLineNum(match),
|
||||||
input: input
|
input: getLineContent(match)
|
||||||
};
|
};
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLineContent(match) {
|
||||||
|
var idx1 = match.input.lastIndexOf('\n', match.index);
|
||||||
|
var idx2 = match.input.indexOf('\n', match.index);
|
||||||
|
if (idx2 === -1) idx2 = match.input.length;
|
||||||
|
return match.input.slice(idx1 + 1, idx2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLineNum(match) {
|
||||||
|
var lines = match.input.slice(_idx + 1, match.index).split('\n');
|
||||||
|
_count += lines.length - 1;
|
||||||
|
_idx = match.index;
|
||||||
|
return _count + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.parse = parse;
|
exports.parse = parse;
|
||||||
|
|||||||
Reference in New Issue
Block a user