<commit_before>var vm = require('vm');
var Promise = require('bluebird');
var extend = require('extend');
var format = require('util').format;

var ChildCompiler = require('../ChildCompiler');
var loaderPath = require('./loader').LOADER_PATH;
var generateLoaderRequestString = require('../../utils/loader/generateLoaderRequestString');

var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');

/**
 * @param {Compilation} compilation
 * @param {string} templatePath Absolute path to template
 * @constructor
 */
function TemplateCompiler(compilation, templatePath) {
  ChildCompiler.call(this, compilation, {
    name: 'Template compiler: ' + templatePath,
    output: {
      filename: templatePath
    }
  });

  var compiler = this._compiler;
  var outputOptions = compiler.options.output;
  var templateRequest = generateLoaderRequestString(loaderPath, null, templatePath);

  this.parentCompilation = compilation;
  this.assetsBeforeCompilation = extend({}, compilation.assets[outputOptions.filename]);
  this.templatePath = templatePath;
  this.templateRequest = templateRequest;

  compiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('WEBPACK_DOCS_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(compiler.context, templateRequest),
    new LoaderTargetPlugin('node')
  );
}

TemplateCompiler.prototype = Object.create(ChildCompiler.prototype);

var cache = {};

/**
 * @returns {Promise}
 */
TemplateCompiler.prototype.run = function() {
  var compiler = this;
  var _compiler = this._compiler;
  var outputFilename = _compiler.options.output.filename;
  var parentCompilation = this.parentCompilation;
  var assetsBeforeCompilation = this.assetsBeforeCompilation;
  var templateRequest = this.templateRequest;

  return ChildCompiler.prototype.run.call(this)
    .then(function (compilation) {

      // Restore the parent compilation to the state like it
      // was before the child compilation
      parentCompilation.assets[outputFilename] = assetsBeforeCompilation[outputFilename];

      if (assetsBeforeCompilation[outputFilename] === undefined) {
        // If it wasn't there - delete it
        delete parentCompilation.assets[outputFilename];
      }

      var assetName = Object.keys(compilation.assets)[0];
      var hash = compilation.chunks[0].hash;
      var source = compilation.assets[assetName].source();
      var isCached = templateRequest in cache && cache[templateRequest].hash == hash;

      if (isCached) {
        return Promise.resolve(cache[templateRequest].template);
      }
      else {
        return compiler.evaluateCompilationResult(source).then(function(func) {
          cache[templateRequest] = {
            hash: hash,
            template: func
          };

          return Promise.resolve(func);
        });
      }

    });
};

/**
 * @param {string} source Template source
 * @returns {Promise}
 */
TemplateCompiler.prototype.evaluateCompilationResult = function (source) {
  if (!source)
    return Promise.reject('The child compilation didn\'t provide a result');

  // The LibraryTemplatePlugin stores the template result in a local variable.
  // To extract the result during the evaluation this part has to be removed.
  source = source.replace('var WEBPACK_DOCS_PLUGIN_RESULT =', '');

  var templatePath = this.templatePath;
  var vmContext = vm.createContext(extend(true, {WEBPACK_DOCS_PLUGIN: true, require: require}, global));
  var vmScript = new vm.Script(source, {filename: templatePath});

  // Evaluate code and cast to string
  var newSource;
  try {<commit_msg>Hacky cache in TemplateCompiler<commit_after>var vm = require('vm');
var Promise = require('bluebird');
var extend = require('extend');
var format = require('util').format;

var ChildCompiler = require('../ChildCompiler');
var loaderPath = require('./loader').LOADER_PATH;
var generateLoaderRequestString = require('../../utils/loader/generateLoaderRequestString');

var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');

/**
 * @param {Compilation} compilation
 * @param {string} templatePath Absolute path to template
 * @constructor
 */
function TemplateCompiler(compilation, templatePath) {
  ChildCompiler.call(this, compilation, {
    name: 'Template compiler: ' + templatePath,
    output: {
      filename: templatePath
    }
  });

  var compiler = this._compiler;
  var outputOptions = compiler.options.output;
  var templateRequest = generateLoaderRequestString(loaderPath, null, templatePath);

  this.parentCompilation = compilation;
  this.assetsBeforeCompilation = extend({}, compilation.assets[outputOptions.filename]);
  this.templatePath = templatePath;
  this.templateRequest = templateRequest;

  compiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('WEBPACK_DOCS_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(compiler.context, templateRequest),
    new LoaderTargetPlugin('node')
  );
}

TemplateCompiler.cache = {};

/**
 *
 * @param {string} templateRequest
 * @returns {boolean}
 */
TemplateCompiler.isCached = function(templateRequest) {
  return templateRequest in TemplateCompiler.cache;
};

/**
 * @param {string} templateRequest
 * @param {Object} data
 */
TemplateCompiler.saveToCache = function(templateRequest, data) {
  TemplateCompiler.cache[templateRequest] = data;
};

/**
 * @param {string} templateRequest
 * @returns {Function}
 */
TemplateCompiler.getFromCache = function(templateRequest) {
  return TemplateCompiler.cache[templateRequest];
};

TemplateCompiler.prototype = Object.create(ChildCompiler.prototype);

/**
 * @returns {Promise}
 */
TemplateCompiler.prototype.run = function() {
  var compiler = this;
  var _compiler = this._compiler;
  var outputFilename = _compiler.options.output.filename;
  var parentCompilation = this.parentCompilation;
  var assetsBeforeCompilation = this.assetsBeforeCompilation;
  var templateRequest = this.templateRequest;
  var isCached = TemplateCompiler.isCached(templateRequest);
  var cached;

  if (isCached) {
    cached = TemplateCompiler.getFromCache(templateRequest);
    return cached.promise.isPending()
      ? cached.promise
      : Promise.resolve(cached.template)
  } else {
    cached = {
      template: null,
      promise: null
    };

    cached.promise = ChildCompiler.prototype.run.call(this)
      .then(function (compilation) {

        // Restore the parent compilation to the state like it
        // was before the child compilation
        parentCompilation.assets[outputFilename] = assetsBeforeCompilation[outputFilename];

        if (assetsBeforeCompilation[outputFilename] === undefined) {
          // If it wasn't there - delete it
          delete parentCompilation.assets[outputFilename];
        }

        var assetName = Object.keys(compilation.assets)[0];
        var hash = compilation.chunks[0].hash;
        var source = compilation.assets[assetName].source();

        return compiler.evaluateCompilationResult(source);
      })
      .then(function(func) {
        cached.template = func;
        return func;
      });

    TemplateCompiler.saveToCache(templateRequest, cached);
    return cached.promise;
  }


};

/**
 * @param {string} source Template source
 * @returns {Promise}
 */
TemplateCompiler.prototype.evaluateCompilationResult = function (source) {
  if (!source)
    return Promise.reject('The child compilation didn\'t provide a result');

  // The LibraryTemplatePlugin stores the template result in a local variable.
  // To extract the result during the evaluation this part has to be removed.
  source = source.replace('var WEBPACK_DOCS_PLUGIN_RESULT =', '');

  var templatePath = this.templatePath;
  var vmContext = vm.createContext(extend(true, {WEBPACK_DOCS_PLUGIN: true, require: require}, global));
  var vmScript = new vm.Script(source, {filename: templatePath});

  // Evaluate code and cast to string
  var newSource;
  try {
{'fraction_non_alphanumeric': 0.11178742830387452, 'fraction_numerical': 0.0004682195949900503, 'mean_word_length': 4.306832298136646, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3853118', 'n_tokens_mistral': 2263, 'n_tokens_neox': 2239, 'n_words': 544}