UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

253 lines (243 loc) 12.3 kB
"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.default = void 0; var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify")); var _from = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/array/from")); var _assign = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/assign")); var _entries = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/entries")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var _path = _interopRequireDefault(require("path")); var _index = require("../../cli-shared-utils/index.js"); var paths = _interopRequireWildcard(require("../lib/paths.js")); var _manifest = require("../lib/manifest.js"); var _glob = _interopRequireDefault(require("glob")); var _chalk = _interopRequireDefault(require("chalk")); 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; } /** * 打出带 main.js 入口的游戏包,适配联盟引擎 */ /** * 将webpack引用的js资源保存到webpack_use_moudle.json文件中,动态编译js时在webpack的webpackEmptyContext中可能会用到 * @param {*} modules */ function saveWebpackUseModules(chunkGraph, modules, unUsedFile) { try { const modulePathMap = {}; unUsedFile.forEach(file => { try { modulePathMap[file.replace(/\\/g, '/')] = -1; } catch (err) { (0, _index.error)(err); } }); // 遍历所有模块,生成模块 ID 和路径的映射 modules.forEach(module => { try { const id = chunkGraph.getModuleId(module); if (id != null) { const ss = module.resource; if (ss && ss.indexOf(paths.SRC) !== -1) { const arr = _path.default.relative(paths.SRC, ss).replace(/\\/g, '/'); modulePathMap[arr] = chunkGraph.getModuleId(module); } } } catch (err) {} }); const webpackUseModule = _path.default.join(paths.BUILD, 'webpack_use_moudle.json'); if (!_index.fs.existsSync(webpackUseModule)) { (0, _index.info)(`webpackUseModule 保存到 ${webpackUseModule}`); _index.fs.writeFileSync(webpackUseModule, (0, _stringify.default)(modulePathMap), 'utf8'); } } catch (err) { (0, _index.error)(err); } } /** * 将webpack未引用的js文件copy到build目录下参与打包,动态编译js时在webpack的webpackEmptyContext中可能会用到 * @param {*} fileDependencies * @returns */ function copyUnUsedFiles(fileDependencies) { try { // 过滤掉workers配置文件 const workersPath = (0, _manifest.getWorkersPath)(); // 获取webpack使用的js模块 const usedModules = (0, _from.default)(fileDependencies).filter(file => file.indexOf(paths.SRC) !== -1 && (workersPath === '' || file.indexOf(workersPath) === -1)).reduce((obj, item) => (0, _assign.default)(obj, { [item]: true }), {}); const normalizedUsedModules = Object.fromEntries((0, _entries.default)(usedModules).map(([key, value]) => [_path.default.normalize(key), value])); // 获取src下所有的js文件 const files = _glob.default.sync('**/*.js', { ignore: ['node_modules/**'], cwd: paths.SRC, absolute: true }); // 过滤src下未使用的js文件 const unUsedFiles = files.filter(file => !normalizedUsedModules[_path.default.normalize(file)]); const allFiles = unUsedFiles.reduce((array, item) => array.concat(item), []); if (!allFiles.length) { return []; } // 将所有未使用的js文件copy到build目录下 (0, _index.info)(_chalk.default.green('*** Unused Plugin ***')); (0, _index.info)(`${allFiles.length} unused source files found.`); const unUsedFile = []; allFiles.forEach(file => { if (!file.includes('@babel/runtime') && _index.fs.existsSync(file) && !_index.fs.statSync(file).isDirectory()) { const relative = _path.default.relative(paths.SRC, file); const destFile = _path.default.resolve(paths.BUILD, relative); if (!_index.fs.existsSync(destFile)) { (0, _index.info)(`unused JS File ${relative} copy to Build dir.`); // 默认情况下,如果 dest 已经存在,则覆盖它。 const destDir = _path.default.dirname(destFile); if (!_index.fs.existsSync(destDir)) { _index.fs.mkdirSync(destDir, { recursive: true }); } _index.fs.copyFileSync(file, destFile); unUsedFile.push(relative); } } }); // 将未引用的文件缓存下来,webpack完成打包时使用 paths.unUsedFiles = unUsedFile; return unUsedFile; } catch (err) {} } function retryCopyFileSync(src, dest, maxRetries = 3) { let attempts = 0; while (attempts < maxRetries) { let isCopySuccess = false; try { // 自动创建目标目录并复制文件 _index.fs.copySync(src, dest, { overwrite: true }); (0, _index.info)(`[成功] 文件已复制至: ${dest}`); isCopySuccess = true; } catch (e) { (0, _index.error)(`[错误] 复制失败: ${e.message}`); isCopySuccess = false; } // 如果复制失败,尝试重试 if (isCopySuccess) { return true; } attempts++; (0, _index.info)(`[重试] 第 ${attempts} 次尝试...`); } (0, _index.error)(`[终止] 重试 ${maxRetries} 次后仍失败`); return false; } class ZipMainPlugin { constructor(options) { this.options = options; } apply(compiler) { const pluginName = 'ZipMainPlugin'; const { singlePackage, alliance, subpackages } = this.options; compiler.hooks.afterEmit.tapPromise(pluginName, compilation => { return new _promise.default(resolve => { // 将webpack未引入的js文件 copy到build目录下 const unUsedFile = copyUnUsedFiles(compilation.fileDependencies); // 将webpack的映射信息保存下来 saveWebpackUseModules(compilation.chunkGraph, compilation.modules, unUsedFile); // 如果是打原整包 if (singlePackage) { // 联盟适配转换代码 let needTransferAlliance = true; const allianceAdapterJS = 'alliance_adapter'; const gameContent = _index.fs.readFileSync(_path.default.resolve(paths.BUILD, './game.js'), { encoding: 'utf-8' }); if (gameContent.includes(allianceAdapterJS)) { needTransferAlliance = false; if (alliance) { (0, _index.info)('game.js 包含联盟 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, './alliance_adapter.js'); if (!_index.fs.existsSync(allianceJs)) { const allianceJs = _path.default.join(__dirname, '../alliance_adapter/alliance_adapter.js'); retryCopyFileSync(allianceJs, _path.default.resolve(paths.BUILD, './alliance_adapter.js')); } // 判断main.js是否已引入适配js if (!content.includes(allianceAdapterJS)) { // 重新写入vivo_alliance_adapter const allianceAdapterContent = 'require(\'alliance_adapter.js\');\n'; _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), allianceAdapterContent + content, 'utf8'); } } else { const allianceJs = _path.default.join(__dirname, '../alliance_adapter/alliance_adapter.js'); retryCopyFileSync(allianceJs, _path.default.resolve(paths.BUILD, './alliance_adapter.js')); _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), 'require(\'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为入口文件,不允许作为业务文件引入进来,如果游戏有报错,请修改文件名,重新打包'); } else { _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, './main.js'), 'require("game.js");', 'utf8'); } } resolve(); } else { // 如果是打分包,检查是目录的分包,增加main.js,引入 game.js subpackages.forEach(({ root }) => { const ext = _path.default.extname(root); if (ext === '') { // 如果是联盟,则为了兼容荣耀分包,将分包内的game.js copy到main.js,防止require引用失败 if (alliance) { try { if (_index.fs.existsSync(_path.default.resolve(paths.BUILD, root, './game.js'))) { _index.fs.renameSync(_path.default.resolve(paths.BUILD, root, './game.js'), _path.default.resolve(paths.BUILD, root, './main.js')); } if (_index.fs.existsSync(_path.default.resolve(paths.BUILD, root, './game.js.map'))) { _index.fs.renameSync(_path.default.resolve(paths.BUILD, root, './game.js.map'), _path.default.resolve(paths.BUILD, root, './main.js.map')); } } catch (err) { console.error(err); } } else { // 如果是目录 // 生成 main.js文件,引入 game.js,作为分包适配联盟api的入口 if (_index.fs.existsSync(_path.default.resolve(paths.BUILD, root, './main.js'))) { // console.warn('main.js为入口文件,不允许作为业务文件引入进来,如果游戏有报错,请修改文件名,重新打包') (0, _index.info)(root + '/main.js为入口文件,不允许作为业务文件引入进来,如果游戏有报错,请修改文件名,重新打包'); // throw new Error('main.js为子包入口文件,不允许作为业务文件引入,请修改文件名,重新打包') } else { _index.fs.outputFileSync(_path.default.resolve(paths.BUILD, root, './main.js'), 'require("./game.js");', 'utf8'); } } } }); resolve(); } }); }); } } var _default = exports.default = ZipMainPlugin;