UNPKG

ldjtool

Version:

**ludingji tool**

523 lines (459 loc) 19.2 kB
/** * Created by swellee on 2016/9/19. * use as a forked node process; */ var xml = require("pixl-xml"); var path = require("path"); var fs = require("fs"); var util = require("./util"); var os = require("os"); var validResTyes = ["jpg", "png"]; var userCfgDir = path.join(os.homedir(), ".ldjtool"); var cfg = require(path.join(userCfgDir, "cfg.json")); var rule = require(path.join(cfg.clientDir, ".ui_rule.json")); var baseUiPackDir = path.resolve(cfg.clientDir, "src/app/modules"); var sh = require("child_process"); var paras = process.argv; var file = paras.pop(); var dir = path.dirname(file); var dirName = path.basename(dir); var actTask = 0; //活动任务数 const guide_valid_node_names = ["View", "Panel"]; parseUI(file); function parseUI(file) { if (path.extname(file) != ".ui") { process.send("跳过非ui文件" + file); return; } actTask++; var content = fs.readFileSync(file, { encoding: "utf8" }); ///-----------------解析分类---------------------------------- var imports = {}; //通过key来记录导包并去重 var res = {}; //通过key来记录资源和去重 var usedTempDefine = { "var": true }; //已经使用过的局部变量名 var declares = {}; //记录变量声明 var fileName = path.basename(file, ".ui"); var creates = []; var rootNode = JSON.parse(content); var rootName = rootNode.type; //根容器,用于生成父类名 rootNode.props.var = ''; var lists = []; var gdEles = guide_valid_node_names.indexOf(rootNode.type) != -1 ? [] : null; listNodes("this", rootName, rootNode, lists, rootName, declares, usedTempDefine); parseNode(lists, imports, res, declares, creates, usedTempDefine, gdEles); ///-----------------解析完成---------------------------------- ///-----------------按结构导出-------------------------------- //生成UI类 genUIfile(fileName, "", rootName, imports, declares, res, creates, gdEles); //生成逻辑类 genLgFile(fileName, fileName + "UI", "", rule.noCallCreateChildren[rootName] ? "" : "createChildren"); //拷贝使用了的资源到bin/h5/assets/下对应模块目录 var cmd = "cp"; sh.exec("ls", function(err, stdout, stderr) { if (err) { cmd = "copy"; //windows } }); for (var key in res) { if (key.indexOf("/comp/") != -1) { //跳过默认组件资源 continue; } var rP = key.replace(/("|')/g, ''); var resP = path.resolve(cfg.clientDir, "ui/laya/", rP.replace("img", "")); var toP = path.resolve(cfg.clientDir, "bin/h5", rP); //如果是btn,则尝试拷贝其多态皮肤 if (resP.indexOf("btn_") != -1) { var bsname = path.basename(resP); var ddname = path.dirname(resP); var bsname2 = bsname.replace(/\d$/, function(str) { return str == "0" ? "1" : "0"; }); var btnres2 = resP.replace(bsname, bsname2); var btnto2 = toP.replace(bsname, bsname2); copyres(cmd, btnres2, btnto2); } // copyres(cmd, resP, toP); } } function copyres(cmd, resP, toP) { try { sh.execSync(`${cmd} ${resP} ${toP}`); } catch (e) { console.log(`警告:拷贝UI文件>>${file}的资源出错, 你可能需要手动拷贝该资源到客户端项目下的同名目录里!`) } } function getPackName(file) { var dname = path.dirname(file); dname = dname.split(cfg.baseUiPackRootName + path.sep)[1]; var pks = dname.split(path.sep).join(".").toLowerCase(); return cfg.baseUiPackgeIdr + pks; } function getDeclare(declares) { var arr = []; for (var key in declares) { arr.push({ dfine: key, type: declares[key] }) } return arr; } function quote(str) { return "'" + str + "'"; } function listNodes(parentName, nodeName, nodeData, list, rootName, declares, usedTempDefine) { parentName = parentName == rootName.toLowerCase() ? "this" : parentName; if (nodeData.props) { var attrs = nodeData.props; var rec = recDefine(nodeName, attrs, declares, usedTempDefine); //本节点数据 list.push({ parentName: parentName, nodeName: nodeName, nodeData: nodeData, define: rec.define, clazz: rec.clazz }); if (nodeData.props["name"]) { //需要作为子类导出的节点数据 if (rule.specialAttr.name[nodeName]) { var nameRule = rule.specialAttr.name; if (nameRule[nodeName] && nameRule[nodeName]["type"] && (nameRule[nodeName]["type"] == "childClass" || nameRule[nodeName]["type"] == "internalClass")) { return; } } } //子级 if (nodeData.child) { nodeData.child.forEach(function(c) { listNodes(rec.define, c.type, c, list, rootName, declares, usedTempDefine); }) } } } function parseNode(nodeLists, imports, res, declares, creates, usedTempDefine, gdEles) { for (var i = 0; i < nodeLists.length; i++) { var node = nodeLists[i]; var parentNodeName = node.parentName; var nodeName = node.nodeName; var nodeData = node.nodeData; var clazz = nodeName; if (clazz && rule.specialClass[nodeName]) { clazz = rule.specialClass[nodeName]; //特殊的类映射 } var funcs = {}; var props = {}; //处理当前节点的属性 var attrs = nodeData.props; var dfine = node.define; var clazz = node.clazz; for (var attKey in attrs) { var attValue = attrs[attKey]; //需要特殊处理的属性 if (rule.specialAttr.hasOwnProperty(attKey)) { var specialRule = rule.specialAttr[attKey]; if (!specialRule) { //特殊规则为空,表示丢弃该属性 continue; } if (attKey == "var") { continue; //变量名已经在前面处理过了 } //通过name,特殊处理 if (attKey == "name") { if (specialRule[nodeName]) { var nameRule = specialRule[nodeName]; //将子对象映射为类 if (nameRule["type"] == "childClass") { //兼容老的规则,老规则默认只生成ItemRenderer子类 var names = attValue.trim().split(":"); //eg. pack.className:baseClassName:false var pkfile = names[0].split("."); var packName = names.length > 1 && pkfile.length > 1 ? pkfile[0] : "renderer"; var fileName = names.length > 1 && pkfile.length > 1 ? pkfile[1] : names[0] + "ItemRenderer"; var rootName = names.length > 1 ? names[1] : "ItemRenderer"; var needOverridePrefix = names.length > 2 ? (names[2] == "true") : true; //是否需要对createChildren添加oveerride前缀,默认true if (nameRule["prop"]) { //将子类映射成该变量的一个属性 specialRule = nameRule.prop; props[specialRule] = fileName; //将子类包引入 var dname = dir.split(cfg.baseUiPackRootName)[1].substr(1); var pks = dname.split(path.sep).join(".").toLowerCase(); var ipt = cfg.baseUiPackgeIdr + pks + ".view." + packName + "." + fileName; imports[ipt] = true; rule.overrideCreateChildren[rootName] = needOverridePrefix; } //额外添加的属性 if (nameRule["addProp"]) { var adds = nameRule["addProp"]; for (var ad in adds) { props[ad] = adds[ad]; } } //生成子类代码 parseChildUI(packName, fileName, rootName, nodeData, res); } //直接映射成一个新类 else if (nameRule["type"] == "internalClass") { //生成映射类 var nms = attValue.trim().split(":"); var flNm = nms[0]; var bsNm = nms.length > 1 ? nms[1] : "View"; var dname = dir.split(cfg.baseUiPackRootName)[1].substr(1); var pks = dname.split(path.sep).join(".").toLowerCase(); var ipt = cfg.baseUiPackgeIdr + pks + ".view."; rule.import[flNm] = ipt; parseChildUI("", flNm, bsNm, nodeData, res); } //将name作为属性处理 else if (nameRule["type"] == "prop") { recProps(nameRule["prop"], attValue, props, res, true); } } } //带过滤的特殊属性转换 else if (specialRule.hasOwnProperty("when")) { if (specialRule.when.indexOf(clazz) != -1) { recProps(specialRule.prop, attValue, props, res); } else { recProps(attKey, attValue, props, res); } } else { var sp = specialRule.lastIndexOf("_"); if (sp != -1) { var funName = specialRule.substring(0, sp); var paraIdx = specialRule.substr(sp + 1); if (!funcs[funName]) { funcs[funName] = []; } var num = parseInt(attValue); if (!isNaN(num)) { //能作为数字的,转为数字 attValue = num; } funcs[funName][paraIdx] = attValue; } else { recProps(specialRule, attValue, props, res); } } } //普通setter属性 else { recProps(attKey, attValue, props, res); } } ///-------------生成----------------- if (clazz) { //imports var ipt = rule.import[clazz] || rule.import.default; ipt = (ipt + "." + clazz).replace("..", "."); imports[ipt] = true; if (i == 0) { continue; //节点0是最外层,不生成 } //self class var skipVar = false; if (declares.hasOwnProperty(dfine)) { skipVar = true; } creates.push((skipVar ? dfine : "var " + dfine + ":" + clazz) + " = new " + clazz + "();"); if (declares.hasOwnProperty(dfine)) { creates.push(dfine + ".name = " + quote(dfine) + ";"); gdEles && gdEles.push(dfine); } } if (dfine) { //调用函数 for (var f in funcs) { var fn = f.split("_"); var fname = fn[0]; var pcount = fn[1]; for (var pp = 0; pp < pcount; pp++) { funcs[f][pp] = funcs[f][pp] || 0; //若无完整的参数量,则补0 } creates.push(dfine + "." + fname + "(" + funcs[f].join(",") + ");") } //其他属性 for (var p in props) { creates.push(dfine +  "." + p + " = " + props[p] + ";"); } if (parentNodeName) { creates.push(parentNodeName + ".addChild(" + dfine + ");"); } creates.push("\n"); } } } function recDefine(nodeName, attrs, declares, usedTempDefine) { var define = attrs["var"] || nodeName.toLowerCase(); var clazz = nodeName; if (rule.specialClass[clazz]) { clazz = rule.specialClass[clazz]; } var needRec = true; if (attrs["name"]) { if (rule.specialAttr.name[nodeName]) { var nameRule = rule.specialAttr.name[nodeName]; if (nameRule.type == "internalClass") { clazz = attrs["name"]; } if (!attrs["var"]) { needRec = false; } } else { if (attrs["var"]) { define = attrs["var"]; } else { needRec = false; define = attrs["name"].toLowerCase(); } clazz = attrs["name"]; } } else if (attrs["var"]) { define = attrs["var"]; } else { needRec = false; } if (attrs["runtime"]){//是否二次转换类 clazz = attrs["runtime"].trim() || clazz; } if (needRec) { //已命名的成员变量 declares[define] = clazz; } else { //临时变量 if (usedTempDefine.hasOwnProperty(define)) { var tempDf = define + "1"; var renameDf = tempDf.replace(/[0-9]$/, function(tidx) { var tid = parseInt(tidx); while (usedTempDefine.hasOwnProperty(tempDf)) { tid++; tempDf = define + tid; } return tid; }); define = renameDf; } usedTempDefine[define] = true; } return { define: define, clazz: clazz }; } function recProps(attKey, attValue, props, res, force) { //-------处理attvalue--------- if (attValue == "true" || attValue == "false") { //布尔值 attValue = Boolean(attValue == "true"); } else if (typeof attValue == "number") { } //--------------------------- else if (typeof attValue == "string") { //作为字符串处理 attValue = attValue.replace(/\s/g, ''); if (attKey == "sizeGrid") { //新ui里九宫顺序一致,不再处理 attValue = quote(attValue); } else if (attKey == "skin" || attKey == "vScrollBarSkin" || attKey == "hScrollBarSkin") { var attFix = attValue.split("."); var tail = attFix.pop(); //标记资源 if (validResTyes.indexOf(tail) != -1) { attValue = quote("assets/img/" + attValue); res[attValue] = true; } else { attValue = quote(tail); } } else { if (isNaN(attValue)) { attValue = quote(attValue); //包一层引号 } else { attValue = parseInt(attValue); } } } //--------记录key-value----- if (!props[attKey] || force) { props[attKey] = attValue; } } function parseChildUI(packName, fileName, rootName, node, res) { ///-----------------解析子类---------------------------------- var imports = {}; //key记录导包信息 var declares = {}; //记录变量声明 var creates = []; var usedTempDefine = { "var": true }; //已经使用过的局部变量名 var lists = []; node.props.name = rootName; node.props.var = ''; listNodes("this", rootName, node, lists, rootName, declares, usedTempDefine); var gdEles = guide_valid_node_names.indexOf(node.type) != -1 ? [] : null; parseNode(lists, imports, res, declares, creates, usedTempDefine, gdEles); ///-----------------按结构导出UI类-------------------------------- genUIfile(fileName, packName, rootName, imports, declares, null, creates, gdEles); //生成逻辑类 genLgFile(fileName, fileName + "UI", packName, rule.noCallCreateChildren[rootName] ? "" : "createChildren"); } //生成UI类 function genUIfile(fileName, packName, rootName, imports, declares, res, creates, gdEles) { var clzNm = fileName + "UI"; var filePath = path.join(dir, "view", packName, fileName); //生成UI类 var clzNm = fileName + "UI"; var dustData = { pack: getPackName(filePath), imports: Object.keys(imports), className: clzNm, viewName: fileName, declares: getDeclare(declares), rootName: rootName, creates: creates, gdEles: gdEles, createFunType: rule.overrideCreateChildren[rootName] ? "override protected" : '', ui: true } if (res) { dustData.res = Object.keys(res); dustData.genRes = true; } util.dust("ui", dustData, function(out) { writeTo(path.resolve(baseUiPackDir, dirName, "view", packName, clzNm + ".as"), out); }) } //生成UI逻辑类 function genLgFile(fileName, rootName, packName, superCall) { var filePath = path.join(dir, "view", packName, fileName); var logicFile = path.resolve(baseUiPackDir, dirName, "view", packName, fileName + ".as"); fs.access(logicFile, fs.R_OK, (err) => { if (err) { var lgData = { pack: getPackName(filePath), className: fileName, rootName: rootName, superCall: superCall } util.dust("ui", lgData, function(out) { writeTo(logicFile, out); }) } }); } function writeTo(filePath, data) { var rpath = path.relative(baseUiPackDir, filePath).toLowerCase(); var idx = rpath.lastIndexOf("." + path.sep) + 1; var delta = path.dirname(rpath.substring(idx)); var targetDir = path.join(baseUiPackDir, delta); util.mkdirs(targetDir, function() { fs.appendFile(filePath, data, { flag: "w" }, function(err) { if (err) { util.err(err); } else { if (--actTask <= 0) { process.send("ok"); //通知父进程,此操作已完成 } } }) }) } module.exports = parseUI;