mirror of
https://github.com/fooflington/selfdefined.git
synced 2025-04-21 08:59:07 +00:00
409 lines
8.8 KiB
JavaScript
409 lines
8.8 KiB
JavaScript
![]() |
'use strict';
|
||
|
|
||
|
var lib = require('./lib');
|
||
|
|
||
|
var arrayFrom = Array.from;
|
||
|
var supportsIterators = typeof Symbol === 'function' && Symbol.iterator && typeof arrayFrom === 'function'; // Frames keep track of scoping both at compile-time and run-time so
|
||
|
// we know how to access variables. Block tags can introduce special
|
||
|
// variables, for example.
|
||
|
|
||
|
var Frame =
|
||
|
/*#__PURE__*/
|
||
|
function () {
|
||
|
function Frame(parent, isolateWrites) {
|
||
|
this.variables = {};
|
||
|
this.parent = parent;
|
||
|
this.topLevel = false; // if this is true, writes (set) should never propagate upwards past
|
||
|
// this frame to its parent (though reads may).
|
||
|
|
||
|
this.isolateWrites = isolateWrites;
|
||
|
}
|
||
|
|
||
|
var _proto = Frame.prototype;
|
||
|
|
||
|
_proto.set = function set(name, val, resolveUp) {
|
||
|
// Allow variables with dots by automatically creating the
|
||
|
// nested structure
|
||
|
var parts = name.split('.');
|
||
|
var obj = this.variables;
|
||
|
var frame = this;
|
||
|
|
||
|
if (resolveUp) {
|
||
|
if (frame = this.resolve(parts[0], true)) {
|
||
|
frame.set(name, val);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < parts.length - 1; i++) {
|
||
|
var id = parts[i];
|
||
|
|
||
|
if (!obj[id]) {
|
||
|
obj[id] = {};
|
||
|
}
|
||
|
|
||
|
obj = obj[id];
|
||
|
}
|
||
|
|
||
|
obj[parts[parts.length - 1]] = val;
|
||
|
};
|
||
|
|
||
|
_proto.get = function get(name) {
|
||
|
var val = this.variables[name];
|
||
|
|
||
|
if (val !== undefined) {
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
};
|
||
|
|
||
|
_proto.lookup = function lookup(name) {
|
||
|
var p = this.parent;
|
||
|
var val = this.variables[name];
|
||
|
|
||
|
if (val !== undefined) {
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
return p && p.lookup(name);
|
||
|
};
|
||
|
|
||
|
_proto.resolve = function resolve(name, forWrite) {
|
||
|
var p = forWrite && this.isolateWrites ? undefined : this.parent;
|
||
|
var val = this.variables[name];
|
||
|
|
||
|
if (val !== undefined) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
return p && p.resolve(name);
|
||
|
};
|
||
|
|
||
|
_proto.push = function push(isolateWrites) {
|
||
|
return new Frame(this, isolateWrites);
|
||
|
};
|
||
|
|
||
|
_proto.pop = function pop() {
|
||
|
return this.parent;
|
||
|
};
|
||
|
|
||
|
return Frame;
|
||
|
}();
|
||
|
|
||
|
function makeMacro(argNames, kwargNames, func) {
|
||
|
var _this = this;
|
||
|
|
||
|
return function () {
|
||
|
for (var _len = arguments.length, macroArgs = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
|
macroArgs[_key] = arguments[_key];
|
||
|
}
|
||
|
|
||
|
var argCount = numArgs(macroArgs);
|
||
|
var args;
|
||
|
var kwargs = getKeywordArgs(macroArgs);
|
||
|
|
||
|
if (argCount > argNames.length) {
|
||
|
args = macroArgs.slice(0, argNames.length); // Positional arguments that should be passed in as
|
||
|
// keyword arguments (essentially default values)
|
||
|
|
||
|
macroArgs.slice(args.length, argCount).forEach(function (val, i) {
|
||
|
if (i < kwargNames.length) {
|
||
|
kwargs[kwargNames[i]] = val;
|
||
|
}
|
||
|
});
|
||
|
args.push(kwargs);
|
||
|
} else if (argCount < argNames.length) {
|
||
|
args = macroArgs.slice(0, argCount);
|
||
|
|
||
|
for (var i = argCount; i < argNames.length; i++) {
|
||
|
var arg = argNames[i]; // Keyword arguments that should be passed as
|
||
|
// positional arguments, i.e. the caller explicitly
|
||
|
// used the name of a positional arg
|
||
|
|
||
|
args.push(kwargs[arg]);
|
||
|
delete kwargs[arg];
|
||
|
}
|
||
|
|
||
|
args.push(kwargs);
|
||
|
} else {
|
||
|
args = macroArgs;
|
||
|
}
|
||
|
|
||
|
return func.apply(_this, args);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function makeKeywordArgs(obj) {
|
||
|
obj.__keywords = true;
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
function isKeywordArgs(obj) {
|
||
|
return obj && Object.prototype.hasOwnProperty.call(obj, '__keywords');
|
||
|
}
|
||
|
|
||
|
function getKeywordArgs(args) {
|
||
|
var len = args.length;
|
||
|
|
||
|
if (len) {
|
||
|
var lastArg = args[len - 1];
|
||
|
|
||
|
if (isKeywordArgs(lastArg)) {
|
||
|
return lastArg;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
function numArgs(args) {
|
||
|
var len = args.length;
|
||
|
|
||
|
if (len === 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
var lastArg = args[len - 1];
|
||
|
|
||
|
if (isKeywordArgs(lastArg)) {
|
||
|
return len - 1;
|
||
|
} else {
|
||
|
return len;
|
||
|
}
|
||
|
} // A SafeString object indicates that the string should not be
|
||
|
// autoescaped. This happens magically because autoescaping only
|
||
|
// occurs on primitive string objects.
|
||
|
|
||
|
|
||
|
function SafeString(val) {
|
||
|
if (typeof val !== 'string') {
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
this.val = val;
|
||
|
this.length = val.length;
|
||
|
}
|
||
|
|
||
|
SafeString.prototype = Object.create(String.prototype, {
|
||
|
length: {
|
||
|
writable: true,
|
||
|
configurable: true,
|
||
|
value: 0
|
||
|
}
|
||
|
});
|
||
|
|
||
|
SafeString.prototype.valueOf = function valueOf() {
|
||
|
return this.val;
|
||
|
};
|
||
|
|
||
|
SafeString.prototype.toString = function toString() {
|
||
|
return this.val;
|
||
|
};
|
||
|
|
||
|
function copySafeness(dest, target) {
|
||
|
if (dest instanceof SafeString) {
|
||
|
return new SafeString(target);
|
||
|
}
|
||
|
|
||
|
return target.toString();
|
||
|
}
|
||
|
|
||
|
function markSafe(val) {
|
||
|
var type = typeof val;
|
||
|
|
||
|
if (type === 'string') {
|
||
|
return new SafeString(val);
|
||
|
} else if (type !== 'function') {
|
||
|
return val;
|
||
|
} else {
|
||
|
return function wrapSafe(args) {
|
||
|
var ret = val.apply(this, arguments);
|
||
|
|
||
|
if (typeof ret === 'string') {
|
||
|
return new SafeString(ret);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function suppressValue(val, autoescape) {
|
||
|
val = val !== undefined && val !== null ? val : '';
|
||
|
|
||
|
if (autoescape && !(val instanceof SafeString)) {
|
||
|
val = lib.escape(val.toString());
|
||
|
}
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
function ensureDefined(val, lineno, colno) {
|
||
|
if (val === null || val === undefined) {
|
||
|
throw new lib.TemplateError('attempted to output null or undefined value', lineno + 1, colno + 1);
|
||
|
}
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
function memberLookup(obj, val) {
|
||
|
if (obj === undefined || obj === null) {
|
||
|
return undefined;
|
||
|
}
|
||
|
|
||
|
if (typeof obj[val] === 'function') {
|
||
|
return function () {
|
||
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||
|
args[_key2] = arguments[_key2];
|
||
|
}
|
||
|
|
||
|
return obj[val].apply(obj, args);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return obj[val];
|
||
|
}
|
||
|
|
||
|
function callWrap(obj, name, context, args) {
|
||
|
if (!obj) {
|
||
|
throw new Error('Unable to call `' + name + '`, which is undefined or falsey');
|
||
|
} else if (typeof obj !== 'function') {
|
||
|
throw new Error('Unable to call `' + name + '`, which is not a function');
|
||
|
}
|
||
|
|
||
|
return obj.apply(context, args);
|
||
|
}
|
||
|
|
||
|
function contextOrFrameLookup(context, frame, name) {
|
||
|
var val = frame.lookup(name);
|
||
|
return val !== undefined ? val : context.lookup(name);
|
||
|
}
|
||
|
|
||
|
function handleError(error, lineno, colno) {
|
||
|
if (error.lineno) {
|
||
|
return error;
|
||
|
} else {
|
||
|
return new lib.TemplateError(error, lineno, colno);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function asyncEach(arr, dimen, iter, cb) {
|
||
|
if (lib.isArray(arr)) {
|
||
|
var len = arr.length;
|
||
|
lib.asyncIter(arr, function iterCallback(item, i, next) {
|
||
|
switch (dimen) {
|
||
|
case 1:
|
||
|
iter(item, i, len, next);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
iter(item[0], item[1], i, len, next);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
iter(item[0], item[1], item[2], i, len, next);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
item.push(i, len, next);
|
||
|
iter.apply(this, item);
|
||
|
}
|
||
|
}, cb);
|
||
|
} else {
|
||
|
lib.asyncFor(arr, function iterCallback(key, val, i, len, next) {
|
||
|
iter(key, val, i, len, next);
|
||
|
}, cb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function asyncAll(arr, dimen, func, cb) {
|
||
|
var finished = 0;
|
||
|
var len;
|
||
|
var outputArr;
|
||
|
|
||
|
function done(i, output) {
|
||
|
finished++;
|
||
|
outputArr[i] = output;
|
||
|
|
||
|
if (finished === len) {
|
||
|
cb(null, outputArr.join(''));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lib.isArray(arr)) {
|
||
|
len = arr.length;
|
||
|
outputArr = new Array(len);
|
||
|
|
||
|
if (len === 0) {
|
||
|
cb(null, '');
|
||
|
} else {
|
||
|
for (var i = 0; i < arr.length; i++) {
|
||
|
var item = arr[i];
|
||
|
|
||
|
switch (dimen) {
|
||
|
case 1:
|
||
|
func(item, i, len, done);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
func(item[0], item[1], i, len, done);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
func(item[0], item[1], item[2], i, len, done);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
item.push(i, len, done);
|
||
|
func.apply(this, item);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
var keys = lib.keys(arr || {});
|
||
|
len = keys.length;
|
||
|
outputArr = new Array(len);
|
||
|
|
||
|
if (len === 0) {
|
||
|
cb(null, '');
|
||
|
} else {
|
||
|
for (var _i = 0; _i < keys.length; _i++) {
|
||
|
var k = keys[_i];
|
||
|
func(k, arr[k], _i, len, done);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function fromIterator(arr) {
|
||
|
if (typeof arr !== 'object' || arr === null || lib.isArray(arr)) {
|
||
|
return arr;
|
||
|
} else if (supportsIterators && Symbol.iterator in arr) {
|
||
|
return arrayFrom(arr);
|
||
|
} else {
|
||
|
return arr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
Frame: Frame,
|
||
|
makeMacro: makeMacro,
|
||
|
makeKeywordArgs: makeKeywordArgs,
|
||
|
numArgs: numArgs,
|
||
|
suppressValue: suppressValue,
|
||
|
ensureDefined: ensureDefined,
|
||
|
memberLookup: memberLookup,
|
||
|
contextOrFrameLookup: contextOrFrameLookup,
|
||
|
callWrap: callWrap,
|
||
|
handleError: handleError,
|
||
|
isArray: lib.isArray,
|
||
|
keys: lib.keys,
|
||
|
SafeString: SafeString,
|
||
|
copySafeness: copySafeness,
|
||
|
markSafe: markSafe,
|
||
|
asyncEach: asyncEach,
|
||
|
asyncAll: asyncAll,
|
||
|
inOperator: lib.inOperator,
|
||
|
fromIterator: fromIterator
|
||
|
};
|