1'use strict'; 2 3var lib = require('./lib'); 4var arrayFrom = Array.from; 5var supportsIterators = typeof Symbol === 'function' && Symbol.iterator && typeof arrayFrom === 'function'; 6 7// Frames keep track of scoping both at compile-time and run-time so 8// we know how to access variables. Block tags can introduce special 9// variables, for example. 10var Frame = /*#__PURE__*/function () { 11 function Frame(parent, isolateWrites) { 12 this.variables = Object.create(null); 13 this.parent = parent; 14 this.topLevel = false; 15 // if this is true, writes (set) should never propagate upwards past 16 // this frame to its parent (though reads may). 17 this.isolateWrites = isolateWrites; 18 } 19 var _proto = Frame.prototype; 20 _proto.set = function set(name, val, resolveUp) { 21 // Allow variables with dots by automatically creating the 22 // nested structure 23 var parts = name.split('.'); 24 var obj = this.variables; 25 var frame = this; 26 if (resolveUp) { 27 if (frame = this.resolve(parts[0], true)) { 28 frame.set(name, val); 29 return; 30 } 31 } 32 for (var i = 0; i < parts.length - 1; i++) { 33 var id = parts[i]; 34 if (!obj[id]) { 35 obj[id] = {}; 36 } 37 obj = obj[id]; 38 } 39 obj[parts[parts.length - 1]] = val; 40 }; 41 _proto.get = function get(name) { 42 var val = this.variables[name]; 43 if (val !== undefined) { 44 return val; 45 } 46 return null; 47 }; 48 _proto.lookup = function lookup(name) { 49 var p = this.parent; 50 var val = this.variables[name]; 51 if (val !== undefined) { 52 return val; 53 } 54 return p && p.lookup(name); 55 }; 56 _proto.resolve = function resolve(name, forWrite) { 57 var p = forWrite && this.isolateWrites ? undefined : this.parent; 58 var val = this.variables[name]; 59 if (val !== undefined) { 60 return this; 61 } 62 return p && p.resolve(name); 63 }; 64 _proto.push = function push(isolateWrites) { 65 return new Frame(this, isolateWrites); 66 }; 67 _proto.pop = function pop() { 68 return this.parent; 69 }; 70 return Frame; 71}(); 72function makeMacro(argNames, kwargNames, func) { 73 return function macro() { 74 for (var _len = arguments.length, macroArgs = new Array(_len), _key = 0; _key < _len; _key++) { 75 macroArgs[_key] = arguments[_key]; 76 } 77 var argCount = numArgs(macroArgs); 78 var args; 79 var kwargs = getKeywordArgs(macroArgs); 80 if (argCount > argNames.length) { 81 args = macroArgs.slice(0, argNames.length); 82 83 // Positional arguments that should be passed in as 84 // keyword arguments (essentially default values) 85 macroArgs.slice(args.length, argCount).forEach(function (val, i) { 86 if (i < kwargNames.length) { 87 kwargs[kwargNames[i]] = val; 88 } 89 }); 90 args.push(kwargs); 91 } else if (argCount < argNames.length) { 92 args = macroArgs.slice(0, argCount); 93 for (var i = argCount; i < argNames.length; i++) { 94 var arg = argNames[i]; 95 96 // Keyword arguments that should be passed as 97 // positional arguments, i.e. the caller explicitly 98 // used the name of a positional arg 99 args.push(kwargs[arg]); 100 delete kwargs[arg]; 101 } 102 args.push(kwargs); 103 } else { 104 args = macroArgs; 105 } 106 return func.apply(this, args); 107 }; 108} 109function makeKeywordArgs(obj) { 110 obj.__keywords = true; 111 return obj; 112} 113function isKeywordArgs(obj) { 114 return obj && Object.prototype.hasOwnProperty.call(obj, '__keywords'); 115} 116function getKeywordArgs(args) { 117 var len = args.length; 118 if (len) { 119 var lastArg = args[len - 1]; 120 if (isKeywordArgs(lastArg)) { 121 return lastArg; 122 } 123 } 124 return {}; 125} 126function numArgs(args) { 127 var len = args.length; 128 if (len === 0) { 129 return 0; 130 } 131 var lastArg = args[len - 1]; 132 if (isKeywordArgs(lastArg)) { 133 return len - 1; 134 } else { 135 return len; 136 } 137} 138 139// A SafeString object indicates that the string should not be 140// autoescaped. This happens magically because autoescaping only 141// occurs on primitive string objects. 142function SafeString(val) { 143 if (typeof val !== 'string') { 144 return val; 145 } 146 this.val = val; 147 this.length = val.length; 148} 149SafeString.prototype = Object.create(String.prototype, { 150 length: { 151 writable: true, 152 configurable: true, 153 value: 0 154 } 155}); 156SafeString.prototype.valueOf = function valueOf() { 157 return this.val; 158}; 159SafeString.prototype.toString = function toString() { 160 return this.val; 161}; 162function copySafeness(dest, target) { 163 if (dest instanceof SafeString) { 164 return new SafeString(target); 165 } 166 return target.toString(); 167} 168function markSafe(val) { 169 var type = typeof val; 170 if (type === 'string') { 171 return new SafeString(val); 172 } else if (type !== 'function') { 173 return val; 174 } else { 175 return function wrapSafe(args) { 176 var ret = val.apply(this, arguments); 177 if (typeof ret === 'string') { 178 return new SafeString(ret); 179 } 180 return ret; 181 }; 182 } 183} 184function suppressValue(val, autoescape) { 185 val = val !== undefined && val !== null ? val : ''; 186 if (autoescape && !(val instanceof SafeString)) { 187 val = lib.escape(val.toString()); 188 } 189 return val; 190} 191function ensureDefined(val, lineno, colno) { 192 if (val === null || val === undefined) { 193 throw new lib.TemplateError('attempted to output null or undefined value', lineno + 1, colno + 1); 194 } 195 return val; 196} 197function memberLookup(obj, val) { 198 if (obj === undefined || obj === null) { 199 return undefined; 200 } 201 if (typeof obj[val] === 'function') { 202 return function () { 203 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 204 args[_key2] = arguments[_key2]; 205 } 206 return obj[val].apply(obj, args); 207 }; 208 } 209 return obj[val]; 210} 211function callWrap(obj, name, context, args) { 212 if (!obj) { 213 throw new Error('Unable to call `' + name + '`, which is undefined or falsey'); 214 } else if (typeof obj !== 'function') { 215 throw new Error('Unable to call `' + name + '`, which is not a function'); 216 } 217 return obj.apply(context, args); 218} 219function contextOrFrameLookup(context, frame, name) { 220 var val = frame.lookup(name); 221 return val !== undefined ? val : context.lookup(name); 222} 223function handleError(error, lineno, colno) { 224 if (error.lineno) { 225 return error; 226 } else { 227 return new lib.TemplateError(error, lineno, colno); 228 } 229} 230function asyncEach(arr, dimen, iter, cb) { 231 if (lib.isArray(arr)) { 232 var len = arr.length; 233 lib.asyncIter(arr, function iterCallback(item, i, next) { 234 switch (dimen) { 235 case 1: 236 iter(item, i, len, next); 237 break; 238 case 2: 239 iter(item[0], item[1], i, len, next); 240 break; 241 case 3: 242 iter(item[0], item[1], item[2], i, len, next); 243 break; 244 default: 245 item.push(i, len, next); 246 iter.apply(this, item); 247 } 248 }, cb); 249 } else { 250 lib.asyncFor(arr, function iterCallback(key, val, i, len, next) { 251 iter(key, val, i, len, next); 252 }, cb); 253 } 254} 255function asyncAll(arr, dimen, func, cb) { 256 var finished = 0; 257 var len; 258 var outputArr; 259 function done(i, output) { 260 finished++; 261 outputArr[i] = output; 262 if (finished === len) { 263 cb(null, outputArr.join('')); 264 } 265 } 266 if (lib.isArray(arr)) { 267 len = arr.length; 268 outputArr = new Array(len); 269 if (len === 0) { 270 cb(null, ''); 271 } else { 272 for (var i = 0; i < arr.length; i++) { 273 var item = arr[i]; 274 switch (dimen) { 275 case 1: 276 func(item, i, len, done); 277 break; 278 case 2: 279 func(item[0], item[1], i, len, done); 280 break; 281 case 3: 282 func(item[0], item[1], item[2], i, len, done); 283 break; 284 default: 285 item.push(i, len, done); 286 func.apply(this, item); 287 } 288 } 289 } 290 } else { 291 var keys = lib.keys(arr || {}); 292 len = keys.length; 293 outputArr = new Array(len); 294 if (len === 0) { 295 cb(null, ''); 296 } else { 297 for (var _i = 0; _i < keys.length; _i++) { 298 var k = keys[_i]; 299 func(k, arr[k], _i, len, done); 300 } 301 } 302 } 303} 304function fromIterator(arr) { 305 if (typeof arr !== 'object' || arr === null || lib.isArray(arr)) { 306 return arr; 307 } else if (supportsIterators && Symbol.iterator in arr) { 308 return arrayFrom(arr); 309 } else { 310 return arr; 311 } 312} 313module.exports = { 314 Frame: Frame, 315 makeMacro: makeMacro, 316 makeKeywordArgs: makeKeywordArgs, 317 numArgs: numArgs, 318 suppressValue: suppressValue, 319 ensureDefined: ensureDefined, 320 memberLookup: memberLookup, 321 contextOrFrameLookup: contextOrFrameLookup, 322 callWrap: callWrap, 323 handleError: handleError, 324 isArray: lib.isArray, 325 keys: lib.keys, 326 SafeString: SafeString, 327 copySafeness: copySafeness, 328 markSafe: markSafe, 329 asyncEach: asyncEach, 330 asyncAll: asyncAll, 331 inOperator: lib.inOperator, 332 fromIterator: fromIterator 333};