diff --git a/lib/server/README.md b/lib/server/README.md new file mode 100644 index 00000000..1a14d09b --- /dev/null +++ b/lib/server/README.md @@ -0,0 +1,4 @@ +minify +====== + +Minifier of js, css, html and img \ No newline at end of file diff --git a/lib/server/minify.js b/lib/server/minify.js new file mode 100644 index 00000000..c15fe766 --- /dev/null +++ b/lib/server/minify.js @@ -0,0 +1,350 @@ +/* Модуль сжатия js-скриптов и css*/ + +console.log('minify.js loaded...'); + +/* функция сжимает js-скрипты + * и сохраняет их с именем .min.js + */ + +var fs = require('fs'); +var path=require('path'); + +var MinFolder='min/'; +/* function clear MinFolder + * if we could not create + * directory and it is + * not exist + */ +var folderExist = function(pError, pStat){ + "use strict"; + /*file found and it's directory */ + if(!pError && pStat.isDirectory()) + console.log('folder exist: ' + MinFolder); + else MinFolder='/'; +}; + +/* + * function says thet folder created + * if everything is OK, or + * moves to folderExist function + */ +var makeFolder = function(pError){ + "use strict"; + /*folder created successfully*/ + if(!pError) + console.log('folder created: min'); + else fs.stat(MinFolder,folderExist); +}; + +/* Trying to create folder min + * where woud be minifyed versions + * of files 511(10)=777(8) + * rwxrwxrwx + */ +fs.mkdir(MinFolder,511,makeFolder); + +exports.MinFolder = MinFolder; + +/* function which minificate js-files + * @pJSFiles_a - varible, wich contain array + * of js file names or string, if name + * single + */ +exports.jsScripts=function jsScripts(pJSFiles_a){ + 'use strict'; + /* подключаем модуль uglify-js + * если его нет - дальнейшая + * работа функции не имеет смысла + */ + try{ + var jsp = require("uglify-js").parser; + var pro = require("uglify-js").uglify; + }catch(error){ + console.log('can\'n load uglify-js\n' + + 'to use js-minification you need to install uglify-js\n' + + 'npm install uglify-js\n' + + 'https://github.com/mishoo/UglifyJS'); + return false; + } + /* Константы */ + var CLIENT_JS='client.js'; + var CLOUDFUNC_JS='lib/cloudfunc.js'; + + if(typeof pJSFiles_a === "string") + pJSFiles_a=[pJSFiles_a]; + + var dataReaded_f=function(pFileName, pData){ + console.log('file ' + pFileName + ' readed'); + + /*********************************/ + /* сжимаем код через uglify-js */ + var uglify_js=function(pDdata){ + var orig_code = pDdata.toString(); + var ast = jsp.parse(orig_code); // parse code and get the initial AST + ast = pro.ast_mangle(ast); // get a new AST with mangled names + ast = pro.ast_squeeze(ast); // get an AST with compression optimizations + var result_code = pro.gen_code(ast); // compressed code here + return result_code; + }; + /*********************************/ + var final_code=uglify_js(pData); + + var minFileName=pFileName.replace('.js','.min.js'); + + /* если мы сжимаем client.js - + * меняем строку cloudfunc.js на + * cloudfunc.min.js и выводим сообщение + * + * меняем строку keyBinding.js на + * keyBinding.min.js + * если другой файл - ничего не деалем + */ + /* + * temporary changed dir path, + * becouse directory lib is write + * protected by others by default + * so if node process is started + * from other user (root for example + * in nodester) we can not write + * minified versions + */ + if(pFileName===CLIENT_JS) + console.log('file name of ' + + CLOUDFUNC_JS + + ' in ' + + CLIENT_JS + + ' changed. size:', + (final_code=final_code + .replace('cloudfunc.js','cloudfunc.min.js') + .replace('keyBinding.js','keyBinding.min.js') + .replace('/lib/', MinFolder) + .replace('/lib/client/', MinFolder)).length); + + + /* minimized file will be in min file + * if it's possible if not - + * in root + */ + minFileName = MinFolder + path.basename(minFileName); + /* записываем сжатый js-скрипт*/ + fs.writeFile(minFileName, final_code, fileWrited(minFileName)); + }; + + /* moving thru all elements of js files array */ + for(var i=0;pJSFiles_a[i];i++){ + console.log('reading file ' + pJSFiles_a[i]+'...'); + fs.readFile(pJSFiles_a[i],fileReaded(pJSFiles_a[i],dataReaded_f)); + } + + return true; +}; + +/* функция сжимает css-стили + * и сохраняет их с именем .min.css + * @pImgConvertToBase64_b - булевый признак, + * который отвечает за то, что быконвертировать + * картинки в base64 и поместить в выходной css файл + * @pCSSFiles_a - масив имен css файлов или строка, + * если имя одно + */ +exports.cssStyles=function cssStyles(pCSSFiles_a, pImgConvertToBase64_b){ + 'use strict'; + + /* connecting cleanCSS, + * if we can't find it - + * return false + */ + var cleanCSS; + try{ + cleanCSS = require('clean-css'); + }catch(error){ + console.log('can\'n load clean-css \n' + + 'to use css-minification you need to install clean-css \n' + + 'npm install clean-css\n' + + 'https://github.com/GoalSmashers/clean-css'); + return false; + } + + if(typeof pCSSFiles_a === "string") + pCSSFiles_a=[pCSSFiles_a]; + /* Varible contains information + * about readed css file + */ + var lCSSFiles_doneCount=0; + + var lAllStyle=''; + + var dataReaded_f=function(pFileName, pData){ + console.log('file ' + pFileName + ' readed'); + /*********************************/ + /* сжимаем код через clean-css */ + var clean_css=function(pData){ + /* Сохраняем весь стиль в одну переменную*/ + return cleanCSS.process(pData); + }; + /*********************************/ + var final_code=clean_css(pData); + + lAllStyle+=final_code; + + var minFileName=pFileName.replace('.css','.min.css'); + + ++lCSSFiles_doneCount; + + /* if all files writed we + * save all minimized css + * to one file all.min.css + */ + if(pCSSFiles_a.length === lCSSFiles_doneCount){ + /* если включена конвертация картинок в base64 + * вызываем её + */ + if(pImgConvertToBase64_b) + base64_images(lAllStyle); + else + fs.writeFile(MinFolder + 'all.min.css', lAllStyle, fileWrited(MinFolder + 'all.min.css')); + } + /* в другом случае - записываем сжатый css файл*/ + else{ + minFileName = MinFolder + path.basename(minFileName); + fs.writeFile(minFileName, final_code, fileWrited(minFileName)); + } + }; + + /* moving thru all elements of css files array */ + for(var i=0;pCSSFiles_a[i];i++){ + console.log('reading file ' + pCSSFiles_a[i]+'...'); + fs.readFile(pCSSFiles_a[i],fileReaded(pCSSFiles_a[i],dataReaded_f)); + } + + return true; +}; + +/* функция сжимает html файлы + * и сохраняет их с именем .min.html + * @pHTMLFiles_a - массим имен html + * файлов, или строка если имя одно + */ +exports.html=function(pHTMLFiles_a){ + 'use strict'; + + /* connecting cleanCSS, + * if we can't find it - + * return false + */ + var htmlMinifier; + try{ + htmlMinifier = require('html-minifier'); + }catch(error){ + console.log('can\'n load html-minifier \n' + + 'to use html-minification you need to install html-minifier\n' + + 'npm install html-minifier\n' + + 'https://github.com/kangax/html-minifier'); + return false; + } + + if(typeof pHTMLFiles_a === "string") + pHTMLFiles_a=[pHTMLFiles_a]; + + var dataReaded_f=function(pFileName, pData){ + console.log('file ' + pFileName + ' readed'); + /*********************************/ + /* сжимаем код через clean-css */ + var html_minify=function(pData){ + /* Сохраняем весь стиль в одну переменную*/ + + var lOptions={ + removeComments: true, + removeCommentsFromCDATA: true, + removeCDATASectionsFromCDATA: true, + collapseWhitespace: true, + collapseBooleanAttributes: true, + removeAttributeQuotes: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + /* оставляем, поскольку у нас + * в элемент fm генерируеться + * таблица файлов + */ + removeEmptyElements: false, + removeOptionalTags: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }; + + + return htmlMinifier.minify(pData,lOptions); + }; + /*********************************/ + var final_code=html_minify(pData); + + var minFileName=pFileName.replace('.html','.min.html'); + + /* adding Min folder to file path */ + minFileName = MinFolder + minFileName; + /* записываем сжатый html файл*/ + fs.writeFile(minFileName, final_code, fileWrited(minFileName)); + }; + + + /* moving thru all elements of css files array */ + for(var i=0;pHTMLFiles_a[i];i++){ + console.log('reading file ' + pHTMLFiles_a[i]+'...'); + fs.readFile(pHTMLFiles_a[i],fileReaded(pHTMLFiles_a[i],dataReaded_f)); + } + + return true; +}; + +/* функция переводит картинки в base64 и записывает в css-файл*/ +function base64_images(pFileContent_s){ + 'use strict'; + var b64img; + try{ + b64img = require('css-b64-images'); + }catch(error){ + console.log('can\'n load clean-css \n' + + 'to use images to base64 convertation you need to install css-base64-images \n' + + 'npm install -g css-b64-images\n' + + 'https://github.com/Filirom1/css-base64-images'); + return false; + } + b64img.fromString(pFileContent_s, '.','', function(err, css){ + console.log('images converted to base64 and saved in css file'); + fs.writeFile(MinFolder + 'all.min.css', css, fileWrited(MinFolder + 'all.min.css')); + }); +} + +/* Функция создаёт асинхроную версию + * для чтения файла + * @pFileName - имя считываемого файла + */ +function fileReaded(pFileName,pCompressFunc){ + "use strict"; + return function(pError,pData){ + /* функция в которую мы попадаем, + * если данные считались + * + * если ошибка - показываем её + * иначе если переданная функция - + * функция запускаем её + */ + if(!pError) + if (pCompressFunc && typeof pCompressFunc==="function") + pCompressFunc(pFileName,pData.toString()); + else console.log(pError); + }; +} + +/* + * Функция вызываеться после записи файла + * и выводит ошибку или сообщает, + * что файл успешно записан + */ +function fileWrited(pFileName){ + "use strict"; + return function(error){ + console.log(error?error:('file '+pFileName+' writed...')); + }; +} \ No newline at end of file