UNPKG

page-app

Version:

Builder for rich single-page js apps (frontend)

324 lines (264 loc) 9.23 kB
var sys = require('util'), fs = require('fs.extra'), extend = require('extend'), path = require('path'), csso = require('csso'), UglifyJS = require('uglify-js'); module.exports = function (options) { var settings = options, File = require('./file')(settings); var Folder = function (folderpath, options) { options = extend(true, { app: 'common', combine: true, minify: settings.config.minify }, options); this.options = options; this.options.path = this.options.path || this.options.app; this.path = path.resolve(settings.source, folderpath); this.results = []; this.resultFiles = {}; this.resultErrors = []; this.resultCsErrors = []; }; Folder.prototype.scan = function () { var folder = this, targetName = 'target.' + settings.target, targetReplace = settings.target ? path.resolve(this.path, targetName) : false; //читаем файлы в директории this.files = fs.readdirSync(this.path); //если собирается какой-то не-дефолтный, то if (targetReplace && fs.existsSync(targetReplace)) { //ищем файлы, которые надо будет заменить targetReplace = fs.readdirSync(targetReplace); targetReplace.forEach(function (filename) { var index = folder.files.indexOf(filename); if (index > -1) { //заменяем дефолтные файлы билдовыми folder.files[index] = [targetName, filename]; } }); } this.sortFiles(); }; Folder.prototype.sortFiles = function () { var filesOrder = this.files.indexOf('files.json'); if (filesOrder > -1) { this.files.splice(filesOrder, 1); filesOrder = JSON.parse(fs.readFileSync(path.join(this.path, 'files.json'), {encoding: 'utf8'})); this.files.sort(function (a, b) { var _a = filesOrder.indexOf(a), _b = filesOrder.indexOf(b); if (_a > -1 && _b === -1) { return -1; } if (_a === -1 && _b > -1) { return 1; } if (_a > -1 && _b > -1) { return _a - _b; } if (a === b) { return 0; } return a < b ? -1 : 1; }); } }; Folder.prototype.processFiles = function (recursive, results) { var folder = this, parentPath = recursive ? (typeof(recursive) === 'string' ? recursive : '') : undefined; this.files.forEach(function (filename) { var targetRX = /^target\.([^.]*)$/, targetName, foldername, filepath, recursiveOptions; if (!filename || (folder.options.ignore && folder.options.ignore.indexOf(filename) > -1)) { return; } //если файл надо заменить на билдовый if (filename instanceof Array) { targetName = filename[0]; filename = filename[1]; } //папки для билдов игнорируем if (targetRX.test(filename)) { return; } foldername = parentPath ? path.join(parentPath, filename) : filename; filepath = path.resolve(folder.path, targetName || '', filename); if (fs.statSync(filepath).isDirectory()) { //если это вложенная директория и мы обходим все папки рекурсивно if (recursive) { recursiveOptions = extend({}, folder.options, {ignore: []}); (new Folder(filepath, recursiveOptions)).process(foldername, results || folder.results); } } else { //если это файл - обрабатываем его //и сохраняем в списке файлов самого верхнего родительского Folder (results || folder.results).push(new File(filepath, { path: parentPath || '', parent: folder.options.parent, app: folder.options.app, onlyBinaries: folder.options.onlyBinaries, testDeploy: settings.test })); } }); if (results === undefined) { //если это родительский фолдер – значит, //все внутренние файлы и папки обработаны //и можно сохранять результаты this.joinResults(); } }; Folder.prototype.process = function (recursive, results) { this.scan(); this.processFiles(recursive, results); return { content: this.resultContent, files: this.resultFiles, errors: this.resultErrors, csErrors: this.resultCsErrors }; }; Folder.prototype.joinResults = function () { var folder = this, results = { css: '', cssIE: '', js: '', jsIE: '', jsTest: '', jsTestCases: '', modules: [] }, resultFiles = { css: [], cssIE: [], js: [], jsIE: [], jsTest: [], jsTestCases: [] }; this.results.forEach(function (file) { var destination = path.resolve(settings.www, folder.options.path, file.params.path, folder.options.parent || ''), resultType, type; //бинарные файлы просто записываем без изменений if (file.params.binary && file.params.deploy) { fs.mkdirpSync(destination); fs.writeFileSync(path.resolve(destination, file.basename), file.content); return; } if (file.params.deploy) { if (file.params.type === 'js') { resultType = type = 'js'; } if (file.params.type === 'css') { resultType = type = 'css'; } if (file.modules && file.modules.length > 0) { // пробрасываем найденные в файле модули results.modules = results.modules.concat(file.modules); } if (resultType) { if (file.params.test) { resultType += 'Test'; if (!file.params.vendor) { resultType += 'Cases'; } } else if (file.params.ie) { resultType += 'IE'; } //"чужие" файлы (библиотеки, фреймворки и т.п.) //добавляем в список файлов, чтоб подключить на страничку if (file.params.vendor) { /* //use *.min versions of vendor files if (file.params.type === 'js') { file.content = folder.minifyJS(file.content); } if (file.params.type === 'css') { file.content = folder.minifyCSS(file.content); } */ resultFiles[resultType].push(folder.writeContent(type, false, file.content, file.basename)); } else { results[resultType] += file.content; } } } if (file.errors) { folder.resultErrors = folder.resultErrors.concat(file.errors); } if (file.csErrors) { folder.resultCsErrors = folder.resultCsErrors.concat(file.csErrors); } }); //если файлы надо склеить в один, то делаем это и сохраняем объединённый контент //для каждого типа (один js, один css и ещё по одному – для ИЕ) if (this.options.combine) { if (results.js) { results.js = this.minifyJS(results.js); resultFiles.js.push(this.writeContent('js', results.js)); } if (results.css) { results.css = this.minifyCSS(results.css); resultFiles.css.push(this.writeContent('css', results.css)); } if (results.jsIE) { results.jsIE = this.minifyJS(results.jsIE); resultFiles.jsIE.push(this.writeContent('js', true, results.jsIE)); } if (results.cssIE) { results.cssIE = this.minifyCSS(results.cssIE); resultFiles.cssIE.push(this.writeContent('css', true, results.cssIE)); } if (results.jsTest) { resultFiles.jsTest.push(this.writeContent('js', results.jsTest, '_test.js')); } if (results.jsTestCases) { resultFiles.jsTestCases.push(this.writeContent('js', results.jsTestCases, '_testCases.js')); } } this.resultContent = results; this.resultFiles = resultFiles; }; Folder.prototype.minifyCSS = function (content) { if (this.options.minify) { return csso.justDoIt(content); } return content; }; Folder.prototype.minifyJS = function (content) { if (this.options.minify) { return UglifyJS.minify(content, { fromString: true }).code; } return content; }; Folder.prototype.writeContent = function (type, isIE, content, name) { if (typeof(isIE) !== 'boolean') { name = content; content = isIE; isIE = false; } //сохраняем контент в соответствующий файл //если имя не задано, то оно создаётся по принципу //_appname.ie-version.js var basePath = path.resolve(settings.www, this.options.path), destination = path.resolve(basePath, type), filename = name || '_' + this.options.app + (isIE ? '.ie' : '') + '-' + this.options.version + '.' + type, filepath = path.resolve(destination, filename); fs.mkdirpSync(destination); fs.writeFileSync(filepath, content); return (this.options.host || '') + type + '/' + filename; }; return { Folder: Folder, File: File }; };