├── README.md ├── package.json ├── wuJs.js ├── wuConfig.js ├── wuWxapkg.js ├── wuLib.js ├── wuRestoreZ.js ├── wuWxss.js └── wuWxml.js /README.md: -------------------------------------------------------------------------------- 1 | # wxappUnpacker-master 2 | 2019小程序解包 解决$gwx报错问题 3 | 分包解法 4 | node wuWxapkg.js -d D:\Unpacker\_1357420259_6.wxapkg 5 | node wuWxss.js D:\Unpacker\_1357420259_6\component 6 | node wuWxml.js D:\Unpacker\_1357420259_6\component\page-frame.js 7 | node wuJs.js D:/Unpacker/_1357420259_6/component/app-service.js 8 | 若解分包报错可以根据报错修改$gwx的处理 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wxapp-unpacker", 3 | "version": "0.3", 4 | "description": "Wechat App(微信小程序, .wxapkg)解包及相关文件(.wxss, .json, .wxs, .wxml)还原工具", 5 | "main": "wuWxapkg.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/qwerty472123/wxappUnpacker.git" 9 | }, 10 | "author": "qwerty472123", 11 | "license": "GPL-3.0-or-later", 12 | "bugs": { 13 | "url": "https://github.com/qwerty472123/wxappUnpacker/issues" 14 | }, 15 | "homepage": "https://github.com/qwerty472123/wxappUnpacker#readme", 16 | "dependencies": { 17 | "css-tree": "^1.0.0-alpha.29", 18 | "cssbeautify": "^0.3.1", 19 | "escodegen": "^1.11.0", 20 | "esprima": "^4.0.1", 21 | "js-beautify": "^1.8.9", 22 | "uglify-es": "^3.3.9", 23 | "vm2": "^3.6.10" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wuJs.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | const path=require("path"); 3 | const UglifyJS=require("uglify-es"); 4 | const {js_beautify}=require("js-beautify"); 5 | const {VM}=require('vm2'); 6 | function jsBeautify(code){ 7 | return UglifyJS.minify(code,{mangle:false,compress:false,output:{beautify:true,comments:true}}).code; 8 | } 9 | function splitJs(name,cb){ 10 | let dir=path.dirname(name); 11 | wu.get(name,code=>{ 12 | let needDelList={}; 13 | let vm=new VM({sandbox:{ 14 | require(){}, 15 | define(name,func){ 16 | let code=func.toString(); 17 | code=code.slice(code.indexOf("{")+1,code.lastIndexOf("}")-1).trim(); 18 | let bcode=code; 19 | if(code.startsWith('"use strict";')||code.startsWith("'use strict';"))code=code.slice(13); 20 | else if((code.startsWith('(function(){"use strict";')||code.startsWith("(function(){'use strict';"))&&code.endsWith("})();"))code=code.slice(25,-5); 21 | let res=jsBeautify(code); 22 | if(typeof res=="undefined"){ 23 | console.log("Fail to delete 'use strict' in \""+name+"\"."); 24 | res=jsBeautify(bcode); 25 | } 26 | needDelList[path.resolve(dir,name)]=-8; 27 | wu.save(path.resolve(dir,name),jsBeautify(res)); 28 | } 29 | }}); 30 | vm.run(code.slice(code.indexOf("define("))); 31 | console.log("Splitting \""+name+"\" done."); 32 | if(!needDelList[name])needDelList[name]=8; 33 | cb(needDelList); 34 | }); 35 | } 36 | module.exports={jsBeautify:jsBeautify,wxsBeautify:js_beautify,splitJs:splitJs}; 37 | if(require.main===module){ 38 | wu.commandExecute(splitJs,"Split and beautify weapp js file.\n\n\n\n js files to split and beautify."); 39 | } 40 | -------------------------------------------------------------------------------- /wuConfig.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | const fs=require("fs"); 3 | const path=require("path"); 4 | const crypto=require("crypto"); 5 | const {VM}=require('vm2'); 6 | function getWorkerPath(name){ 7 | let code=fs.readFileSync(name,{encoding:'utf8'}); 8 | let commPath=false; 9 | let vm=new VM({sandbox:{ 10 | require(){}, 11 | define(name){ 12 | name=path.dirname(name)+'/'; 13 | if(commPath===false)commPath=name; 14 | commPath=wu.commonDir(commPath,name); 15 | } 16 | }}); 17 | vm.run(code.slice(code.indexOf("define("))); 18 | if(commPath.length>0)commPath=commPath.slice(0,-1); 19 | console.log("Worker path: \""+commPath+"\""); 20 | return commPath; 21 | } 22 | function doConfig(configFile,cb){ 23 | let dir=path.dirname(configFile); 24 | wu.get(configFile,content=>{ 25 | let e=JSON.parse(content); 26 | let k=e.pages; 27 | k.splice(k.indexOf(wu.changeExt(e.entryPagePath)),1); 28 | k.unshift(wu.changeExt(e.entryPagePath)); 29 | let app={pages:k,window:e.global&&e.global.window,tabBar:e.tabBar,networkTimeout:e.networkTimeout}; 30 | if(e.subPackages){ 31 | app.subPackages=e.subPackages; 32 | console.log("=======================================================\nNOTICE: SubPackages exist in this package.\nDetails: ",app.subPackages,"\n======================================================="); 33 | } 34 | if(e.navigateToMiniProgramAppIdList)app.navigateToMiniProgramAppIdList=e.navigateToMiniProgramAppIdList; 35 | if(fs.existsSync(path.resolve(dir,"workers.js")))app.workers=getWorkerPath(path.resolve(dir,"workers.js")); 36 | if(e.extAppid) 37 | wu.save(path.resolve(dir,'ext.json'),JSON.stringify({extEnable:true,extAppid:e.extAppid,ext:e.ext},null,4)); 38 | if(typeof e.debug!="undefined")app.debug=e.debug; 39 | let cur=path.resolve("./file"); 40 | for(let a in e.page)if(e.page[a].window.usingComponents) 41 | for(let name in e.page[a].window.usingComponents){ 42 | let componentPath=e.page[a].window.usingComponents[name]+".html"; 43 | let file=componentPath.startsWith('/')?componentPath.slice(1):wu.toDir(path.resolve(path.dirname(a),componentPath),cur); 44 | if(!e.page[file])e.page[file]={}; 45 | if(!e.page[file].window)e.page[file].window={}; 46 | e.page[file].window.component=true; 47 | } 48 | if(fs.existsSync(path.resolve(dir,"app-service.js"))){ 49 | let matches=fs.readFileSync(path.resolve(dir,"app-service.js"),{encoding:'utf8'}).match(/\_\_wxAppCode\_\_\['[^\.]+\.json[^;]+\;/g); 50 | if(matches){ 51 | let attachInfo={}; 52 | (new VM({sandbox:{ 53 | __wxAppCode__:attachInfo 54 | }})).run(matches.join("")); 55 | for(let name in attachInfo)e.page[wu.changeExt(name,".html")]={window:attachInfo[name]}; 56 | } 57 | } 58 | let delWeight=8; 59 | for(let a in e.page){ 60 | let fileName=path.resolve(dir,wu.changeExt(a,".json")); 61 | wu.save(fileName,JSON.stringify(e.page[a].window,null,4)); 62 | if(configFile==fileName)delWeight=0; 63 | } 64 | if(app.tabBar&&app.tabBar.list) wu.scanDirByExt(dir,"",li=>{//search all files 65 | let digests=[],digestsEvent=new wu.CntEvent,rdir=path.resolve(dir); 66 | function fixDir(dir){return dir.startsWith(rdir)?dir.slice(rdir.length+1):dir;} 67 | digestsEvent.add(()=>{ 68 | for(let e of app.tabBar.list){ 69 | e.pagePath=wu.changeExt(e.pagePath); 70 | if(e.iconData){ 71 | let hash=crypto.createHash("MD5").update(e.iconData,'base64').digest(); 72 | for(let [buf,name] of digests)if(hash.equals(buf)){ 73 | delete e.iconData; 74 | e.iconPath=fixDir(name).replace(/\\/g,'/'); 75 | break; 76 | } 77 | } 78 | if(e.selectedIconData){ 79 | let hash=crypto.createHash("MD5").update(e.selectedIconData,'base64').digest(); 80 | for(let [buf,name] of digests)if(hash.equals(buf)){ 81 | delete e.selectedIconData; 82 | e.selectedIconPath=fixDir(name).replace(/\\/g,'/'); 83 | break; 84 | } 85 | } 86 | } 87 | wu.save(path.resolve(dir,'app.json'),JSON.stringify(app,null,4)); 88 | cb({[configFile]:delWeight}); 89 | }); 90 | for(let name of li){ 91 | digestsEvent.encount(); 92 | wu.get(name,data=>{ 93 | digests.push([crypto.createHash("MD5").update(data).digest(),name]); 94 | digestsEvent.decount(); 95 | },{}); 96 | } 97 | });else{ 98 | wu.save(path.resolve(dir,'app.json'),JSON.stringify(app,null,4)); 99 | cb({[configFile]:delWeight}); 100 | } 101 | }); 102 | } 103 | module.exports={doConfig:doConfig}; 104 | if(require.main===module){ 105 | wu.commandExecute(doConfig,"Split and make up weapp app-config.json file.\n\n\n\n app-config.json files to split and make up."); 106 | } 107 | -------------------------------------------------------------------------------- /wuWxapkg.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | const wuJs=require("./wuJs.js"); 3 | const wuCfg=require("./wuConfig.js"); 4 | const wuMl=require("./wuWxml.js"); 5 | const wuSs=require("./wuWxss.js"); 6 | const path=require("path"); 7 | const fs=require("fs"); 8 | function header(buf){ 9 | console.log("\nHeader info:"); 10 | let firstMark=buf.readUInt8(0); 11 | console.log(" firstMark: 0x%s",firstMark.toString(16)); 12 | let unknownInfo=buf.readUInt32BE(1); 13 | console.log(" unknownInfo: ",unknownInfo); 14 | let infoListLength=buf.readUInt32BE(5); 15 | console.log(" infoListLength: ",infoListLength); 16 | let dataLength=buf.readUInt32BE(9); 17 | console.log(" dataLength: ",dataLength); 18 | let lastMark=buf.readUInt8(13); 19 | console.log(" lastMark: 0x%s",lastMark.toString(16)); 20 | if(firstMark!=0xbe||lastMark!=0xed)throw Error("Magic number is not correct!"); 21 | return [infoListLength,dataLength]; 22 | } 23 | function genList(buf){ 24 | console.log("\nFile list info:"); 25 | let fileCount=buf.readUInt32BE(0); 26 | console.log(" fileCount: ",fileCount); 27 | let fileInfo=[],off=4; 28 | for(let i=0;i{ 53 | wu.addIO(()=>{ 54 | console.log("Split and make up done."); 55 | if(!order.includes("d")){ 56 | console.log("Delete files..."); 57 | wu.addIO(()=>console.log("Deleted.\n\nFile done.")); 58 | for(let name in needDelete)if(needDelete[name]>=8)wu.del(name); 59 | } 60 | cb(); 61 | }); 62 | }); 63 | function doBack(deletable){ 64 | for(let key in deletable){ 65 | if(!needDelete[key])needDelete[key]=0; 66 | needDelete[key]+=deletable[key];//all file have score bigger than 8 will be delete. 67 | } 68 | weappEvent.decount(); 69 | } 70 | //This will be the only func running this time, so async is needless. 71 | if(fs.existsSync(path.resolve(dir,"app-service.js"))){//weapp 72 | console.log("Split app-service.js and make up configs & wxss & wxml & wxs..."); 73 | wuCfg.doConfig(path.resolve(dir,"app-config.json"),doBack); 74 | wuJs.splitJs(path.resolve(dir,"app-service.js"),doBack); 75 | if(fs.existsSync(path.resolve(dir,"workers.js"))) 76 | wuJs.splitJs(path.resolve(dir,"workers.js"),doBack); 77 | if(fs.existsSync(path.resolve(dir,"page-frame.html"))) 78 | wuMl.doFrame(path.resolve(dir,"page-frame.html"),doBack,order); 79 | else if(fs.existsSync(path.resolve(dir,"app-wxss.js"))) { 80 | wuMl.doFrame(path.resolve(dir,"app-wxss.js"),doBack,order); 81 | if(!needDelete[path.resolve(dir,"page-frame.js")])needDelete[path.resolve(dir,"page-frame.js")]=8; 82 | } else throw Error("page-frame-like file is not found in the package by auto."); 83 | wuSs.doWxss(dir,doBack);//Force it run at last, becuase lots of error occured in this part 84 | }else if(fs.existsSync(path.resolve(dir,"game.js"))){//wegame 85 | console.log("Split game.js and rewrite game.json..."); 86 | let gameCfg=path.resolve(dir,"app-config.json"); 87 | wu.get(gameCfg,cfgPlain=>{ 88 | let cfg=JSON.parse(cfgPlain); 89 | if(cfg.subContext){ 90 | console.log("Found subContext, splitting it...") 91 | delete cfg.subContext; 92 | let contextPath=path.resolve(dir,"subContext.js"); 93 | wuJs.splitJs(contextPath,()=>wu.del(contextPath)); 94 | } 95 | wu.save(path.resolve(dir,"game.json"),JSON.stringify(cfg,null,4)); 96 | wu.del(gameCfg); 97 | }); 98 | wuJs.splitJs(path.resolve(dir,"game.js"),()=>{ 99 | wu.addIO(()=>{ 100 | console.log("Split and rewrite done."); 101 | cb(); 102 | }); 103 | }); 104 | }else throw Error("This package is unrecognizable.\nMay be this package is a subPackage which should be unpacked with -s=.\nOtherwise, please decrypted every type of file by hand.") 105 | } 106 | function doFile(name,cb,order){ 107 | for(let ord of order)if(ord.startsWith("s="))global.subPack=ord.slice(3); 108 | console.log("Unpack file "+name+"..."); 109 | let dir=path.resolve(name,"..",path.basename(name,".wxapkg")); 110 | wu.get(name,buf=>{ 111 | let [infoListLength,dataLength]=header(buf.slice(0,14)); 112 | if(order.includes("o"))wu.addIO(console.log.bind(console),"Unpack done."); 113 | else wu.addIO(packDone,dir,cb,order); 114 | saveFile(dir,buf,genList(buf.slice(14,infoListLength+14))); 115 | },{}); 116 | } 117 | module.exports={doFile:doFile}; 118 | if(require.main===module){ 119 | wu.commandExecute(doFile,"Unpack a wxapkg file.\n\n[-o] [-d] [-s=
] \n\n-d Do not delete transformed unpacked files.\n-o Do not execute any operation after unpack.\n-s=
Regard all packages provided as subPackages and\n regard
as the directory of sources of the main package.\n wxapkg files to unpack"); 120 | } 121 | -------------------------------------------------------------------------------- /wuLib.js: -------------------------------------------------------------------------------- 1 | const fs=require("fs"); 2 | const path=require("path"); 3 | class CntEvent{ 4 | constructor(){ 5 | this.cnt=0; 6 | this.emptyEvent=[]; 7 | this.encount=this.encount.bind(this); 8 | this.decount=this.decount.bind(this); 9 | this.add=this.add.bind(this); 10 | } 11 | encount(delta=1){ 12 | this.cnt+=delta; 13 | } 14 | decount(){ 15 | if(this.cnt>0)--this.cnt; 16 | if(this.cnt==0){ 17 | for(let info of this.emptyEvent)info[0](...info[1]); 18 | this.emptyEvent=[]; 19 | } 20 | } 21 | add(cb,...attach){ 22 | this.emptyEvent.push([cb,attach]); 23 | } 24 | check(cb,...attach){ 25 | if(this.cnt==0)cb(...attach); 26 | else this.add(cb,...attach); 27 | } 28 | } 29 | class LimitedRunner{ 30 | constructor(limit){ 31 | this.limit=limit; 32 | this.cnt=0; 33 | this.funcs=[]; 34 | } 35 | run(func){ 36 | if(this.cnt0)this.cnt--; 45 | if(this.funcs.length>0){ 46 | this.cnt++; 47 | setTimeout(this.funcs.shift(),0); 48 | } 49 | } 50 | runWithCb(func,...args){ 51 | let cb=args.pop(),self=this; 52 | function agent(...args){ 53 | self.done(); 54 | return cb.apply(this,args); 55 | } 56 | args.push(agent); 57 | this.run(()=>func(...args)); 58 | } 59 | } 60 | let ioEvent=new CntEvent; 61 | let ioLimit=new LimitedRunner(4096); 62 | function mkdirs(dir,cb){ 63 | ioLimit.runWithCb(fs.stat.bind(fs),dir,(err,stats)=>{ 64 | if(err)mkdirs(path.dirname(dir),()=>fs.mkdir(dir,cb)); 65 | else if(stats.isFile())throw Error(dir+" was created as a file, so we cannot put file into it."); 66 | else cb(); 67 | }); 68 | } 69 | function save(name,content){ 70 | ioEvent.encount(); 71 | mkdirs(path.dirname(name),()=>ioLimit.runWithCb(fs.writeFile.bind(fs),name,content,err=>{ 72 | if(err)throw Error("Save file error: "+err); 73 | ioEvent.decount(); 74 | })); 75 | } 76 | function get(name,cb,opt={encoding:'utf8'}){ 77 | ioEvent.encount(); 78 | ioLimit.runWithCb(fs.readFile.bind(fs),name,opt,(err,data)=>{ 79 | if(err)throw Error("Read file error: "+err); 80 | else cb(data); 81 | ioEvent.decount(); 82 | }); 83 | } 84 | function del(name){ 85 | ioEvent.encount(); 86 | ioLimit.runWithCb(fs.unlink.bind(fs),name,ioEvent.decount); 87 | } 88 | function changeExt(name,ext=""){ 89 | return name.slice(0,name.lastIndexOf("."))+ext; 90 | } 91 | function scanDirByExt(dir,ext,cb){ 92 | let result=[],scanEvent=new CntEvent; 93 | function helper(dir){ 94 | scanEvent.encount(); 95 | ioLimit.runWithCb(fs.readdir.bind(fs),dir,(err,files)=>{ 96 | if(err)throw Error("Scan dir error: "+err); 97 | for(let file of files){ 98 | scanEvent.encount(); 99 | let name=path.resolve(dir,file); 100 | fs.stat(name,(err,stats)=>{ 101 | if(err)throw Error("Scan dir error: "+err); 102 | if(stats.isDirectory())helper(name); 103 | else if(stats.isFile()&&name.endsWith(ext))result.push(name); 104 | scanEvent.decount(); 105 | }); 106 | } 107 | scanEvent.decount(); 108 | }); 109 | } 110 | scanEvent.add(cb,result); 111 | helper(dir,ext,scanEvent); 112 | } 113 | function toDir(to,from){//get relative path without posix/win32 problem 114 | if(from[0]==".")from=from.slice(1); 115 | if(to[0]==".")to=to.slice(1); 116 | from=from.replace(/\\/g,'/');to=to.replace(/\\/g,'/'); 117 | let a=Math.min(to.length,from.length); 118 | for(let i=1,m=Math.min(to.length,from.length);i<=m;i++)if(!to.startsWith(from.slice(0,i))){a=i-1;break;} 119 | let pub=from.slice(0,a); 120 | let len=pub.lastIndexOf("/")+1; 121 | let k=from.slice(len); 122 | let ret=""; 123 | for(let i=0;iconsole.timeEnd("Total use")); 140 | } 141 | let orders=[]; 142 | for(let order of process.argv)if(order.startsWith("-"))orders.push(order.slice(1)); 143 | let iter=process.argv[Symbol.iterator](),nxt=iter.next(),called=false,faster=orders.includes("f"),fastCnt; 144 | if(faster){ 145 | fastCnt=new CntEvent; 146 | fastCnt.add(endTime); 147 | } 148 | function doNext(){ 149 | let nxt=iter.next(); 150 | while(!nxt.done&&nxt.value.startsWith("-"))nxt=iter.next(); 151 | if(nxt.done){ 152 | if(!called)console.log("Command Line Helper:\n\n"+helper); 153 | else if(!faster)endTime(); 154 | }else{ 155 | called=true; 156 | if(faster)fastCnt.encount(),cb(nxt.value,fastCnt.decount,orders),doNext(); 157 | else cb(nxt.value,doNext,orders); 158 | } 159 | } 160 | while(!nxt.done&&!nxt.value.endsWith(".js"))nxt=iter.next(); 161 | doNext(); 162 | } 163 | module.exports={mkdirs:mkdirs,get:get,save:save,toDir:toDir,del:del,addIO:ioEvent.add, 164 | changeExt:changeExt,CntEvent:CntEvent,scanDirByExt:scanDirByExt,commonDir:commonDir, 165 | commandExecute:commandExecute}; 166 | -------------------------------------------------------------------------------- /wuRestoreZ.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | const {VM}=require('vm2'); 3 | function catchZGroup(code,groupPreStr,cb){ 4 | const debugPre="(function(z){var a=11;function Z(ops,debugLine){"; 5 | let zArr={}; 6 | for(let preStr of groupPreStr){ 7 | let content=code.slice(code.indexOf(preStr)),z=[]; 8 | content=content.slice(content.indexOf("(function(z){var a=11;")); 9 | content=content.slice(0,content.indexOf("})(__WXML_GLOBAL__.ops_cached.$gwx"))+"})(z);"; 10 | let vm=new VM({sandbox:{z:z,debugInfo:[]}}); 11 | vm.run(content); 12 | if(content.startsWith(debugPre))for(let i=0;i=":11,"<=":11,">":11,"<":11,"<<":12,">>":12, 98 | "-":len==3?13:16 99 | }; 100 | return priorList[op]?priorList[op]:0; 101 | } 102 | function getOp(i){ 103 | let ret=restoreNext(ops[i],true); 104 | if(ops[i] instanceof Object&&typeof ops[i][0]=="object"&&ops[i][0][0]==2){ 105 | //Add brackets if we need 106 | if(getPrior(op[1],ops.length)>getPrior(ops[i][0][1],ops[i].length))ret=enBrace(ret,'(');; 107 | } 108 | return ret; 109 | } 110 | switch(op[1]){ 111 | case"?:": 112 | ans=getOp(1)+"?"+getOp(2)+":"+getOp(3); 113 | break; 114 | case "!": 115 | case "~": 116 | ans=op[1]+getOp(1); 117 | break; 118 | case"-": 119 | if(ops.length!=3){ 120 | ans=op[1]+getOp(1); 121 | break; 122 | }//shoud not add more in there![fall through] 123 | default: 124 | ans=getOp(1)+op[1]+getOp(2); 125 | } 126 | break; 127 | } 128 | case 4://unkown-arrayStart? 129 | ans=restoreNext(ops[1],true); 130 | break; 131 | case 5://merge-array 132 | { 133 | switch (ops.length) { 134 | case 2: 135 | ans=enBrace(restoreNext(ops[1],true),'['); 136 | break; 137 | case 1: 138 | ans='[]'; 139 | break; 140 | default: 141 | { 142 | let a=restoreNext(ops[1],true); 143 | //console.log(a,a.startsWith('[')&&a.endsWith(']')); 144 | if(a.startsWith('[')&&a.endsWith(']')){ 145 | if(a!='[]'){ 146 | ans=enBrace(a.slice(1,-1).trim()+','+restoreNext(ops[2],true),'['); 147 | //console.log('-',a); 148 | }else{ 149 | ans=enBrace(restoreNext(ops[2],true),'['); 150 | } 151 | }else{ 152 | ans=enBrace('...'+a+','+restoreNext(ops[2],true),'[');//may/must not support in fact 153 | } 154 | } 155 | } 156 | break; 157 | } 158 | case 6://get value of an object 159 | { 160 | let sonName=restoreNext(ops[2],true); 161 | if(sonName._type==="var") 162 | ans=restoreNext(ops[1],true)+enBrace(sonName,'['); 163 | else{ 164 | let attach=""; 165 | if(/^[A-Za-z\_][A-Za-z\d\_]*$/.test(sonName)/*is a qualified id*/) 166 | attach='.'+sonName; 167 | else attach=enBrace(sonName,'['); 168 | ans=restoreNext(ops[1],true)+attach; 169 | } 170 | break; 171 | } 172 | case 7://get value of str 173 | { 174 | switch(ops[1][0]){ 175 | case 11: 176 | ans=enBrace("__unTestedGetValue:"+enBrace(jsoToWxon(ops),'['),'{'); 177 | break; 178 | case 3: 179 | ans=new String(ops[1][1]); 180 | ans._type="var"; 181 | break; 182 | default: 183 | throw Error("Unknown type to get value"); 184 | } 185 | break; 186 | } 187 | case 8://first object 188 | ans=enBrace(ops[1]+':'+restoreNext(ops[2],true),'{');//ops[1] have only this way to define 189 | break; 190 | case 9://object 191 | { 192 | function type(x){ 193 | if(x.startsWith('...'))return 1; 194 | if(x.startsWith('{')&&x.endsWith('}'))return 0; 195 | return 2; 196 | } 197 | let a=restoreNext(ops[1],true); 198 | let b=restoreNext(ops[2],true); 199 | let xa=type(a),xb=type(b); 200 | if(xa==2||xb==2)ans=enBrace("__unkownMerge:"+enBrace(a+","+b,'['),'{'); 201 | else{ 202 | if(!xa)a=a.slice(1,-1).trim(); 203 | if(!xb)b=b.slice(1,-1).trim(); 204 | //console.log(l,r); 205 | ans=enBrace(a+','+b,'{'); 206 | } 207 | break; 208 | } 209 | case 10://...object 210 | ans='...'+restoreNext(ops[1],true); 211 | break; 212 | case 12: 213 | { 214 | let arr=restoreNext(ops[2],true); 215 | if(arr.startsWith('[')&&arr.endsWith(']')) 216 | ans=restoreNext(ops[1],true)+enBrace(arr.slice(1,-1).trim(),'('); 217 | else ans=restoreNext(ops[1],true)+'.apply'+enBrace('null,'+arr,'('); 218 | break; 219 | } 220 | default: 221 | ans=enBrace("__unkownSpecific:"+jsoToWxon(ops),'{'); 222 | } 223 | return scope(ans); 224 | } 225 | } 226 | function restoreGroup(z){ 227 | let ans=[]; 228 | for(let g in z.mul){ 229 | let singleAns=[]; 230 | for(let e of z.mul[g])singleAns.push(restoreSingle(e,false)); 231 | ans[g]=singleAns; 232 | } 233 | let ret=[];//Keep a null array for remaining global Z array. 234 | ret.mul=ans; 235 | return ret; 236 | } 237 | function restoreAll(z){ 238 | if(z.mul)return restoreGroup(z); 239 | let ans=[]; 240 | for(let e of z)ans.push(restoreSingle(e,false)); 241 | return ans; 242 | } 243 | module.exports={getZ(code,cb){ 244 | catchZ(code,z=>cb(restoreAll(z))); 245 | }}; 246 | -------------------------------------------------------------------------------- /wuWxss.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | 3 | const path=require("path"); 4 | 5 | const fs=require("fs"); 6 | 7 | const {VM}=require('vm2'); 8 | 9 | const cssbeautify=require('cssbeautify'); 10 | 11 | const csstree=require('css-tree'); 12 | 13 | function doWxss(dir,cb){ 14 | 15 | function GwxCfg(){} 16 | 17 | GwxCfg.prototype={$gwx(){}}; 18 | 19 | for(let i=0;i<300;i++){ 20 | 21 | GwxCfg.prototype["$gwx"+i]=GwxCfg.prototype.$gwx; 22 | 23 | } 24 | 25 | let runList={},pureData={},result={},actualPure={},importCnt={},frameName="",onlyTest=true,blockCss=[];//custom block css file which won't be imported by others.(no extension name) 26 | 27 | function cssRebuild(data){//need to bind this as {cssFile:__name__} before call 28 | 29 | let cssFile; 30 | 31 | function statistic(data){ 32 | 33 | function addStat(id){ 34 | 35 | if(!importCnt[id])importCnt[id]=1,statistic(pureData[id]); 36 | 37 | else ++importCnt[id]; 38 | 39 | } 40 | 41 | if(typeof data==="number")return addStat(data); 42 | 43 | for(let content of data)if(typeof content==="object"&&content[0]==2)addStat(content[1]); 44 | 45 | } 46 | 47 | function makeup(data){ 48 | 49 | var isPure=typeof data==="number"; 50 | 51 | if(onlyTest){ 52 | 53 | statistic(data); 54 | 55 | if(!isPure){ 56 | 57 | if(data.length==1&&data[0][0]==2)data=data[0][1]; 58 | 59 | else return ""; 60 | 61 | } 62 | 63 | if(!actualPure[data]&&!blockCss.includes(wu.changeExt(wu.toDir(cssFile,frameName),""))){ 64 | 65 | console.log("Regard "+cssFile+" as pure import file."); 66 | 67 | actualPure[data]=cssFile; 68 | 69 | } 70 | 71 | return ""; 72 | 73 | } 74 | 75 | let res=[],attach=""; 76 | 77 | if(isPure&&actualPure[data]!=cssFile){ 78 | 79 | if(actualPure[data])return '@import "'+wu.changeExt(wu.toDir(actualPure[data],cssFile),".wxss")+'";\n'; 80 | 81 | else{ 82 | 83 | res.push("/*! Import by _C["+data+"], whose real path we cannot found. */"); 84 | 85 | attach="/*! Import end */"; 86 | 87 | } 88 | 89 | } 90 | 91 | let exactData=isPure?pureData[data]:data; 92 | 93 | for(let content of exactData) 94 | 95 | if(typeof content==="object"){ 96 | 97 | switch(content[0]){ 98 | 99 | case 0://rpx 100 | 101 | res.push(content[1]+"rpx"); 102 | 103 | break; 104 | 105 | case 1://add suffix, ignore it for restoring correct! 106 | 107 | break; 108 | 109 | case 2://import 110 | 111 | res.push(makeup(content[1])); 112 | 113 | break; 114 | 115 | } 116 | 117 | }else res.push(content); 118 | 119 | return res.join("")+attach; 120 | 121 | } 122 | 123 | return ()=>{ 124 | 125 | cssFile=this.cssFile; 126 | 127 | if(!result[cssFile])result[cssFile]=""; 128 | 129 | result[cssFile]+=makeup(data); 130 | 131 | }; 132 | 133 | } 134 | 135 | function runVM(name,code){ 136 | 137 | let wxAppCode={},handle={cssFile:name}; 138 | 139 | let gg = new GwxCfg() 140 | 141 | let tsandbox = {$gwx:GwxCfg.prototype["$gwx"],__mainPageFrameReady__:GwxCfg.prototype["$gwx"],__wxAppCode__:wxAppCode,setCssToHead:cssRebuild.bind(handle)} 142 | 143 | let vm=new VM({sandbox:tsandbox}); 144 | 145 | vm.run(code); 146 | 147 | for(let name in wxAppCode)if(name.endsWith(".wxss")){ 148 | 149 | handle.cssFile=path.resolve(frameName,"..",name); 150 | 151 | wxAppCode[name](); 152 | 153 | } 154 | 155 | } 156 | 157 | function preRun(dir,frameFile,mainCode,files,cb){ 158 | 159 | wu.addIO(cb); 160 | 161 | runList[path.resolve(dir,"./app.wxss")]=mainCode; 162 | 163 | for(let name of files)if(name!=frameFile){ 164 | 165 | wu.get(name,code=>{ 166 | 167 | code=code.slice(0,code.indexOf("\n")); 168 | 169 | if(code.indexOf("setCssToHead")>-1)runList[name]=code.slice(code.indexOf("setCssToHead")); 170 | 171 | }); 172 | 173 | } 174 | 175 | } 176 | 177 | function runOnce(){ 178 | 179 | for(let name in runList)runVM(name,runList[name]); 180 | 181 | } 182 | 183 | function transformCss(style){ 184 | 185 | let ast=csstree.parse(style); 186 | 187 | csstree.walk(ast,function(node){ 188 | 189 | if(node.type=="Comment"){//Change the comment because the limit of css-tree 190 | 191 | node.type="Raw"; 192 | 193 | node.value="\n/*"+node.value+"*/\n"; 194 | 195 | } 196 | 197 | if(node.type=="TypeSelector"){ 198 | 199 | if(node.name.startsWith("wx-"))node.name=node.name.slice(3); 200 | 201 | else if(node.name=="body")node.name="page"; 202 | 203 | } 204 | 205 | if(node.children){ 206 | 207 | const removeType=["webkit","moz","ms","o"]; 208 | 209 | let list={}; 210 | 211 | node.children.each((son,item)=>{ 212 | 213 | if(son.type=="Declaration"){ 214 | 215 | if(list[son.property]){ 216 | 217 | let a=item,b=list[son.property],x=son,y=b.data,ans=null; 218 | 219 | if(x.value.type=='Raw'&&x.value.value.startsWith("progid:DXImageTransform")){ 220 | 221 | node.children.remove(a); 222 | 223 | ans=b; 224 | 225 | }else if(y.value.type=='Raw'&&y.value.value.startsWith("progid:DXImageTransform")){ 226 | 227 | node.children.remove(b); 228 | 229 | ans=a; 230 | 231 | }else{ 232 | 233 | let xValue=x.value.children&&x.value.children.head&&x.value.children.head.data.name,yValue=y.value.children&&y.value.children.head&&y.value.children.head.data.name; 234 | 235 | if(xValue&&yValue)for(let type of removeType)if(xValue==`-${type}-${yValue}`){ 236 | 237 | node.children.remove(a); 238 | 239 | ans=b; 240 | 241 | break; 242 | 243 | }else if(yValue==`-${type}-${xValue}`){ 244 | 245 | node.children.remove(b); 246 | 247 | ans=a; 248 | 249 | break; 250 | 251 | }else{ 252 | 253 | let mValue=`-${type}-`; 254 | 255 | if(xValue.startsWith(mValue))xValue=xValue.slice(mValue.length); 256 | 257 | if(yValue.startsWith(mValue))yValue=yValue.slice(mValue.length); 258 | 259 | } 260 | 261 | if(ans===null)ans=b; 262 | 263 | } 264 | 265 | list[son.property]=ans; 266 | 267 | }else list[son.property]=item; 268 | 269 | } 270 | 271 | }); 272 | 273 | for(let name in list)if(!name.startsWith('-')) 274 | 275 | for(let type of removeType){ 276 | 277 | let fullName=`-${type}-${name}`; 278 | 279 | if(list[fullName]){ 280 | 281 | node.children.remove(list[fullName]); 282 | 283 | delete list[fullName]; 284 | 285 | } 286 | 287 | } 288 | 289 | } 290 | 291 | }); 292 | 293 | return cssbeautify(csstree.generate(ast),{indent:' ',autosemicolon:true}); 294 | 295 | } 296 | 297 | wu.scanDirByExt(dir,".html",files=>{ 298 | 299 | let frameFile=""; 300 | 301 | if(fs.existsSync(path.resolve(dir,"page-frame.html"))) 302 | 303 | frameFile=path.resolve(dir,"page-frame.html"); 304 | 305 | else if(fs.existsSync(path.resolve(dir,"app-wxss.js"))) 306 | 307 | frameFile=path.resolve(dir,"app-wxss.js"); 308 | 309 | else if(fs.existsSync(path.resolve(dir,"page-frame.js"))) 310 | 311 | frameFile=path.resolve(dir,"page-frame.js"); 312 | 313 | else throw Error("page-frame-like file is not found in the package by auto."); 314 | 315 | wu.get(frameFile,code=>{ 316 | 317 | code=code.slice(code.indexOf('var setCssToHead = function(file, _xcInvalid')); 318 | 319 | code=code.slice(code.indexOf('\nvar _C= ')+1); 320 | 321 | let oriCode=code; 322 | 323 | code=code.slice(0,code.indexOf('\n')); 324 | 325 | let vm=new VM({sandbox:{}}); 326 | 327 | pureData=vm.run(code+"\n_C"); 328 | 329 | let mainCode=oriCode.slice(oriCode.indexOf("setCssToHead"),oriCode.lastIndexOf(";var __pageFrameEndTime__")); 330 | 331 | console.log("Guess wxss(first turn)..."); 332 | 333 | preRun(dir,frameFile,mainCode,files,()=>{ 334 | 335 | frameName=frameFile; 336 | 337 | onlyTest=true; 338 | 339 | runOnce(); 340 | 341 | onlyTest=false; 342 | 343 | console.log("Import count info: %j",importCnt); 344 | 345 | for(let id in pureData)if(!actualPure[id]){ 346 | 347 | if(!importCnt[id])importCnt[id]=0; 348 | 349 | if(importCnt[id]<=1){ 350 | 351 | console.log("Cannot find pure import for _C["+id+"] which is only imported "+importCnt[id]+" times. Let importing become copying."); 352 | 353 | }else{ 354 | 355 | let newFile=path.resolve(dir,"__wuBaseWxss__/"+id+".wxss"); 356 | 357 | console.log("Cannot find pure import for _C["+id+"], force to save it in ("+newFile+")."); 358 | 359 | id=Number.parseInt(id); 360 | 361 | actualPure[id]=newFile; 362 | 363 | cssRebuild.call({cssFile:newFile},id)(); 364 | 365 | } 366 | 367 | } 368 | 369 | console.log("Guess wxss(first turn) done.\nGenerate wxss(second turn)..."); 370 | 371 | runOnce() 372 | 373 | console.log("Generate wxss(second turn) done.\nSave wxss..."); 374 | 375 | for(let name in result)wu.save(wu.changeExt(name,".wxss"),transformCss(result[name])); 376 | 377 | let delFiles={}; 378 | 379 | for(let name of files)delFiles[name]=8; 380 | 381 | delFiles[frameFile]=4; 382 | 383 | cb(delFiles); 384 | 385 | }); 386 | 387 | }); 388 | 389 | }); 390 | 391 | } 392 | 393 | module.exports={doWxss:doWxss}; 394 | 395 | if(require.main===module){ 396 | 397 | wu.commandExecute(doWxss,"Restore wxss files.\n\n\n\n restore wxss file from a unpacked directory(Have page-frame.html (or app-wxss.js) and other html file)."); 398 | 399 | } -------------------------------------------------------------------------------- /wuWxml.js: -------------------------------------------------------------------------------- 1 | const wu=require("./wuLib.js"); 2 | const {getZ}=require("./wuRestoreZ.js"); 3 | const {wxsBeautify}=require("./wuJs.js"); 4 | const fs=require('fs'); 5 | const path=require("path"); 6 | const esprima=require('esprima'); 7 | const {VM}=require('vm2'); 8 | const escodegen=require('escodegen'); 9 | function analyze(core,z,namePool,xPool,fakePool={},zMulName="0"){ 10 | function anaRecursion(core,fakePool={}){ 11 | return analyze(core,z,namePool,xPool,fakePool,zMulName); 12 | } 13 | function push(name,elem){ 14 | namePool[name]=elem; 15 | } 16 | function pushSon(pname,son){ 17 | if(fakePool[pname])fakePool[pname].son.push(son); 18 | else namePool[pname].son.push(son); 19 | } 20 | for(let ei=0;ei0) 116 | throw Error("Noticable generics content: "+dec.init.arguments[2].toString()); 117 | let mv={}; 118 | let name=null,base=0; 119 | for(let x of dec.init.arguments[1].elements){ 120 | let v=x.value; 121 | if(!v&&typeof v!="number"){ 122 | if(x.type=="UnaryExpression"&&x.operator=="-")v=-x.argument.value; 123 | else throw Error("Unknown type of object in _m attrs array: "+x.type); 124 | } 125 | if(name===null){ 126 | name=v; 127 | }else{ 128 | if(base+v<0)mv[name]=null;else{ 129 | mv[name]=z[base+v]; 130 | if(base==0)base=v; 131 | } 132 | name=null; 133 | } 134 | } 135 | push(dec.id.name,{tag:dec.init.arguments[0].value,son:[],v:mv}); 136 | } 137 | break; 138 | case "_mz": 139 | { 140 | if(dec.init.arguments[3].elements.length>0) 141 | throw Error("Noticable generics content: "+dec.init.arguments[3].toString()); 142 | let mv={}; 143 | let name=null,base=0; 144 | for(let x of dec.init.arguments[2].elements){ 145 | let v=x.value; 146 | if(!v&&typeof v!="number"){ 147 | if(x.type=="UnaryExpression"&&x.operator=="-")v=-x.argument.value; 148 | else throw Error("Unknown type of object in _mz attrs array: "+x.type); 149 | } 150 | if(name===null){ 151 | name=v; 152 | }else{ 153 | if(base+v<0)mv[name]=null;else{ 154 | mv[name]=z.mul[zMulName][base+v]; 155 | if(base==0)base=v; 156 | } 157 | name=null; 158 | } 159 | } 160 | push(dec.id.name,{tag:dec.init.arguments[1].value,son:[],v:mv}); 161 | } 162 | break; 163 | case "_gd"://template use/is 164 | { 165 | let is=namePool[dec.init.arguments[1].name].content; 166 | let data=null,obj=null; 167 | ei++; 168 | for(let e of core[ei].consequent.body){ 169 | if(e.type=="VariableDeclaration"){ 170 | for(let f of e.declarations){ 171 | if(f.init.type=="LogicalExpression"&&f.init.left.type=="CallExpression"){ 172 | if(f.init.left.callee.name=="_1")data=z[f.init.left.arguments[0].value]; 173 | else if(f.init.left.callee.name=="_1z")data=z.mul[zMulName][f.init.left.arguments[1].value]; 174 | } 175 | } 176 | }else if(e.type=="ExpressionStatement"){ 177 | let f=e.expression; 178 | if(f.type=="AssignmentExpression"&&f.operator=="="&&f.left.property&&f.left.property.name=="wxXCkey"){ 179 | obj=f.left.object.name; 180 | } 181 | } 182 | } 183 | namePool[obj].tag="template"; 184 | Object.assign(namePool[obj].v,{is:is,data:data}); 185 | } 186 | break; 187 | default: 188 | { 189 | let funName=dec.init.callee.name; 190 | if(funName.startsWith("gz$gwx")){ 191 | zMulName=funName.slice(6); 192 | }else throw Error("Unknown init callee "+funName); 193 | } 194 | } 195 | }else if(dec.init.type=="FunctionExpression"){ 196 | push(dec.id.name,{tag:"gen",func:dec.init}); 197 | }else if(dec.init.type=="MemberExpression"){ 198 | if(dec.init.object.type=="MemberExpression"&&dec.init.object.object.name=="e_"&&dec.init.object.property.type=="MemberExpression"&&dec.init.object.property.object.name=="x"){ 199 | if(dec.init.property.name=="j"){//include 200 | //do nothing 201 | }else if(dec.init.property.name=="i"){//import 202 | //do nothing 203 | }else throw Error("Unknown member expression declaration."); 204 | }else throw Error("Unknown member expression declaration."); 205 | }else throw Error("Unknown declaration init type " + dec.init.type); 206 | } 207 | break; 208 | case "IfStatement": 209 | if(e.test.callee.name.startsWith("_o")){ 210 | function parse_OFun(e){ 211 | if(e.test.callee.name=="_o")return z[e.test.arguments[0].value]; 212 | else if(e.test.callee.name=="_oz")return z.mul[zMulName][e.test.arguments[1].value]; 213 | else throw Error("Unknown if statement test callee name:"+e.test.callee.name); 214 | } 215 | let vname=e.consequent.body[0].expression.left.object.name; 216 | let nif={tag:"block",v:{"wx:if":parse_OFun(e)},son:[]}; 217 | anaRecursion(e.consequent.body,{[vname]:nif}); 218 | pushSon(vname,nif); 219 | if(e.alternate){ 220 | while(e.alternate&&e.alternate.type=="IfStatement"){ 221 | e=e.alternate; 222 | nif={tag:"block",v:{"wx:elif":parse_OFun(e)},son:[]}; 223 | anaRecursion(e.consequent.body,{[vname]:nif}); 224 | pushSon(vname,nif); 225 | } 226 | if(e.alternate&&e.alternate.type=="BlockStatement"){ 227 | e=e.alternate; 228 | nif={tag:"block",v:{"wx:else":null},son:[]}; 229 | anaRecursion(e.body,{[vname]:nif}); 230 | pushSon(vname,nif); 231 | } 232 | } 233 | }else throw Error("Unknown if statement."); 234 | break; 235 | default: 236 | throw Error("Unknown type "+e.type); 237 | } 238 | } 239 | } 240 | function wxmlify(str,isText){ 241 | if(typeof str=="undefined"||str===null)return "Empty";//throw Error("Empty str in "+(isText?"text":"prop")); 242 | if(isText)return str;//may have some bugs in some specific case(undocumented by tx) 243 | else return str.replace(/"/g, '\\"'); 244 | } 245 | function elemToString(elem,dep,moreInfo=false){ 246 | const longerList=[];//put tag name which can't be style. 247 | const indent=' '.repeat(4); 248 | function isTextTag(elem){ 249 | return elem.tag=="__textNode__"&&elem.textNode; 250 | } 251 | function elemRecursion(elem,dep){ 252 | return elemToString(elem,dep,moreInfo); 253 | } 254 | function trimMerge(rets){ 255 | let needTrimLeft=false,ans=""; 256 | for(let ret of rets){ 257 | if(ret.textNode==1){ 258 | if(!needTrimLeft){ 259 | needTrimLeft=true; 260 | ans=ans.trimRight(); 261 | } 262 | }else if(needTrimLeft){ 263 | needTrimLeft=false; 264 | ret=ret.trimLeft(); 265 | } 266 | ans+=ret; 267 | } 268 | return ans; 269 | } 270 | if(isTextTag(elem)){ 271 | //In comment, you can use typify text node, which beautify its code, but may destroy ui. 272 | //So, we use a "hack" way to solve this problem by letting typify program stop when face textNode 273 | let str=new String(wxmlify(elem.content,true)); 274 | str.textNode=1; 275 | return wxmlify(str,true);//indent.repeat(dep)+wxmlify(elem.content.trim(),true)+"\n"; 276 | } 277 | if(elem.tag=="block"&&!moreInfo){ 278 | if(elem.son.length==1&&!isTextTag(elem.son[0])){ 279 | let ok=true,s=elem.son[0]; 280 | for(let x in elem.v)if(x in s.v){ 281 | ok=false; 282 | break; 283 | } 284 | if(ok&&!(("wx:for" in s.v||"wx:if" in s.v)&&("wx:if" in elem.v||"wx:else" in elem.v||"wx:elif" in elem.v))){//if for and if in one tag, the default result is an if in for. And we should block if nested in elif/else been combined. 285 | Object.assign(s.v,elem.v); 286 | return elemRecursion(s,dep); 287 | } 288 | }else if(Object.keys(elem.v).length==0){ 289 | let ret=[]; 290 | for(let s of elem.son)ret.push(elemRecursion(s,dep)); 291 | return trimMerge(ret); 292 | } 293 | } 294 | let ret=indent.repeat(dep)+"<"+elem.tag; 295 | for(let v in elem.v)ret+=" "+v+(elem.v[v]!==null?"=\""+wxmlify(elem.v[v])+"\"":""); 296 | if(elem.son.length==0){ 297 | if(longerList.includes(elem.tag))return ret+" />\n"; 298 | else return ret+">\n"; 299 | } 300 | ret+=">\n"; 301 | let rets=[ret]; 302 | for(let s of elem.son)rets.push(elemRecursion(s,dep+1)); 303 | rets.push(indent.repeat(dep)+"\n"); 304 | return trimMerge(rets); 305 | } 306 | function doWxml(state,dir,name,code,z,xPool,rDs,wxsList,moreInfo){ 307 | let rname=code.slice(code.lastIndexOf("return")+6).replace(/[\;\}]/g,"").trim(); 308 | code=code.slice(code.indexOf("\n"),code.lastIndexOf("return")).trim(); 309 | let r={son:[]}; 310 | console.log(code) 311 | analyze(esprima.parseScript(code).body,z,{[rname]:r},xPool,{[rname]:r}); 312 | let ans=[]; 313 | for(let elem of r.son)ans.push(elemToString(elem,0,moreInfo)); 314 | let result=[ans.join("")]; 315 | for(let v in rDs){ 316 | state[0]=v; 317 | let oriCode=rDs[v].toString(); 318 | let rname=oriCode.slice(oriCode.lastIndexOf("return")+6).replace(/[\;\}]/g,"").trim(); 319 | let tryPtr=oriCode.indexOf("\ntry{"); 320 | let zPtr=oriCode.indexOf("var z=gz$gwx"); 321 | let code=oriCode.slice(tryPtr+5,oriCode.lastIndexOf("\n}catch(")).trim(); 322 | if(zPtr!=-1&&tryPtr>zPtr){ 323 | let attach=oriCode.slice(zPtr); 324 | attach=attach.slice(0,attach.indexOf("()"))+"()\n"; 325 | code=attach+code; 326 | } 327 | let r={tag:"template",v:{name:v},son:[]}; 328 | analyze(esprima.parseScript(code).body,z,{[rname]:r},xPool,{[rname]:r}); 329 | result.unshift(elemToString(r,0,moreInfo)); 330 | } 331 | name=path.resolve(dir,name); 332 | if(wxsList[name])result.push(wxsList[name]); 333 | console.log(name) 334 | wu.save(name,result.join("")); 335 | } 336 | function tryWxml(dir,name,code,z,xPool,rDs,...args){ 337 | console.log("Decompile "+name+"..."); 338 | let state=[null]; 339 | try{ 340 | doWxml(state,dir,name,code,z,xPool,rDs,...args); 341 | console.log("Decompile success!"); 342 | }catch(e){ 343 | console.log("error on "+name+"("+(state[0]===null?"Main":"Template-"+state[0])+")\nerr: ",e); 344 | if(state[0]===null)wu.save(path.resolve(dir,name+".ori.js"),code); 345 | else wu.save(path.resolve(dir,name+".tem-"+state[0]+".ori.js"),rDs[state[0]].toString()); 346 | } 347 | } 348 | function doWxs(code){ 349 | const before='nv_module={nv_exports:{}};'; 350 | return wxsBeautify(code.slice(code.indexOf(before)+before.length,code.lastIndexOf('return nv_module.nv_exports;}')).replace(/nv\_/g,'')); 351 | } 352 | function doFrame(name,cb,order){ 353 | let moreInfo=order.includes("m"); 354 | wxsList={}; 355 | wu.get(name,code=>{ 356 | getZ(code,z=>{ 357 | const before="\nvar nv_require=function(){var nnm="; 358 | code=code.slice(code.indexOf(before)+before.length,code.lastIndexOf("if(path&&e_[path]){")); 359 | json=code.slice(0,code.indexOf("};")+1); 360 | let endOfRequire=code.indexOf("()\r\n")+4; 361 | if(endOfRequire==4-1)endOfRequire=code.indexOf("()\n")+3; 362 | code=code.slice(endOfRequire); 363 | let rD={},rE={},rF={},requireInfo,x,vm=new VM({sandbox:{d_:rD,e_:rE,f_:rF,_vmRev_(data){ 364 | [x,requireInfo]=data; 365 | },nv_require(path){ 366 | return ()=>path; 367 | }}}); 368 | vm.run(code+"\n_vmRev_([x,"+json+"])"); 369 | let dir=path.dirname(name),pF=[]; 370 | for(let info in rF)if(typeof rF[info]=="function"){ 371 | let name=path.resolve(dir,(info[0]=='/'?'.':'')+info),ref=rF[info](); 372 | pF[ref]=info; 373 | wu.save(name,doWxs(requireInfo[ref].toString())); 374 | } 375 | for(let info in rF)if(typeof rF[info]=="object"){ 376 | let name=path.resolve(dir,(info[0]=='/'?'.':'')+info); 377 | let res=[],now=rF[info]; 378 | for(let deps in now){ 379 | let ref=now[deps](); 380 | if(ref.includes(":"))res.push("\n"+doWxs(requireInfo[ref].toString())+"\n"); 381 | else if(pF[ref])res.push(""); 382 | else res.push(""); 383 | wxsList[name]=res.join("\n"); 384 | } 385 | } 386 | for(let name in rE)tryWxml(dir,name,rE[name].f.toString(),z,x,rD[name],wxsList,moreInfo); 387 | cb({[name]:4}); 388 | }); 389 | }); 390 | } 391 | module.exports={doFrame:doFrame}; 392 | if(require.main===module){ 393 | wu.commandExecute(doFrame,"Restore wxml files.\n\n\n\n restore wxml file from page-frame.html or app-wxss.js."); 394 | } 395 | --------------------------------------------------------------------------------