Home Reference Source

src/utils/Evaluator.js

import _ from 'lodash';
import stringHash from 'string-hash';
const Evaluator = {
  noeval: false,
  protectedEval: false, // This property can be customized only by plugins
  cache: {},
  templateSettings: {
    evaluate: /\{%([\s\S]+?)%\}/g,
    interpolate: /\{\{([\s\S]+?)\}\}/g,
    escape: /\{\{\{([\s\S]+?)\}\}\}/g
  },
  evaluator(func, ...params) {
    if (Evaluator.noeval) {
      console.warn('No evaluations allowed for this renderer.');
      return _.noop;
    }

    if (typeof params[0] === 'object') {
      params = _.keys(params[0]);
    }
    return new Function(...params, func);
  },
  template(template, hash) {
    hash = hash || stringHash(template);
    try {
      // Ensure we handle copied templates from the ejs files.
      template = template.replace(/ctx\./g, '');
      return (Evaluator.cache[hash] = _.template(template, Evaluator.templateSettings));
    }
    catch (err) {
      console.warn('Error while processing template', err, template);
    }
  },
  interpolate(rawTemplate, data, _options) {
    // Ensure reverse compatability.
    const options = _.isObject(_options) ? _options : { noeval: _options };
    if (typeof rawTemplate === 'function') {
      try {
        return rawTemplate(data);
      }
      catch (err) {
        console.warn('Error interpolating template', err, data);
        return err.message;
      }
    }

    rawTemplate = String(rawTemplate);

    const hash = stringHash(rawTemplate);
    let template;
    if (Evaluator.cache[hash]) {
      template = Evaluator.cache[hash];
    }
    else if (Evaluator.noeval || options.noeval) {
      // No cached template methods available. Use poor-mans interpolate without eval.
      return rawTemplate.replace(/({{\s*(.*?)\s*}})/g, (match, $1, $2) => {
        // Allow for conditional values.
        const parts = $2.split('||').map(item => item.trim());
        let value = '';
        let path = '';
        for (let i = 0; i < parts.length; i++) {
          path = parts[i];
          value = _.get(data, path);
          if (value) {
            break;
          }
        }
        if (options.data) {
          _.set(options.data, path, value);
        }
        return value;
      });
    }
    else {
      template = Evaluator.template(rawTemplate, hash);
    }
    if (typeof template === 'function') {
      try {
        return template(data);
      }
      catch (err) {
        console.warn('Error interpolating template', err, rawTemplate, data);
        return err.message;
      }
    }
    return template;
  },
  evaluate(func, args) {
    return Array.isArray(args) ? func(...args) : func(args);
  }
};

Evaluator.registerEvaluator = (evaluator) => {
  Object.keys(evaluator).forEach((key) => {
    Evaluator[key] = evaluator[key];
  });
};

export default Evaluator;