185 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import fs, { existsSync } from "fs";
 | 
						|
import { ensureDirSync, copyFileSync } from "fs-extra";
 | 
						|
import { createHash } from "crypto";
 | 
						|
import path from "path";
 | 
						|
import { CfgUtils } from "./cfgutils";
 | 
						|
 | 
						|
 | 
						|
 | 
						|
export interface GenPackageConfig {
 | 
						|
    pkgName: string;
 | 
						|
    files: string[];
 | 
						|
    version?: string;
 | 
						|
}
 | 
						|
 | 
						|
class Manifest {
 | 
						|
    packageUrl: string = "";
 | 
						|
    remoteManifestUrl: string = "";
 | 
						|
    remoteVersionUrl: string = "";
 | 
						|
    version: string = "";
 | 
						|
    assets: object = {};
 | 
						|
    searchPaths: string[] = [];
 | 
						|
}
 | 
						|
 | 
						|
class VersionManifest {
 | 
						|
    packageUrl: string = "";
 | 
						|
    remoteManifestUrl: string = "";
 | 
						|
    remoteVersionUrl: string = "";
 | 
						|
    version: string = "";
 | 
						|
}
 | 
						|
 | 
						|
function readDir(buildDir: string, destination: string, dir: string, obj: any) {
 | 
						|
    try {
 | 
						|
        const stat = fs.statSync(dir);
 | 
						|
        if (stat.isFile()) {
 | 
						|
            const subpath = dir;
 | 
						|
            const size = stat.size;
 | 
						|
            const md5 = createHash('md5').update(fs.readFileSync(subpath)).digest('hex');
 | 
						|
            const compressed = path.extname(subpath).toLowerCase() === '.zip';
 | 
						|
 | 
						|
            let relv = path.relative(buildDir, subpath);
 | 
						|
            relv = relv.replace(/\\/g, '/');
 | 
						|
            relv = encodeURI(relv);
 | 
						|
            obj[relv] = {
 | 
						|
                "size": size,
 | 
						|
                "md5": md5
 | 
						|
            };
 | 
						|
            if (compressed) {
 | 
						|
                obj[relv].compressed = true;
 | 
						|
            }
 | 
						|
            const index = relv.lastIndexOf("/");
 | 
						|
            const subdir = relv.substring(0, index);
 | 
						|
            mkdirSync(path.join(destination, subdir));
 | 
						|
            copyFileSync(subpath, path.join(destination, relv));
 | 
						|
        } else {
 | 
						|
            const subpaths = fs.readdirSync(dir);
 | 
						|
            for (let i = 0; i < subpaths.length; i++) {
 | 
						|
                if (subpaths[i][0] === '.') {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                const subpath = path.join(dir, subpaths[i]);
 | 
						|
                const stat = fs.statSync(subpath);
 | 
						|
                if (stat.isDirectory()) {
 | 
						|
                    readDir(buildDir, destination, subpath, obj);
 | 
						|
                } else if (stat.isFile()) {
 | 
						|
                    const size = stat.size;
 | 
						|
                    const md5 = createHash('md5').update(fs.readFileSync(subpath)).digest('hex');
 | 
						|
                    const compressed = path.extname(subpath).toLowerCase() === '.zip';
 | 
						|
 | 
						|
                    let relv = path.relative(buildDir, subpath);
 | 
						|
                    relv = relv.replace(/\\/g, '/');
 | 
						|
                    relv = encodeURI(relv);
 | 
						|
                    obj[relv] = {
 | 
						|
                        "size": size,
 | 
						|
                        "md5": md5
 | 
						|
                    };
 | 
						|
                    if (compressed) {
 | 
						|
                        obj[relv].compressed = true;
 | 
						|
                    }
 | 
						|
                    const index = relv.lastIndexOf("/");
 | 
						|
                    const subdir = relv.substring(0, index);
 | 
						|
                    mkdirSync(path.join(destination, subdir));
 | 
						|
                    copyFileSync(subpath, path.join(destination, relv));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } catch (error) {
 | 
						|
        console.error(error);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function mkdirSync(path: string) {
 | 
						|
    try {
 | 
						|
        ensureDirSync(path);
 | 
						|
    } catch (error: any) {
 | 
						|
        if (error.code != 'EEXIST') throw error;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
function versionCompare(versionA: string, versionB: string) {
 | 
						|
    const vA = versionA.split('.');
 | 
						|
    const vB = versionB.split('.');
 | 
						|
    for (let i = 0; i < vA.length; ++i) {
 | 
						|
        const a = parseInt(vA[i]);
 | 
						|
        const b = parseInt(vB[i] || '0');
 | 
						|
        if (a === b) {
 | 
						|
            continue;
 | 
						|
        } else {
 | 
						|
            return a - b;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return vB.length > vA.length ? -1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
function clearDir(dir: string) {
 | 
						|
    try {
 | 
						|
        if (existsSync(dir)) {
 | 
						|
            const files = fs.readdirSync(dir);
 | 
						|
            for (const file of files) {
 | 
						|
                const filePath = path.join(dir, file);
 | 
						|
                const stat = fs.statSync(filePath);
 | 
						|
                if (stat.isDirectory()) {
 | 
						|
                    clearDir(filePath);
 | 
						|
                    fs.rmdirSync(filePath);
 | 
						|
                } else {
 | 
						|
                    fs.unlinkSync(filePath);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } catch (err) {
 | 
						|
        console.error('清理目录失败:', err);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
export function generator(buildDir: string, output: string, cfg: GenPackageConfig) {
 | 
						|
    const destination = path.join(output,cfg.pkgName);
 | 
						|
    const manifest = new Manifest();
 | 
						|
    const version = new VersionManifest();
 | 
						|
 | 
						|
    const cfgData = CfgUtils.getCfgData(cfg.pkgName);
 | 
						|
 | 
						|
    if (cfg.version) {
 | 
						|
        cfgData.version = cfg.version;
 | 
						|
    } else {
 | 
						|
        const versionParts = cfgData.version.split('.');
 | 
						|
        const patch = parseInt(versionParts[2] || '0') + 1;
 | 
						|
        cfgData.version = `${versionParts[0]}.${versionParts[1]}.${patch}`;
 | 
						|
    }
 | 
						|
 | 
						|
    manifest.version = cfgData.version;
 | 
						|
    manifest.packageUrl = `${cfg.pkgName}/${cfgData.version}`;
 | 
						|
    manifest.remoteManifestUrl = `${cfg.pkgName}/project.manifest`;
 | 
						|
    manifest.remoteVersionUrl = `${cfg.pkgName}/version.manifest`;
 | 
						|
    version.version = cfgData.version;
 | 
						|
 | 
						|
    // 清理输出目录
 | 
						|
    clearDir(destination);
 | 
						|
    ensureDirSync(destination);
 | 
						|
 | 
						|
    // 处理文件
 | 
						|
    for (const file of cfg.files) {
 | 
						|
        const filePath = path.join(buildDir, file);
 | 
						|
        if (existsSync(filePath)) {
 | 
						|
            readDir(buildDir, destination, filePath, manifest.assets);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // 生成 manifest 文件到输出目录
 | 
						|
    fs.writeFileSync(path.join(destination, 'project.manifest'), JSON.stringify(manifest, null, 2));
 | 
						|
    fs.writeFileSync(path.join(destination, 'version.manifest'), JSON.stringify(version, null, 2));
 | 
						|
 | 
						|
    // 将 manifest 和 version 文件也存储到 buildDir + "/assets/" + pkgName 目录下
 | 
						|
    const buildAssetsDir = path.join(buildDir, "assets", cfg.pkgName);
 | 
						|
    ensureDirSync(buildAssetsDir);
 | 
						|
    fs.writeFileSync(path.join(buildAssetsDir, 'project.manifest'), JSON.stringify(manifest, null, 2));
 | 
						|
    fs.writeFileSync(path.join(buildAssetsDir, 'version.manifest'), JSON.stringify(version, null, 2));
 | 
						|
 | 
						|
    console.log(`Manifest文件已生成到构建目录: ${buildAssetsDir}`);
 | 
						|
    console.log(`Manifest文件已生成到输出目录: ${destination}`);
 | 
						|
 | 
						|
    // 保存配置
 | 
						|
    CfgUtils.savaConfig(cfg.pkgName, cfgData);
 | 
						|
 | 
						|
    console.log(`热更新包生成完成: ${cfg.pkgName} v${cfgData.version}`);
 | 
						|
}
 |