From 5040f013d37285c637f750bb34cbba37dc758963 Mon Sep 17 00:00:00 2001 From: andrewblond Date: Fri, 18 Jul 2014 15:38:56 +0400 Subject: [PATCH 1/3] Added async level scanner --- lib/levels/level.js | 222 ++++++++++++++++---------------------------- package.json | 1 + 2 files changed, 81 insertions(+), 142 deletions(-) diff --git a/lib/levels/level.js b/lib/levels/level.js index 41cb3ab..606ac79 100644 --- a/lib/levels/level.js +++ b/lib/levels/level.js @@ -3,11 +3,12 @@ * ===== */ var inherit = require('inherit'); -var fs = require('fs'); +var path = require('path'); var vow = require('vow'); +var naming = require('bem-naming'); +var vfs = require('enb/lib/fs/async-fs'); var LevelBuilder = require('./level-builder'); -// TODO: Сделать 1-в-1 асинхронный аналог (с точно таким же порядком файлов на выходе). /** * Level — объектная модель уровня переопределения. * @name Level @@ -59,136 +60,6 @@ module.exports = inherit({ 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) { - 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 = true; - } - } - 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); - } - }); - }, - /** * Загружает уровень перепределения: загружает структуру блоков, элементов и модификаторов. */ @@ -207,22 +78,89 @@ module.exports = inherit({ deferred.resolve(_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); + scan(this._path, function (fileInfo) { + var dirname = path.basename(path.dirname(fileInfo.fullname)); + var bemname = fileInfo.name.split('.')[0]; + var notation = naming.parse(bemname); + + if (notation && + naming.isBlock(notation) && dirname === notation.block || + naming.isElem(notation) && dirname === '__' + notation.elem || + (naming.isBlockMod(notation) || naming.isElemMod(notation)) && dirname === '_' + notation.modName + ) { + var collectionKey = fileInfo.isDirectory ? 'dirs' : 'files'; + var block = _this.blocks[notation.block] || (_this.blocks[notation.block] = { + name: notation.block, + files: [], + dirs: [], + elements: {}, + mods: {} + }); + var dest = block; + + if (notation.elem) { + dest = block.elements[notation.elem] || (block.elements[notation.elem] = { + name: notation.elem, + files: [], + dirs: [], + mods: {} + }); + } + + if (notation.modName) { + var mod = dest.mods[notation.modName] || (dest.mods[notation.modName] = {}); + var modVals = (mod[notation.modVal] || (mod[notation.modVal] = {files: [], dirs: []})); + + modVals[collectionKey].push(fileInfo); + } else { + dest[collectionKey].push(fileInfo); + } } + }) + .then(function () { + deferred.resolve(_this); }); - deferred.resolve(this); } return promise; } }); -function filterFiles(filenames) { - return filenames.filter(function (filename) { - return filename.charAt(0) !== '.'; - }); +function scan(dirname, callback) { + return vfs.listDir(dirname) + .then(function (dirlist) { + return vow.all(dirlist.filter(function (filename) { + return filename.charAt(0) !== '.'; + }).map(function (basename) { + var filename = path.join(dirname, basename); + + return vfs.stats(filename) + .then(function (stat) { + var isDirectory = stat.isDirectory(); + var suffix = basename.split('.').slice(1).join('.'); + var info = { + name: basename, + fullname: filename, + isDirectory: isDirectory, + suffix: suffix, + mtime: stat.mtime.getTime() + }; + + if (isDirectory) { + return scan(filename, callback) + .then(function (files) { + info.files = files; + + callback(info); + + return info; + }); + } else { + callback(info); + + return info; + } + }); + })); + }); } diff --git a/package.json b/package.json index d4455c9..f17b75c 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "enb": ">= 0.13.0 < 1.0.0" }, "dependencies": { + "bem-naming": "0.1.0", "vow": "0.4.5", "inherit": "2.2.2", "js-yaml": "3.1.0" From c0d189974c163622959efb8f83758dbfcdd5fab1 Mon Sep 17 00:00:00 2001 From: andrewblond Date: Fri, 18 Jul 2014 15:40:27 +0400 Subject: [PATCH 2/3] Updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 495c4d5..e4a7760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ ### Также в релиз вошли следующие изменения +* Интроспекция уровней теперь происходит асинхронно. * Исправлена ошибка в `deps` и `deps-old` технологиях, из-за которой было невозможно выразить булевый модификатор со значением `true` в `deps` формате. * Модуль `vow` обновлён до версии `0.4.5`. * Модуль `inherit` обновлён до версии `2.2.2`. From 6a84ea937276f087b917b12074ef31e7d875eba1 Mon Sep 17 00:00:00 2001 From: andrewblond Date: Fri, 18 Jul 2014 17:35:07 +0400 Subject: [PATCH 3/3] Fixed typo --- lib/levels/level.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/levels/level.js b/lib/levels/level.js index 606ac79..71d12b2 100644 --- a/lib/levels/level.js +++ b/lib/levels/level.js @@ -134,7 +134,7 @@ function scan(dirname, callback) { }).map(function (basename) { var filename = path.join(dirname, basename); - return vfs.stats(filename) + return vfs.stat(filename) .then(function (stat) { var isDirectory = stat.isDirectory(); var suffix = basename.split('.').slice(1).join('.');