1 | "use strict";
|
2 |
|
3 | module.exports = {
|
4 | compile,
|
5 | initialize
|
6 | };
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | const
|
28 | FS = require( "fs" ),
|
29 | Path = require( "path" ),
|
30 | ToloframeworkPermissiveJson = require( "toloframework-permissive-json" ),
|
31 | MinifyJS = require( "./minifyJS" ),
|
32 | CompilerCOM = require( "./compiler-com" ),
|
33 | CompilerJS = require( "./compiler-js" ),
|
34 | ParserHTML = require( "./tlk-htmlparser" ),
|
35 | PathUtils = require( "./pathutils" ),
|
36 | Template = require( "./template" ),
|
37 | Source = require( "./source" ),
|
38 | Fatal = require( "./fatal" ),
|
39 | Util = require( "./util" ),
|
40 | Tree = require( "./htmltree" ),
|
41 | Libs = require( "./compiler-com-libs" ),
|
42 | Tpl = require( "./template" ),
|
43 | Rework = require( 'rework' );
|
44 |
|
45 | let
|
46 | Project = null,
|
47 | Components = {},
|
48 | Scopes = [ {} ];
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | function initialize( prj ) {
|
56 | Project = prj;
|
57 | Components = {};
|
58 | Scopes = [ {} ];
|
59 | CompilerCOM.loadComponents( prj );
|
60 | }
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | function compile( file, _options ) {
|
70 | const
|
71 | options = typeof _options !== 'undefined' ? _options : {},
|
72 | sourceHTML = new Source( Project, file ),
|
73 |
|
74 | sourcesToLink = [];
|
75 | let
|
76 |
|
77 | outputPage = '',
|
78 |
|
79 | |
80 |
|
81 |
|
82 |
|
83 | outputOfFirstPage = '',
|
84 |
|
85 | pageFilename = '';
|
86 |
|
87 | if ( !isUptodate( sourceHTML ) ) {
|
88 | Scopes[ 0 ].$filename = sourceHTML.name();
|
89 | console.log( `Compile HTML: ${sourceHTML.name().cyan}` );
|
90 | let root = ParserHTML.parse( sourceHTML.read() );
|
91 |
|
92 | const output = compileRoot( root, sourceHTML, options );
|
93 | if ( output ) {
|
94 | sourceHTML.tag( 'output', output );
|
95 | while ( typeof root.type === 'undefined' &&
|
96 | root.children &&
|
97 | root.children.length === 1 ) {
|
98 | root = root.children[ 0 ];
|
99 | }
|
100 | if ( root.type === Tree.PAGES ) {
|
101 | outputPage = ToloframeworkPermissiveJson.parse( JSON.stringify( output ) );
|
102 | root.children.forEach( function ( child, idx ) {
|
103 | console.log( ( " Page " + ( idx + 1 ) ).cyan );
|
104 | var src = sourceHTML;
|
105 | pageFilename = src.name();
|
106 | if ( idx !== 0 ) {
|
107 | pageFilename = file.substr( 0, file.length - 4 ) + idx + '.html';
|
108 | src = new Source( Project, pageFilename );
|
109 | }
|
110 | outputPage = compileRoot(
|
111 | child, src, options, ToloframeworkPermissiveJson.parse( JSON.stringify( output ) )
|
112 | );
|
113 | outputPage.filename = pageFilename;
|
114 | src.tag( "output", outputPage );
|
115 | src.save();
|
116 | sourcesToLink.push( pageFilename );
|
117 | if ( idx === 0 ) {
|
118 |
|
119 | outputOfFirstPage = outputPage;
|
120 | }
|
121 | } );
|
122 | sourceHTML.tag( 'pages', sourcesToLink );
|
123 | sourceHTML.tag( 'output', outputOfFirstPage );
|
124 | }
|
125 | sourceHTML.save();
|
126 | }
|
127 | }
|
128 |
|
129 |
|
130 | linkPages( sourceHTML, options );
|
131 | return sourceHTML;
|
132 | }
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | function linkPages( sourceHTML, options ) {
|
141 | console.log( `Link: ${sourceHTML.name().yellow}` );
|
142 | const sourcesToLink = sourceHTML.tag( 'pages' );
|
143 | if ( sourcesToLink ) {
|
144 |
|
145 | sourcesToLink.forEach( function forEachPage( pageFilename ) {
|
146 | const src = new Source( Project, pageFilename );
|
147 | link( src, options );
|
148 | } );
|
149 | } else {
|
150 |
|
151 | link( sourceHTML, options );
|
152 | }
|
153 | }
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | function compileRoot( root, sourceHTML, options, _output ) {
|
163 |
|
164 | const output = typeof _output !== 'undefined' ? _output : {
|
165 |
|
166 | innerCSS: {},
|
167 |
|
168 | outerCSS: {},
|
169 |
|
170 | innerJS: {},
|
171 |
|
172 | require: {},
|
173 |
|
174 | modules: [],
|
175 |
|
176 | initJS: {},
|
177 |
|
178 | postInitJS: {},
|
179 |
|
180 | include: {},
|
181 |
|
182 |
|
183 |
|
184 |
|
185 | resource: {},
|
186 |
|
187 |
|
188 | dynamicModules: {}
|
189 | };
|
190 | var libs = Libs( sourceHTML, Components, Scopes, output );
|
191 | libs.compile( root, options );
|
192 | Tree.trim( root );
|
193 | output.root = root;
|
194 | return output;
|
195 | }
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | function isUptodate( sourceHTML ) {
|
205 | console.info("[compiler-html2] sourceHTML=", sourceHTML.name());
|
206 | if ( !sourceHTML.isUptodate() ) return false;
|
207 |
|
208 | const output = sourceHTML.tag( 'output' );
|
209 | if ( !output || !Array.isArray( output.include ) ) return false;
|
210 |
|
211 | const currentFileModificationTime = sourceHTML.modificationTime();
|
212 |
|
213 | for ( const includeFilename of output.include ) {
|
214 |
|
215 | const includeSource = new Source(
|
216 | Project,
|
217 | sourceHTML.getPathRelativeToSource( includeFilename )
|
218 | );
|
219 | const stats = FS.statSync( includeSource.getAbsoluteFilePath() );
|
220 | if ( stats.mtime > currentFileModificationTime ) {
|
221 |
|
222 | return false;
|
223 | }
|
224 | }
|
225 | return true;
|
226 | }
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 | function link( src, options ) {
|
237 | const
|
238 | htmlDir = Path.dirname( src.name() ),
|
239 | pathWWW = Project.wwwPath( htmlDir ),
|
240 | pathJS = Path.join( pathWWW, "js" ),
|
241 | pathCSS = Path.join( pathWWW, "css" );
|
242 |
|
243 | Project.mkdir( pathJS );
|
244 | Project.mkdir( pathCSS );
|
245 |
|
246 | const output = linkForRelease( src, pathJS, pathCSS, options, htmlDir );
|
247 |
|
248 | PathUtils.file(
|
249 | Project.wwwPath( src.name() ),
|
250 | `<!DOCTYPE html>\n${Tree.toString(output.root).trim()}`
|
251 | );
|
252 |
|
253 |
|
254 | writeResources( output );
|
255 | }
|
256 |
|
257 |
|
258 |
|
259 |
|
260 | function linkForRelease( src, pathJS, pathCSS, options, htmlDir ) {
|
261 | Project.mkdir( Path.join( pathJS, "map" ) );
|
262 | Project.mkdir( Path.join( pathCSS, "map" ) );
|
263 |
|
264 | var key, val;
|
265 | var prj = src.prj();
|
266 | var nameWithoutExt = src.name().substr( 0, src.name().length - 5 );
|
267 |
|
268 |
|
269 | var backToRoot = getBackToRoot( nameWithoutExt );
|
270 | backToRoot = '';
|
271 |
|
272 | var output = src.tag( "output" ) || {};
|
273 | var root = output.root;
|
274 | if ( !root ) {
|
275 | Fatal.fire(
|
276 | "The cache seems to be corrupted. Try `tfw clean` to clean it up. " +
|
277 | "And try building again.",
|
278 | "Please cleanup the cache!"
|
279 | );
|
280 | }
|
281 | output.filename = src.name();
|
282 | var head = findHead( root );
|
283 | addDescriptionToHead( head, options );
|
284 | var innerJS = Tpl.file( "require.js" ).out + concatDicValues( output.innerJS );
|
285 | innerJS += getInitJS( output );
|
286 |
|
287 | var innerCSS = concatDicValues( output.innerCSS );
|
288 |
|
289 |
|
290 | if ( FS.existsSync( Project.srcOrLibPath( nameWithoutExt + '.css' ) ) ) {
|
291 | console.log( "Found: " + ( nameWithoutExt + ".css" ) );
|
292 | innerCSS += PathUtils.file( Project.srcOrLibPath( nameWithoutExt + '.css' ) );
|
293 | }
|
294 |
|
295 | function addInnerJS() {
|
296 |
|
297 | const zippedJS = MinifyJS.minify( {
|
298 | name: src.name(),
|
299 | content: innerJS
|
300 | } ).zip;
|
301 | prj.flushContent( "js/" + addFilePrefix( nameWithoutExt ) + ".js", zippedJS, htmlDir );
|
302 | head.children.push( {
|
303 | type: Tree.TAG,
|
304 | name: 'script',
|
305 | attribs: {
|
306 | defer: null,
|
307 | src: backToRoot + "js/" + addFilePrefix( nameWithoutExt ) + ".js"
|
308 | }
|
309 | } );
|
310 | }
|
311 |
|
312 | const combination = combineRequires( output, options );
|
313 | makeDependenciesGraph( combination.js, src, Project );
|
314 |
|
315 | var externalDeps = lookForExternalDependencies( combination.js );
|
316 | externalDeps.js.forEach( function ( code ) {
|
317 | innerJS += code;
|
318 | } );
|
319 | for ( key in externalDeps.res ) {
|
320 | val = externalDeps.res[ key ];
|
321 | try {
|
322 | Project.copyFile( key, val );
|
323 | } catch ( ex ) {
|
324 | throw Error( "Unable to copy external dependency `" + key + "` into `" + val + "`!\n" +
|
325 | JSON.stringify( externalDeps, null, ' ' ) );
|
326 | }
|
327 | }
|
328 |
|
329 |
|
330 | if ( options.dev ) {
|
331 | addInnerJS();
|
332 |
|
333 | for ( key in combination.css ) {
|
334 | val = combination.css[ key ];
|
335 | if ( key.substr( 0, 4 ) == 'mod/' ) {
|
336 | key = key.substr( 4 );
|
337 | }
|
338 | head.children.push( {
|
339 | type: Tree.TAG,
|
340 | name: 'link',
|
341 | void: true,
|
342 | attribs: {
|
343 | rel: "stylesheet",
|
344 | type: "text/css",
|
345 | href: backToRoot + "css/" + key + ".css"
|
346 | }
|
347 | } );
|
348 | prj.flushContent( "css/" + key + ".css", val.src, htmlDir );
|
349 | }
|
350 | for ( key in combination.js ) {
|
351 | val = combination.js[ key ];
|
352 | if ( key.substr( 0, 4 ) == 'mod/' ) {
|
353 | key = key.substr( 4 );
|
354 | }
|
355 | head.children.push( {
|
356 | type: Tree.TAG,
|
357 | name: 'script',
|
358 | attribs: {
|
359 | defer: null,
|
360 | src: backToRoot + "js/" + key + ".js"
|
361 | }
|
362 | } );
|
363 | prj.flushContent( "js/" + key + ".js", val.src, htmlDir );
|
364 | prj.flushContent( "js/" + key + ".js.map", JSON.stringify( val.map ), htmlDir );
|
365 | }
|
366 | } else {
|
367 |
|
368 | for ( key in combination.css ) {
|
369 | val = combination.css[ key ];
|
370 | innerCSS += val.zip;
|
371 | }
|
372 | for ( key in combination.js ) {
|
373 | val = combination.js[ key ];
|
374 | innerJS += val.zip + "\n";
|
375 | }
|
376 | addInnerJS();
|
377 | }
|
378 |
|
379 |
|
380 | prj.flushContent( "css/" + addFilePrefix( nameWithoutExt ) + ".css", innerCSS, htmlDir );
|
381 | head.children.push( {
|
382 | type: Tree.TAG,
|
383 | name: 'link',
|
384 | void: true,
|
385 | attribs: {
|
386 | rel: "stylesheet",
|
387 | type: "text/css",
|
388 | href: backToRoot + "css/" + addFilePrefix( nameWithoutExt ) + ".css"
|
389 | }
|
390 | } );
|
391 |
|
392 | return output;
|
393 | }
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 | function lookForExternalDependencies( jsFiles ) {
|
402 | const
|
403 | javascriptSources = [],
|
404 | resources = {};
|
405 |
|
406 | for ( const jsFileName of Object.keys( jsFiles ) ) {
|
407 | const depFileName = `${jsFileName}.dep`;
|
408 | if ( Project.srcOrLibPath( depFileName ) ) {
|
409 | const depFile = new Source( Project, depFileName );
|
410 | try {
|
411 | const
|
412 | json = depFile.read(),
|
413 | dependencies = ToloframeworkPermissiveJson.parse( json );
|
414 |
|
415 | lookForExternalDependenciesJS( dependencies.js, javascriptSources );
|
416 |
|
417 | lookForExternalDependenciesRES( dependencies.res, resources, depFile );
|
418 | } catch ( ex ) {
|
419 | Fatal.fire(
|
420 | `Unable to parse JSON file "${depFile.getAbsoluteFilePath()}"!\n${ex}`,
|
421 | Project.srcOrLibPath( jsFileName )
|
422 | );
|
423 | }
|
424 | }
|
425 | }
|
426 |
|
427 | return {
|
428 | js: javascriptSources,
|
429 | res: resources
|
430 | };
|
431 | }
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 | function lookForExternalDependenciesRES( _def, resources, depFile ) {
|
442 | if ( !_def ) return;
|
443 | try {
|
444 | const def = convertExternalDependencyDefinition( _def );
|
445 | for ( const dep of Object.keys( def ) ) {
|
446 | if ( def[ dep ] === "" ) {
|
447 |
|
448 |
|
449 | def[ dep ] = dep;
|
450 | }
|
451 | const
|
452 | srcDep = Project.srcOrLibPath( `mod/${dep}` ) ||
|
453 | Project.srcOrLibPath( dep );
|
454 | if ( !srcDep ) {
|
455 | Fatal.fire(
|
456 | `Unable to find dependency file "${dep}" nor "mod/${dep}"!`,
|
457 | depFile.getAbsoluteFilePath()
|
458 | );
|
459 | }
|
460 | resources[ srcDep ] = Project.wwwPath( def[ dep ] );
|
461 | }
|
462 | } catch ( ex ) {
|
463 | Fatal.fire(
|
464 | `Unable to parse RES dependencies: ${JSON.stringify(_def)}!\n${ex}`,
|
465 | depFile
|
466 | );
|
467 | }
|
468 | }
|
469 |
|
470 |
|
471 |
|
472 |
|
473 |
|
474 |
|
475 |
|
476 |
|
477 | function lookForExternalDependenciesJS( _def, javascriptSources ) {
|
478 | if ( !_def ) return;
|
479 | try {
|
480 | const def = convertExternalDependencyDefinition( _def );
|
481 | Object.keys( def ).forEach( function ( js ) {
|
482 | const
|
483 | filename = `mod/${js}`,
|
484 | src = new Source( Project, filename ),
|
485 | code = src.read();
|
486 | pushUnique( javascriptSources, code );
|
487 | } );
|
488 | } catch ( ex ) {
|
489 | Fatal.fire(
|
490 | `Unable to parse JS dependencies: ${JSON.stringify(_def)}!\n${ex}`,
|
491 | javascriptSources
|
492 | );
|
493 | }
|
494 | }
|
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 | function convertExternalDependencyDefinition( def ) {
|
508 | if ( typeof def === 'string' ) {
|
509 | const defAsString = {};
|
510 | defAsString[ def ] = "";
|
511 | return defAsString
|
512 | }
|
513 | if ( Array.isArray( def ) ) {
|
514 | const defAsArray = {};
|
515 | def.forEach( function ( file ) {
|
516 | defAsString[ file ] = "";
|
517 | } );
|
518 | }
|
519 | return def;
|
520 | }
|
521 |
|
522 |
|
523 |
|
524 |
|
525 | function pushUnique( arr, item ) {
|
526 | if ( arr.indexOf( item ) > -1 ) return false;
|
527 | arr.push( item );
|
528 | return true;
|
529 | }
|
530 |
|
531 | function writeResources( output ) {
|
532 |
|
533 | var resourceName;
|
534 |
|
535 | var resourceData;
|
536 |
|
537 | var dstPath;
|
538 |
|
539 | var srcPath;
|
540 |
|
541 | var content;
|
542 |
|
543 | for ( resourceName in output.resource ) {
|
544 | resourceData = output.resource[ resourceName ];
|
545 | dstPath = Project.wwwPath( resourceData.dst );
|
546 | if ( PathUtils.isDirectory( dstPath ) ) {
|
547 |
|
548 |
|
549 | } else {
|
550 |
|
551 | Project.mkdir( Path.dirname( dstPath ) );
|
552 | if ( resourceData.src ) {
|
553 | srcPath = Project.srcOrLibPath( resourceData.src );
|
554 | Project.copyFile( srcPath, dstPath );
|
555 | } else {
|
556 | content = resourceData.txt;
|
557 | PathUtils.file( dstPath, content );
|
558 | }
|
559 | }
|
560 | }
|
561 |
|
562 |
|
563 | var moduleName;
|
564 |
|
565 | var resourcePath;
|
566 | output.modules.forEach( function ( moduleName ) {
|
567 | resourcePath = Project.srcOrLibPath( moduleName );
|
568 | if ( resourcePath ) {
|
569 |
|
570 |
|
571 | var dst = Path.join( Path.dirname( output.filename ), moduleName.substr( 4 ) );
|
572 | dst = dst.replace( /\\/g, '/' );
|
573 | Project.copyFile( resourcePath, Project.wwwPath( 'css/' + dst ) );
|
574 | }
|
575 | } );
|
576 | }
|
577 |
|
578 |
|
579 | function concatDicValues( map ) {
|
580 | if ( !map ) return '';
|
581 | var key, out = '';
|
582 | for ( key in map ) {
|
583 | if ( out != '' ) out += "\n";
|
584 | out += key;
|
585 | }
|
586 | return out;
|
587 | }
|
588 |
|
589 |
|
590 | function findHead( root ) {
|
591 | if ( !root ) return null;
|
592 |
|
593 | var head = Tree.getElementByName( root, "head" );
|
594 | if ( !head ) {
|
595 |
|
596 | var html = Tree.getElementByName( root, "html" );
|
597 | if ( !html ) {
|
598 | html = {
|
599 | type: Tree.TAG,
|
600 | name: "html",
|
601 | children: []
|
602 | };
|
603 | root.children.push( html );
|
604 | }
|
605 | head = {
|
606 | type: Tree.TAG,
|
607 | name: "head",
|
608 | children: []
|
609 | };
|
610 | html.children.push( head );
|
611 | }
|
612 | return head;
|
613 | }
|
614 |
|
615 |
|
616 | function getInitJS( output ) {
|
617 | var js = '';
|
618 | var dynamicModule, code;
|
619 | for ( dynamicModule in output.dynamicModules ) {
|
620 | code = output.dynamicModules[ dynamicModule ];
|
621 | js += code;
|
622 | }
|
623 | js += concatDicValues( output.initJS ) +
|
624 | "\n" + concatDicValues( output.postInitJS );
|
625 | if ( js.length > 0 ) {
|
626 | return Tpl.file( "init.js", {
|
627 | INIT_JS: js
|
628 | } ).out;
|
629 | }
|
630 | return js;
|
631 | }
|
632 |
|
633 |
|
634 | function writeInnerCSS( innerCSS, pathCSS, nameWithoutExt, head, sourcemap ) {
|
635 | if ( innerCSS.length > 0 ) {
|
636 |
|
637 | writeCSS( '@' + nameWithoutExt + ".css", innerCSS );
|
638 | head.children.push( {
|
639 | type: Tree.TAG,
|
640 | name: 'link',
|
641 | void: true,
|
642 | attribs: {
|
643 | rel: "stylesheet",
|
644 | type: "text/css",
|
645 | href: "css/@" + nameWithoutExt + ".css"
|
646 | }
|
647 | } );
|
648 | }
|
649 | }
|
650 |
|
651 |
|
652 | function writeInnerJS( innerJS, pathJS, nameWithoutExt, head, sourcemap ) {
|
653 | if ( innerJS.length > 0 ) {
|
654 |
|
655 | writeJS( '@' + nameWithoutExt + ".js", innerJS );
|
656 | head.children.push( {
|
657 | type: Tree.TAG,
|
658 | name: 'script',
|
659 | attribs: {
|
660 | defer: null,
|
661 | src: "js/@" + nameWithoutExt + ".js"
|
662 | }
|
663 | } );
|
664 | }
|
665 | }
|
666 |
|
667 |
|
668 |
|
669 |
|
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 | function combineRequires( output, options ) {
|
676 |
|
677 |
|
678 |
|
679 | var cache = {},
|
680 |
|
681 | modules = output.require || {},
|
682 |
|
683 | fringe = [],
|
684 |
|
685 | moduleName,
|
686 |
|
687 | css = '',
|
688 |
|
689 | src,
|
690 |
|
691 | dependencies,
|
692 |
|
693 | jsFiles = {},
|
694 |
|
695 | cssFiles = {},
|
696 |
|
697 | i;
|
698 |
|
699 | if ( !Array.isArray( output.modules ) ) output.modules = [];
|
700 |
|
701 |
|
702 | for ( moduleName in output.dynamicModules ) {
|
703 | cache[ moduleName ] = 1;
|
704 | }
|
705 |
|
706 |
|
707 | modules[ 'mod/$' ] = 1;
|
708 |
|
709 | for ( moduleName in modules ) {
|
710 | fringe.push( moduleName );
|
711 | }
|
712 |
|
713 |
|
714 | while ( fringe.length > 0 ) {
|
715 | moduleName = fringe.pop();
|
716 | cache[ moduleName ] = 1;
|
717 | if ( moduleName.substr( 0, 4 ) == 'cls/' ) {
|
718 |
|
719 | output.innerJS[ Template.file( 'tfw3.js' ).out ] = 1;
|
720 | } else if ( moduleName.substr( 0, 4 ) == 'mod/' ) {
|
721 |
|
722 | if ( output.modules.indexOf( moduleName ) < 0 ) {
|
723 | output.modules.push( moduleName );
|
724 | }
|
725 | }
|
726 |
|
727 |
|
728 |
|
729 |
|
730 |
|
731 | src = CompilerJS.compile( Project, moduleName + ".js", options, output );
|
732 | if ( !jsFiles[ moduleName ] ) {
|
733 | jsFiles[ moduleName ] = {
|
734 | src: src.tag( 'src' ),
|
735 | zip: src.tag( 'zip' ),
|
736 | map: src.tag( 'map' ),
|
737 | dep: src.tag( "dependencies" )
|
738 | };
|
739 | }
|
740 |
|
741 | dependencies = src.tag( "dependencies" );
|
742 | if ( Array.isArray( dependencies ) ) {
|
743 | dependencies.forEach( function ( dep ) {
|
744 | if ( !cache[ dep ] ) {
|
745 | fringe.push( dep );
|
746 | }
|
747 | } );
|
748 | }
|
749 |
|
750 |
|
751 |
|
752 | src = compileCSS( moduleName + ".css", options );
|
753 | if ( src ) {
|
754 | if ( !cssFiles[ moduleName ] ) {
|
755 | cssFiles[ moduleName ] = {
|
756 | src: src.tag( 'src' ),
|
757 | zip: src.tag( 'zip' )
|
758 | };
|
759 | }
|
760 | }
|
761 | }
|
762 |
|
763 | return {
|
764 | js: jsFiles,
|
765 | css: cssFiles
|
766 | };
|
767 | }
|
768 |
|
769 |
|
770 | function writeJS( name, sourceZip, sourceMap ) {
|
771 | if ( name.substr( -3 ) == '.js' ) {
|
772 | name = name.substr( 0, name.length - 3 );
|
773 | }
|
774 | var path = Path.join( Project.wwwPath( "js" ), name + ".js" );
|
775 | FS.writeFileSync( path, sourceZip );
|
776 | if ( sourceMap ) {
|
777 | path = Path.join( Project.wwwPath( "js" ), name + ".js.map" );
|
778 | FS.writeFileSync( path, sourceMap );
|
779 | }
|
780 |
|
781 | var src = Project.srcOrLibPath( name );
|
782 | if ( FS.existsSync( src ) ) {
|
783 | var dst = Path.join( Project.wwwPath( "css" ), name );
|
784 | Project.copyFile( src, dst );
|
785 | }
|
786 | }
|
787 |
|
788 |
|
789 | function writeCSS( name, content, sourceMap ) {
|
790 | if ( name.substr( -4 ) == '.css' ) {
|
791 | name = name.substr( 0, name.length - 4 );
|
792 | }
|
793 | var path = Path.join( Project.wwwPath( "css" ), name + ".css" );
|
794 | FS.writeFileSync( path, content );
|
795 | if ( sourceMap ) {
|
796 | path = Path.join( Project.wwwPath( "css" ), name + ".css.map" );
|
797 | FS.writeFileSync( path, sourceMap );
|
798 | }
|
799 | }
|
800 |
|
801 |
|
802 | function moduleExists( requiredModule ) {
|
803 | var path = Project.srcOrLibPath( requiredModule + ".js" );
|
804 | if ( path ) return true;
|
805 | return false;
|
806 | }
|
807 |
|
808 |
|
809 | function minifyCSS( name, code, options ) {
|
810 | var result = null;
|
811 | if ( !code ) return null;
|
812 |
|
813 | if ( code.trim().length == 0 ) {
|
814 |
|
815 | console.log( " Warning! ".yellowBG.black + name + " is EMPTY!" );
|
816 | return null;
|
817 | }
|
818 |
|
819 | try {
|
820 | var css = Util.zipCSS( code );
|
821 | result = {
|
822 | src: code,
|
823 | zip: css.styles,
|
824 | map: css.sourceMap
|
825 | };
|
826 | } catch ( ex ) {
|
827 | throw Error( "Unable to minify CSS \"" + name + "\":\n" + ex +
|
828 | "\n\nCSS content was:\n" + code.substr( 0, 256 ) +
|
829 | ( code.length > 256 ? '\n[...]' : '' ) );
|
830 | }
|
831 | return result;
|
832 | }
|
833 |
|
834 |
|
835 |
|
836 |
|
837 | function compileCSS( path, options ) {
|
838 | var absPath = Project.srcOrLibPath( path );
|
839 | if ( !absPath ) return null;
|
840 | var src = new Source( Project, path );
|
841 | if ( !src.exists() ) return null;
|
842 | if ( !src.isUptodate() ) {
|
843 | console.log( "Compiling CSS " + path.yellow );
|
844 | var cssCode = src.read();
|
845 |
|
846 | var multiBrowserCssCode = Rework( cssCode ).toString();
|
847 | var minify = minifyCSS( src.name(), multiBrowserCssCode, options );
|
848 | src.tag( 'src', cssCode );
|
849 | src.tag( 'zip', minify.zip );
|
850 | src.tag( 'map', minify.map );
|
851 | src.save();
|
852 | }
|
853 | return src;
|
854 | }
|
855 |
|
856 |
|
857 |
|
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 | function addFilePrefix( path, prefix ) {
|
867 | if ( typeof prefix === 'undefined' ) prefix = '@';
|
868 |
|
869 | var separatorPosition = path.lastIndexOf( '/' );
|
870 | if ( separatorPosition < 0 ) {
|
871 |
|
872 | separatorPosition = path.lastIndexOf( '\\' );
|
873 | }
|
874 | var filenameStart = separatorPosition > -1 ? separatorPosition + 1 : 0;
|
875 | var result = path.substr( 0, filenameStart ) + prefix + path.substr( filenameStart );
|
876 | return result.replace( /\\/g, '/' );
|
877 | }
|
878 |
|
879 |
|
880 |
|
881 |
|
882 |
|
883 |
|
884 |
|
885 | function getBackToRoot( path ) {
|
886 |
|
887 | var standardFolderSepCount = 0;
|
888 |
|
889 | var windowsFolderSepCount = 0;
|
890 |
|
891 | var i;
|
892 |
|
893 | var c;
|
894 |
|
895 | for ( i = 0; i < path.length; i++ ) {
|
896 | c = path.charAt( i );
|
897 | if ( c == '/' ) standardFolderSepCount++;
|
898 | if ( c == '\\' ) windowsFolderSepCount++;
|
899 | }
|
900 | var folderSepCount = Math.max( standardFolderSepCount, windowsFolderSepCount );
|
901 | if ( folderSepCount == 0 ) {
|
902 |
|
903 | return '';
|
904 | }
|
905 |
|
906 | var result = '';
|
907 | var folderSep = '/';
|
908 | for ( i = 0; i < folderSepCount; i++ ) {
|
909 | result += '..' + folderSep;
|
910 | }
|
911 | return result;
|
912 | }
|
913 |
|
914 |
|
915 |
|
916 |
|
917 |
|
918 |
|
919 | function addDescriptionToHead( head, options ) {
|
920 | if ( !options || !options.config || typeof options.config.description !== 'string' ) {
|
921 | return false;
|
922 | }
|
923 |
|
924 | if ( !Array.isArray( head.children ) ) {
|
925 | head.children = [];
|
926 | }
|
927 | for ( let i = 0; i < head.children.length; i++ ) {
|
928 | const child = head.children[ i ];
|
929 | if ( child.type !== Tree.ELEMENT ) continue;
|
930 | if ( child.name.toLowerCase() != 'meta' ) continue;
|
931 | if ( !child.attribs ) continue;
|
932 | if ( typeof child.attribs.name !== 'string' ) continue;
|
933 | if ( child.attribs.name.toLowerCase() === 'description' ) {
|
934 |
|
935 | return false;
|
936 | }
|
937 | }
|
938 | head.children.push( {
|
939 | type: Tree.ELEMENT,
|
940 | name: 'meta',
|
941 | attribs: {
|
942 | name: 'description',
|
943 | content: options.config.description
|
944 | }
|
945 | } );
|
946 | return true;
|
947 | }
|
948 |
|
949 |
|
950 |
|
951 |
|
952 |
|
953 |
|
954 |
|
955 |
|
956 |
|
957 |
|
958 | function makeDependenciesGraph( modules, source, project ) {
|
959 | const
|
960 | lines = Object.keys( modules ).map( ( moduleName ) => {
|
961 | const
|
962 | module = modules[ moduleName ],
|
963 | moduleShortName = Util.removeFirstSubFolder( moduleName ),
|
964 | mapper = function mapper( dependencyName ) {
|
965 | return ` "${moduleShortName}" -> "${Util.removeFirstSubFolder(dependencyName)}"\n`;
|
966 | };
|
967 | return module.dep.map( mapper ).join( "\n" );
|
968 | } ),
|
969 | content = `digraph dependencies {\n${lines.join('')}\n}`,
|
970 | destinationPath = project.docPath( `${source.name()}.dot` );
|
971 | FS.writeFileSync( destinationPath, content );
|
972 | }
|