Files
Max-Cocos-Demo/extensions/max-studio/source/hotupdate/hooks.ts
2025-10-28 21:55:41 +08:00

228 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
BuildHook,
IBuildResult,
IBuildTaskOption,
} from "@cocos/creator-types/editor/packages/builder/@types/public";
import path from "path";
import { existsSync, readFile, writeFile } from "fs";
import { ensureDirSync } from "fs-extra";
import { generator, GenPackageConfig } from "./version_generator";
import { createMinIOUploader, MinIOConfig } from "./minio-uploader";
const injectScript = `(function () {
const native = globalThis?.jsb || window.jsb || native;
if (typeof native === 'object') {
var hotUpdateSearchPaths = localStorage.getItem('HotUpdateSearchPaths');
if (hotUpdateSearchPaths) {
var paths = JSON.parse(hotUpdateSearchPaths);
native.fileUtils.setSearchPaths(paths);
var fileList = [];
var storagePath = paths[0] || '';
var tempPath = storagePath + '_temp/';
var baseOffset = tempPath.length;
if (native.fileUtils.isDirectoryExist(tempPath) && !native.fileUtils.isFileExist(tempPath + 'project.manifest.temp')) {
native.fileUtils.listFilesRecursively(tempPath, fileList);
fileList.forEach(srcPath => {
var relativePath = srcPath.substr(baseOffset);
var dstPath = storagePath + relativePath;
if (srcPath[srcPath.length - 1] === '/') {
native.fileUtils.createDirectory(dstPath)
}
else {
if (native.fileUtils.isFileExist(dstPath)) {
native.fileUtils.removeFile(dstPath)
}
native.fileUtils.renameFile(srcPath, dstPath);
}
})
native.fileUtils.removeDirectory(tempPath);
}
}
}
})();`;
export const onBeforeBuild: BuildHook.onBeforeBuild = async function (
options: IBuildTaskOption,
result: IBuildResult
) {};
export const onAfterBuild: BuildHook.onAfterBuild = async function (
options: IBuildTaskOption,
result: IBuildResult
) {
console.log("onAfterBuild platform", options.platform);
if (!["android", "mac", "ios", "windows"].includes(options.platform)) {
return;
}
let url = path.join(result.dest, "data", "main.js");
console.log("onAfterBuild url", url);
if (!existsSync(url)) {
url = path.join(result.dest, "assets", "main.js");
}
console.log("onAfterBuild url", url);
const hotupdateConfig: {
hotupdatePluginCheck: boolean;
configInput: string;
outInput: string;
// MinIO 配置
minioAutoUpload: boolean;
minioEndpoint: string;
minioAccessKeyId: string;
minioSecretAccessKey: string;
minioBucketName: string;
minioUseSSL: boolean;
minioPathPrefix: string;
} = options.packages?.["max-framework"];
console.log(
"onAfterBuild hotupdateConfig",
url,
hotupdateConfig.hotupdatePluginCheck,
hotupdateConfig.configInput,
hotupdateConfig.outInput,
JSON.stringify(hotupdateConfig)
);
if (!!hotupdateConfig.hotupdatePluginCheck) {
// 验证配置参数
if (!hotupdateConfig.configInput || !hotupdateConfig.outInput) {
console.error("热更新配置不完整configInput 和 outInput 不能为空");
return;
}
try {
// configInput 是带 project:// 前缀的,需要转换为实际文件路径
let configPath = hotupdateConfig.configInput;
if (configPath.startsWith("project://")) {
configPath = path.resolve(
Editor.Project.path,
configPath.replace("project://", "")
);
} else if (!path.isAbsolute(configPath)) {
configPath = path.resolve(Editor.Project.path, configPath);
}
let outInput = hotupdateConfig.outInput;
if (outInput.startsWith("project://")) {
outInput = path.resolve(
Editor.Project.path,
outInput.replace("project://", "")
);
} else if (!path.isAbsolute(outInput)) {
outInput = path.resolve(Editor.Project.path, outInput);
}
console.log("onAfterBuild configPath", configPath, outInput);
const obj = require(configPath) as GenPackageConfig[];
for (const item of obj) {
// 验证包名不能为空
if (!item.pkgName || item.pkgName.trim() === "") {
console.error("热更新包名不能为空:", item);
continue;
}
const source = path.join(result.dest, "data");
console.log("开始生成Package: ", item.pkgName, source);
ensureDirSync(
path.join(outInput, item.pkgName)
);
generator(source, outInput, item);
}
// MinIO上传功能
if (hotupdateConfig.minioAutoUpload) {
console.log("开始MinIO自动上传流程...");
await uploadToMinIO(hotupdateConfig, outInput, obj);
}
} catch (error) {
console.error("热更新配置文件加载失败:", error);
return;
}
}
return new Promise((resolve, reject) => {
readFile(url, "utf8", (err, data) => {
if (err) {
reject(err);
return;
}
const newStr = injectScript + data;
writeFile(url, newStr, (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
});
};
/**
* 上传到MinIO
* @param config 热更新配置
* @param outputPath 输出路径
* @param packages 包配置列表
*/
async function uploadToMinIO(
config: {
minioEndpoint: string;
minioAccessKeyId: string;
minioSecretAccessKey: string;
minioBucketName: string;
minioUseSSL: boolean;
minioPathPrefix: string;
},
outputPath: string,
packages: GenPackageConfig[]
) {
try {
// 验证MinIO配置
if (!config.minioEndpoint || !config.minioAccessKeyId ||
!config.minioSecretAccessKey || !config.minioBucketName) {
console.error("MinIO配置不完整跳过上传");
return;
}
// 创建MinIO上传器
const minioConfig: MinIOConfig = {
endpoint: config.minioEndpoint,
accessKeyId: config.minioAccessKeyId,
secretAccessKey: config.minioSecretAccessKey,
bucketName: config.minioBucketName,
useSSL: config.minioUseSSL,
pathPrefix: config.minioPathPrefix
};
const uploader = createMinIOUploader(minioConfig);
// 上传每个包
for (const pkg of packages) {
if (!pkg.pkgName || pkg.pkgName.trim() === "") {
continue;
}
const packageDir = path.join(outputPath, pkg.pkgName);
if (!existsSync(packageDir)) {
console.error(`包目录不存在: ${packageDir}`);
continue;
}
console.log(`开始上传包到MinIO: ${pkg.pkgName}`);
const result = await uploader.uploadDirectory(packageDir, pkg.pkgName);
if (result.success) {
console.log(`${pkg.pkgName} 上传成功,共 ${result.uploadedFiles.length} 个文件`);
console.log(`上传的文件: ${result.uploadedFiles.join(', ')}`);
} else {
console.error(`${pkg.pkgName} 上传失败: ${result.error}`);
}
}
console.log("所有包MinIO上传完成");
} catch (error) {
console.error("MinIO上传过程中发生错误:", error);
}
}