@quick-game/cli
Version:
Command line interface for rapid qg development
164 lines (154 loc) • 7.23 kB
JavaScript
;var _WeakMap = require("@babel/runtime-corejs2/core-js/weak-map");var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");_Object$defineProperty(exports, "__esModule", { value: true });exports.default = rpk;var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));var _path = _interopRequireDefault(require("path"));
var _jszip = _interopRequireDefault(require("jszip"));
var _crypto = _interopRequireDefault(require("crypto"));
var _index = require("../../cli-shared-utils/index.js");
var _bundle = require("./bundle.js");
var _constanst = require("./constanst.js");
var fflate = _interopRequireWildcard(require("fflate"));function _getRequireWildcardCache(e) {if ("function" != typeof _WeakMap) return null;var r = new _WeakMap(),t = new _WeakMap();return (_getRequireWildcardCache = function (e) {return e ? t : r;})(e);}function _interopRequireWildcard(e, r) {if (!r && e && e.__esModule) return e;if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };var t = _getRequireWildcardCache(r);if (t && t.has(e)) return t.get(e);var n = { __proto__: null },a = _Object$defineProperty && _Object$getOwnPropertyDescriptor;for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {var i = a ? _Object$getOwnPropertyDescriptor(e, u) : null;i && (i.get || i.set) ? _Object$defineProperty(n, u, i) : n[u] = e[u];}return n.default = e, t && t.set(e, n), n;}
// 文件摘要包(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.default.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.default();
metaZipFile.file(
'hash.json',
(0, _stringify.default)({
algorithm: 'SHA-256',
digests: fileHashObj
})
);
const content = await metaZipFile.generateAsync(COMPRESS_OPTS);
const digestHash = {
name: 'hash.json',
hash: getBufferDigest(content)
};
return (0, _bundle.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((0, _stringify.default)(res));
for (let index = newRes.length - 1; index >= 0; index--) {
const element = newRes[index];
const extname = _path.default.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 小游戏配置文件
*/
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();
_index.fs.ensureDirSync(pathTo);
(0, _index.info)(`${_constanst.LOG_TITLE}开始fflate压缩:${name} from:${pathFrom}`);
const fileHashObj = {}; // 文件hash列表 缓存
const zipFileHashs = []; // 文件hash列表打包使用
const fileContents = {}; // fflate压缩的文件列表
const privatekey = _index.fs.readFileSync(signFile.privatekey);
const certificate = _index.fs.readFileSync(signFile.certificate);
// 对打包的文件 进行排序
const newBuildRess = sortAgain(buildReses, manifest);
newBuildRess.forEach((name) => {
const file = _path.default.join(pathFrom, name);
const buf = _index.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.default((resolve, reject) => {
fflate.zip(files, options, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
});
});
return Buffer.from(zippedContent);
} catch (err) {
return _promise.default.reject(new Error(`compress failed: ${err}`));
}
};
// fflate 打包zip包
const content = await fflateZip(fileContents, { level: 9 });
const size = Number(content.length / 1024).toFixed(2);
// 对压缩包进行签名
(0, _bundle.signZip)(content, zipFileHashs, privatekey, certificate, _path.default.join(pathTo, `${name}`));
(0, _index.info)(`${_constanst.LOG_TITLE}压缩fflate完成:${name} 耗时:${(new Date().valueOf() - rpkStartTime) / 1000}s 大小:${size}Kb`);
}