diff --git a/lib/deps/deps-resolver.js b/lib/deps/deps-resolver.js deleted file mode 100644 index bd0c9f7..0000000 --- a/lib/deps/deps-resolver.js +++ /dev/null @@ -1,458 +0,0 @@ -var inherit = require('inherit'), - vm = require('vm'), - vow = require('vow'), - vfs = require('enb/lib/fs/async-fs'), - yaml = require('js-yaml'); - -function DepsError(message) { - this.message = message; - Error.captureStackTrace(this, DepsError); -} -DepsError.prototype = Object.create(Error.prototype); -DepsError.prototype.name = 'Deps error'; - -module.exports = inherit(/** @lends DepsResolver.prototype */{ - /** - * @constructs DepsResolver - * @classdesc Dependencies resolver. - * - * @param {Level[]} levels - */ - __constructor: function (introspection) { - this.introspection = introspection; - this.declarations = []; - this.resolved = {}; - this.declarationIndex = {}; - }, - - /** - * Normalizes shortcut to verbose array. - * - * @param {String|Object} dep - * @param {String} blockName - * @param {String} elemName - * @returns {Array} - */ - normalizeDep: function (dep, blockName, elemName) { - if (typeof dep === 'string') { - return [{ name: dep }]; - } else { - var res = []; - if (!dep || !(dep instanceof Object)) { - throw new DepsError('Deps shoud be instance of Object or String'); - } - if (dep.elem) { - (Array.isArray(dep.elem) ? dep.elem : [dep.elem]).forEach(function (elem) { - if (dep.mods) { - var elemModObj = {}; - if (Array.isArray(dep.mods)) { - dep.mods.forEach(function (modName) { - elemModObj[modName] = true; - }); - } else { - elemModObj = dep.mods || {}; - } - Object.keys(elemModObj).forEach(function (modName) { - var modVals = elemModObj[modName]; - if (!Array.isArray(modVals)) { - modVals = [modVals]; - } - res = res.concat(modVals.map(function (modVal) { - return { name: dep.block || blockName, elem: elem, modName: modName, modVal: modVal }; - })); - }); - } else if (dep.mod) { - res.push({ name: dep.block || blockName, elem: elem, modName: dep.mod, modVal: dep.val }); - } else { - res.push({ name: dep.block || blockName, elem: elem }); - } - }); - } else if (dep.mod || dep.mods || dep.elems) { - if (dep.mod) { - if (elemName) { - res.push( - { name: dep.block || blockName, elem: elemName, modName: dep.mod }, - { name: dep.block || blockName, elem: elemName, modName: dep.mod, modVal: dep.val } - ); - } else { - res.push( - { name: dep.block || blockName, modName: dep.mod }, - { name: dep.block || blockName, modName: dep.mod, modVal: dep.val } - ); - } - } - var modObj = {}; - if (Array.isArray(dep.mods)) { - dep.mods.forEach(function (modName) { - modObj[modName] = true; - }); - } else { - modObj = dep.mods || {}; - } - Object.keys(modObj).forEach(function (modName) { - var modVals = modObj[modName]; - - if (!Array.isArray(modVals)) { - modVals = [modVals]; - } - res = res.concat(modVals.map(function (modVal) { - if (elemName && !dep.block && !dep.elem) { - return { name: dep.block || blockName, elem: elemName, modName: modName, modVal: modVal }; - } else { - return { name: dep.block || blockName, modName: modName, modVal: modVal }; - } - })); - }); - if (dep.elems) { - res.push({ name: dep.block || blockName }); - var elems = dep.elems || []; - if (!Array.isArray(elems)) { - elems = [elems]; - } - elems.forEach(function (elem) { - if (typeof elem === 'object') { - res.push({ name: dep.block || blockName, elem: elem.elem }); - Object.keys(elem.mods || {}).forEach(function (modName) { - var modVals = elem.mods[modName]; - if (!Array.isArray(modVals)) { - modVals = [modVals]; - } - res = res.concat(modVals.map(function (modVal) { - return { - name: dep.block || blockName, - elem: elem.elem, - modName: modName, - modVal: modVal - }; - })); - }); - } else { - res.push({ name: dep.block || blockName, elem: elem }); - } - }); - } - } else { - res = [{ name: dep.block || blockName }]; - } - if (dep.required) { - res.forEach(function (subDep) { - subDep.required = true; - }); - } - return res; - } - }, - - /** - * Normalizes declaration with shortcuts to verbose array. - * - * @param {String|Object|Array} deps - * @param {String} [blockName] - * @param {String} [elemName] - * @returns {Array} - */ - normalizeDeps: function (deps, blockName, elemName) { - if (Array.isArray(deps)) { - var result = []; - for (var i = 0, l = deps.length; i < l; i++) { - result = result.concat(this.normalizeDep(deps[i], blockName, elemName)); - } - return result; - } else { - return this.normalizeDep(deps, blockName, elemName); - } - }, - - /** - * Returns dependencies by {@link Levels}. - * - * @param {Object} decl - * @returns {{mustDeps: Array, shouldDeps: Array}} - */ - getDeps: function (decl) { - var _this = this, - mustDecls, - files, - introspection = this.introspection, - entity = { block: decl.name }; - - decl.elem && (entity.elem = decl.elem); - decl.modName && (entity.modName = decl.modName); - decl.modVal && (entity.modVal = decl.modVal); - - files = introspection.getFilesByEntity(entity).filter(function (file) { - var suffix = file.tech; - - return suffix === 'deps.js' || suffix === 'deps.yaml'; - }); - - var mustDepIndex = {}, - shouldDepIndex = {}; - mustDepIndex[declKey(decl)] = true; - var mustDeps = []; - if (decl.modName) { - if (decl.elem) { - mustDecls = [ - { name: decl.name, elem: decl.elem, fake: true } - ]; - if (decl.modVal) { - mustDecls.push({ name: decl.name, elem: decl.elem, modName: decl.modName }); - } - } else { - mustDecls = [ - { name: decl.name, fake: true } - ]; - if (decl.modVal) { - mustDecls.push({ name: decl.name, modName: decl.modName }); - } - } - mustDecls.forEach(function (mustDecl) { - mustDecl.key = declKey(mustDecl); - mustDepIndex[mustDecl.key] = true; - mustDeps.push(mustDecl); - }); - } - var shouldDeps = []; - - function keepWorking(file) { - var filename = file.path; - - return vfs.read(filename, 'utf8').then(function (depContent) { - var suffix = file.tech; - - if (suffix === 'deps.js') { - var depData; - try { - depData = vm.runInThisContext(depContent); - } catch (e) { - throw new Error('Syntax error in file "' + filename + '": ' + e.message); - } - depData = Array.isArray(depData) ? depData : [depData]; - depData.forEach(function (dep) { - if (!dep) { - return; - } - - if (!dep.tech) { - if (dep.mustDeps) { - _this.normalizeDeps(dep.mustDeps, decl.name, decl.elem).forEach(function (nd) { - var key = declKey(nd); - if (!mustDepIndex[key]) { - mustDepIndex[key] = true; - nd.key = key; - mustDeps.push(nd); - } - }); - } - if (dep.shouldDeps) { - _this.normalizeDeps(dep.shouldDeps, decl.name, decl.elem).forEach(function (nd) { - var key = declKey(nd); - if (!shouldDepIndex[key]) { - shouldDepIndex[key] = true; - nd.key = key; - shouldDeps.push(nd); - } - }); - } - if (dep.noDeps) { - _this.normalizeDeps(dep.noDeps, decl.name, decl.elem).forEach(function (nd) { - var key = declKey(nd); - nd.key = key; - removeFromDeps(nd, mustDepIndex, mustDeps); - removeFromDeps(nd, shouldDepIndex, shouldDeps); - }); - } - } - }); - } else if (suffix === 'deps.yaml') { - var depYamlStructure = yaml.safeLoad(depContent, { - filename: filename, - strict: true - }); - if (!Array.isArray(depYamlStructure)) { - throw new Error('Invalid yaml deps structure at: ' + filename); - } - _this.normalizeDeps(depYamlStructure, decl.name, decl.elem).forEach(function (nd) { - var key = declKey(nd), - index, - depList; - if (nd.required) { - index = mustDepIndex; - depList = mustDeps; - } else { - index = shouldDepIndex; - depList = shouldDeps; - } - if (!index[key]) { - index[key] = true; - nd.key = key; - depList.push(nd); - } - }); - } - if (files.length > 0) { - return keepWorking(files.shift()); - } else { - return null; - } - }).fail(function (err) { - if (err instanceof DepsError) { - err.message += ' in file "' + filename + '"'; - } - throw err; - }); - } - - function removeFromDeps(decl, index, list) { - if (index[decl.key]) { - for (var i = 0, l = list.length; i < l; i++) { - if (list[i].key === decl.key) { - return list.splice(i, 1); - } - } - } else { - index[decl.key] = true; - } - return null; - } - - var result = { mustDeps: mustDeps, shouldDeps: shouldDeps }; - - if (files.length > 0) { - return keepWorking(files.shift()).then(function () { - return result; - }); - } else { - return vow.fulfill(result); - } - }, - - /** - * Adds declaration to resolver. - * - * @param {Object} decl - * @returns {Promise} - */ - addDecl: function (decl) { - var _this = this, - key = declKey(decl); - if (this.declarationIndex[key]) { - return null; - } - this.declarations.push(decl); - this.declarationIndex[key] = decl; - return this.getDeps(decl).then(function (deps) { - decl.key = key; - decl.deps = {}; - decl.depCount = 0; - return _this.addDecls(deps.mustDeps, function (dep) { - decl.deps[dep.key] = dep.fake ? 'fake' : true; - decl.depCount++; - }).then(function () { - return _this.addDecls(deps.shouldDeps); - }); - }); - }, - - /** - * Adds declarations to resolver. - * - * @param {Array} decls - * @returns {Promise} - * @param {Function} [preCallback] - */ - addDecls: function (decls, preCallback) { - var promise = vow.fulfill(), - _this = this; - decls.forEach(function (decl) { - promise = promise.then(function () { - if (preCallback) { - preCallback(decl); - } - return _this.addDecl(decl); - }); - }); - return promise; - }, - - /** - * Supplements declaration of BEM entities. Returns them in dependencies order. - * - * @returns {Array} - */ - resolve: function () { - var items = this.declarations.slice(0), - result = [], - hasChanges = true, - newItems; - while (hasChanges) { - var i, j, l; - - newItems = []; - hasChanges = false; - - for (i = 0, l = items.length; i < l; i++) { - var decl = items[i]; - if (decl.depCount === 0) { - hasChanges = true; - for (j = 0; j < l; j++) { - var subDecl = items[j], - subDeclKey = decl.key; - - if (subDecl.deps[subDeclKey]) { - delete subDecl.deps[subDeclKey]; - subDecl.depCount--; - } - } - var item = { - block: decl.name - }; - if (decl.elem) { - item.elem = decl.elem; - } - if (decl.modName) { - item.mod = decl.modName; - if (decl.hasOwnProperty('modVal')) { - item.val = decl.modVal; - } - } - result.push(item); - } else { - newItems.push(decl); - } - } - items = newItems; - - if (!hasChanges && items.length) { - for (i = 0, l = items.length; i < l; i++) { - var curDecl = items[i], - curDeps = curDecl.deps, - declKeys = Object.keys(curDeps); - - for (j = 0; j < declKeys.length; j++) { - var declKey = declKeys[j]; - - if (curDeps[declKey] === 'fake') { - delete curDeps[declKey]; - curDecl.depCount--; - - hasChanges = true; - } - } - } - } - } - if (items.length) { - var errorMessage = items.map(function (item) { - return item.key + ' <- ' + Object.keys(item.deps).join(', '); - }); - throw Error('Unresolved deps: \n' + errorMessage.join('\n')); - } - return result; - } -}); - -function declKey(decl) { - return decl.name + (decl.elem ? '__' + decl.elem : '') + - (decl.modName ? '_' + decl.modName + (decl.modVal ? '_' + decl.modVal : '') : ''); -} diff --git a/package.json b/package.json index 78d71e2..9513b2b 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "enb-async-require": "1.0.1", "enb-require-or-eval": "1.0.2", "inherit": "2.2.3", - "js-yaml": "3.5.2", "lodash": "4.11.1", "vow": "0.4.12" }, @@ -65,8 +64,7 @@ "mock-enb": "0.3.2", "mock-fs": "3.8.0", "must": "0.13.1", - "proxyquire": "1.7.4", - "seedrandom": "2.4.2" + "proxyquire": "1.7.4" }, "main": "index.js", "files": [ @@ -79,6 +77,7 @@ "test": "npm run lint && npm run unit", "lint": "jshint . && jscs .", "unit": "mocha -R spec test/unit test/techs", + "generated-test": "mocha -R spec test/generated/**", "bench": "npm run bench-deps && matcha benchmark/*.js", "bench-deps": "cd benchmark/fixtures && bower i", "cover": "istanbul cover _mocha test/unit test/techs",