• Jump To … +
    modules/_baseCreate.js modules/_baseIteratee.js modules/_cb.js modules/_chainResult.js modules/_collectNonEnumProps.js modules/_createAssigner.js modules/_createEscaper.js modules/_createIndexFinder.js modules/_createPredicateIndexFinder.js modules/_createReduce.js modules/_createSizePropertyCheck.js modules/_deepGet.js modules/_escapeMap.js modules/_executeBound.js modules/_flatten.js modules/_getByteLength.js modules/_getLength.js modules/_group.js modules/_has.js modules/_hasObjectTag.js modules/_isArrayLike.js modules/_isBufferLike.js modules/_keyInObj.js modules/_methodFingerprint.js modules/_optimizeCb.js modules/_setup.js modules/_shallowProperty.js modules/_stringTagBug.js modules/_tagTester.js modules/_toBufferView.js modules/_toPath.js modules/_unescapeMap.js modules/after.js modules/allKeys.js modules/before.js modules/bind.js modules/bindAll.js modules/chain.js modules/chunk.js modules/clone.js modules/compact.js modules/compose.js modules/constant.js modules/contains.js modules/countBy.js modules/create.js modules/debounce.js modules/defaults.js modules/defer.js modules/delay.js modules/difference.js modules/each.js modules/escape.js modules/every.js modules/extend.js modules/extendOwn.js modules/filter.js modules/find.js modules/findIndex.js modules/findKey.js modules/findLastIndex.js modules/findWhere.js modules/first.js modules/flatten.js modules/functions.js modules/get.js modules/groupBy.js modules/has.js modules/identity.js modules/index-all.js modules/index-default.js modules/index.js modules/indexBy.js modules/indexOf.js modules/initial.js modules/intersection.js modules/invert.js modules/invoke.js modules/isArguments.js modules/isArray.js modules/isArrayBuffer.js modules/isBoolean.js modules/isDataView.js modules/isDate.js modules/isElement.js modules/isEmpty.js modules/isEqual.js modules/isError.js modules/isFinite.js modules/isFunction.js modules/isMap.js modules/isMatch.js modules/isNaN.js modules/isNull.js modules/isNumber.js modules/isObject.js modules/isRegExp.js modules/isSet.js modules/isString.js modules/isSymbol.js modules/isTypedArray.js modules/isUndefined.js modules/isWeakMap.js modules/isWeakSet.js modules/iteratee.js modules/keys.js modules/last.js modules/lastIndexOf.js modules/map.js modules/mapObject.js modules/matcher.js modules/max.js modules/memoize.js modules/min.js modules/mixin.js modules/negate.js modules/noop.js modules/now.js modules/object.js modules/omit.js modules/once.js modules/pairs.js modules/partial.js modules/partition.js modules/pick.js modules/pluck.js modules/property.js modules/propertyOf.js modules/random.js modules/range.js modules/reduce.js modules/reduceRight.js modules/reject.js modules/rest.js modules/restArguments.js modules/result.js modules/sample.js modules/shuffle.js modules/size.js modules/some.js modules/sortBy.js modules/sortedIndex.js modules/tap.js modules/template.js modules/templateSettings.js modules/throttle.js modules/times.js modules/toArray.js modules/toPath.js modules/underscore-array-methods.js modules/underscore.js modules/unescape.js modules/union.js modules/uniq.js modules/uniqueId.js modules/unzip.js modules/values.js modules/where.js modules/without.js modules/wrap.js modules/zip.js
    • template.js

    • ¶
      import defaults from './defaults.js';
      import _ from './underscore.js';
      import './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.

      export default function template(text, settings, oldSettings) {
        if (!settings && oldSettings) settings = oldSettings;
        settings = defaults({}, settings, _.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, _);
        };
    • ¶

      Provide the compiled source as a convenience for precompilation.

        template.source = 'function(' + argument + '){\n' + source + '}';
      
        return template;
      }