import { IBuildTaskOption, BuildHook, IBuildResult } from "../@types"; const fs = require("fs"); const path = require("path"); const axios = require("axios"); const { shell } = require("electron"); // 添加用于解析和生成 JavaScript 代码的库 const parser = require("@babel/parser"); const traverse = require("@babel/traverse").default; const generator = require("@babel/generator").default; const t = require("@babel/types"); const HTTP_URL = "https://miniapp.bilibili.com"; //"http://127.0.0.1:3000"; //https://miniapp.bilibili.com const getUseAsmJs = (L3, B3) => { return `function useAsmJs(t, e) { return new Promise(function (i, r) { var n; ((n = e), new Promise(function (t, e) { (function (t) { return new Promise(function (e) { e("cocos-js/" + t); }); })(n) .then(function (i) { globalThis.fsUtils.readArrayBuffer(i, function (i, r) { i ? e(i) : t(r); }); }) .catch(function () {}); })) .then(function (e) { var i = {}; return ( (i.buffer = new ArrayBuffer(33554432)), t({ wasmMemory: i, memoryInitializerRequest: { response: e, status: 200 }, }).then(function (t) { (${L3} = t), ${B3}.forEach(function (t) { t(${L3}); }); }) ); }) .then(i) .catch(r); }); }`; }; const getUseWebassembly = (L3, B3) => { return `function useWebAssembly(t, e) { return new Promise(function (i, n) { var r = function (t) { return "[Spine]: Spine wasm load failed: " + t; }; t({ instantiateWasm: function (t, i) { (function (t, e) { return ((i = t), new Promise(function (t) { t("cocos-js/" + i); })).then(function (t) { return WebAssembly.instantiate(t, e); }); var i; })(e, t) .then(function (t) { i(t.instance, t.module); }) .catch(function (t) { return n(r(t)); }); }, }) .then(function (t) { (${L3} = t), ${B3}.forEach(function (t) { t(${L3}); }); }) .then(i) .catch(function (t) { return n(r(t)); }); }); }`; }; function getOnPostInfrastructureInitDelegateAddFuncStr(code, isSubPackageWasm) { const startIndex = code.indexOf("Spine wasm load failed"); console.log("开始找 Spine wasm load failed"); if (startIndex === -1) { console.log("未找到 Spine wasm load failed。"); } else { // 从 startIndex 开始向前搜索找到第一个 ; const openBraceIndex = code.lastIndexOf(";", startIndex); if (openBraceIndex === -1) { console.log("未找到方法的起始 ;。"); } else { const lastSemicolonIndex = code.lastIndexOf(";", openBraceIndex - 1); if (lastSemicolonIndex == -1) { console.log("--------"); } else { const funIndex = code.indexOf( "onPostInfrastructureInitDelegate.add", lastSemicolonIndex ); if (funIndex === -1) { console.log("未找到 onPostInfrastructureInitDelegate.add。"); } else { // 从 startIndex 开始向后搜索找到第一个 catch const pushIndex = code.indexOf(".push", funIndex); if (pushIndex === -1) { console.log("未找 .pushIndex 语法。"); } else { const commaIndex = code.lastIndexOf(",", pushIndex + 1); if (commaIndex === -1) { console.log("未找到结束标志 ,"); } else { // 提取起始位置和结束位置之间的子字符串 const methodContent = code.substring( lastSemicolonIndex + 1, commaIndex ); console.log("方法内容:", methodContent); return methodContent; } } } } } } return ""; } const EXTENSION_VERSION = "1.0.3"; // 复制文件的函数 function copyFiles(srcDir, destDir) { // 确保目标目录存在 if (!fs.existsSync(destDir)) { fs.mkdirSync(destDir, { recursive: true }); } // 读取源目录下的所有文件 const files = fs.readdirSync(srcDir); console.log("copyFiles", files); for (const file of files) { const srcFile = path.join(srcDir, file); const destFile = path.join(destDir, file); fs.copyFileSync(srcFile, destFile); } } // 递归复制目录的函数 function copyDirectory(srcDir, destDir) { // 确保目标目录存在 if (!fs.existsSync(destDir)) { fs.mkdirSync(destDir, { recursive: true }); } // 读取源目录下的所有文件和目录 const items = fs.readdirSync(srcDir); items.forEach((item) => { const srcPath = path.join(srcDir, item); const destPath = path.join(destDir, item); const stat = fs.statSync(srcPath); if (stat.isDirectory()) { // 如果是目录,则递归复制 copyDirectory(srcPath, destPath); } else { // 如果是文件,则直接复制 fs.copyFileSync(srcPath, destPath); } }); } // 写文件 function writeFile(localFilePath, response) { fs.writeFileSync(localFilePath, response.data, "utf8"); } // 清空目录的函数 function clearDirectory(directory) { if (fs.existsSync(directory)) { const files = fs.readdirSync(directory); files.forEach((file) => { const filePath = path.join(directory, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { // 如果是目录,则递归删除 clearDirectory(filePath); } else { // 如果是文件,则直接删除 fs.unlinkSync(filePath); } }); // 最后删除目录本身 fs.rmdirSync(directory); } } function compareVersions(version1, version2) { if (!version1 || !version2) { return 0; } const v1 = version1.split(".").map(Number); const v2 = version2.split(".").map(Number); for (let i = 0; i < Math.max(v1.length, v2.length); i++) { const num1 = v1[i] || 0; // 如果不存在,默认为 0 const num2 = v2[i] || 0; // 如果不存在,默认为 0 if (num1 > num2) return 1; // version1 > version2 if (num2 > num1) return -1; // version2 > version1 } return 0; // 版本号相同 } // 替换 System.register 的模块定义 function replaceSystemRegister(sourceCode, moduleName, replacementCode) { const ast = parser.parse(sourceCode, { sourceType: "module", }); traverse(ast, { CallExpression(path) { if ( path.node.callee.type === "MemberExpression" && path.node.callee.object.name === "System" && path.node.callee.property.name === "register" && path.node.arguments.length === 3 && path.node.arguments[0].type === "StringLiteral" && path.node.arguments[0].value === moduleName ) { console.log("找到 System.register 调用: ", moduleName); // 替换整个 System.register 调用 const newRegisterAst = parser.parse(replacementCode, { sourceType: "module", }).program.body[0].expression; path.replaceWith(newRegisterAst); // 如果只需要替换第一个匹配,可以调用 path.stop(); path.stop(); } }, }); return generator(ast, { concise: true }).code; } function replaceSpineWasm(originalCode) { const ast = parser.parse(originalCode, { sourceType: "module", }); console.log("start parse onPostInfrastructureInitDelegateFunc"); const onPostInfrastructureInitDelegateFunc = ` (function (t) { var platform = wx.getSystemInfoSync()._platform || wx.getSystemInfoSync().platform || ""; if (platform.toLowerCase() == "ios") { return useAsmJs(t[0].default, t[1].default); } return useWebAssembly(t[2].default, t[3].default); }) `; const funcExprAst = parser.parse(onPostInfrastructureInitDelegateFunc, { sourceType: "module", plugins: ["typescript"], }).program.body[0].expression; // 遍历 AST 进行修改 traverse(ast, { CallExpression(path) { // 检查我们是否在处理Promise.all的调用 if ( path.node.callee.type === "MemberExpression" && path.node.callee.property.name === "then" ) { console.log("Found 'then'"); // 检查是否为Promise.all的调用结果 if ( path.node.callee.object.type === "CallExpression" && path.node.callee.object.callee.type === "MemberExpression" && path.node.callee.object.callee.object.name === "Promise" && path.node.callee.object.callee.property.name === "all" ) { console.log("Found 'Promise.all'"); // 确保只有一个参数,并且这个参数是数组表达式 if ( path.node.callee.object.arguments.length === 1 && t.isArrayExpression(path.node.callee.object.arguments[0]) ) { console.log("Arguments length is 1 and is an Array"); // 检查数组元素 if ( path.node.callee.object.arguments[0].elements.every( (element) => t.isCallExpression(element) && t.isMemberExpression(element.callee) && t.isIdentifier(element.callee.object, { name: "e" }) && t.isIdentifier(element.callee.property, { name: "import" }) && t.isStringLiteral(element.arguments[0]) && (element.arguments[0].value.startsWith( "external:emscripten/spine/spine" ) || element.arguments[0].value.startsWith("./spine")) ) ) { console.log("All imports match criteria"); console.log("path.get('arguments.0')", path.get("arguments.0")); path.get("arguments.0").replaceWith(funcExprAst); } } } } }, }); // 将函数代码注入 AST 的末尾 // ast.program.body.push(parser.parse(asmJsFunc, { sourceType: "module" }).program.body[0]); // ast.program.body.push(parser.parse(webassemblyFunc, { sourceType: "module" }).program.body[0]); // 生成新的代码 // const { code } = generator(ast); return generator(ast, { concise: true }).code; } function insertFuncs(originalCode) { const ast = parser.parse(originalCode, { sourceType: "module", }); // console.log('insertFuncs originalCode: ', originalCode); // 从原始代码中提取变量名 const varDeclaration = originalCode.split(";")[0]; // 取第一个分号前的部分 const varNames = varDeclaration .replace("var ", "") .split(",") .reduce((acc, item) => { let [key, value] = item.split("="); if (value) { console.log("value", value); if (value.indexOf("null") != -1) { acc.Ont = key.trim(); } else if (value.indexOf("[]") != -1) { acc.Mnt = key.trim(); } } return acc; }, {}); // 注入 getUseAsmJs 和 getUseWebassembly 函数 const asmJsFunc = getUseAsmJs(varNames.Ont, varNames.Mnt); const webassemblyFunc = getUseWebassembly(varNames.Ont, varNames.Mnt); // 解析单个函数定义为 AST 节点 const asmJsFuncAst = parser.parse(asmJsFunc, { sourceType: "module" }).program .body[0]; const webassemblyFuncAst = parser.parse(webassemblyFunc, { sourceType: "module", }).program.body[0]; // 遍历 AST 进行修改 traverse(ast, { CallExpression(path) { // 检查是否为 .add 调用并且前面有 .onPostInfrastructureInitDelegate if ( path.node.callee.type === "MemberExpression" && path.node.callee.property.name === "add" && path.node.callee.object.type === "MemberExpression" && path.node.callee.object.property.name === "onPostInfrastructureInitDelegate" ) { // 在此调用前插入 asmJsFunc 和 webassemblyFunc path.insertBefore(asmJsFuncAst); path.insertBefore(webassemblyFuncAst); } }, }); return generator(ast, { concise: true }).code; } // 递归函数,用于遍历目录 function renameWebAdapterFiles(dirPath) { fs.readdir(dirPath, (err, files) => { if (err) { console.error('Error reading directory:', err); return; } files.forEach(file => { const filePath = path.join(dirPath, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { // 如果是目录,递归调用 renameWebAdapterFiles(filePath); } else if (file.startsWith('web-adapter.') && file.endsWith('.js')) { // 匹配 web-adapter.xxxx.js 文件 const newFilePath = path.join(dirPath, 'web-adapter.js'); // 检查新文件名是否已存在,如果存在则跳过或处理冲突 if (files.includes('web-adapter.js')) { console.log(`${newFilePath} already exists, skipping...`); } else { fs.rename(filePath, newFilePath, err => { if (err) { console.error('Error renaming file:', err); } else { console.log(`Renamed ${filePath} to ${newFilePath}`); } }); } } }); }); } interface IOptions { platform: any; webTestOption: any; remoteAddress: string; enterCocos: string; selectTest: string; objectTest: { number: number; string: string; boolean: boolean; }; arrayTest: [number, string, boolean]; isBiliGame: boolean; } const PACKAGE_NAME = "biligame-builder"; interface ITaskOptions extends IBuildTaskOption { packages: { "biligame-builder": IOptions; }; } function log(...arg: any[]) { return console.log(`[${PACKAGE_NAME}] `, ...arg); } let allAssets = []; export const throwError: BuildHook.throwError = true; export const load: BuildHook.load = async function () { console.log(`[${PACKAGE_NAME}] Load cocos plugin example in builder.`); allAssets = await Editor.Message.request("asset-db", "query-assets"); }; export const onBeforeBuild: BuildHook.onBeforeBuild = async function ( options: ITaskOptions, result: IBuildResult ) { const pkgOptions = options.packages[PACKAGE_NAME]; if (options.platform == "wechatgame" && pkgOptions.isBiliGame) { // Todo some thing // log(`${PACKAGE_NAME}.webTestOption`, 'onBeforeBuild'); console.log("options", options); let res = await axios.get( `${HTTP_URL}/game-issues/api/issue/getAdaptation/getVersion` ); const orgVersion = res.data.data.version; const currVersion = EXTENSION_VERSION; if (compareVersions(orgVersion, currVersion) == 1) { return new Promise((resolve, reject) => { // 显示一个对话框提示用户 Editor.Dialog.info("biligame-builder有新更新,是否前往下载最新版本?", { buttons: ["继续构建", "停止构建,并前往官网"], title: "插件更新提醒", }).then((result) => { if (result.response === 0) { // 用户选择继续,解析 Promise 以继续构建 resolve(); } else { // 打开默认浏览器并导航到指定URL shell.openExternal( "https://miniapp.bilibili.com/small-game-doc/guide/compatibility/" ); // open('https://miniapp.bilibili.com/small-game-doc/guide/compatibility/'); // 用户选择取消,拒绝 Promise 以阻止构建 reject(new Error("用户取消操作,构建已停止。")); } }); }); } console.log("res", res); } }; export const onBeforeCompressSettings: BuildHook.onBeforeCompressSettings = async function (options: ITaskOptions, result: IBuildResult) { // const pkgOptions = options.packages[PACKAGE_NAME]; // if (pkgOptions.webTestOption) { // console.debug('webTestOption', true); // } // Todo some thing console.debug("get settings test", result.settings); }; export const onAfterCompressSettings: BuildHook.onAfterCompressSettings = async function (options: ITaskOptions, result: IBuildResult) { // Todo some thing console.log("webTestOption", "onAfterCompressSettings"); }; export const onAfterBuild: BuildHook.onAfterBuild = async function ( options: ITaskOptions, result: IBuildResult ) { // change the uuid to test let orgVersion = Editor.App.version; let version = Editor.App.version.split(".").join(""); const pkgOptions = options.packages[PACKAGE_NAME]; console.log("options.packages:", JSON.stringify(options.packages)); if (options.platform == "wechatgame" && pkgOptions.isBiliGame) { console.log("正在构建bilibili小游戏~"); try { console.log("开始拉取适配文件"); // `https://miniapp.bilibili.com/game-issues/api/issue/getAdaptation?version=${orgVersion.slice( let res = await axios.get( `${HTTP_URL}/game-issues/api/issue/getAdaptation?version=${orgVersion.slice( 0, 3 )}` ); let newRequireStatements = ""; const data = res.data.data; console.log("拉取到了适配文件", data.files); if (data?.files?.length) { const extensionDir = __dirname; const srcDir = path.join(extensionDir, "../static/adapters/download"); const biligamePath = path.join(result.dest, "../biligame"); // 创建新目录 if (!fs.existsSync(biligamePath)) { console.log("创建biligame目录"); fs.mkdirSync(biligamePath, { recursive: true }); } clearDirectory(srcDir); if (!fs.existsSync(srcDir)) { console.log("创建适配文件目录"); fs.mkdirSync(srcDir, { recursive: true }); } clearDirectory(biligamePath); copyDirectory(result.dest, biligamePath); for (let i = 0; i < data?.files?.length; i++) { const fileName = Object.keys(data.files[i])[0]; console.log("fileName", fileName); newRequireStatements = newRequireStatements + `require('./${fileName}');\n`; const localFilePath = path.join(biligamePath, `${fileName}`); const fileRes = await axios({ method: "get", url: data.files[i][fileName], responseType: "stream", }); console.log("fileRes", fileRes); // 把适配文件,写入biligame目录 writeFile(localFilePath, fileRes); } renameWebAdapterFiles(biligamePath); console.log("写入适配文件成功!"); // if (version.length < 3) { // version = version + ""; // } // let versionNum = parseInt(version); const destDir = path.join(biligamePath, "."); // 'result.dest' 是构建目录的路径 // 修改 game.js const gameJsPath = path.join(destDir, "game.js"); let gameJsContent = fs.readFileSync(gameJsPath, "utf8"); if (compareVersions(orgVersion, "3.6.0") == -1) { console.log("引擎版本小于 3.6.x, 使用第1套适配方案"); gameJsContent = gameJsContent.replace( "require('./libs/wrapper/builtin/index')", "" ); } else { console.log("引擎版本大于等于 3.6.x, 使用第2套适配方案"); let start = gameJsContent.indexOf("require('./web-adapter"); if (start !== -1) { // 向前找到前一个分号 let prevSemicolon = gameJsContent.lastIndexOf(';', start) + 1; // 向后找到后一个分号 let nextSemicolon = gameJsContent.indexOf(';', start) + 1; if (prevSemicolon !== -1 && nextSemicolon !== -1) { // 删除这个区间的内容 gameJsContent = gameJsContent.slice(0, prevSemicolon) + gameJsContent.slice(nextSemicolon); } } console.log("gameJsContent", gameJsContent); } // 下方添加launchSuccess(); gameJsContent += "\nif(bl){bl.launchSuccess()};"; // 将新的 require 语句添加到 game.js 的开头 gameJsContent = newRequireStatements + gameJsContent; // 将修改后的内容写回 game.js fs.writeFileSync(gameJsPath, gameJsContent, "utf8"); console.log("game.js适配成功"); const gameJsonPath = path.join(destDir, "game.json"); let gameJsonContent = fs.readFileSync(gameJsonPath, "utf8"); let gameJsonFormat = JSON.parse(gameJsonContent); if (!gameJsonFormat["appId"]) { console.log("没有appId,正在写入中~"); gameJsonFormat["appId"] = pkgOptions["biliGameAppId"] || options.packages["wechatgame"].appid; gameJsonFormat["version"] = pkgOptions["biliGameVersion"] || "1.0.0"; let newGameJsonContent = JSON.stringify(gameJsonFormat); fs.writeFileSync(gameJsonPath, newGameJsonContent, "utf8"); } console.log("game.json适配成功!"); if ( compareVersions(orgVersion, "3.8.0") == 1 || compareVersions(orgVersion, "3.8.0") == 0 ) { if (compareVersions(orgVersion, "3.8.3") == -1) { console.log("版本大于等于3.8.0并且小于3.8.3,开始对 spine wasm 进行适配~"); if (options.packages["wechatgame"].wasmSubpackage) { console.log("已开启 wasm 分包策略,开始处理~"); // 读取 spine.asm.js 和 spine.js.mem 的内容 let spineAsmModulePath = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.asm.js" ); let spineMemModulePath = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.js.mem.js" ); if (compareVersions(orgVersion, "3.8.2") == -1) { console.log("版本低于 3.8.2, 不支持wasm分包, 退出适配"); return; } const spineAsmCode = fs.readFileSync(spineAsmModulePath, "utf8"); const spineMemCode = fs.readFileSync(spineMemModulePath, "utf8"); // 替换 game.js 中的相应模块定义 const gameJsPath = path.join(destDir, "cocos-js/chunks/game.js"); let gameJsContent = fs.readFileSync(gameJsPath, "utf8"); console.log("正在构建chunks/game.js语法树~"); gameJsContent = replaceSystemRegister( gameJsContent, "external:emscripten/spine/spine.asm.js", spineAsmCode ); gameJsContent = replaceSystemRegister( gameJsContent, "external:emscripten/spine/spine.js.mem", spineMemCode ); // 将修改后的 game.js 写回文件 fs.writeFileSync(gameJsPath, gameJsContent, "utf8"); console.log("已生成新的chunks/game.js~"); console.log("spine.asm.js 和 spine.js.mem 替换完成"); console.log("正在拷贝 spine.js.mem.bin ~"); const assetPath = path.join(destDir, "cocos-js/assets"); const spineMemBin = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.js.mem.bin" ); const targetFile = path.join(assetPath, "spine.js.mem.bin"); // 拷贝文件 fs.copyFileSync(spineMemBin, targetFile); console.log("拷贝 spine.js.mem.bin 成功~"); console.log("开始处理cc文件~"); // 指定目录路径 const cocosJSPath = path.join(destDir, "cocos-js"); // 正则表达式匹配可能的 JavaScript 文件 const regex = /^cc(\..*)?\.js$/; let ccPath = ""; // 读取目录内容 fs.readdir(cocosJSPath, (err, files) => { if (err) { return console.error("Unable to scan directory: " + err); } // 过滤出符合条件的文件 const filteredFiles = files.filter((file) => regex.test(file)); // 处理每个找到的文件 filteredFiles.forEach((file) => { ccPath = path.join(cocosJSPath, file); // console.log('Found file:', ccPath); let ccFileCode = fs.readFileSync(ccPath, "utf8"); // console.log("originCode:", ccFileCode); const funcStr = getOnPostInfrastructureInitDelegateAddFuncStr( ccFileCode, true ); let newCode = replaceSpineWasm(funcStr); newCode = insertFuncs(newCode); if (newCode.endsWith(";")) { newCode = newCode.slice(0, newCode.length - 1); } // fs.copyFileSync(ccPath, path.join(cocosJSPath, "cc.old.js")); ccFileCode = ccFileCode.replace(funcStr, newCode); // console.log("newCode", newCode); // console.log("newCCFileCode", ccFileCode); fs.writeFileSync(ccPath, ccFileCode, "utf8"); console.log("重写cc.js 成功!"); }); if (filteredFiles.length === 0) { console.log("No matching JavaScript files found."); } }); } else { console.log("未开启 wasm 分包策略,开始处理~"); let spineAsmModulePath = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.asm.js" ); let spineJsModulePath = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.js.js" ); if (compareVersions(orgVersion, "3.8.2") == -1) { console.log("版本低于 3.8.2, 选用3.8.1适配文件"); spineAsmModulePath = path.join( extensionDir, "../static/spine/cocos3.8.1/wasm-subpackage-close/spine.asm.js" ); spineJsModulePath = path.join( extensionDir, "../static/spine/cocos3.8.1/wasm-subpackage-close/spine.js.js" ); } const spineAsmCode = fs.readFileSync(spineAsmModulePath, "utf8"); const spineJsCode = fs.readFileSync(spineJsModulePath, "utf8"); const cocosJSPath = path.join(destDir, "cocos-js"); console.log("正在拷贝 spine.js.mem.bin ~"); const assetPath = path.join(destDir, "cocos-js/assets"); let spineMemBin = path.join( extensionDir, "../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.js.mem.bin" ); if (compareVersions(orgVersion, "3.8.2") == -1) { console.log("版本低于 3.8.2, 选用3.8.1 spineMemBin 适配文件"); spineMemBin = path.join( extensionDir, "../static/spine/cocos3.8.1/wasm-subpackage-close/spine.js.mem.bin" ); } const targetFile = path.join(assetPath, "spine.js.mem.bin"); fs.copyFileSync(spineMemBin, targetFile); fs.readdir(cocosJSPath, (err, files) => { files.forEach((file) => { let filePath = path.join(cocosJSPath, file); if (file.startsWith("spine.asm-")) { console.log("正在重写 spine.asm.js~"); fs.writeFileSync(filePath, spineAsmCode, "utf8"); } if (file.startsWith("spine.js-")) { console.log("正在重写 spine.js.js~"); fs.writeFileSync(filePath, spineJsCode, "utf8"); } if (file.startsWith("_virtual_cc")) { let ccFileCode = fs.readFileSync(filePath, "utf8"); const funcStr = getOnPostInfrastructureInitDelegateAddFuncStr( ccFileCode, false ); let newCode = replaceSpineWasm(funcStr); newCode = insertFuncs(newCode); if (newCode.endsWith(";")) { newCode = newCode.slice(0, newCode.length - 1); } ccFileCode = ccFileCode.replace(funcStr, newCode); fs.writeFileSync(filePath, ccFileCode, "utf8"); console.log("重新_virtual_cc.js 成功!"); } }); }); } } else { console.log("版本大于等于3.8.3,开始适配"); } } console.log("bilibili小游戏构建成功!"); } } catch (error) { console.log("文件下载失败:", error); } } // test onError hook // throw new Error('Test onError'); }; export const unload: BuildHook.unload = async function () { console.log(`[${PACKAGE_NAME}] Unload cocos plugin example in builder.`); }; export const onError: BuildHook.onError = async function (options, result) { // Todo some thing console.warn(`${PACKAGE_NAME} run onError`); }; export const onBeforeMake: BuildHook.onBeforeMake = async function ( root, options ) { console.log(`onBeforeMake: root: ${root}, options: ${options}`); }; export const onAfterMake: BuildHook.onAfterMake = async function ( root, options ) { console.log(`onAfterMake: root: ${root}, options: ${options}`); };