"use strict";

var RawSource = require("webpack-core/lib/RawSource");

function ChunkManifestPlugin(options) {
  options = options || {};
  this.manifestFilename = options.filename || "manifest.json";
  this.manifestVariable = options.manifestVariable;
  this.inlineManifest = options.inlineManifest || false;
  this.extraOption = options.extra || {};
}
module.exports = ChunkManifestPlugin;

ChunkManifestPlugin.prototype.constructor = ChunkManifestPlugin;
ChunkManifestPlugin.prototype.apply = function (compiler) {
  var manifestFilename = this.manifestFilename;
  var manifestVariable = this.manifestVariable;
  var inlineManifest = this.inlineManifest;
  var oldChunkFilename;
  var chunkManifest;
  var extraOption = this.extraOption;
  compiler.plugin("this-compilation", function (compilation) {
    var mainTemplate = compilation.mainTemplate;
    mainTemplate.plugin("require-ensure", function (_, chunk, hash) {
      var filename = this.outputOptions.chunkFilename || this.outputOptions.filename;
      var initialJS = {};
      if (filename) {
        chunkManifest = [chunk].reduce(function registerChunk(manifest, c) {
          if (c.id in manifest) {
            return manifest;
          }
          var hasRuntime = typeof c.hasRuntime === "function" ? c.hasRuntime() : c.entry;

          if (hasRuntime && !c.isInitial()) {
            //c.isInital issue existing plugin
            manifest[c.id] = undefined;
          } else {
            var filePath = mainTemplate.applyPluginsWaterfall("asset-path", filename, {
              hash: hash,
              chunk: c
            });
            if (c.isInitial()) {
              initialJS[c.name || c.id] = filePath;
            }
            manifest[c.id] = filePath;
          }
          return c.chunks.reduce(registerChunk, manifest);
        }, {});
        if (manifestVariable) {
          oldChunkFilename = this.outputOptions.chunkFilename;
          this.outputOptions.chunkFilename = "__CHUNK_MANIFEST__";
        }
        // mark as asset for emitting
        compilation.assets[manifestFilename] = new RawSource(JSON.stringify(Object.assign(extraOption, {
          manifestVariable: manifestVariable,
          manifest: chunkManifest,
          initialJS: initialJS
        })));
        chunk.files.push(manifestFilename);
      }

      return _;
    });
  });

  compiler.plugin("compilation", function (compilation) {
    if (!manifestVariable) {
      return;
    }
    compilation.mainTemplate.plugin("require-ensure", function (_, chunk, hash, chunkIdVar) {
      if (oldChunkFilename) {
        this.outputOptions.chunkFilename = oldChunkFilename;
      }

      return _.replace('"__CHUNK_MANIFEST__"', 'window["' + manifestVariable + '"][' + chunkIdVar + "]");
    });

    if (inlineManifest) {
      compilation.plugin("html-webpack-plugin-before-html-generation", function (data, callback) {
        var manifestHtml = "<script>window." + manifestVariable + "=" + JSON.stringify(chunkManifest) + "</script>";
        callback(null, data.assets[manifestVariable] = manifestHtml);
      });
    }
  });
};