123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- var defaults = require('./defaults.js');
- var underscore = require('./underscore.js');
- require('./templateSettings.js');
- // When customizing `_.templateSettings`, if you don't want to define an
- // interpolation, evaluation or escaping regex, we need one that is
- // guaranteed not to match.
- var noMatch = /(.)^/;
- // Certain characters need to be escaped so that they can be put into a
- // string literal.
- var escapes = {
- "'": "'",
- '\\': '\\',
- '\r': 'r',
- '\n': 'n',
- '\u2028': 'u2028',
- '\u2029': 'u2029'
- };
- var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
- function escapeChar(match) {
- return '\\' + escapes[match];
- }
- // In order to prevent third-party code injection through
- // `_.templateSettings.variable`, we test it against the following regular
- // expression. It is intentionally a bit more liberal than just matching valid
- // identifiers, but still prevents possible loopholes through defaults or
- // destructuring assignment.
- var bareIdentifier = /^\s*(\w|\$)+\s*$/;
- // JavaScript micro-templating, similar to John Resig's implementation.
- // Underscore templating handles arbitrary delimiters, preserves whitespace,
- // and correctly escapes quotes within interpolated code.
- // NB: `oldSettings` only exists for backwards compatibility.
- function template(text, settings, oldSettings) {
- if (!settings && oldSettings) settings = oldSettings;
- settings = defaults({}, settings, underscore.templateSettings);
- // Combine delimiters into one regular expression via alternation.
- var matcher = RegExp([
- (settings.escape || noMatch).source,
- (settings.interpolate || noMatch).source,
- (settings.evaluate || noMatch).source
- ].join('|') + '|$', 'g');
- // Compile the template source, escaping string literals appropriately.
- var index = 0;
- var source = "__p+='";
- text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
- source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
- index = offset + match.length;
- if (escape) {
- source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
- } else if (interpolate) {
- source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
- } else if (evaluate) {
- source += "';\n" + evaluate + "\n__p+='";
- }
- // Adobe VMs need the match returned to produce the correct offset.
- return match;
- });
- source += "';\n";
- var argument = settings.variable;
- if (argument) {
- // Insure against third-party code injection. (CVE-2021-23358)
- if (!bareIdentifier.test(argument)) throw new Error(
- 'variable is not a bare identifier: ' + argument
- );
- } else {
- // If a variable is not specified, place data values in local scope.
- source = 'with(obj||{}){\n' + source + '}\n';
- argument = 'obj';
- }
- source = "var __t,__p='',__j=Array.prototype.join," +
- "print=function(){__p+=__j.call(arguments,'');};\n" +
- source + 'return __p;\n';
- var render;
- try {
- render = new Function(argument, '_', source);
- } catch (e) {
- e.source = source;
- throw e;
- }
- var template = function(data) {
- return render.call(this, data, underscore);
- };
- // Provide the compiled source as a convenience for precompilation.
- template.source = 'function(' + argument + '){\n' + source + '}';
- return template;
- }
- module.exports = template;
|