1 | var fs = require('fs');
|
2 | var path = require('path');
|
3 | var url = require('url');
|
4 | var util = require('util');
|
5 | var md5 = require('blueimp-md5');
|
6 | var ariestp = require("./ariestp.js")
|
7 | var te = ariestp.Engine();
|
8 | var async = require('async');
|
9 | var domain = require('domain');
|
10 | var hasSet = false;
|
11 | var atry = require('./atry.js');
|
12 | var aries = {};
|
13 |
|
14 |
|
15 | aries.set = function(opt){
|
16 | if(hasSet) return te;
|
17 |
|
18 | if(opt.path){
|
19 |
|
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 |
|
63 | aries.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 |
|
71 | aries.includes = function(tempstr, cb){
|
72 | cb.isIncludes = true;
|
73 | aries.compile(tempstr, {}, cb);
|
74 | }
|
75 |
|
76 |
|
77 |
|
78 | aries.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 |
|
93 |
|
94 |
|
95 |
|
96 |
|
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 |
|
135 | }
|
136 | }catch(e){
|
137 | console.log(e);
|
138 | return err;
|
139 | }
|
140 |
|
141 |
|
142 | return err;
|
143 | }
|
144 |
|
145 |
|
146 | aries.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 |
|
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 |
|
174 | if(compileFinish) return;
|
175 | if(err){
|
176 | compileFinish = true
|
177 | err = aries.prettyStackFunc(err, incMapStr);
|
178 | return cb(err);
|
179 | }
|
180 |
|
181 |
|
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 |
|
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 |
|
234 | aries.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 |
|
250 | aries.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 |
|
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 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
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 |
|
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 |
|
365 | if(matchList && matchList[0] && matchList[1]){
|
366 | includeList.push({
|
367 | 's':templateSymbol[i]['s'],
|
368 | 'e':templateSymbol[i]['e'],
|
369 | 'type':1,
|
370 | 'val':matchList[1],
|
371 | })
|
372 |
|
373 | cb.includeFiles.push(matchList[1].split("?")[0]);
|
374 |
|
375 |
|
376 | include++;
|
377 | continue;
|
378 | }
|
379 |
|
380 |
|
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,
|
387 | 'val':matchListId[1],
|
388 | })
|
389 |
|
390 | cb.includeIds.push(matchListId[1].split("?")[0]);
|
391 |
|
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,
|
403 | 'val':includeMatchStr,
|
404 | })
|
405 | }
|
406 |
|
407 | }
|
408 |
|
409 |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 | if(include === 0){
|
415 | var notHaveInclude = false
|
416 | aries.assemble(tempStrList, includeList, notHaveInclude, ctx, cb);
|
417 |
|
418 | }else{
|
419 | aries.replaceIncludeStr(tempStrList, includeList, count, ctx, cb);
|
420 | }
|
421 |
|
422 | }
|
423 |
|
424 | aries.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 |
|
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 |
|
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 |
|
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 |
|
477 | okCallback && callback();
|
478 | okCallback = false
|
479 | });
|
480 | return;
|
481 | });
|
482 | }).catch(function(err) {
|
483 |
|
484 | ctx._ariestpError = err;
|
485 |
|
486 | okCallback && callback(err);
|
487 | okCallback = false
|
488 | return
|
489 | });
|
490 |
|
491 | }
|
492 | }
|
493 | else{
|
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 |
|
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 |
|
519 | aries.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 |
|
531 | aries.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 |
|
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 |
|
560 | exports.set = aries.set;
|
561 | exports.compileFile = aries.compileFile;
|
562 | exports.compile = aries.compile;
|
563 | exports.includes = aries.includes;
|
564 | exports.splitIncludeParam = aries.splitIncludeParam;
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|