ejs.js (43184B)
1 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ejs = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){ 2 /* 3 * EJS Embedded JavaScript templates 4 * Copyright 2112 Matthew Eernisse (mde@fleegix.org) 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 'use strict'; 21 22 /** 23 * @file Embedded JavaScript templating engine. {@link http://ejs.co} 24 * @author Matthew Eernisse <mde@fleegix.org> 25 * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com> 26 * @project EJS 27 * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0} 28 */ 29 30 /** 31 * EJS internal functions. 32 * 33 * Technically this "module" lies in the same file as {@link module:ejs}, for 34 * the sake of organization all the private functions re grouped into this 35 * module. 36 * 37 * @module ejs-internal 38 * @private 39 */ 40 41 /** 42 * Embedded JavaScript templating engine. 43 * 44 * @module ejs 45 * @public 46 */ 47 48 var fs = require('fs'); 49 var path = require('path'); 50 var utils = require('./utils'); 51 52 var scopeOptionWarned = false; 53 var _VERSION_STRING = require('../package.json').version; 54 var _DEFAULT_OPEN_DELIMITER = '<'; 55 var _DEFAULT_CLOSE_DELIMITER = '>'; 56 var _DEFAULT_DELIMITER = '%'; 57 var _DEFAULT_LOCALS_NAME = 'locals'; 58 var _NAME = 'ejs'; 59 var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)'; 60 var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compileDebug', 61 'client', '_with', 'rmWhitespace', 'strict', 'filename', 'async']; 62 // We don't allow 'cache' option to be passed in the data obj for 63 // the normal `render` call, but this is where Express 2 & 3 put it 64 // so we make an exception for `renderFile` 65 var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache'); 66 var _BOM = /^\uFEFF/; 67 68 /** 69 * EJS template function cache. This can be a LRU object from lru-cache NPM 70 * module. By default, it is {@link module:utils.cache}, a simple in-process 71 * cache that grows continuously. 72 * 73 * @type {Cache} 74 */ 75 76 exports.cache = utils.cache; 77 78 /** 79 * Custom file loader. Useful for template preprocessing or restricting access 80 * to a certain part of the filesystem. 81 * 82 * @type {fileLoader} 83 */ 84 85 exports.fileLoader = fs.readFileSync; 86 87 /** 88 * Name of the object containing the locals. 89 * 90 * This variable is overridden by {@link Options}`.localsName` if it is not 91 * `undefined`. 92 * 93 * @type {String} 94 * @public 95 */ 96 97 exports.localsName = _DEFAULT_LOCALS_NAME; 98 99 /** 100 * Promise implementation -- defaults to the native implementation if available 101 * This is mostly just for testability 102 * 103 * @type {Function} 104 * @public 105 */ 106 107 exports.promiseImpl = (new Function('return this;'))().Promise; 108 109 /** 110 * Get the path to the included file from the parent file path and the 111 * specified path. 112 * 113 * @param {String} name specified path 114 * @param {String} filename parent file path 115 * @param {Boolean} isDir parent file path whether is directory 116 * @return {String} 117 */ 118 exports.resolveInclude = function(name, filename, isDir) { 119 var dirname = path.dirname; 120 var extname = path.extname; 121 var resolve = path.resolve; 122 var includePath = resolve(isDir ? filename : dirname(filename), name); 123 var ext = extname(name); 124 if (!ext) { 125 includePath += '.ejs'; 126 } 127 return includePath; 128 }; 129 130 /** 131 * Get the path to the included file by Options 132 * 133 * @param {String} path specified path 134 * @param {Options} options compilation options 135 * @return {String} 136 */ 137 function getIncludePath(path, options) { 138 var includePath; 139 var filePath; 140 var views = options.views; 141 var match = /^[A-Za-z]+:\\|^\//.exec(path); 142 143 // Abs path 144 if (match && match.length) { 145 includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true); 146 } 147 // Relative paths 148 else { 149 // Look relative to a passed filename first 150 if (options.filename) { 151 filePath = exports.resolveInclude(path, options.filename); 152 if (fs.existsSync(filePath)) { 153 includePath = filePath; 154 } 155 } 156 // Then look in any views directories 157 if (!includePath) { 158 if (Array.isArray(views) && views.some(function (v) { 159 filePath = exports.resolveInclude(path, v, true); 160 return fs.existsSync(filePath); 161 })) { 162 includePath = filePath; 163 } 164 } 165 if (!includePath) { 166 throw new Error('Could not find the include file "' + 167 options.escapeFunction(path) + '"'); 168 } 169 } 170 return includePath; 171 } 172 173 /** 174 * Get the template from a string or a file, either compiled on-the-fly or 175 * read from cache (if enabled), and cache the template if needed. 176 * 177 * If `template` is not set, the file specified in `options.filename` will be 178 * read. 179 * 180 * If `options.cache` is true, this function reads the file from 181 * `options.filename` so it must be set prior to calling this function. 182 * 183 * @memberof module:ejs-internal 184 * @param {Options} options compilation options 185 * @param {String} [template] template source 186 * @return {(TemplateFunction|ClientFunction)} 187 * Depending on the value of `options.client`, either type might be returned. 188 * @static 189 */ 190 191 function handleCache(options, template) { 192 var func; 193 var filename = options.filename; 194 var hasTemplate = arguments.length > 1; 195 196 if (options.cache) { 197 if (!filename) { 198 throw new Error('cache option requires a filename'); 199 } 200 func = exports.cache.get(filename); 201 if (func) { 202 return func; 203 } 204 if (!hasTemplate) { 205 template = fileLoader(filename).toString().replace(_BOM, ''); 206 } 207 } 208 else if (!hasTemplate) { 209 // istanbul ignore if: should not happen at all 210 if (!filename) { 211 throw new Error('Internal EJS error: no file name or template ' 212 + 'provided'); 213 } 214 template = fileLoader(filename).toString().replace(_BOM, ''); 215 } 216 func = exports.compile(template, options); 217 if (options.cache) { 218 exports.cache.set(filename, func); 219 } 220 return func; 221 } 222 223 /** 224 * Try calling handleCache with the given options and data and call the 225 * callback with the result. If an error occurs, call the callback with 226 * the error. Used by renderFile(). 227 * 228 * @memberof module:ejs-internal 229 * @param {Options} options compilation options 230 * @param {Object} data template data 231 * @param {RenderFileCallback} cb callback 232 * @static 233 */ 234 235 function tryHandleCache(options, data, cb) { 236 var result; 237 if (!cb) { 238 if (typeof exports.promiseImpl == 'function') { 239 return new exports.promiseImpl(function (resolve, reject) { 240 try { 241 result = handleCache(options)(data); 242 resolve(result); 243 } 244 catch (err) { 245 reject(err); 246 } 247 }); 248 } 249 else { 250 throw new Error('Please provide a callback function'); 251 } 252 } 253 else { 254 try { 255 result = handleCache(options)(data); 256 } 257 catch (err) { 258 return cb(err); 259 } 260 261 cb(null, result); 262 } 263 } 264 265 /** 266 * fileLoader is independent 267 * 268 * @param {String} filePath ejs file path. 269 * @return {String} The contents of the specified file. 270 * @static 271 */ 272 273 function fileLoader(filePath){ 274 return exports.fileLoader(filePath); 275 } 276 277 /** 278 * Get the template function. 279 * 280 * If `options.cache` is `true`, then the template is cached. 281 * 282 * @memberof module:ejs-internal 283 * @param {String} path path for the specified file 284 * @param {Options} options compilation options 285 * @return {(TemplateFunction|ClientFunction)} 286 * Depending on the value of `options.client`, either type might be returned 287 * @static 288 */ 289 290 function includeFile(path, options) { 291 var opts = utils.shallowCopy({}, options); 292 opts.filename = getIncludePath(path, opts); 293 return handleCache(opts); 294 } 295 296 /** 297 * Re-throw the given `err` in context to the `str` of ejs, `filename`, and 298 * `lineno`. 299 * 300 * @implements RethrowCallback 301 * @memberof module:ejs-internal 302 * @param {Error} err Error object 303 * @param {String} str EJS source 304 * @param {String} filename file name of the EJS file 305 * @param {String} lineno line number of the error 306 * @static 307 */ 308 309 function rethrow(err, str, flnm, lineno, esc){ 310 var lines = str.split('\n'); 311 var start = Math.max(lineno - 3, 0); 312 var end = Math.min(lines.length, lineno + 3); 313 var filename = esc(flnm); // eslint-disable-line 314 // Error context 315 var context = lines.slice(start, end).map(function (line, i){ 316 var curr = i + start + 1; 317 return (curr == lineno ? ' >> ' : ' ') 318 + curr 319 + '| ' 320 + line; 321 }).join('\n'); 322 323 // Alter exception message 324 err.path = filename; 325 err.message = (filename || 'ejs') + ':' 326 + lineno + '\n' 327 + context + '\n\n' 328 + err.message; 329 330 throw err; 331 } 332 333 function stripSemi(str){ 334 return str.replace(/;(\s*$)/, '$1'); 335 } 336 337 /** 338 * Compile the given `str` of ejs into a template function. 339 * 340 * @param {String} template EJS template 341 * 342 * @param {Options} opts compilation options 343 * 344 * @return {(TemplateFunction|ClientFunction)} 345 * Depending on the value of `opts.client`, either type might be returned. 346 * Note that the return type of the function also depends on the value of `opts.async`. 347 * @public 348 */ 349 350 exports.compile = function compile(template, opts) { 351 var templ; 352 353 // v1 compat 354 // 'scope' is 'context' 355 // FIXME: Remove this in a future version 356 if (opts && opts.scope) { 357 if (!scopeOptionWarned){ 358 console.warn('`scope` option is deprecated and will be removed in EJS 3'); 359 scopeOptionWarned = true; 360 } 361 if (!opts.context) { 362 opts.context = opts.scope; 363 } 364 delete opts.scope; 365 } 366 templ = new Template(template, opts); 367 return templ.compile(); 368 }; 369 370 /** 371 * Render the given `template` of ejs. 372 * 373 * If you would like to include options but not data, you need to explicitly 374 * call this function with `data` being an empty object or `null`. 375 * 376 * @param {String} template EJS template 377 * @param {Object} [data={}] template data 378 * @param {Options} [opts={}] compilation and rendering options 379 * @return {(String|Promise<String>)} 380 * Return value type depends on `opts.async`. 381 * @public 382 */ 383 384 exports.render = function (template, d, o) { 385 var data = d || {}; 386 var opts = o || {}; 387 388 // No options object -- if there are optiony names 389 // in the data, copy them to options 390 if (arguments.length == 2) { 391 utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA); 392 } 393 394 return handleCache(opts, template)(data); 395 }; 396 397 /** 398 * Render an EJS file at the given `path` and callback `cb(err, str)`. 399 * 400 * If you would like to include options but not data, you need to explicitly 401 * call this function with `data` being an empty object or `null`. 402 * 403 * @param {String} path path to the EJS file 404 * @param {Object} [data={}] template data 405 * @param {Options} [opts={}] compilation and rendering options 406 * @param {RenderFileCallback} cb callback 407 * @public 408 */ 409 410 exports.renderFile = function () { 411 var args = Array.prototype.slice.call(arguments); 412 var filename = args.shift(); 413 var cb; 414 var opts = {filename: filename}; 415 var data; 416 var viewOpts; 417 418 // Do we have a callback? 419 if (typeof arguments[arguments.length - 1] == 'function') { 420 cb = args.pop(); 421 } 422 // Do we have data/opts? 423 if (args.length) { 424 // Should always have data obj 425 data = args.shift(); 426 // Normal passed opts (data obj + opts obj) 427 if (args.length) { 428 // Use shallowCopy so we don't pollute passed in opts obj with new vals 429 utils.shallowCopy(opts, args.pop()); 430 } 431 // Special casing for Express (settings + opts-in-data) 432 else { 433 // Express 3 and 4 434 if (data.settings) { 435 // Pull a few things from known locations 436 if (data.settings.views) { 437 opts.views = data.settings.views; 438 } 439 if (data.settings['view cache']) { 440 opts.cache = true; 441 } 442 // Undocumented after Express 2, but still usable, esp. for 443 // items that are unsafe to be passed along with data, like `root` 444 viewOpts = data.settings['view options']; 445 if (viewOpts) { 446 utils.shallowCopy(opts, viewOpts); 447 } 448 } 449 // Express 2 and lower, values set in app.locals, or people who just 450 // want to pass options in their data. NOTE: These values will override 451 // anything previously set in settings or settings['view options'] 452 utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS); 453 } 454 opts.filename = filename; 455 } 456 else { 457 data = {}; 458 } 459 460 return tryHandleCache(opts, data, cb); 461 }; 462 463 /** 464 * Clear intermediate JavaScript cache. Calls {@link Cache#reset}. 465 * @public 466 */ 467 468 /** 469 * EJS template class 470 * @public 471 */ 472 exports.Template = Template; 473 474 exports.clearCache = function () { 475 exports.cache.reset(); 476 }; 477 478 function Template(text, opts) { 479 opts = opts || {}; 480 var options = {}; 481 this.templateText = text; 482 this.mode = null; 483 this.truncate = false; 484 this.currentLine = 1; 485 this.source = ''; 486 this.dependencies = []; 487 options.client = opts.client || false; 488 options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML; 489 options.compileDebug = opts.compileDebug !== false; 490 options.debug = !!opts.debug; 491 options.filename = opts.filename; 492 options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER; 493 options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER; 494 options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER; 495 options.strict = opts.strict || false; 496 options.context = opts.context; 497 options.cache = opts.cache || false; 498 options.rmWhitespace = opts.rmWhitespace; 499 options.root = opts.root; 500 options.outputFunctionName = opts.outputFunctionName; 501 options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME; 502 options.views = opts.views; 503 options.async = opts.async; 504 options.destructuredLocals = opts.destructuredLocals; 505 options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true; 506 507 if (options.strict) { 508 options._with = false; 509 } 510 else { 511 options._with = typeof opts._with != 'undefined' ? opts._with : true; 512 } 513 514 this.opts = options; 515 516 this.regex = this.createRegex(); 517 } 518 519 Template.modes = { 520 EVAL: 'eval', 521 ESCAPED: 'escaped', 522 RAW: 'raw', 523 COMMENT: 'comment', 524 LITERAL: 'literal' 525 }; 526 527 Template.prototype = { 528 createRegex: function () { 529 var str = _REGEX_STRING; 530 var delim = utils.escapeRegExpChars(this.opts.delimiter); 531 var open = utils.escapeRegExpChars(this.opts.openDelimiter); 532 var close = utils.escapeRegExpChars(this.opts.closeDelimiter); 533 str = str.replace(/%/g, delim) 534 .replace(/</g, open) 535 .replace(/>/g, close); 536 return new RegExp(str); 537 }, 538 539 compile: function () { 540 var src; 541 var fn; 542 var opts = this.opts; 543 var prepended = ''; 544 var appended = ''; 545 var escapeFn = opts.escapeFunction; 546 var ctor; 547 548 if (!this.source) { 549 this.generateSource(); 550 prepended += 551 ' var __output = "";\n' + 552 ' function __append(s) { if (s !== undefined && s !== null) __output += s }\n'; 553 if (opts.outputFunctionName) { 554 prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n'; 555 } 556 if (opts.destructuredLocals && opts.destructuredLocals.length) { 557 var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n'; 558 for (var i = 0; i < opts.destructuredLocals.length; i++) { 559 var name = opts.destructuredLocals[i]; 560 if (i > 0) { 561 destructuring += ',\n '; 562 } 563 destructuring += name + ' = __locals.' + name; 564 } 565 prepended += destructuring + ';\n'; 566 } 567 if (opts._with !== false) { 568 prepended += ' with (' + opts.localsName + ' || {}) {' + '\n'; 569 appended += ' }' + '\n'; 570 } 571 appended += ' return __output;' + '\n'; 572 this.source = prepended + this.source + appended; 573 } 574 575 if (opts.compileDebug) { 576 src = 'var __line = 1' + '\n' 577 + ' , __lines = ' + JSON.stringify(this.templateText) + '\n' 578 + ' , __filename = ' + (opts.filename ? 579 JSON.stringify(opts.filename) : 'undefined') + ';' + '\n' 580 + 'try {' + '\n' 581 + this.source 582 + '} catch (e) {' + '\n' 583 + ' rethrow(e, __lines, __filename, __line, escapeFn);' + '\n' 584 + '}' + '\n'; 585 } 586 else { 587 src = this.source; 588 } 589 590 if (opts.client) { 591 src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src; 592 if (opts.compileDebug) { 593 src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src; 594 } 595 } 596 597 if (opts.strict) { 598 src = '"use strict";\n' + src; 599 } 600 if (opts.debug) { 601 console.log(src); 602 } 603 if (opts.compileDebug && opts.filename) { 604 src = src + '\n' 605 + '//# sourceURL=' + opts.filename + '\n'; 606 } 607 608 try { 609 if (opts.async) { 610 // Have to use generated function for this, since in envs without support, 611 // it breaks in parsing 612 try { 613 ctor = (new Function('return (async function(){}).constructor;'))(); 614 } 615 catch(e) { 616 if (e instanceof SyntaxError) { 617 throw new Error('This environment does not support async/await'); 618 } 619 else { 620 throw e; 621 } 622 } 623 } 624 else { 625 ctor = Function; 626 } 627 fn = new ctor(opts.localsName + ', escapeFn, include, rethrow', src); 628 } 629 catch(e) { 630 // istanbul ignore else 631 if (e instanceof SyntaxError) { 632 if (opts.filename) { 633 e.message += ' in ' + opts.filename; 634 } 635 e.message += ' while compiling ejs\n\n'; 636 e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n'; 637 e.message += 'https://github.com/RyanZim/EJS-Lint'; 638 if (!opts.async) { 639 e.message += '\n'; 640 e.message += 'Or, if you meant to create an async function, pass `async: true` as an option.'; 641 } 642 } 643 throw e; 644 } 645 646 // Return a callable function which will execute the function 647 // created by the source-code, with the passed data as locals 648 // Adds a local `include` function which allows full recursive include 649 var returnedFn = opts.client ? fn : function anonymous(data) { 650 var include = function (path, includeData) { 651 var d = utils.shallowCopy({}, data); 652 if (includeData) { 653 d = utils.shallowCopy(d, includeData); 654 } 655 return includeFile(path, opts)(d); 656 }; 657 return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]); 658 }; 659 returnedFn.dependencies = this.dependencies; 660 if (opts.filename && typeof Object.defineProperty === 'function') { 661 var filename = opts.filename; 662 var basename = path.basename(filename, path.extname(filename)); 663 try { 664 Object.defineProperty(returnedFn, 'name', { 665 value: basename, 666 writable: false, 667 enumerable: false, 668 configurable: true 669 }); 670 } catch (e) {/* ignore */} 671 } 672 return returnedFn; 673 }, 674 675 generateSource: function () { 676 var opts = this.opts; 677 678 if (opts.rmWhitespace) { 679 // Have to use two separate replace here as `^` and `$` operators don't 680 // work well with `\r` and empty lines don't work well with the `m` flag. 681 this.templateText = 682 this.templateText.replace(/[\r\n]+/g, '\n').replace(/^\s+|\s+$/gm, ''); 683 } 684 685 // Slurp spaces and tabs before <%_ and after _%> 686 this.templateText = 687 this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>'); 688 689 var self = this; 690 var matches = this.parseTemplateText(); 691 var d = this.opts.delimiter; 692 var o = this.opts.openDelimiter; 693 var c = this.opts.closeDelimiter; 694 695 if (matches && matches.length) { 696 matches.forEach(function (line, index) { 697 var closing; 698 // If this is an opening tag, check for closing tags 699 // FIXME: May end up with some false positives here 700 // Better to store modes as k/v with openDelimiter + delimiter as key 701 // Then this can simply check against the map 702 if ( line.indexOf(o + d) === 0 // If it is a tag 703 && line.indexOf(o + d + d) !== 0) { // and is not escaped 704 closing = matches[index + 2]; 705 if (!(closing == d + c || closing == '-' + d + c || closing == '_' + d + c)) { 706 throw new Error('Could not find matching close tag for "' + line + '".'); 707 } 708 } 709 self.scanLine(line); 710 }); 711 } 712 713 }, 714 715 parseTemplateText: function () { 716 var str = this.templateText; 717 var pat = this.regex; 718 var result = pat.exec(str); 719 var arr = []; 720 var firstPos; 721 722 while (result) { 723 firstPos = result.index; 724 725 if (firstPos !== 0) { 726 arr.push(str.substring(0, firstPos)); 727 str = str.slice(firstPos); 728 } 729 730 arr.push(result[0]); 731 str = str.slice(result[0].length); 732 result = pat.exec(str); 733 } 734 735 if (str) { 736 arr.push(str); 737 } 738 739 return arr; 740 }, 741 742 _addOutput: function (line) { 743 if (this.truncate) { 744 // Only replace single leading linebreak in the line after 745 // -%> tag -- this is the single, trailing linebreak 746 // after the tag that the truncation mode replaces 747 // Handle Win / Unix / old Mac linebreaks -- do the \r\n 748 // combo first in the regex-or 749 line = line.replace(/^(?:\r\n|\r|\n)/, ''); 750 this.truncate = false; 751 } 752 if (!line) { 753 return line; 754 } 755 756 // Preserve literal slashes 757 line = line.replace(/\\/g, '\\\\'); 758 759 // Convert linebreaks 760 line = line.replace(/\n/g, '\\n'); 761 line = line.replace(/\r/g, '\\r'); 762 763 // Escape double-quotes 764 // - this will be the delimiter during execution 765 line = line.replace(/"/g, '\\"'); 766 this.source += ' ; __append("' + line + '")' + '\n'; 767 }, 768 769 scanLine: function (line) { 770 var self = this; 771 var d = this.opts.delimiter; 772 var o = this.opts.openDelimiter; 773 var c = this.opts.closeDelimiter; 774 var newLineCount = 0; 775 776 newLineCount = (line.split('\n').length - 1); 777 778 switch (line) { 779 case o + d: 780 case o + d + '_': 781 this.mode = Template.modes.EVAL; 782 break; 783 case o + d + '=': 784 this.mode = Template.modes.ESCAPED; 785 break; 786 case o + d + '-': 787 this.mode = Template.modes.RAW; 788 break; 789 case o + d + '#': 790 this.mode = Template.modes.COMMENT; 791 break; 792 case o + d + d: 793 this.mode = Template.modes.LITERAL; 794 this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")' + '\n'; 795 break; 796 case d + d + c: 797 this.mode = Template.modes.LITERAL; 798 this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")' + '\n'; 799 break; 800 case d + c: 801 case '-' + d + c: 802 case '_' + d + c: 803 if (this.mode == Template.modes.LITERAL) { 804 this._addOutput(line); 805 } 806 807 this.mode = null; 808 this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0; 809 break; 810 default: 811 // In script mode, depends on type of tag 812 if (this.mode) { 813 // If '//' is found without a line break, add a line break. 814 switch (this.mode) { 815 case Template.modes.EVAL: 816 case Template.modes.ESCAPED: 817 case Template.modes.RAW: 818 if (line.lastIndexOf('//') > line.lastIndexOf('\n')) { 819 line += '\n'; 820 } 821 } 822 switch (this.mode) { 823 // Just executing code 824 case Template.modes.EVAL: 825 this.source += ' ; ' + line + '\n'; 826 break; 827 // Exec, esc, and output 828 case Template.modes.ESCAPED: 829 this.source += ' ; __append(escapeFn(' + stripSemi(line) + '))' + '\n'; 830 break; 831 // Exec and output 832 case Template.modes.RAW: 833 this.source += ' ; __append(' + stripSemi(line) + ')' + '\n'; 834 break; 835 case Template.modes.COMMENT: 836 // Do nothing 837 break; 838 // Literal <%% mode, append as raw output 839 case Template.modes.LITERAL: 840 this._addOutput(line); 841 break; 842 } 843 } 844 // In string mode, just add the output 845 else { 846 this._addOutput(line); 847 } 848 } 849 850 if (self.opts.compileDebug && newLineCount) { 851 this.currentLine += newLineCount; 852 this.source += ' ; __line = ' + this.currentLine + '\n'; 853 } 854 } 855 }; 856 857 /** 858 * Escape characters reserved in XML. 859 * 860 * This is simply an export of {@link module:utils.escapeXML}. 861 * 862 * If `markup` is `undefined` or `null`, the empty string is returned. 863 * 864 * @param {String} markup Input string 865 * @return {String} Escaped string 866 * @public 867 * @func 868 * */ 869 exports.escapeXML = utils.escapeXML; 870 871 /** 872 * Express.js support. 873 * 874 * This is an alias for {@link module:ejs.renderFile}, in order to support 875 * Express.js out-of-the-box. 876 * 877 * @func 878 */ 879 880 exports.__express = exports.renderFile; 881 882 /** 883 * Version of EJS. 884 * 885 * @readonly 886 * @type {String} 887 * @public 888 */ 889 890 exports.VERSION = _VERSION_STRING; 891 892 /** 893 * Name for detection of EJS. 894 * 895 * @readonly 896 * @type {String} 897 * @public 898 */ 899 900 exports.name = _NAME; 901 902 /* istanbul ignore if */ 903 if (typeof window != 'undefined') { 904 window.ejs = exports; 905 } 906 907 },{"../package.json":6,"./utils":2,"fs":3,"path":4}],2:[function(require,module,exports){ 908 /* 909 * EJS Embedded JavaScript templates 910 * Copyright 2112 Matthew Eernisse (mde@fleegix.org) 911 * 912 * Licensed under the Apache License, Version 2.0 (the "License"); 913 * you may not use this file except in compliance with the License. 914 * You may obtain a copy of the License at 915 * 916 * http://www.apache.org/licenses/LICENSE-2.0 917 * 918 * Unless required by applicable law or agreed to in writing, software 919 * distributed under the License is distributed on an "AS IS" BASIS, 920 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 921 * See the License for the specific language governing permissions and 922 * limitations under the License. 923 * 924 */ 925 926 /** 927 * Private utility functions 928 * @module utils 929 * @private 930 */ 931 932 'use strict'; 933 934 var regExpChars = /[|\\{}()[\]^$+*?.]/g; 935 936 /** 937 * Escape characters reserved in regular expressions. 938 * 939 * If `string` is `undefined` or `null`, the empty string is returned. 940 * 941 * @param {String} string Input string 942 * @return {String} Escaped string 943 * @static 944 * @private 945 */ 946 exports.escapeRegExpChars = function (string) { 947 // istanbul ignore if 948 if (!string) { 949 return ''; 950 } 951 return String(string).replace(regExpChars, '\\$&'); 952 }; 953 954 var _ENCODE_HTML_RULES = { 955 '&': '&', 956 '<': '<', 957 '>': '>', 958 '"': '"', 959 "'": ''' 960 }; 961 var _MATCH_HTML = /[&<>'"]/g; 962 963 function encode_char(c) { 964 return _ENCODE_HTML_RULES[c] || c; 965 } 966 967 /** 968 * Stringified version of constants used by {@link module:utils.escapeXML}. 969 * 970 * It is used in the process of generating {@link ClientFunction}s. 971 * 972 * @readonly 973 * @type {String} 974 */ 975 976 var escapeFuncStr = 977 'var _ENCODE_HTML_RULES = {\n' 978 + ' "&": "&"\n' 979 + ' , "<": "<"\n' 980 + ' , ">": ">"\n' 981 + ' , \'"\': """\n' 982 + ' , "\'": "'"\n' 983 + ' }\n' 984 + ' , _MATCH_HTML = /[&<>\'"]/g;\n' 985 + 'function encode_char(c) {\n' 986 + ' return _ENCODE_HTML_RULES[c] || c;\n' 987 + '};\n'; 988 989 /** 990 * Escape characters reserved in XML. 991 * 992 * If `markup` is `undefined` or `null`, the empty string is returned. 993 * 994 * @implements {EscapeCallback} 995 * @param {String} markup Input string 996 * @return {String} Escaped string 997 * @static 998 * @private 999 */ 1000 1001 exports.escapeXML = function (markup) { 1002 return markup == undefined 1003 ? '' 1004 : String(markup) 1005 .replace(_MATCH_HTML, encode_char); 1006 }; 1007 exports.escapeXML.toString = function () { 1008 return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr; 1009 }; 1010 1011 /** 1012 * Naive copy of properties from one object to another. 1013 * Does not recurse into non-scalar properties 1014 * Does not check to see if the property has a value before copying 1015 * 1016 * @param {Object} to Destination object 1017 * @param {Object} from Source object 1018 * @return {Object} Destination object 1019 * @static 1020 * @private 1021 */ 1022 exports.shallowCopy = function (to, from) { 1023 from = from || {}; 1024 for (var p in from) { 1025 to[p] = from[p]; 1026 } 1027 return to; 1028 }; 1029 1030 /** 1031 * Naive copy of a list of key names, from one object to another. 1032 * Only copies property if it is actually defined 1033 * Does not recurse into non-scalar properties 1034 * 1035 * @param {Object} to Destination object 1036 * @param {Object} from Source object 1037 * @param {Array} list List of properties to copy 1038 * @return {Object} Destination object 1039 * @static 1040 * @private 1041 */ 1042 exports.shallowCopyFromList = function (to, from, list) { 1043 for (var i = 0; i < list.length; i++) { 1044 var p = list[i]; 1045 if (typeof from[p] != 'undefined') { 1046 to[p] = from[p]; 1047 } 1048 } 1049 return to; 1050 }; 1051 1052 /** 1053 * Simple in-process cache implementation. Does not implement limits of any 1054 * sort. 1055 * 1056 * @implements Cache 1057 * @static 1058 * @private 1059 */ 1060 exports.cache = { 1061 _data: {}, 1062 set: function (key, val) { 1063 this._data[key] = val; 1064 }, 1065 get: function (key) { 1066 return this._data[key]; 1067 }, 1068 remove: function (key) { 1069 delete this._data[key]; 1070 }, 1071 reset: function () { 1072 this._data = {}; 1073 } 1074 }; 1075 1076 },{}],3:[function(require,module,exports){ 1077 1078 },{}],4:[function(require,module,exports){ 1079 (function (process){ 1080 // Copyright Joyent, Inc. and other Node contributors. 1081 // 1082 // Permission is hereby granted, free of charge, to any person obtaining a 1083 // copy of this software and associated documentation files (the 1084 // "Software"), to deal in the Software without restriction, including 1085 // without limitation the rights to use, copy, modify, merge, publish, 1086 // distribute, sublicense, and/or sell copies of the Software, and to permit 1087 // persons to whom the Software is furnished to do so, subject to the 1088 // following conditions: 1089 // 1090 // The above copyright notice and this permission notice shall be included 1091 // in all copies or substantial portions of the Software. 1092 // 1093 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1094 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1095 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 1096 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 1097 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 1098 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 1099 // USE OR OTHER DEALINGS IN THE SOFTWARE. 1100 1101 // resolves . and .. elements in a path array with directory names there 1102 // must be no slashes, empty elements, or device names (c:\) in the array 1103 // (so also no leading and trailing slashes - it does not distinguish 1104 // relative and absolute paths) 1105 function normalizeArray(parts, allowAboveRoot) { 1106 // if the path tries to go above the root, `up` ends up > 0 1107 var up = 0; 1108 for (var i = parts.length - 1; i >= 0; i--) { 1109 var last = parts[i]; 1110 if (last === '.') { 1111 parts.splice(i, 1); 1112 } else if (last === '..') { 1113 parts.splice(i, 1); 1114 up++; 1115 } else if (up) { 1116 parts.splice(i, 1); 1117 up--; 1118 } 1119 } 1120 1121 // if the path is allowed to go above the root, restore leading ..s 1122 if (allowAboveRoot) { 1123 for (; up--; up) { 1124 parts.unshift('..'); 1125 } 1126 } 1127 1128 return parts; 1129 } 1130 1131 // Split a filename into [root, dir, basename, ext], unix version 1132 // 'root' is just a slash, or nothing. 1133 var splitPathRe = 1134 /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; 1135 var splitPath = function(filename) { 1136 return splitPathRe.exec(filename).slice(1); 1137 }; 1138 1139 // path.resolve([from ...], to) 1140 // posix version 1141 exports.resolve = function() { 1142 var resolvedPath = '', 1143 resolvedAbsolute = false; 1144 1145 for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { 1146 var path = (i >= 0) ? arguments[i] : process.cwd(); 1147 1148 // Skip empty and invalid entries 1149 if (typeof path !== 'string') { 1150 throw new TypeError('Arguments to path.resolve must be strings'); 1151 } else if (!path) { 1152 continue; 1153 } 1154 1155 resolvedPath = path + '/' + resolvedPath; 1156 resolvedAbsolute = path.charAt(0) === '/'; 1157 } 1158 1159 // At this point the path should be resolved to a full absolute path, but 1160 // handle relative paths to be safe (might happen when process.cwd() fails) 1161 1162 // Normalize the path 1163 resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { 1164 return !!p; 1165 }), !resolvedAbsolute).join('/'); 1166 1167 return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; 1168 }; 1169 1170 // path.normalize(path) 1171 // posix version 1172 exports.normalize = function(path) { 1173 var isAbsolute = exports.isAbsolute(path), 1174 trailingSlash = substr(path, -1) === '/'; 1175 1176 // Normalize the path 1177 path = normalizeArray(filter(path.split('/'), function(p) { 1178 return !!p; 1179 }), !isAbsolute).join('/'); 1180 1181 if (!path && !isAbsolute) { 1182 path = '.'; 1183 } 1184 if (path && trailingSlash) { 1185 path += '/'; 1186 } 1187 1188 return (isAbsolute ? '/' : '') + path; 1189 }; 1190 1191 // posix version 1192 exports.isAbsolute = function(path) { 1193 return path.charAt(0) === '/'; 1194 }; 1195 1196 // posix version 1197 exports.join = function() { 1198 var paths = Array.prototype.slice.call(arguments, 0); 1199 return exports.normalize(filter(paths, function(p, index) { 1200 if (typeof p !== 'string') { 1201 throw new TypeError('Arguments to path.join must be strings'); 1202 } 1203 return p; 1204 }).join('/')); 1205 }; 1206 1207 1208 // path.relative(from, to) 1209 // posix version 1210 exports.relative = function(from, to) { 1211 from = exports.resolve(from).substr(1); 1212 to = exports.resolve(to).substr(1); 1213 1214 function trim(arr) { 1215 var start = 0; 1216 for (; start < arr.length; start++) { 1217 if (arr[start] !== '') break; 1218 } 1219 1220 var end = arr.length - 1; 1221 for (; end >= 0; end--) { 1222 if (arr[end] !== '') break; 1223 } 1224 1225 if (start > end) return []; 1226 return arr.slice(start, end - start + 1); 1227 } 1228 1229 var fromParts = trim(from.split('/')); 1230 var toParts = trim(to.split('/')); 1231 1232 var length = Math.min(fromParts.length, toParts.length); 1233 var samePartsLength = length; 1234 for (var i = 0; i < length; i++) { 1235 if (fromParts[i] !== toParts[i]) { 1236 samePartsLength = i; 1237 break; 1238 } 1239 } 1240 1241 var outputParts = []; 1242 for (var i = samePartsLength; i < fromParts.length; i++) { 1243 outputParts.push('..'); 1244 } 1245 1246 outputParts = outputParts.concat(toParts.slice(samePartsLength)); 1247 1248 return outputParts.join('/'); 1249 }; 1250 1251 exports.sep = '/'; 1252 exports.delimiter = ':'; 1253 1254 exports.dirname = function(path) { 1255 var result = splitPath(path), 1256 root = result[0], 1257 dir = result[1]; 1258 1259 if (!root && !dir) { 1260 // No dirname whatsoever 1261 return '.'; 1262 } 1263 1264 if (dir) { 1265 // It has a dirname, strip trailing slash 1266 dir = dir.substr(0, dir.length - 1); 1267 } 1268 1269 return root + dir; 1270 }; 1271 1272 1273 exports.basename = function(path, ext) { 1274 var f = splitPath(path)[2]; 1275 // TODO: make this comparison case-insensitive on windows? 1276 if (ext && f.substr(-1 * ext.length) === ext) { 1277 f = f.substr(0, f.length - ext.length); 1278 } 1279 return f; 1280 }; 1281 1282 1283 exports.extname = function(path) { 1284 return splitPath(path)[3]; 1285 }; 1286 1287 function filter (xs, f) { 1288 if (xs.filter) return xs.filter(f); 1289 var res = []; 1290 for (var i = 0; i < xs.length; i++) { 1291 if (f(xs[i], i, xs)) res.push(xs[i]); 1292 } 1293 return res; 1294 } 1295 1296 // String.prototype.substr - negative index don't work in IE8 1297 var substr = 'ab'.substr(-1) === 'b' 1298 ? function (str, start, len) { return str.substr(start, len) } 1299 : function (str, start, len) { 1300 if (start < 0) start = str.length + start; 1301 return str.substr(start, len); 1302 } 1303 ; 1304 1305 }).call(this,require('_process')) 1306 },{"_process":5}],5:[function(require,module,exports){ 1307 // shim for using process in browser 1308 var process = module.exports = {}; 1309 1310 // cached from whatever global is present so that test runners that stub it 1311 // don't break things. But we need to wrap it in a try catch in case it is 1312 // wrapped in strict mode code which doesn't define any globals. It's inside a 1313 // function because try/catches deoptimize in certain engines. 1314 1315 var cachedSetTimeout; 1316 var cachedClearTimeout; 1317 1318 function defaultSetTimout() { 1319 throw new Error('setTimeout has not been defined'); 1320 } 1321 function defaultClearTimeout () { 1322 throw new Error('clearTimeout has not been defined'); 1323 } 1324 (function () { 1325 try { 1326 if (typeof setTimeout === 'function') { 1327 cachedSetTimeout = setTimeout; 1328 } else { 1329 cachedSetTimeout = defaultSetTimout; 1330 } 1331 } catch (e) { 1332 cachedSetTimeout = defaultSetTimout; 1333 } 1334 try { 1335 if (typeof clearTimeout === 'function') { 1336 cachedClearTimeout = clearTimeout; 1337 } else { 1338 cachedClearTimeout = defaultClearTimeout; 1339 } 1340 } catch (e) { 1341 cachedClearTimeout = defaultClearTimeout; 1342 } 1343 } ()) 1344 function runTimeout(fun) { 1345 if (cachedSetTimeout === setTimeout) { 1346 //normal enviroments in sane situations 1347 return setTimeout(fun, 0); 1348 } 1349 // if setTimeout wasn't available but was latter defined 1350 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { 1351 cachedSetTimeout = setTimeout; 1352 return setTimeout(fun, 0); 1353 } 1354 try { 1355 // when when somebody has screwed with setTimeout but no I.E. maddness 1356 return cachedSetTimeout(fun, 0); 1357 } catch(e){ 1358 try { 1359 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 1360 return cachedSetTimeout.call(null, fun, 0); 1361 } catch(e){ 1362 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error 1363 return cachedSetTimeout.call(this, fun, 0); 1364 } 1365 } 1366 1367 1368 } 1369 function runClearTimeout(marker) { 1370 if (cachedClearTimeout === clearTimeout) { 1371 //normal enviroments in sane situations 1372 return clearTimeout(marker); 1373 } 1374 // if clearTimeout wasn't available but was latter defined 1375 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { 1376 cachedClearTimeout = clearTimeout; 1377 return clearTimeout(marker); 1378 } 1379 try { 1380 // when when somebody has screwed with setTimeout but no I.E. maddness 1381 return cachedClearTimeout(marker); 1382 } catch (e){ 1383 try { 1384 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 1385 return cachedClearTimeout.call(null, marker); 1386 } catch (e){ 1387 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. 1388 // Some versions of I.E. have different rules for clearTimeout vs setTimeout 1389 return cachedClearTimeout.call(this, marker); 1390 } 1391 } 1392 1393 1394 1395 } 1396 var queue = []; 1397 var draining = false; 1398 var currentQueue; 1399 var queueIndex = -1; 1400 1401 function cleanUpNextTick() { 1402 if (!draining || !currentQueue) { 1403 return; 1404 } 1405 draining = false; 1406 if (currentQueue.length) { 1407 queue = currentQueue.concat(queue); 1408 } else { 1409 queueIndex = -1; 1410 } 1411 if (queue.length) { 1412 drainQueue(); 1413 } 1414 } 1415 1416 function drainQueue() { 1417 if (draining) { 1418 return; 1419 } 1420 var timeout = runTimeout(cleanUpNextTick); 1421 draining = true; 1422 1423 var len = queue.length; 1424 while(len) { 1425 currentQueue = queue; 1426 queue = []; 1427 while (++queueIndex < len) { 1428 if (currentQueue) { 1429 currentQueue[queueIndex].run(); 1430 } 1431 } 1432 queueIndex = -1; 1433 len = queue.length; 1434 } 1435 currentQueue = null; 1436 draining = false; 1437 runClearTimeout(timeout); 1438 } 1439 1440 process.nextTick = function (fun) { 1441 var args = new Array(arguments.length - 1); 1442 if (arguments.length > 1) { 1443 for (var i = 1; i < arguments.length; i++) { 1444 args[i - 1] = arguments[i]; 1445 } 1446 } 1447 queue.push(new Item(fun, args)); 1448 if (queue.length === 1 && !draining) { 1449 runTimeout(drainQueue); 1450 } 1451 }; 1452 1453 // v8 likes predictible objects 1454 function Item(fun, array) { 1455 this.fun = fun; 1456 this.array = array; 1457 } 1458 Item.prototype.run = function () { 1459 this.fun.apply(null, this.array); 1460 }; 1461 process.title = 'browser'; 1462 process.browser = true; 1463 process.env = {}; 1464 process.argv = []; 1465 process.version = ''; // empty string to avoid regexp issues 1466 process.versions = {}; 1467 1468 function noop() {} 1469 1470 process.on = noop; 1471 process.addListener = noop; 1472 process.once = noop; 1473 process.off = noop; 1474 process.removeListener = noop; 1475 process.removeAllListeners = noop; 1476 process.emit = noop; 1477 process.prependListener = noop; 1478 process.prependOnceListener = noop; 1479 1480 process.listeners = function (name) { return [] } 1481 1482 process.binding = function (name) { 1483 throw new Error('process.binding is not supported'); 1484 }; 1485 1486 process.cwd = function () { return '/' }; 1487 process.chdir = function (dir) { 1488 throw new Error('process.chdir is not supported'); 1489 }; 1490 process.umask = function() { return 0; }; 1491 1492 },{}],6:[function(require,module,exports){ 1493 module.exports={ 1494 "name": "ejs", 1495 "description": "Embedded JavaScript templates", 1496 "keywords": [ 1497 "template", 1498 "engine", 1499 "ejs" 1500 ], 1501 "version": "3.0.1", 1502 "author": "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)", 1503 "license": "Apache-2.0", 1504 "main": "./lib/ejs.js", 1505 "repository": { 1506 "type": "git", 1507 "url": "git://github.com/mde/ejs.git" 1508 }, 1509 "bugs": "https://github.com/mde/ejs/issues", 1510 "homepage": "https://github.com/mde/ejs", 1511 "dependencies": {}, 1512 "devDependencies": { 1513 "browserify": "^13.1.1", 1514 "eslint": "^4.14.0", 1515 "git-directory-deploy": "^1.5.1", 1516 "jake": "^10.3.1", 1517 "jsdoc": "^3.4.0", 1518 "lru-cache": "^4.0.1", 1519 "mocha": "^5.0.5", 1520 "uglify-js": "^3.3.16" 1521 }, 1522 "engines": { 1523 "node": ">=0.10.0" 1524 }, 1525 "scripts": { 1526 "test": "mocha", 1527 "postinstall": "node ./postinstall.js" 1528 } 1529 } 1530 1531 },{}]},{},[1])(1) 1532 });