UNPKG

enb

Version:

Faster BEM/BEViS assembler

239 lines (225 loc) 9.32 kB
/** * Level * ===== */ var inherit = require('inherit'); var fs = require('fs'); var Vow = require('vow'); var LevelBuilder = require('./level-builder'); // TODO: Сделать 1-в-1 асинхронный аналог (с точно таким же порядком файлов на выходе). /** * Level — объектная модель уровня переопределения. * @name Level */ module.exports = inherit({ /** * Конструктор. * @param {String} path Путь к уровню переопределения. * @param {Function} [schemeBuilder] */ __constructor: function (path, schemeBuilder) { this._path = path; this.blocks = {}; this._loadPromise = null; this._schemeBuilder = schemeBuilder; }, /** * Загружает из кэша. */ loadFromCache: function (data) { this.blocks = data; this._loadPromise = Vow.fulfill(this); }, /** * Возвращает структуру блоков. * @returns {Object} */ getBlocks: function () { return this.blocks; }, /** * Проверяет наличие блока с указанным именем. * @param blockName * @returns {Boolean} */ hasBlock: function (blockName) { return this.blocks[blockName]; }, /** * Возвращает абсолютный путь к уровню переопределения. * @returns {String} */ getPath: function () { return this._path; }, /** * Обрабатывает файл, добавляет его в необходимое место в структуре. * @param {String} filename * @param {Object} stat node.js Stat * @param {String} parentElementName * @param {String} elementName * @param {String} modName * @private */ _processFile: function (filename, stat, parentElementName, elementName, modName) { var requiredBaseNameWithoutExt = (parentElementName ? parentElementName + '__' : '') + elementName + (modName ? '_' + modName : ''); var baseName = filename.split('/').slice(-1)[0]; var baseNameParts = baseName.split('.'); var baseNameWithoutExt = stat.isDirectory() ? baseNameParts.slice(0, baseNameParts.length - 1).join('.') : baseNameParts[0]; var rl = requiredBaseNameWithoutExt.length; var modVal; var processFile = baseNameWithoutExt.indexOf(requiredBaseNameWithoutExt) === 0 && ( modName ? (rl === baseNameWithoutExt.length) || baseNameWithoutExt.charAt(rl) === '_' : baseNameWithoutExt === requiredBaseNameWithoutExt ); if (!processFile && !modName && !parentElementName) { var baseNameModParts = baseNameWithoutExt.split('_'); if (baseNameModParts.length === 2 && baseNameModParts[0] === requiredBaseNameWithoutExt) { processFile = true; modName = 'view'; modVal = baseNameModParts[1]; } } if (processFile) { var suffix = stat.isDirectory() ? baseNameParts.pop() : baseNameParts.slice(1).join('.'); var fileInfo = { name: baseName, fullname: filename, suffix: suffix, mtime: stat.mtime.getTime(), isDirectory: stat.isDirectory() }; if (fileInfo.isDirectory) { fileInfo.files = filterFiles(fs.readdirSync(filename)).map(function (subFilename) { var subFullname = filename + '/' + subFilename; var subStat = fs.statSync(subFullname); return { name: subFilename, fullname: subFullname, suffix: subFilename.split('.').slice(1).join('.'), mtime: subStat.mtime.getTime(), isDirectory: subStat.isDirectory() }; }); } var blockName = parentElementName || elementName; var block = this.blocks[blockName] || (this.blocks[blockName] = { name: blockName, files: [], dirs: [], elements: {}, mods: {} }); var destElement; if (parentElementName) { destElement = block.elements[elementName] || (block.elements[elementName] = { name: elementName, files: [], dirs: [], mods: {} }); } else { destElement = block; } var collectionKey = fileInfo.isDirectory ? 'dirs' : 'files'; if (modName) { if (!modVal) { if (rl !== baseNameWithoutExt.length) { modVal = baseNameWithoutExt.substr(rl + 1); } else { modVal = ''; } } if (modName === 'view' && modVal === 'view') { modVal = ''; } var mod = destElement.mods[modName] || (destElement.mods[modName] = {}); var modValueFiles = (mod[modVal] || (mod[modVal] = {files: [], dirs: []}))[collectionKey]; modValueFiles.push(fileInfo); } else { destElement[collectionKey].push(fileInfo); } } }, /** * Загружает файлы и директории в папке модификатора. * @param {String} parentElementName * @param {String} elementName * @param {String} modName * @param {String} modDirPath * @private */ _loadMod: function (parentElementName, elementName, modName, modDirPath) { var _this = this; filterFiles(fs.readdirSync(modDirPath)).forEach(function (filename) { var fullname = modDirPath + '/' + filename; var stat = fs.statSync(fullname); _this._processFile(fullname, stat, parentElementName, elementName, modName); }); }, /** * Загружает файлы и директории в папке элемента или блока (если не указан parentElementName). * @param {String} parentElementName * @param {String} elementName * @param {String} elementDirPath * @param {String} containsElements * @private */ _loadElement: function (parentElementName, elementName, elementDirPath, containsElements) { var _this = this; var requiredBaseNameWithoutExt = (parentElementName ? parentElementName + '__' : '') + elementName; filterFiles(fs.readdirSync(elementDirPath)).forEach(function (filename) { var fullname = elementDirPath + '/' + filename; var stat = fs.statSync(fullname); if (stat.isDirectory()) { if (containsElements && filename.substr(0, 2) === '__') { _this._loadElement(elementName, filename.substr(2), fullname, false); } else if (filename.charAt(0) === '_') { _this._loadMod(parentElementName, elementName, filename.substr(1), fullname); } else if (filename.indexOf('.') !== -1 && filename.indexOf(requiredBaseNameWithoutExt + '.') === 0) { _this._processFile(fullname, stat, parentElementName, elementName); } else if (containsElements) { _this._loadElement(elementName, filename, fullname, false); } } else if (stat.isFile()) { _this._processFile(fullname, stat, parentElementName, elementName); } }); }, /** * Загружает уровень перепределения: загружает структуру блоков, элементов и модификаторов. */ load: function () { if (this._loadPromise) { return this._loadPromise; } this._loadPromise = Vow.promise(); var _this = this; if (this._schemeBuilder) { var levelBuilder = new LevelBuilder(); Vow.when(this._schemeBuilder.buildLevel(this._path, levelBuilder)).then(function () { _this.blocks = levelBuilder.getBlocks(); _this._loadPromise.fulfill(_this); }); } else { var path = this._path; filterFiles(fs.readdirSync(path)).forEach(function (blockDir) { var blockDirPath = path + '/' + blockDir; if (fs.statSync(blockDirPath).isDirectory()) { _this._loadElement(null, blockDir, blockDirPath, true); } }); this._loadPromise.fulfill(this); } return this._loadPromise; } }); function filterFiles(filenames) { return filenames.filter(function (filename) { return filename.charAt(0) !== '.'; }); }