UNPKG

15.4 kBJavaScriptView Raw
1var fs = require('fs');
2var path = require('path');
3var url = require('url');
4var util = require('util');
5var md5 = require('blueimp-md5');
6var ariestp = require("./ariestp.js")
7var te = ariestp.Engine();
8var async = require('async');
9var domain = require('domain');
10var hasSet = false;
11var atry = require('./atry.js');
12var aries = {};
13
14
15aries.set = function(opt){
16 if(hasSet) return te;
17
18 if(opt.path){
19 //检查 opt.path是否存在
20 fs.exists(opt.path, function (exists) {
21 if(!exists){
22 var errMsg = "[ariestp] ariestp.set opt.path is not exist!"
23 console.error(errMsg);
24 throw new Error(errMsg);
25 }
26 });
27 }
28
29 if(opt.includeId && "function" !== typeof opt.includeId){
30 var errMsg = "[ariestp] ariestp.set opt.includeId must be function!"
31 console.error(errMsg);
32 throw new Error(errMsg);
33 }
34
35 if(opt.cacheTime){
36 if(isNaN(opt.cacheTime - 0)){
37 var errMsg = "[ariestp] ariestp.set opt.cacheTime must be a number!"
38 console.error(errMsg);
39 throw new Error(errMsg);
40 }
41 if(opt.cacheTime < 10*1000){
42 var errMsg = "[ariestp] ariestp.set opt.cacheTime must gte 10 secs!"
43 console.error(errMsg);
44 throw new Error(errMsg);
45 }
46 if(opt.cacheTime > 3600*1000){
47 var errMsg = "[ariestp] ariestp.set opt.cacheTime must lt 1 hour!"
48 console.error(errMsg);
49 throw new Error(errMsg);
50 }
51
52 }
53
54 te = ariestp.Engine(opt);
55 hasSet = true;
56 console.log("[ariestp] ariestp.set ok, ready for compile.");
57 return te;
58}
59
60
61
62//渲染文件路径
63aries.compileFile = function(filename, ctx, cb){
64 aries.getTemplateFromFile(filename, function(err, tempstr){
65 if(err) return cb(err);
66 aries.compile(tempstr.toString(), ctx, cb);
67 })
68}
69
70//渲染文件字符串
71aries.includes = function(tempstr, cb){
72 cb.isIncludes = true;
73 aries.compile(tempstr, {}, cb);
74}
75
76
77//优雅的错误输出
78aries.prettyStackFunc = function(err, incMapStr){
79 var incMapStr = incMapStr || {};
80 var keys = Object.keys(incMapStr);
81 var eId = ''
82
83 try{
84 if(!err.stack){
85 return err;
86 }else{
87 var stackLineReg = /^(.*)at(.*):(.*):(.*)\)$/
88 var stackList = (err.stack || '').split('\n');
89 var stackLen = stackList.length;
90
91
92 //console.log(incMapStr)
93 //console.log(err.stack)
94 //console.log(err.codeStr)
95
96 //当stack length 大于0
97 if(keys.length > 0 && stackLen > 0){
98 var codeStr = '';
99 for(var i=0;i<stackLen;i++){
100 if(stackLineReg.test(stackList[i])){
101 var line = stackList[i].split(':')[1] - 0;
102 if(line && line > 0){//找到出错的行号了
103 codeStr = (((err.codeStr || '').toString().split('\n') || [])[line-1] || '').trim();
104 //如果最后是分号
105 if(codeStr.lastIndexOf(';') == (codeStr.length - 1)){
106 codeStr = codeStr.slice(0, -2);
107 }
108 break;
109 }
110 }
111 }
112
113 // 找到出错的行号
114 if(codeStr != ''){
115 keys.forEach(function(key){
116 var mapStrList = incMapStr[key].split('\n');
117 for(var i=0;i<mapStrList.length;i++){
118 if(mapStrList[i].indexOf(codeStr) >= 0){
119 eId += key + ', '
120 break;
121 }
122 }
123 return;
124 })
125 }
126 }
127
128
129
130
131 var tplId = eId || 'main';
132 err.prettyStack = 'error occur in '+ tplId + '<br/>\n'+stackList.join('<br/>\n');
133
134 //console.log(err.prettyStack)
135 }
136 }catch(e){
137 console.log(e);
138 return err;
139 }
140
141
142 return err;
143}
144
145//渲染文件字符串
146aries.compile = function(tempstr, ctx, cb){
147
148 var compileFinish = false;
149 if(!tempstr){
150 return cb(null, "");
151 }
152 var ctx = ctx || {};
153
154 try{
155 var key = md5(tempstr);
156 }
157 catch(e){
158 e = aries.prettyStackFunc(e);
159 return cb(e);
160 }
161 var tempStrList = [tempstr];
162
163
164 //ctx中途结束的标识符
165 ctx.__hasFinish__ = false;
166 ctx.finished = function(){ //预留函数
167 this.__hasFinish__ = true;
168 }
169
170
171 aries.scanInclude(tempStrList, 0, ctx, function(err, strList, noInclude, includeIds, includeFiles, incMapStr){
172
173 //避免回调2次
174 if(compileFinish) return;
175 if(err){
176 compileFinish = true
177 err = aries.prettyStackFunc(err, incMapStr);
178 return cb(err);
179 }
180
181 //如果仅仅返回includeIds
182 if(cb.isIncludes){
183 return cb(null, includeIds, includeFiles)
184 }
185
186
187
188 //去渲染模版
189 var templatePromise = te.template(strList, key, function (err, t, isUseCache) {
190 //避免回调2次
191 if(compileFinish) return;
192
193 if(err){
194 compileFinish = true;
195 err = aries.prettyStackFunc(err, incMapStr);
196 return cb(err);
197 }
198 var templateResponse = t(te, key, ctx, templatePromise);
199 var bodyList = [];
200
201 templateResponse.addListener("body", function (chunk) {
202 if(compileFinish) return
203 bodyList.push(chunk);
204 });
205
206 templateResponse.addListener("complete", function () {
207 if(compileFinish) return;
208 compileFinish = true;
209
210 //如果出现错误
211 if(ctx._ariestpError){
212 err = aries.prettyStackFunc(ctx._ariestpError, incMapStr);
213 return cb(ctx._ariestpError, null, isUseCache)
214 }
215 strList = strList || [];
216 cb(null, bodyList.join(""), strList.join(""), includeIds, includeFiles, isUseCache);
217
218 });
219
220 templateResponse.addListener("error", function (err) {
221
222 if(compileFinish) return
223 compileFinish = true
224 err = aries.prettyStackFunc(err, incMapStr);
225 cb(err);
226 });
227 });
228
229 });
230
231}
232
233
234aries.getTemplateFromFile = function getTemplateFromFile(filename, cb){
235 //是否是绝对路径
236 if (filename.charAt(0) === '/' || /^[a-zA-Z]:$/.test(filename.slice(0,2))) {
237 filename = filename;
238 } else{
239 filename = path.join(te.path, filename);
240 }
241
242 fs.readFile(filename, function (err, tempstr) {
243 if (err) return cb(err);
244 return cb(null, tempstr)
245 });
246}
247
248
249//内部方法扫描是否具有包含include的方法
250aries.scanInclude = function scanInclude(tempStrList, count, ctx, cb){
251 //符号用
252 var templateSymbol = [];
253 var symbolPos = -1;
254 var isPairing = false;
255 var tmpS, tmpE;
256
257 //如果没有就创建数组
258 if(typeof cb.includeFiles == "undefined"){
259 cb.includeFiles = [];
260 }
261
262 if(typeof cb.includeIds == "undefined"){
263 cb.includeIds = [];
264 }
265
266 if(typeof cb.incMapStr == "undefined"){
267 cb.incMapStr = {};
268 }
269
270
271 //大于5次的嵌套就出错,防止死循环嵌套
272 count++;
273 if(count>5){
274 var err = new Error(util.format("[ariestp] scanInclude include count lt 5 times"));
275 return cb(err);
276 }
277
278 //注释用
279 // var noteList = [];
280 // var notePos = -1;
281 // var isPairingNote = false;
282 // var tmpNS, tmpNE;
283 // 去除注释
284 // 字符串长度
285 // var len = tempStrList[0].length;
286 // for(var i=0;i<len; i++){
287 // // <!-- 11916796840964503050040715 -->
288 // //判断注释
289 // if(tempStrList[0][i] === '<' && tempStrList[0][i+1] === '!' && tempStrList[0][i+2] === '-' && tempStrList[0][i+3] === '-'){
290 // if(isPairingNote === true){
291 // var err = new Error(util.format("[ariestp] template str scan error, not pair <!-- at template char %s, str copy:", i, tempStrList[0].slice(tmpNS, i+4)));
292 // return cb(err);
293 // }
294 // tmpNS = i;
295 // noteList.push({
296 // s:i,
297 // e:-1,
298 // })
299 // notePos = noteList.length - 1;
300 // isPairingNote = true;
301
302 // }else if(tempStrList[0][i] === '-' && tempStrList[0][i+1] === '-' && tempStrList[0][i+2] === '>'){
303 // if(isPairingNote !== true){
304 // var err = new Error(util.format("[ariestp] template str scan error, not pair --> at template char %s, str copy:", i, tempStrList[0].slice(tmpNE, i+3)));
305 // return cb(err);
306 // }
307 // tmpE = i + 3;
308 // noteList[notePos]['e'] = tmpE;
309 // isPairingNote = false;
310 // notePos = -1;
311 // }
312 // }
313
314 // //如果有注释
315 // if(noteList.length > 0){
316 // var noteLen = noteList.length;
317 // //去掉注释
318 // for(var i=noteLen-1; i>=0;i--){
319 // tempStrList[0] = tempStrList[0].slice(0, noteList[i]["s"]) + tempStrList[0].slice(noteList[i]["e"]);
320 // }
321 // }
322
323 //字符串长度
324 var len = tempStrList[0].length;
325 for(var i=0;i<len; i++){
326 if(tempStrList[0][i] === '<' && tempStrList[0][i+1] === '%'){
327 if(isPairing === true){
328 var err = new Error(util.format("[ariestp] template str scan error, not pair <% at template char %s, str copy:", i, tempStrList[0].slice(tmpS, i+2)));
329 return cb(err);
330 }
331 tmpS = i;
332 templateSymbol.push({
333 s:i,
334 e:-1,
335 })
336 symbolPos = templateSymbol.length - 1;
337 isPairing = true
338 } else if(tempStrList[0][i] === '%' && tempStrList[0][i+1] === '>'){
339 if(isPairing !== true){
340 var err = new Error(util.format("[ariestp] template str scan error, not pair %> at template char %s, str copy:", i, tempStrList[0].slice(tmpE, i+2)));
341 return cb(err);
342 }
343 tmpE = i + 2;
344 templateSymbol[symbolPos]['e'] = tmpE;
345 isPairing = false;
346 symbolPos = -1;
347 }
348 }
349
350 //所有 <% xxxx %> 的匹配,存入临时数组
351 var len = templateSymbol.length;
352 var includeList = [];
353 var includeReg = /^<%\s+include\s(.+)\s+%>$/;
354 var includeIdReg = /^<%\s+includeId\s(.+)\s+%>$/;
355 var renderReg = /^<%[=|-](.+)%>$/;
356 var hasFh = /^<%(.+);(\s*?)%>$/;
357
358 var needSemicolon = false;
359 var include = 0;
360 for(var i=0;i<len;i++){
361 var includeMatchStr = tempStrList[0].slice(templateSymbol[i]['s'], templateSymbol[i]['e']);
362 var matchList = includeMatchStr.match(includeReg);
363
364 //如果include文件
365 if(matchList && matchList[0] && matchList[1]){
366 includeList.push({
367 's':templateSymbol[i]['s'],
368 'e':templateSymbol[i]['e'],
369 'type':1,//1 表示include文件
370 'val':matchList[1],
371 })
372
373 cb.includeFiles.push(matchList[1].split("?")[0]);
374 //cb.incMapStr['file_'+matchList[1].split("?")[0]] = "";
375
376 include++;
377 continue;
378 }
379
380 //如果includeid id
381 var matchListId = includeMatchStr.match(includeIdReg);
382 if(matchListId && matchListId[0] && matchListId[1]){
383 includeList.push({
384 's':templateSymbol[i]['s'],
385 'e':templateSymbol[i]['e'],
386 'type':2,//1 表示include文件
387 'val':matchListId[1],
388 })
389 //将includeId存入cb的includeIds数组
390 cb.includeIds.push(matchListId[1].split("?")[0]);
391 //cb.incMapStr['id_'+matchListId[1].split("?")[0]]= "";
392
393 include++;
394 continue;
395 }
396
397 if(!(renderReg.test(includeMatchStr)) && !(hasFh.test(includeMatchStr))){
398 includeMatchStr = includeMatchStr.slice(0, -2) + ';' + includeMatchStr.slice(-2);
399 includeList.push({
400 's':templateSymbol[i]['s'],
401 'e':templateSymbol[i]['e'],
402 'type':3,//表示正常的<% %>对,非include
403 'val':includeMatchStr,
404 })
405 }
406
407 }
408
409 //如果模版不包含 include 或者 includeId
410 // if(includeList.length === 0){
411 // var notHaveInclude = false
412 // return cb(null, tempStrList, notHaveInclude);
413 // }
414 if(include === 0){
415 var notHaveInclude = false
416 aries.assemble(tempStrList, includeList, notHaveInclude, ctx, cb);
417 // return cb(null, tempStrList, notHaveInclude);
418 }else{
419 aries.replaceIncludeStr(tempStrList, includeList, count, ctx, cb);
420 }
421
422}
423
424aries.replaceIncludeStr = function replaceIncludeStr(tempStrList, includeList, count, ctx, cb){
425 var asyncWorker = [];
426
427 includeList.forEach(function(incItem){
428 var okCallback = true
429 var dealFunc = function(callback){
430
431 if(incItem.type == 1){
432
433 //去解析include的内容
434 var tmpList = aries.splitIncludeParam(incItem.val);
435 if(tmpList[2]){
436 return callback(tmpList[2])
437 }
438
439 aries.getTemplateFromFile(tmpList[0], function(err, tempstr){
440 if(err) return callback(err);
441 incItem.template = tmpList[1] + tempstr.toString();
442
443 cb.incMapStr['file_'+incItem.val] = tempstr.toString();
444
445 //tempStrList[0] = tempStrList[0].slice(0, incItem['s']) + tempstr + tempStrList[0].slice(incItem['e']);
446 okCallback && callback();
447 okCallback = false
448 return
449 })
450 }else if(incItem.type == 2){
451
452 if(!te.includeId){
453 okCallback && callback(new Error("[ariestp] not defined opt.includeId can't use includeId in template!"));
454 okCallback = false
455 return
456 }else{
457
458 //去解析include的内容
459 var tmpList = aries.splitIncludeParam(incItem.val);
460 if(tmpList[2]){
461 return callback(tmpList[2])
462 }
463
464 atry(function() {
465 te.includeId(tmpList[0], function(err, tempstr){
466 if(err){
467 okCallback && callback(err);
468 okCallback = false
469 return
470 }
471
472 cb.incMapStr['id_'+incItem.val] = tempstr.toString();
473
474 process.nextTick(function () {
475 incItem.template = tmpList[1] + tempstr.toString();
476 // tempStrList[0] = tempStrList[0].slice(0, incItem['s']) + tempstr + tempStrList[0].slice(incItem['e']);
477 okCallback && callback();
478 okCallback = false
479 });
480 return;
481 });
482 }).catch(function(err) {
483 //定义ariestp错误
484 ctx._ariestpError = err;
485
486 okCallback && callback(err);
487 okCallback = false
488 return
489 });
490
491 }
492 }
493 else{ //type == 3
494 incItem.template = incItem.val;
495 okCallback && callback();
496 okCallback = false
497 return
498 }
499 }
500 asyncWorker.push(dealFunc);
501
502 });
503 //异步并发
504 async.parallel(asyncWorker, function(err){
505 if(err){
506 return cb(err);
507 }
508 //重新做检查,看是否有include
509 var len = includeList.length;
510 for(var i=len-1;i>=0;i--){
511
512 tempStrList[0] = tempStrList[0].slice(0, includeList[i]["s"]) + includeList[i]["template"] + tempStrList[0].slice(includeList[i]["e"]);
513 }
514 aries.scanInclude(tempStrList,count,ctx,cb);
515 })
516
517}
518
519aries.assemble = function assemble(tempStrList, includeList, notHaveInclude, ctx, cb){
520
521 var len = includeList.length;
522 for(var i=len-1;i>=0;i--){
523 tempStrList[0] = tempStrList[0].slice(0, includeList[i]["s"]) + includeList[i]["val"] + tempStrList[0].slice(includeList[i]["e"]);
524 }
525
526 cb(null, tempStrList, notHaveInclude, cb.includeIds, cb.includeFiles, cb.incMapStr);
527}
528
529//添加功能
530//include xxxxx?a=1 这样的方式传参
531aries.splitIncludeParam = function(rawIncludeStr){
532
533 var list = rawIncludeStr.trim().split('?');
534 var includeStr = (list[0]||'').trim();
535 var queryParam = (list[1]||'').trim();
536 //如果没有?没有参数
537 if(!queryParam){
538 return [includeStr, '', null];
539 }
540
541 //对url query字符串进行parse解析
542 try{
543 var obj = url.parse('/?'+queryParam, true);
544 }catch(e){
545 var err = new Error(util.format('include or includeid query error: %s \n %s', rawIncludeStr, e.stack||''));
546 err.stack = e.stack || '';
547 return ['', '', err];
548 }
549
550 //添加模板字符串
551 var tmpStr = '<%';
552 Object.keys(obj.query).forEach(function(key){
553 tmpStr += util.format(' ctx["%s"] = "%s"; ', key, obj.query[key] || '');
554 })
555 tmpStr += '%>';
556
557 return [includeStr, tmpStr, null];
558}
559
560exports.set = aries.set;
561exports.compileFile = aries.compileFile;
562exports.compile = aries.compile;
563exports.includes = aries.includes;
564exports.splitIncludeParam = aries.splitIncludeParam;
565
566
567
568
569