UNPKG

@honor-minigame/cli

Version:

honor minigame pack cli

166 lines (155 loc) 5.49 kB
import path from 'path' import JSZip from 'jszip' import crypto from 'crypto' import fs from 'fs-extra' import { info } from '../../utils/logger.js' import { signZip } from './bundle.js' import { LOG_TITLE } from './constanst.js' import * as fflate from 'fflate' // 文件摘要包(zip 文件) const DIGEST_ZIP_PATH = 'META-INF/CERT' // 压缩参数,设置输出 buffer,以便对 buffer 进行操作 const COMPRESS_OPTS = { type: 'nodebuffer', compression: 'DEFLATE', compressionOptions: { level: 9 } } // 获取buf的hash function getBufferDigest(buf) { const signer = crypto.createHash('SHA256') signer.update(buf) return signer.digest() } /** * 返回摘要文件签名buff d * @param {} fileHashObj - 文件资源列表和对应hash * @param {Buffer} privatekey - 私钥文件 buffer * @param {Buffer} certificate - 证书文件 buffer * @returns {Object} signedDigestBuf - 摘要文件签名buff */ async function signZipResourcesMeta(fileHashObj, privatekey, certificate, pathTo) { // ZIP元文件 const metaZipFile = new JSZip() metaZipFile.file( 'hash.json', JSON.stringify({ algorithm: 'SHA-256', digests: fileHashObj }) ) const content = await metaZipFile.generateAsync(COMPRESS_OPTS) const digestHash = { name: 'hash.json', hash: getBufferDigest(content) } return signZip(content, [digestHash], privatekey, certificate) } /** * 对文件进行重新 排序 * 第一位 入口文件 第二位 js文件 第三位 manifest(读取到这个文件引擎会执行js) * 第四位 icon和防沉迷页面图片 第五位 游戏的资源文件 * @param {Array(String)} res 文件路径数组 * @param {Object} manifest 小游戏配置文件 */ function sortAgain(res, manifest) { const firstFileArr = ['main.js', 'game.js'] const firstFile = [] const secondFile = [] const imageFileArr = [manifest && manifest.icon.substr(1), manifest && manifest.homePage && manifest.homePage.substr(1)] const imageFile = [] const manifestFileArr = ['manifest.json'] const manifestFile = [] const lastFile = [] if (!res) { return [] } // 将输入数组深拷贝,避免源数据被修改,执行后续操作时出问题 const newRes = JSON.parse(JSON.stringify(res)) for (let index = newRes.length - 1; index >= 0; index--) { const element = newRes[index] const extname = path.extname(element) if (firstFileArr.indexOf(element) > -1) { // 入口文件 firstFile.push(element) newRes.splice(index, 1) // 其次是icon和首页图片 } else if (extname === '.js') { // js文件 secondFile.push(element) newRes.splice(index, 1) } else if (manifestFileArr.indexOf(element) > -1) { // 配置文件 manifest.json manifestFile.push(element) newRes.splice(index, 1) } else if (imageFileArr.indexOf(element) > -1) { // icon和防沉迷页面图片 imageFile.push(element) newRes.splice(index, 1) } else { lastFile.push(element) } } return firstFile.concat(secondFile, manifestFile, imageFile, lastFile) } /** * 打rpk包 * @param {String} name rpk包的文件名 * @param {Array(String)} buildReses rpk对应的资源路径 * @param {String} pathFrom 打包rpk到何处 * @param {String} pathTo 打包rpk到何处 * @param {Object} signFile 签名文件信息 * @param {Object} manifest 小游戏配置文件 */ export default async function rpk(name, buildReses, pathFrom, pathTo, signFile, manifest) { await fflateRpk(name, buildReses, pathFrom, pathTo, signFile, manifest) } async function fflateRpk(name, buildReses, pathFrom, pathTo, signFile, manifest) { // 保证打包路径存在,不存在就创建 const rpkStartTime = (new Date()).valueOf() fs.ensureDirSync(pathTo) info(`${LOG_TITLE}开始fflate压缩:${name} from:${pathFrom}`) const fileHashObj = {} // 文件hash列表 缓存 const zipFileHashs = [] // 文件hash列表打包使用 const fileContents = {} // fflate压缩的文件列表 const privatekey = fs.readFileSync(signFile.privatekey) const certificate = fs.readFileSync(signFile.certificate) // 对打包的文件 进行排序 const newBuildRess = sortAgain(buildReses, manifest) newBuildRess.forEach(name => { const file = path.join(pathFrom, name) const buf = fs.readFileSync(file) const fileHash = getBufferDigest(buf) fileHashObj[name] = fileHash.toString('hex') zipFileHashs.push({ name, hash: fileHash }) // 添加文件到压缩文件列表 fileContents[name] = new Uint8Array(buf) }) const fflateZip = async ( files, compressOptions ) => { try { const options = compressOptions || {} const zippedContent = await new Promise((resolve, reject) => { fflate.zip(files, options, (err, data) => { if (err) { reject(err) } resolve(data) }) }) return Buffer.from(zippedContent) } catch (err) { return Promise.reject(new Error(`compress failed: ${err}`)) } } // fflate 打包zip包 const content = await fflateZip(fileContents, { level: 9 }) const size = (Number(content.length / 1024)).toFixed(2) // 对压缩包进行签名 signZip(content, zipFileHashs, privatekey, certificate, path.join(pathTo, `${name}`)) info(`${LOG_TITLE}压缩fflate完成:${name} 耗时:${((new Date()).valueOf() - rpkStartTime) / 1000}s 大小:${size}Kb`) }