1 | import * as fs from 'fs';
|
2 | import * as path from 'path';
|
3 | import * as os from 'os';
|
4 | import * as iconv from 'iconv-lite';
|
5 | import fileUtil = require("./../tool/FileUtil");
|
6 |
|
7 | var exec = require('child_process').exec;
|
8 |
|
9 |
|
10 | export class AutoCodeEui {
|
11 | private rootpath: string;
|
12 | private curExmlName: string;
|
13 | private curExmlPath: string = "";
|
14 | private codeCreate: CreateCode;
|
15 |
|
16 | |
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | constructor(rootUrl: string, exmlName: string) {
|
24 | this.rootpath = rootUrl;
|
25 | this.codeCreate = new CreateCode(this.rootpath);
|
26 | if (exmlName.indexOf(".exml") != -1) exmlName = exmlName.replace(".exml", "")
|
27 | this.curExmlName = exmlName;
|
28 | if (exmlName == "-module") {
|
29 | this.euiOpenCodeModule();
|
30 | } else if (exmlName == "-config") {
|
31 | this.euiOutCodeConfig();
|
32 | } else if (exmlName == "-help") {
|
33 | console.log(`
|
34 | nodetool -eui -config 打开配置文件
|
35 | nodetool -eui -module 打开模板文件夹
|
36 | nodetool -eui -help 显示帮助
|
37 | nodetool -eui 皮肤名 生成皮肤的代码
|
38 | 皮肤名
|
39 | 1. exml的相对路径如: nodetool -eui resource\assets\module\chat\ChatSkin.exml
|
40 | 2. exml的文件名 如: nodetool -eui ChatSkin
|
41 | 3. exml文件全名 如: nodetool -eui ChatSkin.exml
|
42 | 4. exml模糊匹配 如: nodetool -eui Chat* (慎用)
|
43 |
|
44 | 皮肤命名规则
|
45 | 1. 如果皮肤文件名中带有 MainPanel (功能的主界面) 的话 程序生成 :
|
46 | ViewUI(覆盖)
|
47 | MainView(只生成一次)
|
48 | Module(只生成一次)
|
49 | Mediator(只生成一次)
|
50 | Model(只生成一次)
|
51 | 2. 如果皮肤名中包含 Panel(弹窗界面) 程序生成:
|
52 | ViewUI(覆盖)
|
53 | Panel (只生成一次)
|
54 | 3. 如果皮肤名中包含 Render(列表项) 程序生成:
|
55 | RenderUI (覆盖)
|
56 | Render (只生成一次)
|
57 | 4. 如果皮肤名中包含 View(只是界面) 或者上述情况都不符合 程序生成:
|
58 | ViewUI (覆盖)
|
59 |
|
60 | 使用说明:
|
61 | 1.可以先输入 nodetool -eui -config 打开配置app.config.json 修改 auth为自己的名字 (如果不填默认使用计算机名)
|
62 | 2.在程序的跟目录下执行命令 如 D:\workspace\yscq> nodetool -eui Chat*Render
|
63 | `)
|
64 | } else {
|
65 | let exmlPath = path.join(this.rootpath, exmlName + ".exml");
|
66 | if (!fs.existsSync(exmlPath))
|
67 | {
|
68 | let exmlDir = path.join(this.rootpath, "resource/");
|
69 | if (fs.existsSync(exmlDir))
|
70 | fileUtil.FileUtil.walkDir(exmlDir, this.checkFile, undefined, this);
|
71 | else
|
72 | console.log("资源目录不存在:" + exmlDir);
|
73 | } else {
|
74 | this.checkFile(exmlPath, false);
|
75 | }
|
76 | }
|
77 | }
|
78 |
|
79 | public euiOutCode() {
|
80 | let filePath = this.curExmlPath;
|
81 | let fileName = filePath.substring(0, filePath.lastIndexOf("\\"));
|
82 | let ext = path.extname(filePath);
|
83 | if (ext != '.exml') {
|
84 | console.log("当前不是Eui文件" + ext);
|
85 | return;
|
86 | }
|
87 | let content = fs.readFileSync(filePath, 'utf-8');
|
88 | let ids = this.findIds(content);
|
89 |
|
90 | let euiinfo: EUIInfo = {};
|
91 | euiinfo.path = filePath;
|
92 | euiinfo.content = content;
|
93 | euiinfo.ids = ids;
|
94 | let pathinfo = path.parse(filePath);
|
95 | let filename = euiinfo.fileName = pathinfo.name;
|
96 | let filearr = filename.match(/^(.*?)EuiSkin$/i);
|
97 | if (!filearr) {
|
98 | filearr = filename.match(/^(.*?)Skin$/i);
|
99 | }
|
100 | if (filearr)
|
101 | euiinfo.baseClsName = filearr[1];
|
102 | else
|
103 | euiinfo.baseClsName = filename;
|
104 |
|
105 | let skinNameReg = /\s+class=["'](\w+)["']\s+/gi;
|
106 | let skinNameArr = skinNameReg.exec(content);
|
107 | if (skinNameArr && skinNameArr.length > 0) {
|
108 | euiinfo.skinName = skinNameArr[1];
|
109 | } else {
|
110 | euiinfo.skinName = euiinfo.baseClsName;
|
111 | }
|
112 | euiinfo.baseClsName = euiinfo.baseClsName;
|
113 | let pathdirarr = path.normalize(pathinfo.dir).split(path.sep);
|
114 | euiinfo.parentDir = pathdirarr[pathdirarr.length - 1];
|
115 | this.codeCreate.createCode(euiinfo);
|
116 | }
|
117 | |
118 |
|
119 |
|
120 | private findIds(content: string): IdInfo[] {
|
121 | let ids: IdInfo[] = [];
|
122 | let lines = content.split(/[\r\n(\r\n)]/);
|
123 | let nss: any = this.findNameSp(lines.join(" "));
|
124 | let idexp = / id=\"(.*?)\"/ig;
|
125 | let uimodule = this.is_eui(content) ? "eui." : 'egret.gui.';
|
126 | lines.forEach(line => {
|
127 | let temp = line.match(idexp);
|
128 | if (temp && temp.length > 0) {
|
129 | let clsDef = line.match(/<(.+?):(.+?) /);
|
130 | if (!clsDef || clsDef.length < 3) return;
|
131 | let clsMod: string;
|
132 | if (clsDef[1] == "e") {
|
133 | clsMod = uimodule;
|
134 | } else {
|
135 | clsMod = nss[clsDef[1]];
|
136 | if (!clsMod) return;
|
137 | clsMod = clsMod.substring(0, clsMod.length - 1);
|
138 | }
|
139 | let clsName = clsDef[2];
|
140 | let id = temp[0].replace(' id=', "").replace('"', '').replace('"', '');
|
141 | ids.push({ name: id, module: clsMod, clsName: clsName });
|
142 | }
|
143 | })
|
144 | return ids;
|
145 | }
|
146 | /**查找命名空间 */
|
147 | public findNameSp(text: string) {
|
148 | var map: any = {};
|
149 | var names: any = text.match(/xmlns:(.+?)="(.+?)"/g);
|
150 | names.forEach((name: string) => {
|
151 | var result: any = name.match(/xmlns:(.+?)="(.+?)"/);
|
152 | if (result.length == 3) {
|
153 | map[result[1]] = map[result[2]];
|
154 | }
|
155 | });
|
156 | return map;
|
157 | }
|
158 |
|
159 | public is_eui(text: string): boolean {
|
160 | if (text.indexOf('xmlns:e="http://ns.egret.com/eui"') > 0)
|
161 | return true;
|
162 | return false;
|
163 |
|
164 | }
|
165 |
|
166 |
|
167 | public euiOutCodeConfig() {
|
168 | console.log(this.codeCreate.getConfigPath())
|
169 | exec('explorer.exe /select, ' + this.codeCreate.getConfigPath());
|
170 | }
|
171 |
|
172 | public euiOpenCodeModule() {
|
173 | console.log(this.codeCreate.getModulePath())
|
174 | exec('explorer.exe ' + this.codeCreate.getModulePath());
|
175 | }
|
176 |
|
177 | public checkFile(url: string, needCheck = true) {
|
178 | let fileInfo = path.parse(url);
|
179 | let tempName = this.curExmlName;
|
180 | if (tempName.indexOf("*") != -1) {
|
181 | let reg = new RegExp(tempName.replace(/\*/gi, ".*?"), "gi");
|
182 | if (reg.exec(fileInfo.name)) {
|
183 | tempName = fileInfo.name;
|
184 | }
|
185 | }
|
186 | if (fileInfo.name == tempName || !needCheck) {
|
187 | if (!needCheck)
|
188 | this.curExmlName = fileInfo.name;
|
189 | this.curExmlPath = url;
|
190 | console.log("开始处理 " + fileInfo.name)
|
191 | this.euiOutCode();
|
192 | }
|
193 | }
|
194 | }
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | class CreateCode {
|
200 | private rootpath: string = "";
|
201 | private moduleCodePath: string = "";
|
202 | private modulepath = path.join(__dirname, "./../../resource/autocodeeui", "module");
|
203 | private configpath = path.join(__dirname, "./../../resource/autocodeeui/config/app.config.json")
|
204 | private config: any;
|
205 |
|
206 | constructor(rootpath: string) {
|
207 | this.rootpath = rootpath;
|
208 | }
|
209 |
|
210 | public getConfigPath() {
|
211 | return this.configpath;
|
212 | }
|
213 |
|
214 | public getModulePath() {
|
215 | return this.modulepath;
|
216 | }
|
217 |
|
218 | |
219 |
|
220 |
|
221 | public createCode(info: EUIInfo) {
|
222 | if (!fs.existsSync(this.configpath)) {
|
223 | console.log("请添加插件配置" + this.configpath)
|
224 | return;
|
225 | }
|
226 | let configtxt = fs.readFileSync(this.configpath, 'utf-8');
|
227 | this.config = JSON.parse(configtxt);
|
228 | this.moduleCodePath = this.config.moduleCodePath;
|
229 | let varsDic: any = {};
|
230 | varsDic["auth"] = this.config.auth || this.getHostName();
|
231 | let date = new Date();
|
232 | varsDic["time"] = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
|
233 | varsDic['path'] = path.relative(this.rootpath, info.path);
|
234 | varsDic['fileName'] = info.fileName;
|
235 | varsDic['baseClsName'] = info.baseClsName;
|
236 | varsDic['skinName'] = info.skinName;
|
237 | let varids = "";
|
238 | let interfaceIds = "";
|
239 | let ids = info.ids;
|
240 | let createIdDic: { [id: number]: boolean } = {};
|
241 | for (let i = 0; i < ids.length; i++) {
|
242 | let id: any = ids[i];
|
243 | let preAdd = "";
|
244 | if (createIdDic[id.name]) preAdd = "//";
|
245 | varids += preAdd + "public " + id.name + ":" + id.module + id.clsName + ";\n\t";
|
246 | interfaceIds += preAdd + id.name + "?:any;\n\t";
|
247 | createIdDic[id.name] = true;
|
248 | }
|
249 | varsDic['varids'] = varids;
|
250 | varsDic['interfaceIds'] = interfaceIds;
|
251 | let create = this.findModule(this.config, info.fileName);
|
252 | let moduleIds: Array<string> = create.usemodule.split(",");
|
253 | var keys = create.keyword.split("|");
|
254 | for (let j = 0; j < keys.length; j++) {
|
255 | if (info.baseClsName.indexOf(keys[j]) != -1) {
|
256 | varsDic["shortName"] = info.baseClsName.replace(keys[j], "");
|
257 | break;
|
258 | }
|
259 | }
|
260 | if (!varsDic["shortName"]) varsDic["shortName"] = info.baseClsName;
|
261 | varsDic["moduleID"] = this.getModuleID(varsDic["shortName"]);
|
262 | for (let i = 0; i < moduleIds.length; i++) {
|
263 | let mod: ModuleInfo = this.config.module[+moduleIds[i]];
|
264 | if (!mod) {
|
265 | console.log("app.config.json当前没有配置" + moduleIds[i] + "对应的配置")
|
266 | continue;
|
267 | }
|
268 | this.createFileByModuld(mod, info, varsDic);
|
269 | }
|
270 | }
|
271 |
|
272 | private getHostName() {
|
273 | let name = os.hostname();
|
274 | let buff = new Buffer(name, "binary");
|
275 | name = iconv.decode(buff, "gbk");
|
276 | return name;
|
277 | }
|
278 |
|
279 |
|
280 | public getModuleID(moduleName: string) {
|
281 | let idkey: string = "";
|
282 | for (let i = 0; i < moduleName.length; i++) {
|
283 | let char = moduleName[i].toLocaleUpperCase();
|
284 | if (char == moduleName[i]) {
|
285 | if (idkey)
|
286 | idkey = idkey + "_";
|
287 | }
|
288 | idkey += char;
|
289 | }
|
290 | return idkey;
|
291 | }
|
292 |
|
293 |
|
294 | public findModule(config: AppConfig, fileName: string) {
|
295 | let creates = config.create;
|
296 | for (let i = 0; i < creates.length; i++) {
|
297 | let create = creates[i];
|
298 | var keys = create.keyword.split("|");
|
299 | for (let j = 0; j < keys.length; j++) {
|
300 | if (fileName.indexOf(keys[j]) != -1) {
|
301 | return create;
|
302 | }
|
303 | }
|
304 | }
|
305 | return config.create[config.defaultcreate];
|
306 | }
|
307 |
|
308 |
|
309 |
|
310 | public createFileByModuld(modinfo: ModuleInfo, info: EUIInfo, varsDic: any) {
|
311 | let keyReg = /\[(.*?)\]/gi;
|
312 | let keyArr = keyReg.exec(modinfo.outdir);
|
313 | let outpath = path.join(this.rootpath, this.moduleCodePath + info.parentDir + "/" + modinfo.outdir + "/" + varsDic["shortName"] + modinfo.name + "." + modinfo.fileType);
|
314 | if (keyArr) {
|
315 | outpath = path.join(this.rootpath, modinfo.outdir.replace(keyArr[0], keyArr[1]).trim(), varsDic["shortName"] + modinfo.name + "." + modinfo.fileType);
|
316 | }
|
317 | let exists = fs.existsSync(outpath);
|
318 | if (!modinfo.override && exists) {
|
319 |
|
320 | console.log("文件已存在不用生成");
|
321 | return;
|
322 | }
|
323 | let areaDic: any = {};
|
324 |
|
325 | if (exists)
|
326 | {
|
327 | let reg = /\/\*+area(\d+)--start\*+\/([\s\S]*)\/\*+area\1--end\*+\//gi;
|
328 | let oldContent = fs.readFileSync(outpath, 'utf-8');
|
329 | let rect;
|
330 | while (rect = reg.exec(oldContent)) {
|
331 | areaDic[rect[1]] = rect[2];
|
332 | }
|
333 | }
|
334 | let viewcode: string = this.createCodeTxt(path.join(this.modulepath, modinfo.file), info, varsDic);
|
335 | if (exists)
|
336 | {
|
337 | for (let key in areaDic) {
|
338 | let reg = new RegExp("(\\/\\*+area" + key + "--start\\*+\\/)([\\s\\S]*)(\\/\\*+area" + key + "--end\\*+\\/)", "gi");
|
339 | viewcode = viewcode.replace(reg, "$1" + areaDic[key] + "$3");
|
340 | }
|
341 | }
|
342 | this.saveFile(outpath, viewcode);
|
343 | console.log("创建成功" + outpath)
|
344 | }
|
345 |
|
346 |
|
347 | public saveFile(path: string, data: string) {
|
348 | this.checkOrCreateDir(path);
|
349 | fs.writeFileSync(path, data, { encoding: "utf-8" });
|
350 | }
|
351 |
|
352 |
|
353 | public createCodeTxt(moduleFilePath: string, info: EUIInfo, varDic: any): string {
|
354 | if (!fs.existsSync(moduleFilePath)) {
|
355 | console.log("当前文件不存在" + moduleFilePath)
|
356 | return "";
|
357 | }
|
358 | let content = fs.readFileSync(moduleFilePath, 'utf-8');
|
359 | var keyReg = /\$\{(.*?)\}/gi;
|
360 | let keyArr;
|
361 | while (keyArr = keyReg.exec(content)) {
|
362 | content = content.split(keyArr[0]).join(varDic[keyArr[1]]);
|
363 | }
|
364 | return content;
|
365 | }
|
366 |
|
367 |
|
368 |
|
369 | public checkOrCreateDir(filePath: string) {
|
370 | filePath = path.normalize(filePath);
|
371 | let arr = path.parse(filePath).dir.split(path.sep);
|
372 | if (!arr || arr.length == 0) return;
|
373 | let dirpath = arr[0];
|
374 | for (let i = 1; i < arr.length; i++) {
|
375 | dirpath = dirpath + path.sep + arr[i];
|
376 | if (!fs.existsSync(dirpath)) {
|
377 | fs.mkdirSync(dirpath);
|
378 | }
|
379 | }
|
380 | }
|
381 | }
|
382 |
|
383 |
|
384 |
|
385 |
|
386 | interface IdInfo {
|
387 |
|
388 | name: string;
|
389 |
|
390 | module: string;
|
391 | /**变量类名 */
|
392 | clsName: string;
|
393 | }
|
394 |
|
395 | /**
|
396 | * Eui文件的基本信息
|
397 | */
|
398 | interface EUIInfo {
|
399 |
|
400 | path?: string;
|
401 |
|
402 | content?: string;
|
403 |
|
404 |
|
405 |
|
406 |
|
407 | ids?: IdInfo[];
|
408 |
|
409 | parentDir?: string;
|
410 |
|
411 | fileName?: string;
|
412 |
|
413 | baseClsName?: string;
|
414 |
|
415 | skinName?: string;
|
416 | }
|
417 |
|
418 |
|
419 |
|
420 |
|
421 | interface AppConfig {
|
422 | auth: string;
|
423 | defaultcreate: number;
|
424 | create: CreateInfo[];
|
425 | module: { [id: number]: ModuleInfo };
|
426 | moduleCodePath: string;
|
427 | }
|
428 |
|
429 | interface CreateInfo {
|
430 |
|
431 | keyword: string;
|
432 |
|
433 | usemodule: string;
|
434 | }
|
435 |
|
436 |
|
437 | interface ModuleInfo {
|
438 |
|
439 | id: number;
|
440 |
|
441 | name: string;
|
442 |
|
443 | file: string;
|
444 |
|
445 | outdir: string;
|
446 |
|
447 | override: boolean;
|
448 |
|
449 | fileType: string;
|
450 | }
|
451 |
|
452 |
|
453 | export function run(rootUrl: string, exmlName: string): void {
|
454 | new AutoCodeEui(rootUrl, exmlName);
|
455 | console.log("执行完毕");
|
456 | } |
\ | No newline at end of file |