"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _path = _interopRequireDefault(require("path"));

var _webpackSources = require("webpack-sources");

var _utils = require("../utils");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const MODULE_TYPE = 'css/mini-extract';
let {
  chunkSplitEnable
} = (0, _utils.getOptions)().i18n || {};

let filenameParser = filename => {
  let filepaths = filename.split('/');
  let hashedName = filepaths[filepaths.length - 1];

  let {
    name,
    ext
  } = _path.default.parse(hashedName);

  let nameparts = name.split('.');
  return {
    name: nameparts.slice(0, nameparts.length - 1).join('.') + ext,
    hashedName,
    ext
  };
};

class PublicPathChangePlugin {
  constructor({
    publicPaths,
    resourceHints
  }) {
    if (Array.isArray(publicPaths)) {
      let [js, css, image, font, i18njs] = publicPaths; //eslint-disable-next-line

      publicPaths = {
        __REACT_CLI_PUBLIC_PATHS__: {
          js,
          css,
          image,
          font,
          i18njs
        }
      };
    }

    this.publicPaths = publicPaths;
    this.resourceHints = resourceHints;
  }

  getCssChunkObject(mainChunk) {
    const obj = {};

    for (const chunk of mainChunk.getAllAsyncChunks()) {
      for (const module of chunk.modulesIterable) {
        if (module.type === MODULE_TYPE) {
          obj[chunk.id] = 1;
          break;
        }
      }
    }

    return obj;
  }

  needChunkOnDemandLoadingCode(chunk) {
    for (const chunkGroup of chunk.groupsIterable) {
      if (chunkGroup.getNumberOfChildren() > 0) {
        return true;
      }
    }

    return false;
  }

  getResourceHints(resourceHints, manifest, publicPaths) {
    let ruleMapping = {};
    let validFiles = Object.keys(manifest).filter(filename => {
      let isValidFile = false;

      for (let pattern of resourceHints) {
        if (typeof pattern === 'string') {
          isValidFile = new RegExp(pattern).test(filename);
        } else {
          isValidFile = new RegExp(pattern.pattern).test(filename);
        }

        if (isValidFile) {
          ruleMapping[filename] = typeof pattern !== 'string' ? pattern : {};
          break;
        }
      }

      return isValidFile;
    });
    let resourceHintsContent = '';

    for (let file of validFiles) {
      let {
        fileType,
        filename
      } = manifest[file];
      let rules = ruleMapping[file];
      resourceHintsContent += `<link rel=${rules.hint || 'preload'} href='${publicPaths[fileType] + filename}'${rules.hint === 'prefetch' ? '' : `as='${fileType}' crossorigin='${rules.crossorigin || 'false'}'`} />`;
    }

    return resourceHintsContent;
  }

  apply(compiler) {
    compiler.hooks.thisCompilation.tap('PublicPathChangePlugin', compilation => {
      compilation.mainTemplate.hooks.requireEnsure.tap('PublicPathChangePlugin', (source, chunk) => {
        const chunkMap = this.getCssChunkObject(chunk);

        if (Object.keys(chunkMap).length > 0) {
          return source.replace('__webpack_require__.p', '__REACT_CLI_CSS_PUBLIC_PATH__');
        }
      });
    });
    compiler.hooks.compilation.tap('PublicPathChangePlugin', compilation => {
      compilation.mainTemplate.hooks.localVars.tap('PublicPathChangePlugin', (source, chunk) => {
        if (this.needChunkOnDemandLoadingCode(chunk)) {
          return source.replace('__webpack_require__.p', '__REACT_CLI_JS_PUBLIC_PATH__');
        }
      });
    });
    compiler.hooks.emit.tap('PublicPathChangePlugin', compilation => {
      let cdns = Object.keys(this.publicPaths);
      let stats = compilation.getStats().toJson();
      let manifest = stats.assets.reduce((files, asset) => {
        let {
          name: filename
        } = asset;
        let fileType = (0, _utils.getFileType)(filename);

        if (fileType === 'image' || fileType === 'font') {
          let {
            name,
            hashedName,
            ext
          } = filenameParser(filename);
          files[name] = {
            hashedName,
            ext,
            fileType,
            filename
          };
        }

        return files;
      }, {});
      Object.keys(compilation.assets).forEach(filename => {
        if (Array.isArray(cdns)) {
          cdns.forEach(cdn => {
            let source = compilation.assets[filename].source();

            if (/\.css$/g.test(filename)) {
              let publicPaths = this.publicPaths[cdn];
              source = (0, _utils.cssUrlReplacer)(source, publicPaths, manifest, cdn);
            } else if (/\.js$/g.test(filename)) {
              let {
                js,
                css,
                image,
                font
              } = this.publicPaths[cdn];

              if (typeof source === 'string') {
                source = source.replace(/__REACT_CLI_JS_PUBLIC_PATH__/g, `"${js}"`);
                source = source.replace(/__REACT_CLI_CSS_PUBLIC_PATH__/g, `"${css}"`);
                source = source.replace(/__REACT_CLI_IMAGE_PUBLIC_PATH__/g, `"${image}"`);
                source = source.replace(/__REACT_CLI_FONT_PUBLIC_PATH__/g, `"${font}"`);
              }
            } else if (/\.html$/g.test(filename)) {
              let {
                js,
                css,
                i18njs
              } = this.publicPaths[cdn];
              source = source.replace(new RegExp('<script defer src="([^"]*)"', 'g'), (match, arg) => match.replace(arg, js + arg));
              source = source.replace(new RegExp('<link href="([^"]*)"', 'g'), (match, arg) => match.replace(arg, css + arg));
              let [i18nStr] = source.match(/<!--I18nInfoToServer(.*?)I18nInfoToServer-->/gm);

              if (i18nStr && !chunkSplitEnable) {
                try {
                  let [i18nObj] = i18nStr.match(/{(.*?)}/gm);
                  i18nObj = JSON.parse(JSON.parse(JSON.stringify(i18nObj)));
                  Object.keys(i18nObj).forEach(locale => {
                    i18nObj[locale] = i18njs + i18nObj[locale];
                  });
                  source = source.replace(/<!--I18nInfoToServer(.*?)I18nInfoToServer-->/gm, `<!--I18nInfoToServer${JSON.stringify(i18nObj)}I18nInfoToServer-->`);
                } catch (e) {
                  // eslint-disable-next-line no-console
                  console.warn('may be I18nInfoToServer inside object was not valid make sure it is parseable by JSON.parse'); // eslint-disable-next-line no-console

                  console.warn(e);
                }
              }

              if (Array.isArray(this.resourceHints) && this.resourceHints.length) {
                let resourceHintsContent = this.getResourceHints(this.resourceHints, manifest, this.publicPaths[cdn]);
                source = source.replace(/<\/head>/g, `${resourceHintsContent}</head>`);
              }
            }

            compilation.assets[cdn === '__REACT_CLI_PUBLIC_PATHS__' ? filename : `${cdn}/${filename}`] = new _webpackSources.RawSource(source);
          });
        }
      });
    });
  }

}

var _default = PublicPathChangePlugin;
exports.default = _default;