/* eslint-disable no-console */

const pluginName = "JsonpScriptSrcPlugin";
/**
 * This plugin modifies the webpack bootstrap code generated by the plugin at
 * webpack/lib/web/JsonpMainTemplatePlugin.js
 *
 * It will rename the function jsonpScriptSrc generated by that to webpackJsonpScriptSrc
 * and install a new version that check a user provided function for a custom src.
 *
 *   window.__webpack_get_script_src__(chunkId, publicPath, originalSrc)
 *
 * This is only for webpack 4 (tested with 4.43 and 4.44).
 *
 * Webpack 5 has official support for this https://github.com/webpack/webpack/pull/8462
 * so it won't be necessary.
 */
export class JsonpScriptSrcPlugin {
  constructor() {
    //
  }

  _applyMainTemplate(mainTemplate) {
    // tapable/lib/Hook.js
    // use stage 1 to ensure this executes after webpack/lib/web/JsonpMainTemplatePlugin.js
    mainTemplate.hooks.localVars.tap({ name: pluginName, stage: 1 }, (source, chunk, hash) => {
      // no jsonpScriptSrc function detected, do nothing
      if (!source.includes("function jsonpScriptSrc")) {
        return source;
      }

      const modSource = source.replace("function jsonpScriptSrc", "function webpackJsonpScriptSrc");

      return `${modSource}

var userGetScriptSrc = window.__webpack_get_script_src__;
function jsonpScriptSrc(chunkId) {
  var src = webpackJsonpScriptSrc(chunkId);
  return (userGetScriptSrc && userGetScriptSrc(chunkId, ${mainTemplate.requireFn}.p, src)) || src;
}
`;
    });

    //
    // modify code by mini-css-extract-plugin for CSS loading
    //
    mainTemplate.hooks.requireEnsure.tap({ name: pluginName, stage: 1 }, (source, chunk, hash) => {
      if (!source.includes("mini-css-extract-plugin CSS loading")) {
        return source;
      }

      const codeSig = `fullhref = __webpack_require__.p + href;`;

      if (!source.includes(codeSig)) {
        console.error(
          "jsonp-script-src-plugin: unable to locate mini-css-extract-plugin's CSS href generation code"
        );
        return source;
      }

      return source.replace(
        codeSig,
        `${codeSig}
        fullhref = (userGetScriptSrc && userGetScriptSrc(null, ${mainTemplate.requireFn}.p, fullhref)) || fullhref;`
      );
    });
  }

  apply(compiler) {
    compiler.hooks.thisCompilation.tap(pluginName, compilation => {
      this._applyMainTemplate(compilation.mainTemplate);
    });
  }
}
