mirror of
https://gitee.com/dgflash/oops-plugin-hot-update.git
synced 2026-05-08 12:06:40 +08:00
261 lines
9.9 KiB
TypeScript
261 lines
9.9 KiB
TypeScript
import { PACKAGE_NAME } from "./hooks";
|
|
|
|
/** 本地存储文件夹名,配套框架中的热更脚本 */
|
|
var storage = "oops_framework_remote";
|
|
var crypto = require("crypto");
|
|
var fs = require("fs");
|
|
var path = require("path");
|
|
var folder = null;
|
|
if (Editor.App.version.indexOf('3.5') > -1) {
|
|
folder = "data";
|
|
}
|
|
else {
|
|
folder = "assets";
|
|
}
|
|
|
|
class HotUpdate {
|
|
create(options: any) {
|
|
if (!this.isEnable(options)) return;
|
|
|
|
switch (options.platform) {
|
|
case "wechatgame":
|
|
this.wechatgame(options)
|
|
break;
|
|
case "android":
|
|
case "ios":
|
|
this.init(options);
|
|
this.mainJs(options);
|
|
this.manifest(options);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private wechatgame(options) {
|
|
var projectPath = Editor.Project.path;
|
|
var buildPath = `${options.buildPath.replace('project:/', projectPath)}/${options.outputName}`;
|
|
var hotUpdateAssetsPath = path.join(projectPath, storage);
|
|
this.mkdirSync(hotUpdateAssetsPath);
|
|
|
|
var platformPath = path.join(hotUpdateAssetsPath, options.outputName);
|
|
this.deleteDirSync(platformPath);
|
|
this.mkdirSync(platformPath);
|
|
|
|
// 构建后默认资源目录
|
|
var remote = 'remote';
|
|
var assetPath = remote;
|
|
var destPath = path.join(platformPath, assetPath);
|
|
assetPath = path.join(buildPath, assetPath);
|
|
Build.Utils.copyDirSync(assetPath, destPath);
|
|
console.log('[' + PACKAGE_NAME + '] Wechatgame 远程资源拷贝完成');
|
|
|
|
var projectRemotePath = path.join(buildPath, remote);
|
|
this.deleteDirSync(projectRemotePath);
|
|
}
|
|
|
|
private isEnable(options) {
|
|
var packageOptions = options.packages[PACKAGE_NAME];
|
|
if (!packageOptions.hotUpdateEnable) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private mainJs(options) {
|
|
var projectPath = Editor.Project.path;
|
|
var buildPath = `${options.buildPath.replace('project:/', projectPath)}/${options.outputName}`;
|
|
var mainScriptPath = path.resolve(`${buildPath}/${folder}/main.js`);
|
|
var mainScript = fs.readFileSync(mainScriptPath).toString('utf-8');
|
|
mainScript =
|
|
`// ---- 扩展注入热更新脚本开始 ----
|
|
jsb.fileUtils.addSearchPath(jsb.fileUtils.getWritablePath() + "${storage}", true);
|
|
var fileList = [];
|
|
var storagePath = "${storage}";
|
|
var tempPath = storagePath + "_temp/";
|
|
var baseOffset = tempPath.length;
|
|
|
|
if (jsb.fileUtils.isDirectoryExist(tempPath) && !jsb.fileUtils.isFileExist(tempPath + 'project.manifest.temp')) {
|
|
jsb.fileUtils.listFilesRecursively(tempPath, fileList);
|
|
fileList.forEach(srcPath => {
|
|
var relativePath = srcPath.substr(baseOffset);
|
|
var dstPath = storagePath + relativePath;
|
|
if (srcPath[srcPath.length - 1] === "/") {
|
|
jsb.fileUtils.createDirectory(dstPath)
|
|
}
|
|
else {
|
|
if (jsb.fileUtils.isFileExist(dstPath)) {
|
|
jsb.fileUtils.removeFile(dstPath)
|
|
}
|
|
jsb.fileUtils.renameFile(srcPath, dstPath);
|
|
}
|
|
})
|
|
jsb.fileUtils.removeDirectory(tempPath);
|
|
}
|
|
// ---- 扩展注入热更新脚本结束 ----` + mainScript;
|
|
fs.writeFileSync(mainScriptPath, mainScript);
|
|
console.log('[' + PACKAGE_NAME + '] 注入 main.js 成功');
|
|
}
|
|
|
|
private init(options) {
|
|
var projectPath = Editor.Project.path;
|
|
var buildPath = `${options.buildPath.replace('project:/', projectPath)}/${options.outputName}`;
|
|
var assetsRootPath = path.resolve(`${buildPath}/${folder}`);
|
|
var projectManifestName = 'project.manifest';
|
|
var destManifestPath = path.join(assetsRootPath, projectManifestName);
|
|
fs.writeFileSync(destManifestPath, JSON.stringify({}));
|
|
console.log('[' + PACKAGE_NAME + '] 生成 project.manifest 成功');
|
|
}
|
|
|
|
private manifest(options) {
|
|
var packageOptions = options.packages[PACKAGE_NAME];
|
|
var remoteUrl = packageOptions.hotUpdateAddress;
|
|
if (remoteUrl.endsWith('/')) {
|
|
remoteUrl = remoteUrl.slice(0, -1);
|
|
}
|
|
|
|
// 同版本构建次数
|
|
var hotUpdateBuildNum = !isNaN(Number(packageOptions.hotUpdateBuildNum)) ? Number(packageOptions.hotUpdateBuildNum) : 1;
|
|
var hotUpdateVersion = `${packageOptions.hotUpdateVersion.trim()}.${hotUpdateBuildNum.toFixed()}`;
|
|
var projectPath = Editor.Project.path;
|
|
var buildPath = `${options.buildPath.replace('project:/', projectPath)}/${options.outputName}`;
|
|
var assetsRootPath = path.resolve(`${buildPath}/${folder}`);
|
|
var projectManifestName = 'project.manifest';
|
|
var versionManifestName = 'version.manifest';
|
|
var destManifestPath = path.join(assetsRootPath, projectManifestName);
|
|
fs.unlinkSync(destManifestPath);
|
|
|
|
// 构建后默认资源目录
|
|
var assetsPaths = ['src', 'assets', 'jsb-adapter'];
|
|
|
|
// 初始化 manifest
|
|
var packageUrl = `${remoteUrl}/${options.platform}/${hotUpdateVersion}`;
|
|
var manifest = {
|
|
packageUrl: encodeURI(packageUrl),
|
|
version: hotUpdateVersion,
|
|
searchPaths: [storage],
|
|
remoteManifestUrl: encodeURI(`${remoteUrl}/${options.platform}/${projectManifestName}`),
|
|
remoteVersionUrl: encodeURI(`${remoteUrl}/${options.platform}/${versionManifestName}`),
|
|
assets: {},
|
|
};
|
|
|
|
|
|
// 迭代资源和源码文件夹
|
|
var assetsList = [];
|
|
assetsPaths.forEach((o) => {
|
|
assetsList.push(...this.listDir(path.join(assetsRootPath, o)));
|
|
});
|
|
|
|
// 填充 manifest.assets 对象
|
|
var md5, compressed, assetUrl;
|
|
var assetsObj = {};
|
|
assetsList.forEach((assetStat) => {
|
|
md5 = crypto.createHash('md5').update(fs.readFileSync(assetStat.filePath)).digest('hex');
|
|
compressed = path.extname(assetStat.filePath).toLowerCase() === '.zip';
|
|
assetUrl = path.relative(assetsRootPath, assetStat.filePath);
|
|
assetUrl = assetUrl.replace(/\\/g, '/');
|
|
assetUrl = encodeURI(assetUrl);
|
|
assetsObj[assetUrl] = {
|
|
size: assetStat.size,
|
|
md5: md5,
|
|
};
|
|
|
|
if (compressed) {
|
|
assetsObj[assetUrl].compressed = true;
|
|
}
|
|
});
|
|
manifest.assets = assetsObj;
|
|
|
|
// 热更构建结果存储目录
|
|
var hotUpdateAssetsPath = path.join(projectPath, storage);
|
|
this.mkdirSync(hotUpdateAssetsPath);
|
|
var manifestPath = path.join(hotUpdateAssetsPath, options.platform);
|
|
this.mkdirSync(manifestPath);
|
|
hotUpdateAssetsPath = path.join(manifestPath, hotUpdateVersion);
|
|
this.mkdirSync(hotUpdateAssetsPath);
|
|
|
|
// 如果目录不为空, 先清除
|
|
this.deleteDirSync(hotUpdateAssetsPath);
|
|
|
|
// 保存 project.manifest
|
|
fs.writeFileSync(destManifestPath, JSON.stringify(manifest));
|
|
destManifestPath = path.join(manifestPath, projectManifestName);
|
|
fs.writeFileSync(destManifestPath, JSON.stringify(manifest));
|
|
console.log('[' + PACKAGE_NAME + '] 已成功生成清单文件');
|
|
delete manifest.assets;
|
|
delete manifest.searchPaths;
|
|
|
|
// 保存 version.manifest
|
|
var destVersionPath = path.join(manifestPath, versionManifestName);
|
|
fs.writeFileSync(destVersionPath, JSON.stringify(manifest));
|
|
console.log('[' + PACKAGE_NAME + '] 已成功生成版本文件');
|
|
|
|
// 拷贝构建后的热更资源
|
|
assetsPaths.push(projectManifestName);
|
|
assetsPaths.forEach((assetPath) => {
|
|
var destPath = path.join(hotUpdateAssetsPath, assetPath);
|
|
assetPath = path.join(assetsRootPath, assetPath);
|
|
// 拷贝
|
|
Build.Utils.copyDirSync(assetPath, destPath);
|
|
});
|
|
console.log('[' + PACKAGE_NAME + '] 资源拷贝完成');
|
|
}
|
|
|
|
|
|
// 获取目录内所有文件
|
|
private listDir(assetPath) {
|
|
var fileList = [];
|
|
var stat = fs.statSync(assetPath);
|
|
if (stat.isDirectory()) {
|
|
var subpaths = fs.readdirSync(assetPath);
|
|
for (var i = 0; i < subpaths.length; i++) {
|
|
var subpath = subpaths[i];
|
|
if (subpath[0] === '.') {
|
|
continue;
|
|
}
|
|
subpath = path.join(assetPath, subpath);
|
|
fileList.push(...this.listDir(subpath));
|
|
}
|
|
}
|
|
else if (stat.isFile()) {
|
|
fileList.push({
|
|
filePath: assetPath,
|
|
size: stat.size,
|
|
});
|
|
}
|
|
return fileList;
|
|
}
|
|
|
|
// 创建目录
|
|
private mkdirSync(dirName) {
|
|
try {
|
|
fs.mkdirSync(dirName);
|
|
}
|
|
catch (e) {
|
|
if (e.code !== 'EEXIST')
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// 递归删除目录及文件
|
|
private deleteDirSync(dirName) {
|
|
var files = [];
|
|
if (fs.existsSync(dirName)) {
|
|
// 返回文件和子目录的数组
|
|
files = fs.readdirSync(dirName);
|
|
files.forEach((file) => {
|
|
var curPath = path.join(dirName, file);
|
|
// 同步读取文件夹文件,如果是文件夹,在重复触发函数
|
|
if (fs.statSync(curPath).isDirectory()) {
|
|
this.deleteDirSync(curPath);
|
|
}
|
|
else {
|
|
fs.unlinkSync(curPath);
|
|
}
|
|
});
|
|
|
|
// 清除文件夹
|
|
fs.rmdirSync(dirName);
|
|
}
|
|
}
|
|
}
|
|
|
|
export var hot = new HotUpdate(); |