fy-convertor
Version:
Convert excel/xml/json/bin/protocl/ts/as ...
224 lines (221 loc) • 10.6 kB
JavaScript
import fs from "fs-extra";
import path from "path";
import { indent } from "../tools/vendor.js";
import { JsonStructParser } from "./JsonStructParser.js";
import { SvnHolder, adjustSVN } from "./SvnHolder.js";
import { toolchain } from "../tools/toolchain.js";
export class Xml2Ts {
static ins;
static get Instance() {
if (!Xml2Ts.ins)
Xml2Ts.ins = new Xml2Ts();
return Xml2Ts.ins;
}
structPath;
gameConfigPath;
keywordPath;
gameConfigPathsToSync = [];
keywordPathsToSync = [];
constructor() { }
async execute() {
const temp = path.join(toolchain.options.workspace, toolchain.options.publish ? toolchain.options.project + '_publish' : toolchain.options.project);
this.structPath = path.join(temp, 'struct');
this.gameConfigPath = path.join(temp, 'ts/data');
this.keywordPath = path.join(temp, 'ts/constants');
// 更新svn
await SvnHolder.Instance.checkSVN(!toolchain.options.debug, this.structPath, adjustSVN(toolchain.projCfg.Svn.DesignerSvn) + 'xml');
await this.initClientSVNs(toolchain.projCfg.Svn.ClientSvn, this.gameConfigPath, this.keywordPath);
if (toolchain.projCfg.Sync) {
for (let i = 0, len = toolchain.projCfg.Sync.length; i < len; i++) {
const gp = path.join(temp, `ts/dataToSync${i}`);
const kp = path.join(temp, `ts/constantsToSync${i}`);
this.gameConfigPathsToSync.push(gp);
this.keywordPathsToSync.push(kp);
await this.initClientSVNs(toolchain.projCfg.Sync[i], gp, kp);
}
}
// GameConfig.d.ts
// 对EquipConfig_Flash和ThingConfig_Flash进行融合
const structs = await JsonStructParser.Instance.readStructs(this.structPath, true);
if (toolchain.projCfg.xml2ts?.diffThingEquip != 'YES') {
JsonStructParser.Instance.mergeStructs('EquipConfigM', 'ThingConfigM');
}
await this.convert(structs);
// KeyWord.ts
let macros = JsonStructParser.Instance.getMacrosOfFile('KeyWord.xml');
await this.convertKeyWord(macros);
// 提交svn
if (!toolchain.options.debug) {
console.log("Committing...");
await SvnHolder.Instance.commitSVN('xml2ts@fy-convertor~', this.gameConfigPath, this.keywordPath);
// 同步
if (toolchain.projCfg.Sync) {
for (let i = 0, len = toolchain.projCfg.Sync.length; i < len; i++) {
console.log(`Syncing to ${toolchain.projCfg.Sync[i]}...`);
const gp = this.gameConfigPathsToSync[i];
const kp = this.keywordPathsToSync[i];
await fs.copyFile(path.join(this.gameConfigPath, 'GameConfig.d.ts'), path.join(this.gameConfigPath, 'GameConfig.d.ts'));
await fs.copyFile(path.join(this.keywordPath, 'KeyWord.ts'), path.join(this.keywordPath, 'KeyWord.ts'));
await SvnHolder.Instance.commitSVN('xml2ts@fy-convertor~', gp, kp);
}
}
}
else {
console.log("Debug mode, committing skipped.");
}
}
async initClientSVNs(repository, gameConfigPath, keywordPath) {
if (toolchain.projCfg.Project.ProjectType === 'H5ts') {
await SvnHolder.Instance.checkSVN(true, gameConfigPath, adjustSVN(repository) + (toolchain.projCfg.xml2ts?.gameConfigRoot ?? 'project/src/automatic/cfgs') + '/GameConfig.d.ts');
await SvnHolder.Instance.checkSVN(true, keywordPath, adjustSVN(repository) + (toolchain.projCfg.xml2ts?.keyWordRoot ?? 'project/src/automatic/constants') + '/KeyWord.ts');
}
else {
await SvnHolder.Instance.checkSVN(true, gameConfigPath, adjustSVN(repository) + (toolchain.projCfg.xml2ts?.gameConfigRoot ?? 'project/TsScripts/System/data') + '/GameConfig.d.ts');
await SvnHolder.Instance.checkSVN(true, keywordPath, adjustSVN(repository) + (toolchain.projCfg.xml2ts?.keyWordRoot ?? 'project/TsScripts/System/constants') + '/KeyWord.ts');
}
}
async convert(structs) {
const rootStructs = [];
for (let s of structs) {
const def = JsonStructParser.Instance.getStructDef(s);
if (def && def._attributes.rootflag == '1') {
rootStructs.push(s);
}
}
const tnames = rootStructs.map(v => `'${v}'`).join(' | ');
const ncms = rootStructs.map(v => ` ['${v}']: ${v};`).join('\n');
let content = `declare module GameConfig {
export type _TNames = ${tnames};
export interface _NameToConfigMappings {
[name: _TNames]: object;
${ncms}
}
export type ThingOrEquip = ThingConfigM | EquipConfigM;
`;
const dfRules = toolchain.projCfg.xml2json?.dropFieldRules;
const dfMap = {};
if (dfRules) {
for (const rule of dfRules) {
dfMap[rule.name] = rule;
}
}
const dvRules = toolchain.projCfg.xml2json?.diffValueRules;
const diffMap = {};
if (dvRules) {
for (const rule of dvRules) {
diffMap[rule.name] = rule;
}
}
const descGenerator = (s, e) => {
const comments = [];
const dfRule = dfMap[s._attributes.name];
if (dfRule) {
const dk = dfRule.dropKeys.find((v) => v.name == e._attributes.name);
if (dk) {
const s = e._attributes.count || e._attributes.refer ? '某元素' : '';
if (dk.tests) {
const testStr = dk.tests.map((v) => `{@link ${e._attributes.type.replace('_Flash', 'M')}.${v.key}}` + (v.hitValue != null ? `==${v.hitValue}` : '为空')).join(', ');
comments.push(`@dropFieldRule 当${s}满足以下条件之一时将被丢弃:${testStr}`);
}
else {
comments.push(`@dropFieldRule 当${s}为空时将被丢弃`);
}
}
}
const diffRule = diffMap[s._attributes.name];
if (diffRule) {
const df = diffRule.diff_fields.find((v) => v.name == e._attributes.name);
if (df) {
let s = '';
if (diffRule.serial_keys) {
s = `按照${diffRule.serial_keys.map((v) => `{@link ${v}}`).join(', ')}进行分组,然后`;
}
comments.push(`@diffValueRule 先${s}按照${diffRule.serial_sort_keys.map((v) => `{@link ${v}}`).join(', ')}进行排序,再对本字段的${df.key ? df.key : '值'}进行diff处理`);
}
}
return comments;
};
for (let s of structs) {
let structDef = JsonStructParser.Instance.makeStructCode(s, 'interface', descGenerator);
content += indent(structDef);
}
content += '}';
await fs.writeFile(path.join(this.gameConfigPath, 'GameConfig.d.ts'), content, 'utf-8');
}
async convertKeyWord(macros) {
const groupTypeMap = {};
let kwContent = '';
let descContent = '';
let groups = [];
for (let m of macros) {
if (m._attributes.group) {
let gt = groupTypeMap[m._attributes.group];
if (!gt) {
groupTypeMap[m._attributes.group] = gt = { name: m._attributes.group, members: [] };
}
gt.members.push(m._attributes.name);
let comment = m._attributes.value + ', group = {@link ' + m._attributes.group + '}';
if (m._attributes.cname) {
comment += ', cname = ' + m._attributes.cname;
}
if (m._attributes.desc) {
if (m._attributes._convert_desc) {
comment += ', getDesc = ' + m._attributes.desc;
if (!groups.includes(m._attributes.group)) {
groups.push(m._attributes.group);
descContent += `\n KWDesc.m[KeyWord.${m._attributes.group}] = {};\n`;
}
descContent += ` KWDesc.m[KeyWord.${m._attributes.group}][KeyWord.${m._attributes.name}] = "${m._attributes.desc}";\n`;
}
else {
if (m._attributes.desc != m._attributes.cname) {
comment += ', desc = ' + m._attributes.desc;
}
}
}
kwContent += ` /**${comment}*/\n`;
kwContent += ` ${m._attributes.name} = ${m._attributes.value},\n`;
}
else {
console.error("KeyWord should have a group attribute defined:", m._attributes.name);
}
}
let groupTypeContent = '';
for (const gname in groupTypeMap) {
const gt = groupTypeMap[gname];
groupTypeContent += `export type ${gname} = ` + gt.members.map((v) => `KeyWord.${v}`).join(' | ') + '\n';
}
// 添加group信息
groups.sort();
let groupContent = '';
for (let i = 0, len = groups.length; i < len; i++) {
let g = groups[i];
groupContent += ` ${g} = ${i + 1},\n`;
}
const content = `/**表格使用的关键字,由fy-convertor根据KeyWord.xml生成*/
export const enum KeyWord {
// --------------------------- Keyword Groups ------------------------------
${groupContent}
// ------------------------------ Keywords ---------------------------------
${kwContent}
}
export class KWDesc {
static m: { [group: number]: { [keyword: number]: string } };
static getDesc(group: number, keyword: number): string {
if (KWDesc.m == null) {
KWDesc.m = {};
${descContent}
}
let desc: string = null;
let groupDict = KWDesc.m[group];
if(null != groupDict) {
desc = groupDict[keyword];
}
return desc ? desc : "";
}
}
${groupTypeContent}`;
await fs.writeFile(path.join(this.keywordPath, 'KeyWord.ts'), content, 'utf-8');
}
}
//# sourceMappingURL=Xml2Ts.js.map