This commit is contained in:
harttle
2017-08-16 00:07:47 +08:00
parent 11259fae62
commit 942a3224a1
3 changed files with 384 additions and 429 deletions
+101 -146
View File
@@ -175,7 +175,7 @@ function unescape(str) {
} }
function getFixed(v) { function getFixed(v) {
var p = (v + "").split("."); var p = (v + '').split('.');
return p.length > 1 ? p[1].length : 0; return p.length > 1 ? p[1].length : 0;
} }
@@ -184,7 +184,6 @@ function getMaxFixed(l, r) {
} }
function stringify(obj) { function stringify(obj) {
obj = obj || "";
return obj + ''; return obj + '';
} }
@@ -258,11 +257,6 @@ var _engine = {
return _this.parse(html); return _this.parse(html);
}).then(function (tpl) { }).then(function (tpl) {
return _this.render(tpl, ctx, opts); return _this.render(tpl, ctx, opts);
}).catch(function (e) {
if (e instanceof Errors.RenderBreakError) {
return e.html;
}
throw e;
}); });
}, },
renderFile: function renderFile(filepath, ctx, opts) { renderFile: function renderFile(filepath, ctx, opts) {
@@ -294,9 +288,7 @@ var _engine = {
return path; return path;
}); });
}).catch(function (e) { }).catch(function (e) {
if (e.code === 'ENOENT') { e.message = e.code + ': Failed to lookup ' + filepath + ' in: ' + root;
e.message = 'Failed to lookup ' + filepath + ' in: ' + root;
}
throw e; throw e;
}); });
}, },
@@ -639,6 +631,7 @@ function parseLiteral(str) {
if (res) { if (res) {
return str.slice(1, -1); return str.slice(1, -1);
} }
throw new TypeError('cannot parse \'' + str + '\' as literal');
} }
module.exports = { module.exports = {
@@ -907,7 +900,6 @@ module.exports = factory;
var _ = require('./util/underscore.js'); var _ = require('./util/underscore.js');
var lexical = require('./lexical.js'); var lexical = require('./lexical.js');
var assert = require('./util/assert.js'); var assert = require('./util/assert.js');
var toStr = Object.prototype.toString;
var Scope = { var Scope = {
getAll: function getAll() { getAll: function getAll() {
@@ -918,32 +910,16 @@ var Scope = {
return ctx; return ctx;
}, },
get: function get(str) { get: function get(str) {
for (var i = this.scopes.length - 1; i >= 0; i--) {
try { try {
return this.getPropertyByPath(this.scopes[i], str); return this.getPropertyByPath(this.scopes, str);
} catch (e) { } catch (e) {
if (/undefined variable/.test(e.message)) { if (!/undefined variable/.test(e.message) || this.opts.strict_variables) {
continue;
}
if (/Cannot read property/.test(e.message)) {
if (this.opts.strict_variables) {
e.message += ': ' + str;
throw e;
} else {
continue;
}
} else {
e.message += ': ' + str;
throw e; throw e;
} }
} }
}
if (this.opts.strict_variables) {
throw new TypeError('undefined variable: ' + str);
}
}, },
set: function set(k, v) { set: function set(k, v) {
this.setPropertyByPath(this.scopes[this.scopes.length - 1], k, v); setPropertyByPath(this.scopes[this.scopes.length - 1], k, v);
return this; return this;
}, },
push: function push(ctx) { push: function push(ctx) {
@@ -960,39 +936,20 @@ var Scope = {
shift: function shift() { shift: function shift() {
return this.scopes.shift(); return this.scopes.shift();
}, },
setPropertyByPath: function setPropertyByPath(obj, path, val) {
if (_.isString(path)) {
var paths = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
for (var i = 0; i < paths.length; i++) {
var key = paths[i];
if (i === paths.length - 1) {
return obj[key] = val;
}
if (undefined === obj[key]) obj[key] = {};
// case for readonly objects
obj = obj[key] || {};
}
}
},
getPropertyByPath: function getPropertyByPath(obj, path) { getPropertyByPath: function getPropertyByPath(scopes, path) {
var paths = this.propertyAccessSeq(path + ''); var paths = this.propertyAccessSeq(path + '');
var varName = paths.shift(); if (!paths.length) {
if (!obj.hasOwnProperty(varName)) { throw new TypeError('undefined variable: ' + path);
throw new TypeError('undefined variable');
} }
var variable = obj[varName]; var key = paths.shift();
var lastName = paths.pop(); var value = getValueFromScopes(key, scopes);
paths.forEach(function (p) { return paths.reduce(function (value, key) {
return variable = variable[p]; if (_.isNil(value)) {
}); throw new TypeError('undefined variable: ' + key);
if (undefined !== lastName) {
if (lastName === 'size' && (toStr.call(variable) === '[object Array]' || toStr.call(variable) === '[object String]')) {
return variable.length;
} }
variable = variable[lastName]; return getValueFromParent(key, value);
} }, value);
return variable;
}, },
/* /*
@@ -1055,6 +1012,33 @@ var Scope = {
} }
}; };
function setPropertyByPath(obj, path, val) {
var paths = (path + '').replace(/\[/g, '.').replace(/\]/g, '').split('.');
for (var i = 0; i < paths.length; i++) {
var key = paths[i];
if (i === paths.length - 1) {
return obj[key] = val;
}
if (undefined === obj[key]) obj[key] = {};
// case for readonly objects
obj = obj[key] || {};
}
}
function getValueFromParent(key, value) {
return key === 'size' && (_.isArray(value) || _.isString(value)) ? value.length : value[key];
}
function getValueFromScopes(key, scopes) {
for (var i = scopes.length - 1; i > -1; i--) {
var scope = scopes[i];
if (scope.hasOwnProperty(key)) {
return scope[key];
}
}
throw new TypeError('undefined variable: ' + key);
}
function matchRightBracket(str, begin) { function matchRightBracket(str, begin) {
var stack = 1; // count of '[' - count of ']' var stack = 1; // count of '[' - count of ']'
for (var i = begin; i < str.length; i++) { for (var i = begin; i < str.length; i++) {
@@ -1134,6 +1118,7 @@ function evalValue(str, scope) {
if (lexical.isVariable(str)) { if (lexical.isVariable(str)) {
return scope.get(str); return scope.get(str);
} }
throw new TypeError('cannot eval \'' + str + '\' as value');
} }
function isTruthy(val) { function isTruthy(val) {
@@ -1152,7 +1137,6 @@ module.exports = {
'use strict'; 'use strict';
var lexical = require('./lexical.js'); var lexical = require('./lexical.js');
var _ = require('./util/underscore.js');
var Promise = require('any-promise'); var Promise = require('any-promise');
var Syntax = require('./syntax.js'); var Syntax = require('./syntax.js');
var assert = require('./util/assert.js'); var assert = require('./util/assert.js');
@@ -1180,13 +1164,7 @@ module.exports = function () {
return Promise.resolve(''); return Promise.resolve('');
} }
return Promise.resolve().then(function () { return Promise.resolve().then(function () {
return typeof impl.render === 'function' ? impl.render(scope, obj) : ''; return impl.render(scope, obj);
}).catch(function (e) {
if (_.isError(e)) {
throw e;
}
var msg = 'Please reject with an Error in ' + impl.render + ', got ' + e;
throw new Error(msg);
}); });
}, },
parse: function parse(token, tokens) { parse: function parse(token, tokens) {
@@ -1224,7 +1202,7 @@ module.exports = function () {
}; };
}; };
},{"./lexical.js":8,"./syntax.js":13,"./util/assert.js":16,"./util/underscore.js":21,"any-promise":3}],15:[function(require,module,exports){ },{"./lexical.js":8,"./syntax.js":13,"./util/assert.js":16,"any-promise":3}],15:[function(require,module,exports){
'use strict'; 'use strict';
var lexical = require('./lexical.js'); var lexical = require('./lexical.js');
@@ -1331,9 +1309,6 @@ var AssertionError = require('./error.js').AssertionError;
function assert(predicate, message) { function assert(predicate, message) {
if (!predicate) { if (!predicate) {
if (message instanceof Error) {
throw message;
}
message = message || 'expect ' + predicate + ' to be true'; message = message || 'expect ' + predicate + ' to be true';
throw new AssertionError(message); throw new AssertionError(message);
} }
@@ -1346,11 +1321,15 @@ module.exports = assert;
var _ = require('./underscore.js'); var _ = require('./underscore.js');
function TokenizationError(message, token) { function initError() {
this.name = this.constructor.name;
if (Error.captureStackTrace) { if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor); Error.captureStackTrace(this, this.constructor);
} }
this.name = this.constructor.name; }
function initLiquidError(message, token) {
initError.call(this);
this.input = token.input; this.input = token.input;
this.line = token.line; this.line = token.line;
@@ -1358,7 +1337,11 @@ function TokenizationError(message, token) {
var context = mkContext(token.input, token.line); var context = mkContext(token.input, token.line);
this.message = mkMessage(message, token); this.message = mkMessage(message, token);
this.stack = context + '\n' + (this.stack || ''); this.stack = context + '\n' + (this.stack || this.message);
}
function TokenizationError(message, token) {
initLiquidError.call(this, message, token);
} }
TokenizationError.prototype = Object.create(Error.prototype); TokenizationError.prototype = Object.create(Error.prototype);
TokenizationError.prototype.constructor = TokenizationError; TokenizationError.prototype.constructor = TokenizationError;
@@ -1366,15 +1349,8 @@ TokenizationError.prototype.constructor = TokenizationError;
function ParseError(e, token) { function ParseError(e, token) {
_.assign(this, e); _.assign(this, e);
this.originalError = e; this.originalError = e;
this.name = this.constructor.name;
this.input = token.input; initLiquidError.call(this, e.message, token);
this.line = token.line;
this.file = token.file;
var context = mkContext(token.input, token.line);
this.message = mkMessage(e.message || 'Unkown Error', token);
this.stack = context + '\n' + (e.stack || '');
} }
ParseError.prototype = Object.create(Error.prototype); ParseError.prototype = Object.create(Error.prototype);
ParseError.prototype.constructor = ParseError; ParseError.prototype.constructor = ParseError;
@@ -1386,35 +1362,22 @@ function RenderError(e, tpl) {
} }
_.assign(this, e); _.assign(this, e);
this.originalError = e; this.originalError = e;
this.name = this.constructor.name;
this.input = tpl.token.input; initLiquidError.call(this, e.message, tpl.token);
this.line = tpl.token.line;
this.file = tpl.token.file;
var context = mkContext(tpl.token.input, tpl.token.line);
this.message = mkMessage(e.message || 'Unkown Error', tpl.token);
this.stack = context + '\n' + (e.stack || '');
} }
RenderError.prototype = Object.create(Error.prototype); RenderError.prototype = Object.create(Error.prototype);
RenderError.prototype.constructor = RenderError; RenderError.prototype.constructor = RenderError;
function RenderBreakError(message) { function RenderBreakError(message) {
if (Error.captureStackTrace) { initError.call(this);
Error.captureStackTrace(this, this.constructor); this.message = message + '';
}
this.name = this.constructor.name;
this.message = message || '';
} }
RenderBreakError.prototype = Object.create(Error.prototype); RenderBreakError.prototype = Object.create(Error.prototype);
RenderBreakError.prototype.constructor = RenderBreakError; RenderBreakError.prototype.constructor = RenderBreakError;
function AssertionError(message) { function AssertionError(message) {
if (Error.captureStackTrace) { initError.call(this);
Error.captureStackTrace(this, this.constructor); this.message = message + '';
}
this.name = this.constructor.name;
this.message = message;
} }
AssertionError.prototype = Object.create(Error.prototype); AssertionError.prototype = Object.create(Error.prototype);
AssertionError.prototype.constructor = AssertionError; AssertionError.prototype.constructor = AssertionError;
@@ -1688,6 +1651,7 @@ var formatCodes = {
}, },
z: function z(d) { z: function z(d) {
var tz = d.getTimezoneOffset() / 60 * 100; var tz = d.getTimezoneOffset() / 60 * 100;
console.log('tz', tz);
return (tz > 0 ? '-' : '+') + _number.pad(Math.abs(tz), 4); return (tz > 0 ? '-' : '+') + _number.pad(Math.abs(tz), 4);
}, },
'%': function _() { '%': function _() {
@@ -1728,6 +1692,8 @@ module.exports = strftime;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var toStr = Object.prototype.toString;
/* /*
* Checks if value is classified as a String primitive or object. * Checks if value is classified as a String primitive or object.
* @param {any} value The value to check. * @param {any} value The value to check.
@@ -1737,6 +1703,15 @@ function isString(value) {
return value instanceof String || typeof value === 'string'; return value instanceof String || typeof value === 'string';
} }
function isNil(value) {
return value === null || value === undefined;
}
function isArray(value) {
// be compatible with IE 8
return toStr.call(value) === '[object Array]';
}
function isError(value) { function isError(value) {
var signature = Object.prototype.toString.call(value); var signature = Object.prototype.toString.call(value);
// [object XXXError] // [object XXXError]
@@ -1782,7 +1757,6 @@ function assign(object) {
} }
function _assignBinary(dst, src) { function _assignBinary(dst, src) {
if (!dst) return dst;
forOwn(src, function (v, k) { forOwn(src, function (v, k) {
dst[k] = v; dst[k] = v;
}); });
@@ -1844,6 +1818,8 @@ function range(start, stop, step) {
exports.isString = isString; exports.isString = isString;
exports.isObject = isObject; exports.isObject = isObject;
exports.isArray = isArray;
exports.isNil = isNil;
exports.isError = isError; exports.isError = isError;
exports.range = range; exports.range = range;
@@ -1923,7 +1899,6 @@ module.exports = function (liquid) {
'use strict'; 'use strict';
var Liquid = require('..'); var Liquid = require('..');
var assert = require('../src/util/assert.js');
module.exports = function (liquid) { module.exports = function (liquid) {
liquid.registerTag('case', { liquid.registerTag('case', {
@@ -1935,15 +1910,13 @@ module.exports = function (liquid) {
this.cases = []; this.cases = [];
this.elseTemplates = []; this.elseTemplates = [];
var p = [], var p = [];
stream = liquid.parser.parseStream(remainTokens).on('tag:when', function (token) { var stream = liquid.parser.parseStream(remainTokens).on('tag:when', function (token) {
if (!_this.cases[token.args]) {
_this.cases.push({ _this.cases.push({
val: token.args, val: token.args,
templates: p = [] templates: p = []
}); });
} }).on('tag:else', function () {
}).on('tag:else', function (token) {
return p = _this.elseTemplates; return p = _this.elseTemplates;
}).on('tag:endcase', function (token) { }).on('tag:endcase', function (token) {
return stream.stop(); return stream.stop();
@@ -1967,11 +1940,10 @@ module.exports = function (liquid) {
} }
return liquid.renderer.renderTemplates(this.elseTemplates, scope); return liquid.renderer.renderTemplates(this.elseTemplates, scope);
} }
}); });
}; };
},{"..":2,"../src/util/assert.js":16}],25:[function(require,module,exports){ },{"..":2}],25:[function(require,module,exports){
'use strict'; 'use strict';
module.exports = function (liquid) { module.exports = function (liquid) {
@@ -2180,20 +2152,18 @@ module.exports = function (liquid) {
this.branches = []; this.branches = [];
this.elseTemplates = []; this.elseTemplates = [];
var p, var p;
stream = liquid.parser.parseStream(remainTokens).on('start', function (x) { var stream = liquid.parser.parseStream(remainTokens).on('start', function () {
return _this.branches.push({ return _this.branches.push({
cond: tagToken.args, cond: tagToken.args,
templates: p = [] templates: p = []
}); });
}).on('tag:elsif', function (token) { }).on('tag:elsif', function (token) {
if (!_this.branches[token.args]) {
_this.branches.push({ _this.branches.push({
cond: token.args, cond: token.args,
templates: p = [] templates: p = []
}); });
} }).on('tag:else', function () {
}).on('tag:else', function (token) {
return p = _this.elseTemplates; return p = _this.elseTemplates;
}).on('tag:endif', function (token) { }).on('tag:endif', function (token) {
return stream.stop(); return stream.stop();
@@ -2216,7 +2186,6 @@ module.exports = function (liquid) {
} }
return liquid.renderer.renderTemplates(this.elseTemplates, scope); return liquid.renderer.renderTemplates(this.elseTemplates, scope);
} }
}); });
}; };
@@ -2420,7 +2389,7 @@ module.exports = function (liquid) {
'use strict'; 'use strict';
var Liquid = require('..'); var Liquid = require('..');
var Promise = require('any-promise'); var mapSeries = require('../src/util/promise.js').mapSeries;
var lexical = Liquid.lexical; var lexical = Liquid.lexical;
var assert = require('../src/util/assert.js'); var assert = require('../src/util/assert.js');
var re = new RegExp('^(' + lexical.identifier.source + ')\\s+in\\s+' + ('(' + lexical.value.source + ')') + ('(?:\\s+' + lexical.hash.source + ')*$')); var re = new RegExp('^(' + lexical.identifier.source + ')\\s+in\\s+' + ('(' + lexical.value.source + ')') + ('(?:\\s+' + lexical.hash.source + ')*$'));
@@ -2433,19 +2402,19 @@ module.exports = function (liquid) {
var match = re.exec(tagToken.args); var match = re.exec(tagToken.args);
assert(match, 'illegal tag: ' + tagToken.raw); assert(match, 'illegal tag: ' + tagToken.raw);
this.variable = match[1]; this.variable = match[1];
this.collection = match[2]; this.collection = match[2];
this.templates = []; this.templates = [];
var p, var p;
stream = liquid.parser.parseStream(remainTokens).on('start', function (x) { var stream = liquid.parser.parseStream(remainTokens).on('start', function () {
return p = _this.templates; return p = _this.templates;
}).on('tag:endtablerow', function (token) { }).on('tag:endtablerow', function (token) {
return stream.stop(); return stream.stop();
}).on('template', function (tpl) { }).on('template', function (tpl) {
return p.push(tpl); return p.push(tpl);
}).on('end', function (x) { }).on('end', function () {
throw new Error('tag ' + tagToken.raw + ' not closed'); throw new Error('tag ' + tagToken.raw + ' not closed');
}); });
@@ -2461,9 +2430,9 @@ 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, var row;
col; var col;
if (!cols) throw new Error('illegal cols: ' + cols); if (!cols) throw new Error('illegal cols: ' + cols);
// build array of arguments to pass to sequential promises... // build array of arguments to pass to sequential promises...
@@ -2472,17 +2441,12 @@ module.exports = function (liquid) {
collection.some(function (item, i) { collection.some(function (item, i) {
var ctx = {}; var ctx = {};
ctx[_this2.variable] = item; ctx[_this2.variable] = item;
// We are just putting together an array of the arguments we will be passing to our sequential promises
contexts.push(ctx); contexts.push(ctx);
}); });
// This executes an array of promises sequentially for every argument in the contexts array - http://webcache.googleusercontent.com/search?q=cache:rNbMUn9TPtkJ:joost.vunderink.net/blog/2014/12/15/processing-an-array-of-promises-sequentially-in-node-js/+&cd=5&hl=en&ct=clnk&gl=us return mapSeries(contexts, function (context, idx) {
// It's fundamentally equivalent to the following... row = Math.floor(idx / cols) + 1;
// emptyPromise.then(renderTemplates(args0).then(renderTemplates(args1).then(renderTemplates(args2)... col = idx % cols + 1;
var lastPromise = contexts.reduce(function (promise, context, currentIndex) {
return promise.then(function (partial) {
row = Math.floor(currentIndex / cols) + 1;
col = currentIndex % cols + 1;
if (col === 1) { if (col === 1) {
if (row !== 1) { if (row !== 1) {
html += '</tr>'; html += '</tr>';
@@ -2490,35 +2454,26 @@ module.exports = function (liquid) {
html += '<tr class="row' + row + '">'; html += '<tr class="row' + row + '">';
} }
//ctx[this.variable] = context; html += '<td class="col' + col + '">';
return html += '<td class="col' + col + '">';
}).then(function (partial) {
scope.push(context); scope.push(context);
return liquid.renderer.renderTemplates(_this2.templates, scope); return liquid.renderer.renderTemplates(_this2.templates, scope).then(function (partial) {
}).then(function (partial) {
scope.pop(context); scope.pop(context);
html += partial; html += partial;
return html += '</td>'; html += '</td>';
return html;
}); });
}, Promise.resolve('')); // start the reduce chain with a resolved Promise. After first run, the "promise" argument }).then(function () {
// in our reduce callback will be the returned promise from our "then" above. In this
// case, the promise returned from liquid.renderer.renderTemplates.
return lastPromise.then(function () {
if (row > 0) { if (row > 0) {
html += '</tr>'; html += '</tr>';
} }
html += '</table>'; html += '</table>';
return html; return html;
}).catch(function (error) {
throw error;
}); });
} }
}); });
}; };
},{"..":2,"../src/util/assert.js":16,"any-promise":3}],36:[function(require,module,exports){ },{"..":2,"../src/util/assert.js":16,"../src/util/promise.js":19}],36:[function(require,module,exports){
'use strict'; 'use strict';
var Liquid = require('..'); var Liquid = require('..');
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "liquidjs", "name": "liquidjs",
"version": "1.9.3", "version": "1.9.4",
"description": "A Liquid template engine for Node.js and browsers, with all shopify/liquid features.", "description": "A Liquid template engine for Node.js and browsers, with all shopify/liquid features.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {