fy-convertor
Version:
Convert excel/xml/json/bin/protocl/ts/as ...
147 lines • 6.75 kB
JavaScript
import fs from "fs-extra";
import path from "path";
import { toolchain } from "../tools/toolchain.js";
import { runCommand } from "../tools/vendor.js";
import { GitHelper } from "./GitHelper.js";
import { RepoHolder } from "./RepoHolder.js";
const Proto_Git_URL = 'http://192.168.8.72:30000/ppgame/gamedata.git';
export class ProtobufGen {
static ins;
static get Instance() {
if (!ProtobufGen.ins)
ProtobufGen.ins = new ProtobufGen();
return ProtobufGen.ins;
}
async execute() {
const cfg = toolchain.projCfg.protobufgen;
if (!cfg) {
console.error('protobufgen config not found');
return;
}
// 更新proto目录
const protoRepoPath = path.join(toolchain.options.workspace, 'ppgame_gamedata');
const protoPath = path.join(protoRepoPath, 'proto');
await GitHelper.Instance.partialCloneDirectory({
gitUrl: Proto_Git_URL,
sparsePath: 'proto',
localPath: protoRepoPath
});
// 更新svn目录
const temp = path.join(toolchain.options.workspace, toolchain.options.project);
const outputJSRepoPath = path.join(temp, 'proto_js');
const outputJsSubDir = cfg.outputJsSvnDir ?? 'js';
const useGitClientRepo = !toolchain.projCfg.Svn.ClientSvn.startsWith('svn://');
RepoHolder.Instance.setAsType(useGitClientRepo ? 'git' : 'svn');
const outputJSPath = useGitClientRepo ? path.join(outputJSRepoPath, outputJsSubDir) : outputJSRepoPath;
const useDiffTsPath = cfg.outputTsSvnDir && cfg.outputTsSvnDir != cfg.outputJsSvnDir;
const outputTSRepoPath = useDiffTsPath ? path.join(temp, 'proto_ts') : outputJSRepoPath;
const outputTSPath = useDiffTsPath ? (useGitClientRepo ? path.join(outputTSRepoPath, cfg.outputTsSvnDir) : outputTSRepoPath) : outputJSPath;
await RepoHolder.Instance.checkOutSubDir(!toolchain.options.debug, outputJSRepoPath, toolchain.projCfg.Svn.ClientSvn, outputJsSubDir, cfg.outputGitBranch);
if (useDiffTsPath) {
await RepoHolder.Instance.checkOutSubDir(!toolchain.options.debug, outputTSRepoPath, toolchain.projCfg.Svn.ClientSvn, cfg.outputTsSvnDir, cfg.outputGitBranch);
}
// 转换proto文件为js文件
await this.convertProto(cfg, protoPath, outputJSPath, outputTSPath, 'minigame_client_message');
// 修改js文件,适配Browser
if (cfg.wrap != 'commonjs') {
await this.modifyJsFile(path.join(outputJSPath, 'minigame_client_message.js'));
}
// 再使用msg_ids来生成.d.ts文件
await this.convertProto(cfg, protoPath, outputJSPath, outputTSPath, 'msg_ids');
// 删除生成的js文件
await fs.unlink(path.join(outputJSPath, 'msg_ids.js'));
if (cfg.genTds) {
// 修改msg_ids.d.ts文件,只保留msg_ids的定义
await this.modifyMsgIdsDTS(path.join(outputTSPath, 'msg_ids.d.ts'));
}
// 提交svn
if (!toolchain.options.debug) {
const commitPaths = [outputJSPath];
if (cfg.genTds && useDiffTsPath) {
commitPaths.push(outputTSPath);
}
await RepoHolder.Instance.commit('protobufgen@fy-convertor~', ...commitPaths);
}
}
async convertProto(cfg, protoPath, outputJSPath, outputTSPath, protoFileName) {
// 调用pbjs生成js文件
const jsFile = path.join(outputJSPath, `${protoFileName}.js`);
await runCommand('pbjs.cmd', [
'-p',
protoPath,
'-t',
'static-module',
'-w',
cfg.wrap ?? 'default',
'--no-create',
'--no-verify',
'--no-convert',
'--no-delimited',
'--no-typeurl',
'--no-service',
'-o',
jsFile,
path.join(protoPath, `proto_game_base/msg_def/${protoFileName}.proto`)
]);
// 调用pbts生成ts文件
if (cfg.genTds) {
const tsFile = path.join(outputTSPath, `${protoFileName}.d.ts`);
await runCommand('pbts.cmd', [
'-o',
tsFile,
jsFile
]);
}
}
async modifyJsFile(jsFile) {
const content = await fs.promises.readFile(jsFile, 'utf-8');
const lines = content.split(/\r?\n/);
const index = lines.findIndex(v => v.includes('module.exports = factory(require("protobufjs/minimal"));'));
const codes = `
/* Browser */ else
global.minigame_client_message = factory(global.protobuf);
`;
lines.splice(index + 1, 0, codes);
const newContent = lines.join('\n');
await fs.writeFile(jsFile, newContent, 'utf-8');
}
async modifyMsgIdsDTS(msgIdsJsFile) {
const content = await fs.readFile(msgIdsJsFile, 'utf-8');
const lines = content.split(/\r?\n/);
// 只保留msg_def部分
let start = -1;
let end = -1;
for (let i = 0; i < lines.length; i++) {
if (!lines[i].startsWith('/** Namespace ') || !lines[i + 1]?.startsWith('export namespace '))
continue;
let depth = 0;
for (let j = i; j < lines.length; j++) {
const line = lines[j];
depth += (line.match(/{/g) ?? []).length;
depth -= (line.match(/}/g) ?? []).length;
if (depth == 0 && j > i) {
const namespaceLines = lines.slice(i, j + 1);
if (namespaceLines.some(v => v.includes('enum MSG_DIR')) && namespaceLines.some(v => v.includes('enum MSG_ID'))) {
start = i;
end = j;
}
break;
}
}
if (start >= 0) {
break;
}
}
if (start < 0 || end < 0) {
throw new Error(`msg ids namespace not found in ${msgIdsJsFile}`);
}
const reservedLines = lines.slice(start, end + 1);
let newContent = reservedLines.join('\n');
// 再把enum MSG_DIR和enum MSG_ID改成const enum
newContent = newContent.replace(/enum MSG_DIR/g, 'const enum MSG_DIR').replace(/enum MSG_ID/g, 'const enum MSG_ID');
// 把export namespace改为declare namespace
newContent = newContent.replace(/export namespace/g, 'declare namespace');
await fs.writeFile(msgIdsJsFile, newContent, 'utf-8');
}
}
//# sourceMappingURL=ProtobufGen.js.map