UNPKG

19.2 kBJavaScriptView Raw
1(function(){
2
3 var $=require('./$');
4 var fs=require('fs');
5 var syspath=require('path');
6 var urlParser=require('url');
7 var vm=require('vm');
8
9 var uglify=require('uglify-js');
10 var cleancss=require('clean-css');
11 var btcss=new cleancss();
12
13 var MD5=require('MD5');
14
15 var root=process.cwd();
16
17 var patterns={
18 prd: /^\/([a-zA-Z0-9_-]+)\/prd\/(.+)@(dev|[a-z0-9]{5,60})\.(js|css)$/,
19 src: /^\/([a-zA-Z0-9_-]+)\/src\/(.+)\.(js|css)$/,
20 relative: /^\/src\/(.+)\.(js|css)$/,
21 external: /^(http|https)?\:\/\/.+$/,
22 inline: /^([.a-zA-Z0-9_-]*|\/)+\.(js|css)$/,
23 pack: /^[\s|^.]*require[\s|^.]*\([\s|^.]*\[([\s|^.]*('|")[\s]*([.a-zA-Z0-9-_]|\/)+\.js[\s]*('|")[\s|^.]*(,)?[\s|^.]*)*[\s|^.]*\](.*|[\s|^.]*)*\)[\s|^.]*(;)?[\s|^.]*/,
24 devpack: /^[\s|^.]*require[\s|^.]*\([\s|^.]*\[([\s|^.]*('|")[\s]*([.a-zA-Z0-9-_:=@?%]|\/)+\.js[\s]*('|")[\s|^.]*(,)?[\s|^.]*)*[\s|^.]*\](.*|[\s|^.]*)*\)[\s|^.]*(;)?[\s|^.]*/,
25 require: /([\s|^.]*)require[\s|^.]*\([\s|^.]*\[([\s|^.]*('|")[\s]*([.a-zA-Z0-9-_:=@?%]|\/)+\.js[\s]*('|")[\s|^.]*(,)?[\s|^.]*)*[\s|^.]*\]/g,
26 csspack: /require\(('|")(([.a-zA-Z0-9_-]|\/)+?\.css)('|")\)(;)?/g
27 };
28
29 var parsers=function(compiler){
30 var src=function(options){
31 this.config=$.extend({
32 },options);
33 };
34
35 $.extend(src.prototype,{
36 parse: function(req){
37 var request=this.parseRequest(req);
38
39 if(request.mode=='pack'){
40 var requires={error:[],files:[]};
41
42 console.info('#'.grey,'[PRD MODE]'.cyan);
43
44 if(request.type=='js'){
45 requires=compiler.getJSRequires(request.filepath);
46 }else if(request.type=='css'){
47 var requires=compiler.getCssRequires(request.filepath);
48 }
49
50 if(requires.error){
51 requires.error.forEach(function(v){
52 console.info(v);
53 });
54 }
55
56 requires.files.forEach(function(v){
57 console.info(('# [INCLUDE FILE] '+v).grey);
58 });
59
60
61 var ret=[];
62 if(request.type=='js'){
63 requires.files.forEach(function(v){
64 ret.push('document.write(\'<script type="text/javascript" src="'+request.protocol+'://'+request.headers.host+'/'+request.project+'/src/'+v+'?parent='+encodeURIComponent(request.file)+'"></script>\');');
65 });
66 }else if(request.type=='css'){
67 requires.files.forEach(function(v){
68 ret.push('@import "'+request.protocol+'://'+request.headers.host+'/'+request.project+'/src/'+v+'?parent='+encodeURIComponent(request.file)+'";');
69 });
70 }
71
72 return ret.join('\n');
73
74 }else if(request.mode=='require'||request.mode=='source'){
75 var code='';
76 if(request.type=='js'){
77 code=this.getJSSource(request);
78 }else if(request.type=='css'){
79 code=this.getCssSource(request);
80 }
81 return code;
82 }else{
83 //throw new Error('sth. wrong');
84 }
85
86
87 return JSON.stringify(request);
88 },
89
90 getJSSource: function(req){
91
92 var file=compiler.readFile(req.filepath), dir=syspath.dirname(req.file), code=file, parent=req.query.parent||req.query.require;
93
94 var requires=compiler.getJSRequires(syspath.join(req.project,'src',parent));
95
96 if(patterns.pack.test(compiler.removeJSComments(file))){
97 var script=vm.createScript(code);
98
99 if(req.mode=='require'){
100 script.runInNewContext({require: function(list,func){
101
102 code='require('+JSON.stringify(list)+','+func.toString().replace(/^function[\s]*\(/,'function(')+','+JSON.stringify(req.protocol+'://'+req.headers.host+req.url)+');';
103 }});
104 }else{
105 script.runInNewContext({require: function(list,func){
106
107 code='('+func.toString().replace(/^function[\s]*\(/,'function(')+')(this);';
108 }});
109 }
110 }
111
112 code=(code||'').replace(patterns.require,function(hole,pre){
113 var ret=pre;
114
115 var script=vm.createScript(hole+')');
116
117 script.runInNewContext({require: function(list){
118 var children=[];
119 list.forEach(function(v){
120 if(patterns.external.test(v)||!patterns.inline.test(v)){
121 return children.push(v);
122 }
123
124 var path=syspath.join(dir,v).replace(/\\/g,'/');
125
126 if(requires.files.indexOf(path)==-1){
127 children.push(req.protocol+'://'+req.headers.host+'/'+req.project+'/src/'+path+'?require='+encodeURIComponent(parent));
128 }
129 });
130
131 ret+='require('+JSON.stringify(children);
132
133 }});
134
135 return ret;
136 });
137
138 return code;
139 },
140
141 getCssSource: function(req){
142 var code=compiler.readFile(req.filepath);
143 return (code||'').replace(patterns.csspack,'');
144 },
145
146 parseRequest: function(req){
147 var request=compiler.formatRequest(req);
148
149 if(patterns.prd.test(request.path)){
150 request.filepath=request.path.replace(patterns.prd,function(hole/* all url */,project/* project name */,path/* file path without extend */,ver/* version */,type/* js|css */){
151 request.mode='pack';
152 request.file=path+'.'+type;
153 request.project=project;
154 return syspath.join(project,'src',path+'.'+type);
155 });
156 }else if(patterns.src.test(request.path)){
157 request.filepath=request.path.replace(patterns.src,function(hole/* all url */,project/* project name */,path/* file path without extend */,type/* js|css */){
158 request.file=path+'.'+type;
159 request.project=project;
160 return syspath.join(project,'src',path+'.'+type);
161 });
162
163 if(request.query.parent||request.query.require){
164 request.mode=request.query.require?'require':'source';
165 }
166 }
167
168 return request;
169 }
170 });
171
172 var dev=function(options){
173 this.config=$.extend({
174 },options);
175 };
176
177 $.extend(dev.prototype,{
178 parse: function(req){
179 var request=this.parseRequest(req);
180
181 if(request.type=='js'){
182 console.info('#'.grey,'[JS DEV Mode]'.cyan);
183
184 return this.getJSSource(request);
185
186 }else if(request.type=='css'){
187 console.info('#'.grey,'[CSS DEV Mode]'.cyan);
188
189 return this.getCssSource(request);
190 }else{
191 console.info('#'.red,'[UNKNOWN URL]'.red);
192 }
193 return '';
194 },
195
196 parseRequest: function(req){
197 var request=compiler.formatRequest(req);
198
199 if(patterns.prd.test(request.path)){
200 request.filepath=request.path.replace(patterns.prd,function(hole/* all url */,project/* project name */,path/* file path without extend */,ver/* version */,type/* js|css */){
201 request.file=path+'.'+type;
202 request.project=project;
203 return syspath.join(project,'src',path+'.'+type);
204 });
205 }
206
207 return request;
208 },
209
210 getJSSource: function(request){
211
212 var requires=compiler.getJSRequires(request.query.require?syspath.join(request.project,'src',request.query.require):request.filepath);
213
214 if(requires.error){
215 requires.error.forEach(function(v){
216 console.info(v);
217 });
218 }
219
220 var codes=[];
221 if(!request.query.require){
222 requires.files.forEach(function(v){
223 console.info(('# [INCLUDE FILE] '+v).grey);
224
225 codes.push('/* File '+v+' */');
226
227 var path=syspath.join(request.project,'src',v);
228 var dir=syspath.dirname(v);
229
230 var code=compiler.readFile(path);
231 if(patterns.pack.test(compiler.removeJSComments(code))){
232 var script=vm.createScript(code);
233
234 script.runInNewContext({require: function(list,func){
235
236 code='('+func.toString().replace(/^function[\s]*\(/,'function(')+')(this);';
237 }});
238 }
239
240 code=(code||'').replace(patterns.require,function(hole,pre){
241 var ret=pre;
242
243 var script=vm.createScript(hole+')');
244
245 script.runInNewContext({require: function(list){
246 var children=[];
247 list.forEach(function(v){
248
249 if(patterns.external.test(v)||!patterns.inline.test(v)){
250 return children.push(v);
251 }
252
253 var rqpath=syspath.join(dir,v).replace(/\\/g,'/');
254
255 if(requires.files.indexOf(rqpath)==-1){
256 if(request.project){
257 children.push(request.protocol+'://'+request.headers.host+'/'+request.project+'/prd/'+rqpath.replace(/\.(js|css)$/,function(ext){ return '@dev'+ext; })+'?require='+encodeURIComponent(request.file));
258 }else{
259 children.push(rqpath.replace(/\.(js|css)$/,function(ext){ return '@dev'+ext; }));
260 }
261 }
262 });
263
264 ret+='require('+JSON.stringify(children);
265
266 }});
267
268 return ret;
269 });
270
271 codes.push(code+'\n');
272 });
273 }else{
274
275 console.info('# [REQUIRE LOAD FILE]'.grey,request.file.grey);
276
277 var code=compiler.readFile(request.filepath);
278 var dir=syspath.dirname(request.file);
279
280 if(code===false){
281 console.info('[ERROR]'.red,request.file+' not exits');
282 return false;
283 }
284
285 code=(code||'').replace(patterns.require,function(hole,pre){
286 var ret=pre;
287 var script=vm.createScript(hole+')');
288 var children=[];
289
290 script.runInNewContext({require: function(list){
291 (list||[]).forEach(function(v){
292
293 if(patterns.external.test(v)||!patterns.inline.test(v)){
294 return children.push(v);
295 }
296
297 var rqpath=syspath.join(dir,v).replace(/\\/g,'/');
298
299 if(requires.files.indexOf(rqpath)==-1){
300
301 if(request.project){
302 children.push(request.protocol+'://'+request.headers.host+'/'+request.project+'/prd/'+rqpath.replace(/\.(js|css)$/,function(ext){ return '@dev'+ext; })+'?require='+encodeURIComponent(request.query.require));
303 }else{
304 children.push(rqpath.replace(/\.(js|css)$/,function(ext){ return '@dev'+ext; }));
305 }
306 }
307 });
308 }});
309
310 ret+='require('+JSON.stringify(children);
311
312 return ret;
313 });
314
315 if(patterns.devpack.test(compiler.removeJSComments(code))){
316 var script=vm.createScript(code);
317 script.runInNewContext({require: function(list,func){
318 if(request.project){
319 code='require('+JSON.stringify(list)+','+func.toString().replace(/^function[\s]*\(/,'function(')+','+JSON.stringify(request.protocol+'://'+request.headers.host+request.url)+');';
320 }else{
321 code='require('+JSON.stringify(list)+','+func.toString().replace(/^function[\s]*\(/,'function(')+','+JSON.stringify(request.file.replace(/\.(js|css)$/,function(ext){ return '@dev'+ext; }))+');';
322 }
323 }});
324 }
325
326 codes.push(code);
327 }
328
329 return codes.join('\n');
330 },
331
332 getCssSource: function(request){
333 var requires=compiler.getCssRequires(request.filepath), ret=[];
334
335 if(requires.error){
336 requires.error.forEach(function(v){
337 console.info(v);
338 });
339 }
340
341 requires.files.forEach(function(v){
342 console.info(('# [INCLUDE FILE] '+v).grey);
343
344 ret.push('/* File '+v+' */');
345
346 ret.push((compiler.readFile(syspath.join(request.project,'src',v))||'').replace(patterns.csspack,'').replace(/^[\s|^.]*/,'').replace(/[\s|^.]*$/,'')+'\n');
347 });
348
349 return ret.join('\n');
350 }
351 });
352
353 var prd=function(){
354 };
355
356 $.extend(prd.prototype,{
357 parse: function(req){
358 var request=pack.dev.parseRequest(req);
359
360 if(request.type=='js'){
361 console.info('#'.grey,'[JS PRD Mode]'.cyan);
362
363 return uglify.minify(pack.dev.getJSSource(request),{fromString: true}).code;
364
365 }else if(request.type=='css'){
366 console.info('#'.grey,'[CSS PRD Mode]'.cyan);
367
368 return btcss.minify(pack.dev.getCssSource(request));
369 }else{
370 console.info('#'.red,'[UNKNOWN URL]'.red);
371 }
372 return '';
373 },
374
375 parseRequest: function(req){
376 return pack.dev.parseRequest(req);
377 }
378 });
379
380 var pack={
381 src: new src(),
382 dev: new dev(),
383 prd: new prd()
384 };
385
386 return $.extend({
387 parseRequest: function(req,mode){
388 return pack[mode.toLowerCase()].parseRequest(req);
389 },
390
391 isPrd: function(path){
392 return patterns.prd.test(path);
393 },
394
395 isSrc: function(path){
396 return patterns.src.test(path);
397 },
398
399 getAllRequires: function(path){
400 return {
401 requires: compiler.getJSRequires(path),
402 inlines: compiler.getJSInlineRequires(path)
403 }
404 },
405
406 getInlineRequires: function(path, parent){
407 return compiler.getOneJSinlineRequires(path, parent);
408 }
409 },pack);
410
411 }({
412 formatRequest: function(ret){
413 if(/.+\.js$/.test(ret.path)){
414 ret.type='js';
415 }else if(/.+\.css$/.test(ret.path)){
416 ret.type='css';
417 }
418
419 return ret;
420 },
421
422 removeJSComments: function(code){
423 return code.replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g,'\n').replace(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/g,'\n');
424 },
425
426 removeCssComment: function(code){
427 return code.replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g,'\n');
428 },
429
430 getRelativePath: function(p){
431
432 path='/'+p.replace(/\\/g,'/');
433 if(patterns.src.test(path)){
434 return path.replace(patterns.src,function(hole/* all url */,project,path/* file path without extend */,type/* js|css */){
435 return path+'.'+type;
436 });
437 }else if(patterns.relative.test(path)){
438 return path.replace(patterns.relative,function(hole/* all url */,path/* file path without extend */,type/* js|css */){
439 return path+'.'+type;
440 });
441 }
442 return p.replace(/\\/g,'/');
443 },
444
445 getProject: function(path){
446 return ('/'+path.replace(/\\/g,'/')).replace(patterns.src,function(hole/* all url */,project/* project name */,path/* file path without extend */,type/* js|css */){
447 return project;
448 });
449 },
450
451 getJSRequires: function(p){
452 var cache={};
453
454 var ret=$.proxy(function(holepath){
455
456 if(cache[this.getRelativePath(holepath)]){
457 return {
458 error: [],
459 files: []
460 };
461 }
462 var file=this.readFile(holepath), dir=syspath.dirname(holepath);
463
464 var hole=this.removeJSComments(file||''), _self=$.proxy(arguments.callee,this), _this=this, error=(file===false?[['#'.grey,'[WARN]'.yellow,holepath,'not exist'.yellow].join(' ')]:[]);
465
466 cache[this.getRelativePath(holepath)]=true;
467
468 var files=[];
469 //pack mode
470 if(patterns.pack.test(hole)){
471 var script=vm.createScript(hole), children=[];
472 script.runInNewContext({require: function(list,func){
473 children=list;
474 }});
475
476 children.forEach(function(v){
477
478 var path=syspath.join(dir,v);
479 if(cache[_this.getRelativePath(path)]){
480 return;
481 }
482
483 var r=_self(path);
484
485 error=error.concat(r.error);
486 files=files.concat(r.files);
487 });
488 }
489
490 files.push(this.getRelativePath(holepath));
491
492 return {
493 error: error,
494 files: files
495 };
496 },this);
497
498 return ret(p);
499 },
500
501 getOneJSinlineRequires: function(p, parent){
502
503 var parents=[];
504
505 if(parent){
506 parents=this.getJSRequires(parent).files||[];
507 }
508
509 var relativePath=this.getRelativePath(p), _this=this;
510 var path=syspath.join('src',relativePath);
511
512 var code=this.readFile(path), dir=syspath.dirname(relativePath), ret=[];
513 (code||'').replace(patterns.require,function(hole,pre){
514 var script=vm.createScript(hole+')');
515 script.runInNewContext({require: function(list){
516 var children=[];
517 list.forEach(function(v){
518 if(patterns.external.test(v)||!patterns.inline.test(v)){
519 return;
520 }
521
522 var path=syspath.join(dir,v).replace(/\\/g,'/');
523
524 if(parents.indexOf(path)==-1&&ret.indexOf(path)==-1){
525 ret.push(path);
526 }
527 });
528
529 }});
530 });
531
532 if(parent==p){
533 for( var i=0; i<parents.length; i++){
534 if(parents[i]!=relativePath){
535 ret=ret.concat(arguments.callee.call(this,syspath.join('src',parents[i]),parent));
536 }
537 }
538 }
539
540 return ret;
541 },
542
543 getJSInlineRequires: function(p){
544
545 var relativePath=this.getRelativePath(p), _this=this;
546
547 var analysised={}, inlineRequires=[], parents=this.getJSRequires(p).files;
548
549 var getRequires=function(relative){
550
551 var path=syspath.join('src',relative);
552
553 if(analysised[path]){
554 return;
555 }
556 analysised[path]=true;
557
558 var requires=_this.getJSRequires(path);
559
560 requires.files.forEach(function(v){
561
562 if(analysised[v]){
563 return;
564 }
565
566 analysised[v]=true;
567
568 var filepath=syspath.join('src',v), dir=syspath.dirname(v);
569 var code=_this.readFile(filepath), analysis=arguments.callee;
570
571 if(!code){
572 return;
573 }
574
575 /*
576 if(patterns.pack.test(_this.removeJSComments(code))){
577 var script=vm.createScript(code);
578
579 script.runInNewContext({require: function(list,func){
580 code=func.toString();
581 }});
582 }*/
583
584 code=(code||'').replace(patterns.require,function(hole,pre){
585 var script=vm.createScript(hole+')');
586 script.runInNewContext({require: function(list){
587 var children=[];
588 list.forEach(function(v){
589 if(patterns.external.test(v)||!patterns.inline.test(v)){
590 return;
591 }
592
593 var path=syspath.join(dir,v).replace(/\\/g,'/');
594
595 if(parents.indexOf(path)==-1){
596 inlineRequires.push(path);
597
598 getRequires(path);
599 }
600 });
601
602 }});
603 });
604 });
605 };
606
607 getRequires(relativePath);
608
609 return inlineRequires;
610 },
611
612 getCssRequires: function(p){
613 var cache={};
614
615 var ret=$.proxy(function(holepath){
616
617 if(cache[this.getRelativePath(holepath)]){
618 return {
619 error: [],
620 files: []
621 };
622 }
623
624 var file=this.readFile(holepath), dir=syspath.dirname(holepath);
625 var hole=this.removeJSComments(file||''), _self=$.proxy(arguments.callee,this), _this=this, error=(file===false?[['#'.grey,'[WARN]'.yellow,ori,'not exist'.yellow].join(' ')]:[]);
626
627 cache[this.getRelativePath(holepath)]=true;
628
629 var files=[];
630
631 hole.replace(patterns.csspack,function(hole,__i,inc){
632 var _path=syspath.join(dir,inc);
633 if(cache[_this.getRelativePath(_path)]){
634 return;
635 }
636
637 var r=_self(_path);
638
639 files=files.concat(r.files);
640 error=error.concat(r.error);
641 return '';
642 });
643
644 files.push(this.getRelativePath(holepath));
645
646 return {
647 files: files,
648 error: error
649 };
650
651 },this);
652
653 return ret(p);
654 },
655
656 readFile: function(path){
657 path=syspath.join(root,path);
658
659 if(!fs.existsSync(path)){
660 return false;
661 }else{
662 return fs.readFileSync(path).toString();
663 }
664 }
665 });
666
667 $.extend(exports,parsers);
668
669}).call(this);
\No newline at end of file