UNPKG

15.5 kBJavaScriptView Raw
1/*------------------------------------*\
2 TOOLS
3\*------------------------------------*/
4/* jslint node: true */
5
6/**
7 * Engine for compile preprocessor CSS files and minify CSS output.
8 * @private
9 * @function cssCompilation
10 * @memberOf NA#
11 * @this NA
12 * @param {NA~callback} next Next step after preprocessor compilation.
13 */
14exports.cssCompilation = function (next) {
15 var NA = this,
16 async = NA.modules.async;
17
18 if (NA.configuration.generate || NA.webconfig.cssBundlingBeforeResponse) {
19 async.parallel([
20 NA.lessCompilation.bind(NA),
21 NA.stylusCompilation.bind(NA)
22 ], function () {
23 NA.cssMinification(next);
24 });
25 } else {
26 next();
27 }
28};
29
30/**
31 * Engine for compile Less files.
32 * @private
33 * @function lessCompilation
34 * @memberOf NA#
35 * @this NA
36 * @param {NA~callback} next Next step after Less compilation.
37 */
38exports.lessCompilation = function (next) {
39 var NA = this,
40 enableLess = NA.webconfig.less,
41 async = NA.modules.async,
42 path = NA.modules.path,
43 less = NA.modules.less,
44 fs = NA.modules.fs,
45 allLessCompiled,
46 data = {},
47 paths = [];
48
49 if (enableLess && enableLess.files) {
50
51 allLessCompiled = enableLess.files;
52
53 if (enableLess.paths && paths.length === 0) {
54 for (var i = 0; i < enableLess.paths.length; i++) {
55 paths[i] = path.join(NA.webconfig.assetsRelativePath, enableLess.paths[i]);
56 }
57 } else if (paths.length === 0) {
58 paths = [
59 NA.webconfig.assetsRelativePath,
60 path.join(NA.webconfig.assetsRelativePath, 'stylesheets'),
61 path.join(NA.webconfig.assetsRelativePath, 'styles'),
62 path.join(NA.webconfig.assetsRelativePath, 'css')
63 ];
64 }
65
66 async.each(allLessCompiled, function (compiledFile, next) {
67 var currentFile = fs.readFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile), 'utf-8'),
68 prefixLess = new NA.modules.prefixLess(),
69 options = {
70 paths: paths
71 };
72
73 if (enableLess.autoprefix) {
74 options.plugins = [prefixLess];
75 }
76
77 less.render(currentFile, options, function (e, output) {
78 if (e) {
79 NA.log(e);
80 }
81
82 data.pathName = path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile.replace(/\.less$/g,'.css'));
83 fs.writeFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile.replace(/\.less$/g,'.css')), output.css);
84 NA.log(NA.cliLabels.lessGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
85 next();
86 });
87 }, function () {
88 if (next) {
89 next();
90 }
91 });
92 } else {
93 if (next) {
94 next();
95 }
96 }
97
98};
99
100/**
101 * Engine for compile Stylus files.
102 * @private
103 * @function stylusCompilation
104 * @memberOf NA#
105 * @this NA
106 * @param {NA~callback} next Next step after Stylus compilation.
107 */
108exports.stylusCompilation = function (next) {
109 var NA = this,
110 enableStylus = NA.webconfig.stylus,
111 async = NA.modules.async,
112 path = NA.modules.path,
113 stylus = NA.modules.stylus,
114 fs = NA.modules.fs,
115 allStylusCompiled,
116 data = {},
117 paths = [];
118
119 if (enableStylus && enableStylus.files) {
120
121 allStylusCompiled = enableStylus.files;
122
123 if (enableStylus.paths && paths.length === 0) {
124 for (var i = 0; i < enableStylus.paths.length; i++) {
125 paths[i] = path.join(NA.webconfig.assetsRelativePath, enableStylus.paths[i]);
126 }
127 } else if (paths.length === 0) {
128 paths = [
129 NA.webconfig.assetsRelativePath,
130 path.join(NA.webconfig.assetsRelativePath, 'stylesheets'),
131 path.join(NA.webconfig.assetsRelativePath, 'styles'),
132 path.join(NA.webconfig.assetsRelativePath, 'css')
133 ];
134 }
135
136 async.each(allStylusCompiled, function (compiledFile, next) {
137 var currentFile = fs.readFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile), 'utf-8'),
138 stylusFn = stylus(currentFile);
139
140 if (enableStylus.autoprefix) {
141 stylusFn = stylusFn.use(NA.modules.prefixStylus());
142 }
143
144 stylusFn
145 .set('paths', paths)
146 .render(function (e, output) {
147 if (e) {
148 NA.log(e);
149 }
150
151 data.pathName = path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile.replace(/\.styl$/g,'.css'));
152 fs.writeFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compiledFile.replace(/\.styl$/g,'.css')), output);
153 NA.log(NA.cliLabels.stylusGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
154 next();
155 });
156 }, function () {
157 if (next) {
158 next();
159 }
160 });
161 } else {
162 if (next) {
163 next();
164 }
165 }
166
167};
168
169/**
170 * Engine for minification and concatenation of all files with a Bundle configuration.
171 * @private
172 * @function cssMinification
173 * @memberOf NA#
174 * @this NA
175 * @param {NA~callback} next Next step after minification and concatenation of all CSS.
176 */
177exports.cssMinification = function (next) {
178 var NA = this,
179 bundles = NA.webconfig.bundles,
180 cleanCss = NA.modules.cleanCss,
181 async = NA.modules.async,
182 path = NA.modules.path,
183 fs = NA.modules.fs,
184 enable,
185 output = "",
186 data = {},
187 allCssMinified = [];
188
189 /* Verify if bundle is okay and if engine must start. */
190 enable = (NA.configuration.generate ||
191
192 /**
193 * CSS minification before each HTML response.
194 * @public
195 * @alias cssBundlingBeforeResponse
196 * @type {boolean}
197 * @memberOf NA#webconfig
198 * @default false
199 */
200 NA.webconfig.cssBundlingBeforeResponse);
201
202 if (typeof NA.webconfig.cssBundlingEnable === "boolean") {
203
204 /**
205 * No CSS minification if set to false.
206 * @public
207 * @alias cssBundlingEnable
208 * @type {boolean}
209 * @memberOf NA#webconfig
210 * @default true
211 */
212 enable = NA.webconfig.cssBundlingEnable;
213 }
214
215 /* Star engine. */
216 if (bundles && bundles.stylesheets && enable) {
217
218 NA.forEach(bundles.stylesheets, function (compressedFile) {
219 allCssMinified.push(compressedFile);
220 });
221
222 async.each(allCssMinified, function (compressedFile, firstCallback) {
223
224 async.map(bundles.stylesheets[compressedFile], function (sourceFile, secondCallback) {
225 try {
226 secondCallback(null, fs.readFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, sourceFile), 'utf-8'));
227 } catch (e) {
228 secondCallback(e, "");
229 }
230 }, function(error, results) {
231 if (error) {
232 throw error;
233 }
234 for (var i = 0; i < results.length; i++) {
235 output += results[i];
236 }
237
238 output = (new cleanCss().minify(output)).styles;
239 compressedFile = compressedFile.replace(/{version}/g, NA.webconfig.version);
240 fs.writeFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compressedFile), output);
241 data.pathName = path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compressedFile);
242 NA.log(NA.cliLabels.cssGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
243 output = "";
244
245 firstCallback();
246 });
247
248 }, function () {
249 if (next) {
250 next();
251 }
252 });
253 } else {
254 if (next) {
255 next();
256 }
257 }
258};
259
260/**
261 * Engine for obfuscation and concatenation of all files with a Bundle configuration.
262 * @private
263 * @function jsObfuscation
264 * @memberOf NA#
265 * @this NA
266 * @param {NA~callback} next Next step after obfuscation and concatenation of all CSS.
267 */
268exports.jsObfuscation = function (next) {
269 var NA = this,
270 bundles = NA.webconfig.bundles,
271 uglifyEs = NA.modules.uglifyEs,
272 async = NA.modules.async,
273 path = NA.modules.path,
274 fs = NA.modules.fs,
275 enable,
276 output = "",
277 data = {},
278 allJsMinified = [];
279
280 /* Verify if bundle is okay and if engine must start. */
281 enable = (NA.configuration.generate ||
282
283 /**
284 * JavaScript obfuscation before each HTML response.
285 * @public
286 * @alias jsBundlingBeforeResponse
287 * @type {boolean}
288 * @memberOf NA#webconfig
289 * @default false
290 */
291 NA.webconfig.jsBundlingBeforeResponse);
292
293 if (typeof NA.webconfig.jsBundlingEnable === "boolean") {
294
295 /**
296 * No JavaScript obfuscation if set to false.
297 * @public
298 * @alias jsBundlingEnable
299 * @type {boolean}
300 * @memberOf NA#webconfig
301 * @default true
302 */
303 enable = NA.webconfig.jsBundlingEnable;
304 }
305
306 /* Star engine. */
307 if (bundles && bundles.javascripts && enable) {
308
309 NA.forEach(bundles.javascripts, function (compressedFile) {
310 allJsMinified.push(compressedFile);
311 });
312
313 async.each(allJsMinified, function (compressedFile, firstCallback) {
314
315 async.map(bundles.javascripts[compressedFile], function (sourceFile, secondCallback) {
316 var code,
317 result,
318 file;
319
320 /* For the NA.socket and NA.io auto configuration. */
321 if (path.join("/", NA.webconfig.socketClientFile) === path.join("/", sourceFile)) {
322 file = fs.readFileSync(path.join(NA.nodeatlasPath, "src", "socket.io.js"), "utf-8")
323 .replace(/%urlRelativeSubPath%/g, NA.webconfig.urlRelativeSubPath.slice(1))
324 .replace(/%urlRoot%/g, NA.webconfig.urlRoot);
325
326 result = uglifyEs.minify(file);
327 /* And for others real files. */
328 } else {
329 code = fs.readFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, sourceFile), "utf-8");
330 result = uglifyEs.minify(code);
331 }
332
333 secondCallback(null, result.code);
334 }, function(error, results) {
335 if (error) {
336 throw error;
337 }
338
339 for (var i = 0; i < results.length; i++) {
340 output += results[i];
341 }
342
343 compressedFile = compressedFile.replace(/{version}/g, NA.webconfig.version);
344 fs.writeFileSync(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compressedFile), output);
345 data.pathName = path.join(NA.serverPath, NA.webconfig.assetsRelativePath, compressedFile);
346 NA.log(NA.cliLabels.jsGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
347 output = "";
348
349 firstCallback();
350 });
351
352 }, function () {
353
354 if (next) {
355 next();
356 }
357 });
358 } else {
359 if (next) {
360 next();
361 }
362 }
363};
364
365/**
366 * Check if a file have been already parsed.
367 * @private
368 * @function cssAlreadyParse
369 * @memberOf NA#
370 * @param {string} newPath Current file tested.
371 * @param {Array.<string>} allCssFiles Files already tested.
372 * @param {string} inject State for know if injection will be authorized.
373 */
374exports.cssAlreadyParse = function (newPath, allCssFiles, inject) {
375 var NA = this,
376 path = NA.modules.path;
377
378 for (var i = 0; i < allCssFiles.length; i++) {
379 if (path.join(NA.serverPath, NA.webconfig.assetsRelativePath, newPath) === allCssFiles[i]) {
380 inject = false;
381 }
382 }
383
384 return inject;
385};
386
387/**
388 * Inject Css if not already injected.
389 * @private
390 * @function injectCssAuth
391 * @memberOf NA#
392 * @param {string} pathFile Current file for injection.
393 * @param {Array.<string>} allCssFiles Files already tested.
394 * @param {string} inject State for know if injection will be authorized.
395 */
396exports.injectCssAuth = function (pathFile, allCssFiles, inject) {
397 var NA = this,
398 path = NA.modules.path;
399
400 if (inject) {
401 allCssFiles.push(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, pathFile));
402 }
403};
404
405/**
406 * Verify if common or specif file without double are ready for injection CSS.
407 * @private
408 * @function prepareCssInjection
409 * @memberOf NA#
410 * @param {Array.<string>} allCssFiles Files already tested.
411 * @param {string|Array.<string>} injection Represent the injectCss property injection to the template.
412 */
413exports.prepareCssInjection = function (allCssFiles, injection) {
414 var NA = this,
415 path = NA.modules.path,
416 inject = true,
417
418 /**
419 * CSS files for specific injection of CSS.
420 * @public
421 * @alias injectCss
422 * @type {string|Array.<string>}
423 * @memberOf NA#locals.routeParameters
424 */
425 specificInjection = injection,
426
427 /**
428 * CSS files for common injection of CSS.
429 * @public
430 * @alias injectCss
431 * @type {string|Array.<string>}
432 * @memberOf NA#webconfig
433 */
434 commonInjection = NA.webconfig.injectCss;
435
436 /* Add common injections. */
437 if (typeof commonInjection === 'string') {
438 allCssFiles.push(path.join(NA.serverPath, NA.webconfig.assetsRelativePath, commonInjection));
439 } else if (commonInjection) {
440 for (var i = 0; i < commonInjection.length; i++) {
441 /* Inject Css. */
442 NA.injectCssAuth(NA.webconfig.injectCss[i], allCssFiles, inject);
443 }
444 }
445
446 /* Add specific injections. */
447 if (specificInjection) {
448 if (typeof specificInjection === 'string') {
449 /* Check if a file have been already parsed. */
450 inject = NA.cssAlreadyParse(specificInjection, allCssFiles, inject);
451 /* Inject Css. */
452 NA.injectCssAuth(specificInjection, allCssFiles, inject);
453 } else {
454 for (var j = 0; j < specificInjection.length; j++) {
455 /* Check if a file have been already parsed. */
456 inject = NA.cssAlreadyParse(specificInjection[j], allCssFiles, inject);
457 /* Inject Css. */
458 NA.injectCssAuth(specificInjection[j], allCssFiles, inject);
459 inject = true;
460 }
461 }
462 }
463
464 return allCssFiles;
465};
466
467/**
468 * Inject the content of a stylesheets file into a DOM.
469 * @private
470 * @function injectCss
471 * @memberOf NA#
472 * @param {string} dom The ouptput HTML.
473 * @param {string|Array.<string>} injection Represent the injectCss property injection to the template.
474 * @param {injectCss~mainCallback} mainCallback The next steps after injection.
475 */
476exports.injectCss = function (data, injection, mainCallback) {
477 var NA = this,
478 jsdom = NA.modules.jsdom;
479 cssParse = NA.modules.cssParse,
480 async = NA.modules.async,
481 fs = NA.modules.fs,
482 allCssFiles = [],
483 dom = new jsdom.JSDOM(data);
484
485 /* Prepare Injection */
486 allCssFiles = NA.prepareCssInjection(allCssFiles, injection);
487
488 /* Injection */
489 async.map(allCssFiles, function (sourceFile, callback) {
490 /* Concatain all CSS. */
491 callback(null, fs.readFileSync(sourceFile, 'utf-8'));
492 }, function(error, results) {
493 var output = "",
494 css;
495
496 for (var i = 0; i < results.length; i++) {
497 output += results[i];
498 }
499
500 /* Parse CSS in JavaScript. */
501 css = cssParse(output);
502
503 /* Parse all rules Css. */
504 function parseCssRules(callback) {
505 for (var i = 0; i < css.stylesheet.rules.length; i++) {
506 if (typeof css.stylesheet.rules[i].selectors !== 'undefined') {
507 callback(i);
508 }
509 }
510 }
511
512 /* Parse all selector Css. */
513 function parseCssSelector(i, callback) {
514 for (var j = 0; j < css.stylesheet.rules[i].selectors.length; j++) {
515 callback(j);
516 }
517 }
518
519 /* Parse all declaration Css. */
520 function parseCssDeclaration(i, j) {
521 for (var k = 0; k < css.stylesheet.rules[i].declarations.length; k++) {
522 Array.prototype.forEach.call(dom.window.document.querySelectorAll(css.stylesheet.rules[i].selectors[j]), function (node) {
523 node.style[css.stylesheet.rules[i].declarations[k].property] = css.stylesheet.rules[i].declarations[k].value
524 });
525 }
526 }
527
528 /* Apply property on the DOM. */
529 parseCssRules(function (i) {
530 parseCssSelector(i, function (j) {
531 parseCssDeclaration(i, j);
532 });
533 });
534
535 /**
536 * Next steps after injection of CSS.
537 * @callback injectCss~mainCallback
538 * @param {string} dom DOM with modifications.
539 */
540 mainCallback(dom.serialize());
541 });
542};
\No newline at end of file