"use strict";var _WeakMap = require("@babel/runtime-corejs2/core-js/weak-map");var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");_Object$defineProperty(exports, "__esModule", { value: true });exports.release = exports.build = void 0;var _create = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/create"));var _parseFloat2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/parse-float"));var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map"));var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));var _path = _interopRequireDefault(require("path"));
var _index = require("../cli-shared-utils/index.js");
var _http = _interopRequireDefault(require("http"));
var _process = require("./config/process.js");
var paths = _interopRequireWildcard(require("./lib/paths.js"));
var _manifest = require("./lib/manifest.js");
var _constanst = require("./lib/constanst.js");
var _subpackage = require("./lib/subpackage.js");
var _zipPack = _interopRequireDefault(require("./lib/zipPack.js"));
var _fsUtils = require("../cli-shared-utils/lib/fsUtils");





var _index2 = _interopRequireDefault(require("./webpack/lib/index.js"));function _getRequireWildcardCache(e) {if ("function" != typeof _WeakMap) return null;var r = new _WeakMap(),t = new _WeakMap();return (_getRequireWildcardCache = function (e) {return e ? t : r;})(e);}function _interopRequireWildcard(e, r) {if (!r && e && e.__esModule) return e;if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };var t = _getRequireWildcardCache(r);if (t && t.has(e)) return t.get(e);var n = { __proto__: null },a = _Object$defineProperty && _Object$getOwnPropertyDescriptor;for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {var i = a ? _Object$getOwnPropertyDescriptor(e, u) : null;i && (i.get || i.set) ? _Object$defineProperty(n, u, i) : n[u] = e[u];}return n.default = e, t && t.set(e, n), n;} /**
 * 将webpack源码引入进来解决动态编译问题，修改lib\ContextModule.js
 * 1、__webpack_require__.webpackContext = webpackContext,将webpackContext赋值给__webpack_require__
 * 2、修改webpackEmptyContext，if (__webpack_require__.webpackContext.resolve(req))，若存在已编译的路径，则直接运行
**/ /**
 * 发布构建
 *
 * @param {Object} args - 构建参数
 * @returns {Promise<Object>} 构建结果
 */const release = async (args) => {return build({
    ...args,
    isRelease: true,
    buildType: 'release'
  });
};

/**
 * 构建项目包
 *
 * @param {Object} options - 构建选项
 * @param {boolean} [options.isRelease=false] - 是否为发布版本
 * @param {boolean} [options.inject=true] - 是否注入依赖
 * @param {string} [options.buildType] - 构建类型
 * @param {function} [options.complete=() => {}] - 构建完成后的回调函数
 * @param {boolean} [options.amd=true] - 是否使用AMD模块
 * @param {boolean} [options.rpk=false] - 是否使用RPK打包
 * @returns {Promise<void>} 无返回值
 */exports.release = release;
const build = async ({ isRelease = false, inject = true, buildType, complete = () => {}, amd = true, rpk = false }) => {
  // 校验manifest
  (0, _manifest.check)();

  // 校验通过后，可以清空编译目录
  _index.fs.removeSync(paths.BUILD);
  _index.fs.removeSync(paths.DIST);
  // TODO 开发需要看temp里面的包，方便测试，上线后去掉
  _index.fs.removeSync(paths.TEMP);

  // 读取manifest配置
  const manifest = (0, _manifest.get)();

  // 添加manifest配置 buildType
  if (buildType) {
    (0, _manifest.add)('buildType', buildType);
  } else {
    (0, _manifest.add)('buildType', 'dev');
  }

  // 过滤出合法的分包配置
  const subpackages = (0, _subpackage.filter)(manifest.subpackages);
  // 将分包配置转化为对应的webpack的打包入口
  // 将分包配置转化为对应的externals用于打出兼容的原整包
  const { entry, externals, isMain } = (0, _subpackage.toConfigs)(subpackages);
  // 对分包名称判断 不能为main
  if (isMain) {
    (0, _index.error)('请检查分包的配置，分包名称不能为main，请修改分包名称');
    return;
  }
  // 取出包名
  const packageName = manifest.package || 'Bundle';

  const rpkStartTime = new Date().valueOf();

  // 如果配置了rpk，则直接将src目录copy到build目录并压缩
  if (rpk) {
    rpkZip(isRelease, packageName, subpackages, manifest, buildType, rpkStartTime, complete);
    return;
  }

  // 先打原整包
  const singleCompiler = (0, _index2.default)((0, _process.singleConfig)(entry, isRelease, externals, inject, amd));
  /**
   * 编译原整包的完成处理函数
   * @param {Object} err
   * @param {Object} stats
   */
  const singleDone = (err, stats) => {
    if ((0, _process.checkError)(err, stats)) return;
    // 如果存在未使用的模块，则将所有未使用的模块作为入口重新webpack打包
    if (paths.unUsedFiles.length !== 0) {
      (0, _index.info)(`paths.unUsedFiles 存在未引入的文件重新打包，module: ${paths.unUsedFiles}`);
      const entry = (0, _create.default)(null);
      paths.unUsedFiles.forEach((unUsedFile) => {
        const filePath = _path.default.resolve(paths.BUILD, unUsedFile);
        if (_index.fs.existsSync(filePath)) {
          const parsedPath = _path.default.parse(unUsedFile);
          const entryKey = _path.default.join(parsedPath.dir, parsedPath.name);
          entry[entryKey] = filePath;
        }
      });
      paths.unUsedFiles = [];
      const compiler = (0, _index2.default)((0, _process.base)(isRelease, entry, amd));
      compiler.run(singleDone);
      return;
    }

    const compileStartTime = new Date().valueOf();
    const promise = (0, _zipPack.default)(isRelease, packageName, true, subpackages, manifest, buildType, compileStartTime);
    promise.then((resolve) => {
      doneNext();
    });
  };

  // 等待原整包打包完成再继续编译
  const doneNext = () => {
    (0, _index.info)(`${_constanst.LOG_TITLE}原整包打包完成！ 耗时: ${(new Date().valueOf() - rpkStartTime) / 1000}s`);

    // 原整包打包完成后，判断是否存在分包配置，如果有就编译分包，如果没有，拷贝原整包作为最终的输出包
    if (subpackages.length) {
      // 原整包打包完成后，开始打分包
      (0, _index.info)(`${_constanst.LOG_TITLE}开始编译分包...`);
      (0, _index2.default)((0, _process.multiConfig)(entry, isRelease, packageName, externals, subpackages, manifest, buildType, amd), (err, stats) => {
        if ((0, _process.checkError)(err, stats)) return;
        (0, _index.info)(`${_constanst.LOG_TITLE}总包打包完成！ 总耗时: ${(new Date().valueOf() - rpkStartTime) / 1000}s`);
        final(packageName, complete, rpkStartTime);
      });
    } else {
      // 不分包不带插件包的 才走这个逻辑
      // 不分包的带插件包的打包已经在webpack插件里面做了
      const temp = _path.default.join(paths.TEMP, `${packageName}${_constanst.RPK}`);
      const dist = _path.default.join(paths.DIST, `${packageName}${isRelease ? _constanst.RPK_SIGNED : _constanst.RPK}`);
      _index.fs.copy(temp, dist).then(() => {
        final(packageName, complete, rpkStartTime);
      }).catch((err) => {
        (0, _index.error)(err);
      });
    }
  };

  (0, _index.info)(`${_constanst.LOG_TITLE}开始编译原整包...`);
  singleCompiler.run(singleDone);
};

// 全部结束后的处理函数
exports.build = build;const final = (packageName, complete, rpkStartTime) => {
  complete();

  (0, _index.done)(`${_constanst.LOG_TITLE}【统一rpk】压缩包已生成:${paths.DIST} ${(new Date().valueOf() - rpkStartTime) / 1000}s`);

  // 打印包体信息
  logRpk(packageName);

  // 通知引擎自动更新包
  notify();
};

function logRpk(packageName) {
  // 打印包体信息
  try {
    (0, _index.done)(`【统一rpk】打包完成，包体信息如下：\n`);
    const files = _index.fs.readdirSync(paths.TEMP);
    let subAllSize = 0;
    let subSize = 0;
    let mainSize = 0;
    let fileInfoArray = files.map((file) => {
      if (file.endsWith('.rpk')) {
        const filePath = _path.default.resolve(paths.TEMP, file);
        const stats = _index.fs.statSync(filePath);
        let size = Number(stats.size / 1024 / 1024).toFixed(3);
        size = size > 1 ? Number(size).toFixed(1) : size;
        const fileName = file.split('.rpk')[0];
        // 包体名称
        let name = fileName === 'main' ? '主包' : `分包/${fileName}`;
        name = fileName === packageName ? '原整包' : name;
        name = fileName === 'cocos-library' || fileName === 'laya-library' || fileName === 'egret-library' ? `插件包` : name;

        // 分包size加到一起
        if (name.startsWith('分包')) {
          subAllSize += (0, _parseFloat2.default)(size);
          subSize++;
        }

        if (name === '主包') {
          mainSize = size;
        }

        return { name: name, file, size: `${size} MB` };
      }
    }).filter((fileInfo) => fileInfo !== undefined);

    if (fileInfoArray.length === 1) {
      const files = _index.fs.readdirSync(paths.DIST);
      fileInfoArray = files.map((file) => {
        if (file.endsWith('.rpk')) {
          const fileName = file.split('.rpk')[0];
          if (fileName.includes(packageName)) {
            const filePath = _path.default.resolve(paths.DIST, file);
            const stats = _index.fs.statSync(filePath);
            let size = Number(stats.size / 1024 / 1024).toFixed(3);
            size = size > 1 ? Number(size).toFixed(1) : size;
            return { name: 'dist/整包', file, size: `${size} MB` };
          }
        }
      }).filter((fileInfo) => fileInfo !== undefined);
    } else {
      const files = _index.fs.readdirSync(paths.DIST);
      const fileDistArray = files.map((file) => {
        if (file.endsWith('.rpk')) {
          const fileName = file.split('.rpk')[0];
          if (fileName.includes(packageName)) {
            const filePath = _path.default.resolve(paths.DIST, file);
            const stats = _index.fs.statSync(filePath);
            let size = Number(stats.size / 1024 / 1024).toFixed(3);
            size = size > 1 ? Number(size).toFixed(1) : size;
            return { name: 'dist/整包', file, size: `${size} MB` };
          }
        }
      }).filter((fileInfo) => fileInfo !== undefined);

      if (subAllSize !== 0) {
        fileInfoArray.push({ name: '所有分包', file: `${subSize}个`, size: `${Number(subAllSize).toFixed(3)} MB` });
      }

      const sortOrder = new _map.default([
      ['原整包', 1],
      ['主包', 2],
      ['插件包', 3],
      ['所有分包', 4]]
      );

      fileInfoArray.sort((a, b) => {
        if (sortOrder.get(a.name) && sortOrder.get(b.name)) {
          return sortOrder.get(a.name) - sortOrder.get(b.name);
        }

        if (sortOrder.get(a.name)) {
          return -1;
        }

        if (sortOrder.get(b.name)) {
          return 1;
        }

        const sizeA = (0, _parseFloat2.default)(a.size.split('MB')[0]);
        const sizeB = (0, _parseFloat2.default)(b.size.split('MB')[0]);

        // 先按文件大小排序
        if (sizeA !== sizeB) {
          return sizeB - sizeA;
        } else {
          return b.file.localeCompare(a.file);
        }
      });

      fileInfoArray = fileDistArray.concat(fileInfoArray);
    }

    console.log(fileInfoArray.map((obj) => (0, _values.default)(obj)));

    if (fileInfoArray.length === 1) {
      console.info(`\n### 开平会校验单个整包大小：${fileInfoArray[0].size}`);
    } else if (mainSize !== 0 && subAllSize !== 0) {
      console.info(`\n### 开平只会校验 主包main.rpk大小：${mainSize} MB, 所有分包大小：${Number(subAllSize).toFixed(3)} MB`);
    }

    console.log('\n');
  } catch (err) {
  }
}

function notify() {
  try {
    (0, _index.info)(`### App Server ### 发送更新通知请求`);
    const json = _index.fs.readJsonSync(_path.default.resolve(_index.projectPath, 'client.json'));
    console.log(json);

    // 给游戏前台发送更新通知
    const gameRequrl = `http://${json.client}:49517/update`;
    const reqGame = _http.default.request({
      host: json.client,
      port: 49517,
      path: '/update',
      timeout: 2000
    }, (res) => {
      (0, _index.info)(`### App Server ### 给引擎发送更新通知请求: ${gameRequrl} 成功`);
    }).on('error', () => {
      (0, _index.info)(`### App Server ### 给引擎发送更新通知请求: ${gameRequrl} 失败`);

      // 给调试器发送
      const requrl = `http://${json.client}:39517/update`;
      const req = _http.default.request({
        host: json.client,
        port: 39517,
        path: '/update',
        timeout: 2000
      }, (res) => {
        (0, _index.info)(`### App Server ### 发送自动更新调试器器请求: ${requrl} 成功`);
      }).on('error', () => {

        // error(`### App Server ### 发送自动更新调试器请求: ${requrl} 失败: ${err.message}`)
      }).on('timeout', function () {// error(`### App Server ### 发送自动更新调试器请求: ${requrl} 超时: 3000ms`)
          req.abort();
        });
      req.end();
    }).on('timeout', function () {
      (0, _index.info)(`### App Server ### 给引擎发送更新通知请求: ${gameRequrl} 超时: 3000ms`);
      reqGame.abort();
    });
    reqGame.end();
  } catch (error) {
    (0, _index.info)(`### App Server ### 没找到client信息`);
  }
}

/**
 * 不使用webpack直接压缩打包联盟RPK文件
 *
 * @param {boolean} isRelease - 是否为发布版本
 * @param {string} packageName - 包的名称
 * @param {Array} subpackages - 分包配置
 * @param {Object} manifest - 包的清单文件
 * @param {string} buildType - 构建类型
 * @param {number} rpkStartTime - RPK打包开始时间
 * @param {function} complete - 打包完成后的回调函数
 * @returns {Promise} 返回一个Promise，表示打包过程的状态
 */
const rpkZip = (isRelease, packageName, subpackages, manifest, buildType, rpkStartTime, complete) => {
  // copy src目录下文件到build目录
  _fsUtils.fsExt.overwrite(paths.SRC, paths.BUILD);

  // 联盟包体处理
  // 联盟适配转换代码
  let needTransferAlliance = true;
  const vivoAllianceAdapterJS = 'vivo_alliance_adapter';
  const gameContent = _index.fs.readFileSync(_path.default.resolve(paths.BUILD, './game.js'), {
    encoding: 'utf-8'
  });
  if (gameContent.includes(vivoAllianceAdapterJS)) {
    needTransferAlliance = false;
    (0, _index.info)('game.js 包含联盟 vivo_alliance_adapter.js 转换代码，不进行二次转换...');
  }
  if (needTransferAlliance) {
    (0, _index.info)('开始写入联盟转换代码...');
    // 生成 main.js文件，引入 game.js，作为适配联盟api的入口
    if (_index.fs.existsSync(_path.default.resolve(paths.BUILD, './main.js'))) {
      (0, _index.info)('main.js为入口文件，不允许作为业务文件引入进来，如果游戏有报错，请修改文件名，重新打包');
      const content = _index.fs.readFileSync(_path.default.resolve(paths.BUILD, './main.js'), {
        encoding: 'utf-8'
      });

      // 判断build是否包含适配文件
      const allianceJs = _path.default.resolve(paths.BUILD, './vivo_alliance_adapter.js');
      if (!_index.fs.existsSync(allianceJs)) {
        const allianceJs = _path.default.join(__dirname, './plugins/alliance_adapter/vivo_alliance_adapter.js');
        _index.fs.copyFileSync(allianceJs, _path.default.resolve(paths.BUILD, './vivo_alliance_adapter.js'));
      }

      // 判断main.js是否已引入适配js
      if (!content.includes(vivoAllianceAdapterJS)) {
        // 重新写入vivo_alliance_adapter
        const allianceAdapterContent = 'require(\'vivo_alliance_adapter.js\');\n';
        _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), allianceAdapterContent + content, 'utf8');
      }
    } else {
      const allianceJs = _path.default.join(__dirname, './plugins/alliance_adapter/vivo_alliance_adapter.js');
      _index.fs.copyFileSync(allianceJs, _path.default.resolve(paths.BUILD, './vivo_alliance_adapter.js'));
      _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), 'require(\'vivo_alliance_adapter.js\');\nrequire("game.js");', 'utf8');
    }
  } else {
    // 生成 main.js文件，引入 game.js，作为适配联盟api的入口
    if (_index.fs.existsSync(_path.default.resolve(paths.BUILD, './main.js'))) {
      (0, _index.info)('main.js为入口文件，不允许作为业务文件引入进来，如果游戏有报错，请修改文件名，重新打包');
      // throw new Error()
    } else {
      _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), 'require("game.js");', 'utf8');
    }
  }

  // 打包原整包
  const compileStartTime = new Date().valueOf();
  const promise = (0, _zipPack.default)(isRelease, packageName, true, subpackages, manifest, buildType, compileStartTime);
  promise.then((resolve) => {
    doneNext();
  });

  // 等待原整包打包完成再继续编译
  const doneNext = () => {
    (0, _index.info)(`${_constanst.LOG_TITLE}原整包打包完成！ 耗时: ${(new Date().valueOf() - rpkStartTime) / 1000}s`);

    // 原整包打包完成后，判断是否存在分包配置，如果有就编译分包，如果没有，拷贝原整包作为最终的输出包
    if (subpackages.length) {
      // 原整包打包完成后，开始打分包
      (0, _index.info)(`${_constanst.LOG_TITLE}开始编译分包...`);
      const compileStartTime = new Date().valueOf();
      const promise = (0, _zipPack.default)(isRelease, packageName, false, subpackages, manifest, buildType, compileStartTime);
      promise.then((resolve) => {
        final(packageName, complete, rpkStartTime);
      });
    } else {
      const temp = _path.default.join(paths.TEMP, `${packageName}${_constanst.RPK}`);
      const dist = _path.default.join(paths.DIST, `${packageName}${isRelease ? _constanst.RPK_SIGNED : _constanst.RPK}`);
      _index.fs.copy(temp, dist).then(() => {
        final(packageName, complete, rpkStartTime);
      }).catch((err) => {
        (0, _index.error)(err);
      });
    }
  };
};