UNPKG

fy-convertor

Version:

Convert excel/xml/json/bin/protocl/ts/as ...

238 lines (235 loc) 11.2 kB
import fs from "fs-extra"; import path from "path"; import { indent } from "../tools/vendor.js"; import { JsonStructParser } from "./JsonStructParser.js"; import { RepoHolder, adjustSVN } from "./RepoHolder.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 RepoHolder.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 RepoHolder.Instance.commit('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(gp, 'GameConfig.d.ts')); await fs.copyFile(path.join(this.keywordPath, 'KeyWord.ts'), path.join(kp, 'KeyWord.ts')); await fs.copyFile(path.join(this.keywordPath, 'KeyWord.d.ts'), path.join(kp, 'KeyWord.d.ts')); await RepoHolder.Instance.commit('xml2ts@fy-convertor~', gp, kp); } } } else { console.log("Debug mode, committing skipped."); } } async initClientSVNs(repository, gameConfigPath, keywordPath) { let gcRoot, kwRoot; if (toolchain.projCfg.Project.ProjectType === 'H5ts') { gcRoot = adjustSVN(repository) + (toolchain.projCfg.xml2ts?.gameConfigRoot ?? 'project/src/automatic/cfgs'); kwRoot = adjustSVN(repository) + (toolchain.projCfg.xml2ts?.keyWordRoot ?? 'project/src/automatic/constants'); } else { gcRoot = adjustSVN(repository) + (toolchain.projCfg.xml2ts?.gameConfigRoot ?? 'project/TsScripts/System/data'); kwRoot = adjustSVN(repository) + (toolchain.projCfg.xml2ts?.keyWordRoot ?? 'project/TsScripts/System/constants'); } await RepoHolder.Instance.checkSVN(true, gameConfigPath, `${gcRoot}/GameConfig.d.ts`); await RepoHolder.Instance.checkSVN(true, keywordPath, kwRoot); } 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 declareContent = ''; 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`; declareContent += `/**${comment}*/\n`; declareContent += `export type ${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'; } declareContent += '\n\n\n' + groupTypeContent + '\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'); // 生成KeyWord.d.ts const kwdContent = `/**表格使用的关键字声明,由fy-convertor根据KeyWord.xml生成*/ declare namespace KeyWord { ${indent(declareContent, ' ')} }`; await fs.writeFile(path.join(this.keywordPath, 'KeyWord.d.ts'), kwdContent, 'utf-8'); } } //# sourceMappingURL=Xml2Ts.js.map