Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'techs-v2'

v0.6.0
  • Loading branch information...
commit 6af00b350adcd6c87d0732b9b7ee58b84b0f6f1d 2 parents dd1f7b4 + 2a5c5e6
@scf2k scf2k authored
Showing with 3,146 additions and 5,298 deletions.
  1. +4 −0 ChangeLog.md
  2. +11 −0 lib/base-server.js
  3. +2 −0  lib/coa.js
  4. +17 −2 lib/commands/build.js
  5. +11 −0 lib/default-arch.js
  6. +1 −0  lib/index.js
  7. +386 −12 lib/level.js
  8. +9 −0 lib/logger.js
  9. +20 −17 lib/nodes/build.js
  10. +14 −6 lib/nodes/bundle.js
  11. +5 −3 lib/nodes/level.js
  12. +33 −0 lib/nodes/target.js
  13. +1 −0  lib/nodes/workers/borschik.js
  14. +125 −0 lib/tech/index.js
  15. +37 −140 lib/{tech.js → tech/v1.js}
  16. +658 −0 lib/tech/v2.js
  17. +1 −1  lib/techs/bemdecl.js.js
  18. +0 −1  lib/techs/deps.js.js
  19. +90 −0 lib/techs/v2/bemdecl.js.js
  20. +34 −0 lib/techs/v2/css.js
  21. +532 −0 lib/techs/v2/deps.js.js
  22. +55 −0 lib/techs/v2/ie.css.js
  23. +18 −0 lib/techs/v2/ie6.css.js
  24. +1 −0  lib/techs/v2/ie7.css.js
  25. +1 −0  lib/techs/v2/ie8.css.js
  26. +1 −0  lib/techs/v2/ie9.css.js
  27. +16 −0 lib/techs/v2/js-i.js
  28. +16 −0 lib/techs/v2/js.js
  29. +90 −1 lib/util.js
  30. +2 −2 npm-shrinkwrap.json
  31. +2 −2 package.json
  32. +14 −0 test/data/make/project/.bem/make.js
  33. +1 −1  test/data/make/project/bem-bl
  34. +2 −4 test/data/make/project/pages-with-merged/.bem/level.js
  35. +9 −5 test/data/make/project/pages/.bem/level.js
  36. +0 −12 test/data/make/reference-result/.bem/cache/pages-with-merged/page1/page1.deps.js.meta.js
  37. +0 −10 test/data/make/reference-result/.bem/cache/pages-with-merged/page2/page2.deps.js.meta.js
  38. +0 −9 test/data/make/reference-result/.bem/cache/pages/client/client.bemhtml.meta.js
  39. +0 −6 test/data/make/reference-result/.bem/cache/pages/client/client.css.meta.js
  40. +0 −13 test/data/make/reference-result/.bem/cache/pages/client/client.deps.js.meta.js
  41. +0 −19 test/data/make/reference-result/.bem/cache/pages/client/client.i18n.js.meta.js
  42. +0 −1  test/data/make/reference-result/.bem/cache/pages/client/client.i18n.meta.js
  43. +0 −3  test/data/make/reference-result/.bem/cache/pages/client/client.ie.css.meta.js
  44. +0 −9 test/data/make/reference-result/.bem/cache/pages/example/example.bemhtml.meta.js
  45. +0 −6 test/data/make/reference-result/.bem/cache/pages/example/example.css.meta.js
  46. +0 −12 test/data/make/reference-result/.bem/cache/pages/example/example.deps.js.meta.js
  47. +0 −18 test/data/make/reference-result/.bem/cache/pages/example/example.i18n.js.meta.js
  48. +0 −1  test/data/make/reference-result/.bem/cache/pages/example/example.i18n.meta.js
  49. +0 −3  test/data/make/reference-result/.bem/cache/pages/example/example.ie.css.meta.js
  50. +15 −7 test/data/make/reference-result/pages-with-merged/merged/merged.deps.js
  51. +15 −7 test/data/make/reference-result/pages-with-merged/page1/page1.deps.js
  52. +15 −7 test/data/make/reference-result/pages-with-merged/page2/page2.deps.js
  53. +0 −701 test/data/make/reference-result/pages/client/__profile/_client__profile.bemhtml.js
  54. +86 −28 test/data/make/reference-result/pages/client/__profile/_client__profile.en.js
  55. +86 −28 test/data/make/reference-result/pages/client/__profile/_client__profile.js
  56. +86 −28 test/data/make/reference-result/pages/client/__profile/_client__profile.ru.js
  57. +0 −701 test/data/make/reference-result/pages/client/__profile/client__profile.bemhtml.js
  58. +15 −7 test/data/make/reference-result/pages/client/__profile/client__profile.deps.js
  59. +1 −1  test/data/make/reference-result/pages/client/__profile/client__profile.html
  60. +0 −819 test/data/make/reference-result/pages/client/_client.bemhtml.js
  61. +86 −28 test/data/make/reference-result/pages/client/_client.en.js
  62. +86 −28 test/data/make/reference-result/pages/client/_client.js
  63. +86 −28 test/data/make/reference-result/pages/client/_client.ru.js
  64. +0 −819 test/data/make/reference-result/pages/client/client.bemhtml.js
  65. +15 −7 test/data/make/reference-result/pages/client/client.deps.js
  66. +1 −1  test/data/make/reference-result/pages/client/client.html
  67. +0 −819 test/data/make/reference-result/pages/example/_example.bemhtml.js
  68. +86 −28 test/data/make/reference-result/pages/example/_example.en.js
  69. +86 −28 test/data/make/reference-result/pages/example/_example.js
  70. +86 −28 test/data/make/reference-result/pages/example/_example.ru.js
  71. +0 −819 test/data/make/reference-result/pages/example/example.bemhtml.js
  72. +15 −7 test/data/make/reference-result/pages/example/example.deps.js
  73. +1 −1  test/data/make/reference-result/pages/example/example.html
  74. +2 −0  test/level-nested.js
  75. +2 −0  test/level-simple.js
  76. +53 −1 test/level.js
  77. +2 −2 test/tech.js
  78. +1 −1  test/util.js
View
4 ChangeLog.md
@@ -1,5 +1,9 @@
bem-tools changelog
===================
+11.06.2013, Version 0.6.0 (stable)
+----------------------------------
+
+- speed optimizations with techs API V2
30.05.2013, Version 0.5.33 (stable)
----------------------------------
View
11 lib/base-server.js
@@ -9,6 +9,7 @@ var INHERIT = require('inherit'),
PATH = require('./path'),
MAKE = require('./make'),
LOGGER = require('./logger'),
+ LEVEL = require('./level')
U = require('./util');
var defaultDocument = 'index.html';
@@ -141,6 +142,8 @@ exports.Server = INHERIT({
}
},
+ _targetsInProcess: 0,
+
createRequestHandler: function(root, runner) {
var _this = this;
@@ -167,10 +170,18 @@ exports.Server = INHERIT({
.then(function(id) {
if (!id) return;
+ _this._targetsInProcess++;
+
// found, run build
LOGGER.fverbose('*** node found, building "%s"', id);
+ LOGGER.fdebug('targets: %s', _this._targetsInProcess);
LOGGER.time('[t] Build total for "%s"', id);
return runner.process(id).fin(function() {
+ _this._targetsInProcess--;
+
+ if (_this._targetsInProcess === 0) LEVEL.resetLevelsCache();
+
+ LOGGER.fdebug('targets: %s', _this._targetsInProcess);
LOGGER.timeEnd('[t] Build total for "%s"', id);
});
})
View
2  lib/coa.js
@@ -2,6 +2,8 @@ var Q = require('q'),
CP = require('child_process'),
PATH = require('./path');
+Q.longStackJumpLimit = 0;
+
module.exports = require('coa').Cmd()
.name(PATH.basename(process.argv[1]))
.title(['Tools to work with files written using the BEM-method.', '' +
View
19 lib/commands/build.js
@@ -1,6 +1,7 @@
var Q = require('q'),
PATH = require('../path'),
- createLevel = require('../level').createLevel,
+ LEVEL = require('../level'),
+ createLevel = LEVEL.createLevel,
Context = require('../context').Context,
U = require('../util');
@@ -17,6 +18,15 @@ module.exports = function() {
.req()
.end()
.opt()
+ .name('forceCache').title('Force level cache')
+ .long('force-cache')
+ .flag()
+ .def(false)
+ .val(function(force) {
+ if (force) LEVEL.setCachePolicy(true);
+ })
+ .end()
+ .opt()
.name('level').short('l').long('level')
.title('override level, can be used many times')
.val(function (l) {
@@ -64,6 +74,11 @@ module.exports = function() {
.name('val').short('v').long('val')
.title('modifier value')
.end()
+ .opt()
+ .name('force').title('Force build')
+ .long('force')
+ .flag()
+ .end()
.act(function(opts, args) {
if (opts.outputLevel) {
@@ -87,7 +102,7 @@ module.exports = function() {
return context.getTech(techIdent)
.buildByDecl(opts.declaration, context.getLevels(),
- PATH.resolve(opts.outputDir, opts.outputName));
+ PATH.resolve(opts.outputDir, opts.outputName), opts);
})).get(0);
View
11 lib/default-arch.js
@@ -5,6 +5,7 @@ var INHERIT = require('inherit'),
LOGGER = require('./logger'),
Q = require('qq'),
registry = require('./nodesregistry'),
+ level = require('./level'),
node = require('./nodes/node'),
levelNodes = require('./nodes/level'),
@@ -22,6 +23,9 @@ registry.decl('Arch', {
this.arch = arch;
this.root = opts.root;
this.opts = opts;
+
+ var policy = this.getLevelCachePolicy();
+ level.setCachePolicy(policy.cache, policy.except);
},
bundlesLevelsRegexp: /^(pages.*|bundles.*)/i,
@@ -33,6 +37,13 @@ registry.decl('Arch', {
return this.libraries;
},
+ getLevelCachePolicy: function() {
+ return {
+ cache: false,
+ except: []
+ }
+ },
+
alterArch: function() {
var _this = this;
View
1  lib/index.js
@@ -10,6 +10,7 @@ exports.nodes = require('./nodes');
exports.Context = require('./context').Context;
exports.Tech = TECH.Tech;
+exports.TechV2 = TECH.TechV2;
exports.getTechClass = TECH.getTechClass;
exports.createTech = TECH.createTech;
View
398 lib/level.js
@@ -1,7 +1,10 @@
-var PATH = require('./path'),
+var Q = require('q'),
+ PATH = require('./path'),
+ FS = require('fs'),
INHERIT = require('inherit'),
createTech = require('./tech').createTech,
bemUtil = require('./util'),
+ LOGGER = require('./logger'),
isRequireable = bemUtil.isRequireable,
getLevelClass = function(path, optional) {
@@ -12,20 +15,48 @@ var PATH = require('./path'),
requireLevel = function(path) {
return bemUtil.requireWrapper(require)(path, true);
- };
+ },
+
+ levelCache = {},
+ useCache = false,
+ exceptLevels = [],
+
+ allRe = /(?:^([^_.\/]+)\/__([^_.\/]+)\/(?:_([^_.\/]+)\/\1__\2_\3(?:_([^_.\/]+))?|\1__\2)(.*?)$|^([^_.\/]+)\/(?:(?:\6)|(?:_([^_.\/]+)\/\6_\7(?:_([^_.\/]+))?))(.*?)$)/,
+ elemAllRe = /^([^_.\/]+)\/__([^_.\/]+)\/(?:_([^_.\/]+)\/\1__\2_\3(?:_([^_.\/]+))?|\1__\2)(.*?)$/,
+ blockAllRe = /^([^_.\/]+)\/(?:(?:\1)|(?:_([^_.\/]+)\/\1_\2(?:_([^_.\/]+))?))(.*?)$/;
/**
* Create level object from path on filesystem.
*
- * @param {String} path Path to level directory.
+ * @param {String | Object} level Path to level directory.
* @return {Level} Level object.
*/
-exports.createLevel = function(path) {
+exports.createLevel = function(level) {
// NOTE: в директории .bem внутри уровня переопределения
// лежит модуль-конфиг для уровня переопределения
- return new (getLevelClass(PATH.resolve(path, '.bem', 'level.js'), true))(path);
+ var path = level.path || level;
+
+ if (levelCache[path]) return levelCache[path];
+ level = new (getLevelClass(PATH.resolve(path, '.bem', 'level.js'), true))(level);
+ levelCache[path] = level;
+
+ return level;
};
+exports.setCachePolicy = function(useCacheByDefault, except) {
+ useCache = useCacheByDefault;
+ exceptLevels = except || [];
+};
+
+exports.resetLevelsCache = function(all) {
+ for(var l in levelCache) {
+ var level = levelCache[l];
+
+ if (!level.cache || all) level.files = null;
+ }
+};
+
+
var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
/**
@@ -33,12 +64,21 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
*
* @class Level base class.
* @constructs
- * @param {String} path Level directory path.
+ * @param {String | Object} path Level directory path.
*/
__constructor: function(path) {
- this.dir = PATH.resolve(path);
- // NOTE: keep this.path for backwards compatability
+ this.dir = PATH.resolve(path.path || path);
+ path = PATH.relative(this.root, this.dir);
+ // NOTE: keep this.path for backwards compatibility
this.path = this.bemDir = PATH.join(this.dir, '.bem');
+ this.cache = useCache;
+ for(var e in exceptLevels) {
+ var except = exceptLevels[e];
+ if (path.substr(0, except.length) === except) {
+ this.cache = !this.cache;
+ break;
+ }
+ }
// NOTE: tech modules cache
this._techsCache = {};
@@ -59,7 +99,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
* @return {Object} Tech module definitions
*/
getTechs: function() {
- // NOTE: this.techs if for backwards compatability with legacy level configs
+ // NOTE: this.techs is for backwards compatibility with legacy level configs
return this.techs || {};
},
@@ -141,12 +181,17 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
return techPath;
}
- // Trying absolute of relative-withot-dot path
+ // Trying absolute of relative-without-dot path
if(isRequireable(techPath)) {
return techPath;
}
- throw new Error("Tech module with path '" + techPath + "' not found on require search paths");
+
+ try {
+ return require.resolve('./' + PATH.join('./techs', techPath));
+ } catch (err) {
+ throw new Error("Tech module with path '" + techPath + "' not found on require search paths");
+ }
},
/**
@@ -216,6 +261,10 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
return this.getTech(tech).getPath(prefix);
},
+ getPaths: function(prefix, tech) {
+ return (typeof tech === 'string'? this.getTech(tech): tech).getPaths(prefix);
+ },
+
/**
* Construct absolute path to tech file / directory from
* BEM entity object and tech name.
@@ -248,6 +297,48 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
return this.getPath(this.getRelByObj(item), tech);
},
+ getFileByObjIfExists: function(item, tech) {
+ if (this.files) {
+ var blocks = this.files.tree,
+ block = blocks[item.block];
+
+ if (!block) return [];
+
+ if (item.mod && !item.elem) {
+ block = block.mods[item.mod];
+ if (block && item.val) block = block.vals[item.val];
+
+ } else if (item.elem) {
+ block = block.elems[item.elem];
+ if (block && item.mod) {
+ block = block.mods[item.mod];
+ if (block && item.val) block = block.vals[item.val];
+ }
+ }
+
+
+ var files = block? block.files: null;
+
+ if (!files || files.length === 0) return [];
+
+ var suffixes = tech.getSuffixes(),
+ res = [];
+
+ for(var i = 0; i < suffixes.length; i++) {
+ var suffix = suffixes[i],
+ filesBySuffix = files[suffix];
+
+ if (filesBySuffix) res = res.concat(filesBySuffix);
+
+ }
+
+
+ return res;
+ }
+
+ return res;
+ },
+
/**
* Get absolute path prefix on the filesystem to specified
* BEM entity described as an object with special properties.
@@ -424,7 +515,8 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
* @return {String[]} Array of matchers names.
*/
matchOrder: function() {
- return ['elem-mod-val', 'elem-mod', 'block-mod-val',
+
+ return ['elem-all', 'block-all', 'elem-mod-val', 'elem-mod', 'block-mod-val',
'block-mod', 'elem', 'block'];
},
@@ -502,6 +594,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
'match-block': function(path) {
var match = new RegExp(['^(' + this.matchRe() + ')',
'\\1(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
if (!match) return false;
return {
block: match[1],
@@ -522,6 +615,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
match = new RegExp(['^(' + m + ')',
'_(' + m + ')',
'\\1_\\2(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
if (!match) return false;
return {
block: match[1],
@@ -543,6 +637,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
match = new RegExp(['^(' + m + ')',
'_(' + m + ')',
'\\1_\\2_(' + m + ')(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
if (!match) return false;
return {
block: match[1],
@@ -552,6 +647,33 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
};
},
+ 'match-block-all': function(path) {
+ var match = blockAllRe.exec(path);
+ if (!match) return false;
+
+ var res = {
+ block: match[1]
+ };
+
+ if (match[2]) {
+ res.mod = match[2];
+
+ if (match[3]) res.val = match[3];
+ }
+
+ if (match[4]) res.suffix = match[4];
+
+ return res;
+ },
+
+ 'get-block-all': function() {
+
+ },
+
+ 'get-elem-all': function() {
+
+ },
+
/**
* Match if specified path represents element entity.
*
@@ -565,6 +687,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
match = new RegExp(['^(' + m + ')',
'__(' + m + ')',
'\\1__\\2(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
if (!match) return false;
return {
block: match[1],
@@ -587,6 +710,8 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
'__(' + m + ')',
'_(' + m + ')',
'\\1__\\2_\\3(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
+
if (!match) return false;
return {
block: match[1],
@@ -610,6 +735,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
'__(' + m + ')',
'_(' + m + ')',
'\\1__\\2_\\3_(' + m + ')(.*?)$'].join(PATH.dirSepRe)).exec(path);
+
if (!match) return false;
return {
block: match[1],
@@ -620,6 +746,52 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
};
},
+ 'match-elem-all': function(path) {
+ var match = elemAllRe.exec(path);
+ if (!match) return false;
+
+ var res = {
+ block: match[1],
+ elem: match[2]
+ };
+
+ if (match[3]) res.mod = match[3];
+ if (match[4]) res.val = match[4];
+ if (match[5]) res.suffix = match[5];
+
+ return res;
+ },
+
+ 'match-all': function(path) {
+ var match = allRe.exec(path);
+ if (!match) return false;
+
+ var res = {};
+
+ if (match[1]) {
+ res.block = match[1];
+ res.elem = match[2];
+
+ if (match[3]) res.mod = match[3];
+ if (match[4]) res.val = match[4];
+ if (match[5]) res.suffix = match[5];
+ } else if (match[6]) {
+
+ res.block = match[6];
+
+ if (match[7]) {
+ res.mod = match[7];
+
+ if (match[8]) res.val = match[8];
+ }
+
+ if (match[9]) res.suffix = match[9];
+ }
+
+
+ return res;
+ },
+
/**
* Get declaration for block.
*
@@ -668,6 +840,199 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
},
+ scanFiles: function(force) {
+ var list = {},
+ blocks = {},
+ flat = [],
+ files = {
+ files: list,
+ tree: blocks,
+ blocks: flat
+ },
+ items = {
+ push: function(file, item) {
+ file.suffix = item.suffix[0] === '.'?item.suffix.substr(1):item.suffix;
+ (list[file.suffix] || (list[file.suffix] = [])).push(file);
+ flat.push(item);
+
+ var block = blocks[item.block] || (blocks[item.block] = {elems: {}, mods: {}, files: {}});
+ if (item.mod && !item.elem) {
+ block = block.mods[item.mod] || (block.mods[item.mod] = {vals: {}, files: {}});
+
+ if (item.val) block = block.vals[item.val] || (block.vals[item.val] = {files: {}});
+ }
+
+ if (item.elem) {
+ block = block.elems[item.elem] || (block.elems[item.elem] = {mods: {}, files: {}});
+
+ if (item.mod) block = block.mods[item.mod] || (block.mods[item.mod] = {vals: {}, files: {}});
+ if (item.val) block = block.vals[item.val] || (block.vals[item.val] = {files: {}});
+ }
+
+ (block.files[file.suffix] || (block.files[file.suffix] = [])).push(file);
+ }
+ };
+
+ if (this.files && !force) return this.files;
+
+ var _this = this;
+
+ if (this.cache) {
+ try {
+ _this.files = JSON.parse(FS.readFileSync(PATH.join(_this.dir, 'files.json')));
+ return;
+
+ } catch(err) {
+ LOGGER.fdebug('cache for level not found', _this.dir);
+ }
+ }
+
+ _this.scan(items);
+ _this.files = files;
+
+ bemUtil.writeFile(PATH.join(_this.dir, 'files.json'), JSON.stringify(files));
+
+ return files;
+ },
+
+ scan: function(items) {
+
+ if (!bemUtil.isDirectory(this.dir)) return;
+
+ LOGGER.time('scan ' + this.dir);
+
+ var _this = this;
+
+ this.suffixToTech = {};
+ Object.keys(this.getTechs()).forEach(function(tech) {
+ tech = this.getTech(tech);
+ tech.getSuffixes().forEach(function(s) {
+ this.suffixToTech['.' + s] = tech.getTechName();
+ }, this);
+
+ }, this);
+
+ _this.scanBlocks(_this.dir).map(function(block) {
+ return _this.scanBlock(_this.dir, block, items)
+ })
+
+ LOGGER.timeEndLevel('debug', 'scan ' + _this.dir);
+ },
+
+ scanBlocks: function(path) {
+ var dirs = []
+ bemUtil.getDirsFilesSync(path, dirs);
+
+ return dirs.filter(function(dir) {
+ return dir[0] !== '_' && dir[0] !== '.';
+ })
+ },
+
+ scanBlock: function(path, block, items) {
+ var _this = this,
+ dirs = [], files = [];
+
+ bemUtil.getDirsFilesSync(PATH.join(path, block), dirs, files);
+
+ var blockPart = block + '.',
+ blockPartL = blockPart.length;
+
+ files.forEach(function(f) {
+ var file = f.file;
+ if (file.substr(0, blockPartL) !== blockPart) return;
+
+ var suffix = file.substr(blockPartL - 1);
+
+ items.push(f, {
+ block: block,
+ suffix: suffix,
+ tech: _this.suffixToTech[suffix]
+ })
+ });
+
+ dirs.forEach(function(dir) {
+ if (_this.isElemDir(dir)) return _this.scanElem(path, block, dir, items);
+ if (_this.isModDir(dir)) return _this.scanMod(path, block, null, dir, items);
+ });
+ },
+
+ isElemDir: function(dir) {
+ return dir[0] === '_' && dir[1] === '_';
+ },
+
+ blockElemFileSeparator: '__',
+ elemDirPrefix: '__',
+
+ scanElem: function(path, block, elem, items) {
+ var _this = this,
+ dir = path + PATH.dirSep + block + PATH.dirSep + elem,
+ dirs = [], files = [];
+
+ bemUtil.getDirsFilesSync(dir, dirs, files);
+
+ var blockPart = block + _this.blockElemFileSeparator + elem.substr(_this.elemDirPrefix.length) + '.',
+ blockPartL = blockPart.length,
+ prefixLen = _this.elemDirPrefix.length;
+
+ files.forEach(function(f) {
+ var file = f.file;
+ if (file.substr(0, blockPartL) !== blockPart) return;
+
+ var suffix = file.substr(blockPartL - 1);
+
+ items.push(f, {
+ block: block,
+ elem: elem.substr(prefixLen),
+ suffix: suffix,
+ tech: _this.suffixToTech[suffix]
+ })
+ });
+
+ dirs.forEach(function(dir) {
+ if (_this.isModDir(dir)) return _this.scanMod(path, block, elem, dir, items);
+ });
+ },
+
+ isModDir: function(dir) {
+ return dir[0] === '_' && dir[1] !== '_';
+ },
+
+ scanMod: function(path, block, elem, mod, items) {
+ var _this = this,
+ dir = path + PATH.dirSep + block + PATH.dirSep + (elem?elem+PATH.dirSep:'') + mod,
+ dirs = [], files = [];
+
+ bemUtil.getDirsFilesSync(dir, dirs, files);
+
+ var blockPart = block + (elem?_this.blockElemFileSeparator + elem.substr(_this.elemDirPrefix.length):'') + mod,
+ blockPartL = blockPart.length;
+
+ files.forEach(function(f) {
+ var file = f.file;
+ if (file.substr(0, blockPartL) !== blockPart) return;
+
+ var val,
+ modval = file.substr(blockPartL);
+
+ if (modval[0] === '_') val = modval.substr(1)
+ else if (modval[0] !== '.') return;
+
+ var suffix = modval.substr(modval.indexOf('.')),
+ item = {
+ block: block,
+ mod: mod.substr(1),
+ suffix: suffix,
+ tech: _this.suffixToTech[suffix]
+ };
+
+ if (elem) item.elem = elem.substr(_this.elemDirPrefix.length);
+ if (val) item.val = val.substr(0, val.indexOf('.'));
+
+ items.push(f, item);
+ });
+
+ },
+
/**
* Creates preconfigured introspection functions.
*
@@ -683,6 +1048,10 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
var level = this;
+ if (!opts) opts = {
+ opts: false
+ }
+
// clone opts
opts = bemUtil.extend({}, opts);
@@ -719,6 +1088,11 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
*/
return function(from, res) {
+ if (opts.opts === false) {
+ level.scanFiles();
+ return level.files.blocks;
+ }
+
from = PATH.resolve(level.dir, from || opts.from);
res || (res = opts.init.call(level));
View
9 lib/logger.js
@@ -45,6 +45,15 @@ exports.timeEnd = function(label) {
label = UTIL.format.apply(UTIL, arguments);
var duration = Date.now() - times[label];
exports.flog('info', '%s: ' + Color('red', '%dms'), label, duration);
+ delete times[label];
+};
+
+
+exports.timeEndLevel = function(level, label) {
+ label = UTIL.format.apply(UTIL, Array.prototype.slice.call(arguments, 1));
+ var duration = Date.now() - times[label];
+ exports.flog(level, '%s: ' + Color('red', '%dms'), label, duration);
+ delete times[label];
};
/**
View
37 lib/nodes/build.js
@@ -29,7 +29,9 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
__constructor: function(o) {
this.bundlesLevel = o.bundlesLevel;
- this.levelsPaths = o.levels;
+ this.levelsPaths = o.levels.map(function(level) {
+ return level.path || level;
+ });
this.levels = o.levels.map(createLevel);
this.declPath = o.declPath;
this.techName = o.techName;
@@ -39,7 +41,7 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
// NOTE: Every time we need new tech object so we use createTech().
// If we use getTech(), we'll overwrite context of the same tech
// object that is prone to collisions
- this.tech = this.bundlesLevel.createTech(o.techName, o.techPath);
+ this.tech = this.bundlesLevel.getTech(o.techName, o.techPath);
this.techPath = this.tech.getTechPath();
this.tech.setContext(new Context(this.bundlesLevel, { level: this.levels }));
@@ -48,6 +50,9 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
},
isValid: function() {
+
+ if (this.tech.API_VER === 2) return false;
+
var _this = this;
return Q.when(this.__base(), function(valid){
@@ -149,7 +154,9 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
level: this.levelsPaths,
declaration: PATH.resolve(this.root, this.declPath),
tech: this.techPath,
- outputName: PATH.resolve(this.root, this.output)
+ outputName: PATH.resolve(this.root, this.output),
+ force: this.ctx.force,
+ root: this.root
};
this.log('bem.build(forked=%j, %s)', this.forked, JSON.stringify(opts, null, 4));
@@ -159,6 +166,7 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
return BEM.build(opts);
}
+ opts.forceCache = true;
// TODO: generalize forking of bem commands
var _this = this,
d = Q.defer(),
@@ -296,7 +304,7 @@ registry.decl(BemBuildNodeName, GeneratedFileNode, {
createId: function(o) {
return o.bundlesLevel
- .createTech(o.techName, o.techPath)
+ .getTech(o.techName, o.techPath)
.getPath(o.output);
}
@@ -332,10 +340,10 @@ registry.decl(BemBuildMetaNodeName, BemBuildNodeName, {
var decl = U.readDecl(PATH.resolve(this.root, this.declPath)),
tech = this.tech,
+ techv2 = !!this.tech.getBuildPaths,
relativize = getPathRelativizer(this.root),
- prefixes = tech.getBuildPrefixes(tech.transformBuildDecl(decl), this.levels),
- filteredPaths = tech.filterPrefixes(prefixes, tech.getSuffixes())
- .invoke('map', relativize),
+ prefixes = !techv2? tech.getBuildPrefixes(tech.transformBuildDecl(decl), this.levels): Q.resolve([]),
+ filteredPaths = tech.filterPrefixes(prefixes, tech.getSuffixes()).invoke('map', relativize),
savedPaths = this.readMeta();
ctx.prefixes = prefixes;
@@ -365,17 +373,12 @@ registry.decl(BemBuildMetaNodeName, BemBuildNodeName, {
make: function() {
var _this = this,
ctx = this.ctx,
- tech = this.tech,
- arch = ctx.arch,
- relativize = getPathRelativizer(this.root),
- allPaths = ctx.prefixes.then(function(prefixes) {
- return tech.getPaths(prefixes, tech.getSuffixes()).map(relativize);
- });
+ arch = ctx.arch;
- return Q.all([allPaths, ctx.filteredPaths, ctx.savedPaths])
- .spread(function(allPaths, filteredPaths, savedPaths) {
+ return Q.all([ctx.filteredPaths, ctx.savedPaths])
+ .spread(function(filteredPaths, savedPaths) {
- return arch.withLock(_this.alterArch(allPaths, filteredPaths, savedPaths), _this)
+ return arch.withLock(_this.alterArch(filteredPaths, savedPaths), _this)
.then(function() {
// write file list to .meta.js
@@ -392,7 +395,7 @@ registry.decl(BemBuildMetaNodeName, BemBuildNodeName, {
});
},
- alterArch: function(allPaths, filteredPaths, savedPaths) {
+ alterArch: function(filteredPaths, savedPaths) {
return function() {
View
20 lib/nodes/bundle.js
@@ -199,8 +199,13 @@ registry.decl(BundleNodeName, BlockNode, /** @lends BundleNode.prototype */ {
techName: techName,
output: this.getNodePrefix(),
forked: forked === undefined? ~this.getForkedTechs().indexOf(techName): forked
- }),
- metaNode = buildNode.getMetaNode();
+ });
+
+ var tech = this.level.getTech(techName, techPath),
+ metaNode;
+
+ /* techs-v2: don't create meta node */
+ if (tech.API_VER !== 2) metaNode = buildNode.getMetaNode();
// Set bem build node to arch and add dependencies to it
arch.setNode(buildNode)
@@ -219,10 +224,13 @@ registry.decl(BundleNodeName, BlockNode, /** @lends BundleNode.prototype */ {
bundleNode && arch.addParents(buildNode, bundleNode);
magicNode && arch.addChildren(buildNode, magicNode);
- // Set bem build meta node to arch
- arch.setNode(metaNode)
- .addParents(metaNode, buildNode)
- .addChildren(metaNode, metaNode.getDependencies());
+ /* techs-v2: don't add dependency on meta node */
+ if (metaNode) {
+ // Set bem build meta node to arch
+ arch.setNode(metaNode)
+ .addParents(metaNode, buildNode)
+ .addChildren(metaNode, metaNode.getDependencies());
+ }
return buildNode;
View
8 lib/nodes/level.js
@@ -141,8 +141,7 @@ registry.decl(LevelNodeName, MagicNode, {
},
scanLevelItems: function() {
-
- return _.uniq(
+ var r = _.uniq(
_.sortBy(
this.level.getItemsByIntrospection()
.filter(function(item) {
@@ -152,6 +151,7 @@ registry.decl(LevelNodeName, MagicNode, {
true,
U.bemKey);
+ return r;
},
/**
@@ -261,7 +261,7 @@ registry.decl(BundlesLevelNodeName, LevelNodeName, {
*/
scanLevelItems: function() {
- return _.uniq(
+ var r = _.uniq(
_.sortBy(
this.level.getItemsByIntrospection()
.filter(function(item) {
@@ -279,6 +279,8 @@ registry.decl(BundlesLevelNodeName, LevelNodeName, {
true,
U.bemKey);
+
+ return r;
}
});
View
33 lib/nodes/target.js
@@ -0,0 +1,33 @@
+var Q = require('q'),
+ INHERIT = require('inherit'),
+ Node = require('./node').NodeName,
+ registry = require('../nodesregistry'),
+
+ TargetNodeName = exports.TargetNodeName = 'TargetNode';
+
+exports.__defineGetter__(TargetNodeName, function() {
+ return registry.getNodeClass(TargetNodeName);
+});
+
+registry.decl(TargetNodeName, Node, /** @lends TargetNode.prototype */ {
+
+ /**
+ * Verbosity of node log messages.
+ * @type {String}
+ */
+ buildMessageVerbosity: 'verbose',
+ nodeType: 1
+
+}, /** @lends MagicNode */ {
+
+ /**
+ * Create node id.
+ *
+ * @param {Object} o Node options.
+ * @return {String} Node id.
+ */
+ createId: function(o) {
+ return '$' + this.__base(o);
+ }
+
+});
View
1  lib/nodes/workers/borschik.js
@@ -15,6 +15,7 @@ process.once('message', function(m) {
U.oldNode && process.exit(0);
})
.fail(function(err) {
+ console.log(err)
process.send({
code: 1,
msg: 'borschik: ' + (err.stack || err)
View
125 lib/tech/index.js
@@ -0,0 +1,125 @@
+
+var TechV1 = exports.Tech = require('./v1').Tech,
+ TechV2 = exports.TechV2 = require('./v2').Tech,
+ INHERIT = require('inherit'),
+
+ /**
+ * Return tech class for the tech module path.
+ *
+ * @param {String|Object} module Path to tech module or module object.
+ * @param {Level} [level] Level object to resolve techs by name.
+ * @returns {Tech} Tech class.
+ */
+ getTechClass = exports.getTechClass = function(module, level) {
+
+ var tech = typeof module === 'string'? require(module) : module,
+ TechClass = tech.API_VER?TechV2: TechV1;
+
+ // link to tech class found in Tech property
+ if (tech.Tech) return tech.Tech;
+
+ // path to base tech module found in baseTechPath property
+ if (tech.baseTechPath) {
+ TechClass = getTechClass(tech.baseTechPath, level);
+ }
+
+ // base tech name found in baseTechName property
+ else if (tech.baseTechName) {
+ if (!level) throw new Error('getTechClass(): level argument must be specified to resolve techs by name');
+ TechClass = getTechClass(level.resolveTech(tech.baseTechName), level);
+ }
+
+ // link to base tech class found in baseTech property
+ else if (tech.baseTech) {
+ TechClass = tech.baseTech;
+ }
+
+ // legacy tech module detected, should use LegacyTech shim
+ else if (tech.techModule || tech.bemBuild || tech.bemCreate) {
+ return LegacyTech;
+ }
+
+ return INHERIT(TechClass, tech.techMixin || tech);
+
+ },
+
+ /**
+ * Create tech object for tech module, identified by path and name.
+ * @param {String} path Tech module absolute path.
+ * @param {String} name Tech name.
+ * @param {Level} [level] Level object to resolve techs by name.
+ * @returns {Tech} Tech instance object.
+ */
+ createTech = exports.createTech = function(path, name, level) {
+ path = require.resolve(path);
+ return new (getTechClass(path, level))(name, path);
+ },
+
+
+ LegacyTech = INHERIT(TechV1, /** @lends LegacyTech.prototype */{
+ /**
+ * @class Legacy tech modules wrapper class.
+ * @constructs
+ * @private
+ * @param {String} name Tech name.
+ * @param {String} path Tech module absolute path.
+ */
+ __constructor: function(name, path) {
+ this.techObj = new (require('./../legacy-tech').Tech)(path, name);
+ this.__base(name, path);
+ },
+
+ /**
+ * Set context to use in tech module.
+ *
+ * @public
+ * @param {Context} ctx Context instance object.
+ * @returns {LegacyTech} Chainable API
+ */
+ setContext: function(ctx) {
+ this.techObj.setContext(ctx);
+ return this;
+ },
+
+ getContext: function() {
+ return this.techObj.getContext();
+ },
+
+ create: function(prefix, vars, force) {
+ return this.techObj.bemCreate(prefix, vars, force);
+ },
+
+ readAllContent: function(prefix) {
+ var res = {};
+ res[this.getTechName()] = this.techObj.getFileContent(prefix);
+ return res;
+ },
+
+ build: function(prefixes, outputDir, outputName) {
+
+ var _this = this,
+ ctx = this.getContext();
+
+ // wait for declaration to load
+ return Q.when(ctx.opts.declaration, function(decl) {
+
+ ctx.opts.declaration = decl;
+
+ return Q.when(prefixes, function(prefixes) {
+ return _this.techObj.bemBuild(prefixes, outputDir, outputName);
+ });
+
+ });
+
+ },
+
+ getBuildResultChunk: function(relPath, path, outputDir, outputName) {
+ return this.techObj.outFile.apply(this.techObj, arguments);
+ },
+
+ getTechName: function() {
+ return this.techObj.getTechName();
+ }
+
+ });
+
View
177 lib/tech.js → lib/tech/v1.js
@@ -1,61 +1,9 @@
-var Q = require('qq'),
+var Q = require('q'),
QFS = require('q-fs'),
- bemUtil = require('./util'),
- PATH = require('./path'),
+ bemUtil = require('./../util'),
+ PATH = require('./../path'),
INHERIT = require('inherit'),
- LOGGER = require('./logger'),
-
- /**
- * Return tech class for the tech module path.
- *
- * @param {String|Object} module Path to tech module or module object.
- * @param {Level} [level] Level object to resolve techs by name.
- * @returns {Tech} Tech class.
- */
- getTechClass = exports.getTechClass = function(module, level) {
-
- var tech = typeof module === 'string'? require(module) : module,
- TechClass = Tech;
-
- // link to tech class found in Tech property
- if (tech.Tech) return tech.Tech;
-
- // path to base tech module found in baseTechPath property
- if (tech.baseTechPath) {
- TechClass = getTechClass(tech.baseTechPath, level);
- }
-
- // base tech name found in baseTechName property
- else if (tech.baseTechName) {
- if (!level) throw new Error('getTechClass(): level argument must be specified to resolve techs by name');
- TechClass = getTechClass(level.resolveTech(tech.baseTechName), level);
- }
-
- // link to base tech class found in baseTech property
- else if (tech.baseTech) {
- TechClass = tech.baseTech;
- }
-
- // legacy tech module detected, should use LegacyTech shim
- else if (tech.techModule || tech.bemBuild || tech.bemCreate) {
- return LegacyTech;
- }
-
- return INHERIT(TechClass, tech.techMixin || tech);
-
- },
-
- /**
- * Create tech object for tech module, identified by path and name.
- * @param {String} path Tech module absolute path.
- * @param {String} name Tech name.
- * @param {Level} [level] Level object to resolve techs by name.
- * @returns {Tech} Tech instance object.
- */
- createTech = exports.createTech = function(path, name, level) {
- path = require.resolve(path);
- return new (getTechClass(path, level))(name, path);
- },
+ LOGGER = require('./../logger'),
Tech = exports.Tech = INHERIT(/** @lends Tech.prototype */ {
@@ -161,11 +109,16 @@ var Q = require('qq'),
var _this = this,
res = {};
- this.getCreateSuffixes().forEach(function(suffix) {
- res[suffix] = _this.getCreateResult(_this.getPath(prefix, suffix), suffix, vars);
- });
-
- return Q.shallow(res);
+ return Q
+ .all(this.getCreateSuffixes().map(function(suffix) {
+ return Q.when(_this.getCreateResult(_this.getPath(prefix, suffix), suffix, vars))
+ .then(function(r) {
+ res[suffix] = r;
+ });
+ }))
+ .then(function() {
+ return res;
+ });
},
/**
@@ -234,11 +187,16 @@ var Q = require('qq'),
var _this = this,
res = {};
- this.getCreateSuffixes().forEach(function(suffix) {
- res[suffix] = _this.readContent(_this.getPath(prefix, suffix), suffix);
- });
-
- return Q.shallow(res);
+ return Q
+ .all(this.getCreateSuffixes().map(function(suffix) {
+ return _this.readContent(_this.getPath(prefix, suffix), suffix)
+ .then(function(r) {
+ res[suffix] = r;
+ })
+ }))
+ .then(function() {
+ return res;
+ });
},
/**
@@ -498,11 +456,16 @@ var Q = require('qq'),
var _this = this,
res = {};
- this.getBuildSuffixes().forEach(function(suffix) {
- res[suffix] = _this.getBuildResult(prefixes, suffix, outputDir, outputName);
- });
-
- return Q.shallow(res);
+ return Q
+ .all(this.getBuildSuffixes().map(function(suffix) {
+ return _this.getBuildResult(prefixes, suffix, outputDir, outputName)
+ .then(function(r) {
+ res[suffix] = r;
+ })
+ }))
+ .then(function() {
+ return res;
+ });
},
/**
@@ -645,7 +608,7 @@ var Q = require('qq'),
getTechRelativePath: function(from) {
from = PATH.join(from || '.', PATH.dirSep);
var absPath = this.getTechPath(),
- techPath = PATH.relative(PATH.join(__dirname, PATH.unixToOs('../../')), absPath),
+ techPath = PATH.relative(PATH.join(__dirname, PATH.unixToOs('../../../')), absPath),
testDotRe = new RegExp('^[\\.' + PATH.dirSepRe + ']'),
testLibRe = new RegExp('^.*?' + PATH.dirSepRe + 'lib'),
@@ -700,75 +663,9 @@ var Q = require('qq'),
*/
setContext: function(ctx) {
Tech.prototype.context = ctx;
- require('./legacy-tech').Tech.setContext(ctx);
+ require('./../legacy-tech').Tech.setContext(ctx);
}
- }),
-
- LegacyTech = INHERIT(Tech, /** @lends LegacyTech.prototype */{
-
- /**
- * @class Legacy tech modules wrapper class.
- * @constructs
- * @private
- * @param {String} name Tech name.
- * @param {String} path Tech module absolute path.
- */
- __constructor: function(name, path) {
- this.techObj = new (require('./legacy-tech').Tech)(path, name);
- this.__base(name, path);
- },
-
- /**
- * Set context to use in tech module.
- *
- * @public
- * @param {Context} ctx Context instance object.
- * @returns {LegacyTech} Chainable API
- */
- setContext: function(ctx) {
- this.techObj.setContext(ctx);
- return this;
- },
-
- getContext: function() {
- return this.techObj.getContext();
- },
-
- create: function(prefix, vars, force) {
- return this.techObj.bemCreate(prefix, vars, force);
- },
-
- readAllContent: function(prefix) {
- var res = {};
- res[this.getTechName()] = this.techObj.getFileContent(prefix);
- return res;
- },
-
- build: function(prefixes, outputDir, outputName) {
-
- var _this = this,
- ctx = this.getContext();
-
- // wait for declaration to load
- return Q.when(ctx.opts.declaration, function(decl) {
-
- ctx.opts.declaration = decl;
-
- return Q.when(prefixes, function(prefixes) {
- return _this.techObj.bemBuild(prefixes, outputDir, outputName);
- });
-
- });
-
- },
-
- getBuildResultChunk: function(relPath, path, outputDir, outputName) {
- return this.techObj.outFile.apply(this.techObj, arguments);
- },
+ });
- getTechName: function() {
- return this.techObj.getTechName();
- }
- });
View
658 lib/tech/v2.js
@@ -0,0 +1,658 @@
+var Q = require('q'),
+ FS = require('fs'),
+ QFS = require('q-fs'),
+ bemUtil = require('./../util'),
+ _ = require('underscore'),
+ PATH = require('./../path'),
+ INHERIT = require('inherit'),
+ LOGGER = require('./../logger'),
+
+ Tech = exports.Tech = INHERIT(/** @lends Tech.prototype */ {
+
+ API_VER: 2,
+
+ /**
+ * Construct an instance of Tech.
+ *
+ * @class Tech base class
+ * @constructs
+ * @private
+ * @param {String} name Tech name.
+ * @param {String} path Tech module absolute path.
+ */
+ __constructor: function(name, path) {
+ this.techName = name;
+ this.techPath = path;
+ },
+
+ /**
+ * Set context to use in tech module.
+ *
+ * @public
+ * @param {Context} ctx Context instance object.
+ */
+ setContext: function(ctx) {
+ this.context = ctx;
+ return this;
+ },
+
+ /**
+ * Return context.
+ *
+ * @public
+ * @returns {Context} Context instance object.
+ */
+ getContext: function() {
+ return this.context;
+ },
+
+ /**
+ * Implementation of 'bem create block | elem | mod' commands.
+ *
+ * @param {Object} item BEM entity object.
+ * @param {Object} item.block BEM entity block name.
+ * @param {Object} item.elem BEM entity elem name.
+ * @param {Object} item.mod BEM entity modifier name.
+ * @param {Object} item.val BEM entity modifier value.
+ * @param {Level} level Level instance object.
+ * @param {Object} opts Additional options.
+ * @returns {Promise * Undefined}
+ */
+ createByDecl: function(item, level, opts) {
+ opts = opts || {};
+
+ var prefix = level.getByObj(item),
+ vars = {
+ opts: opts,
+ BlockName: item.block,
+ Prefix: prefix
+ };
+
+ if (item.elem) vars.ElemName = item.elem;
+ if (item.mod) vars.ModName = item.mod;
+ if (item.val) vars.ModVal = item.val;
+
+ return this.create(prefix, vars, opts.force);
+ },
+
+ /**
+ * Implementation of 'bem create block | elem | mod' commands.
+ *
+ * @public
+ * @param {String} prefix Path prefix of object to create.
+ * @param {Object} vars Variables to use in template.
+ * @param {Boolean} force Force creation flag.
+ * @returns {Promise * Undefined}
+ */
+ create: function(prefix, vars, force) {
+ return this.storeCreateResults(prefix, this.getCreateResults(prefix, vars), force);
+ },
+
+ /**
+ * Return create result for one tech suffix.
+ *
+ * @protected
+ * @param {String} path Full path of object to create.
+ * @param {String} suffix Suffix of object to create.
+ * @param {Object} vars Variables to use in template.
+ * @returns {Promise * String}
+ */
+ getCreateResult: function(path, suffix, vars) {
+ return Q.resolve('');
+ },
+
+ /**
+ * Return create result for all tech suffixes.
+ *
+ * @protected
+ * @param {String} prefix Oath prefix of object to create.
+ * @param {Object} vars Variables to use in template.
+ * @returns {Promise * Object}
+ */
+ getCreateResults: function(prefix, vars) {
+ var _this = this,
+ res = {};
+
+ return Q
+ .all(this.getCreateSuffixes().map(function(suffix) {
+ return Q.when(_this.getCreateResult(_this.getPath(prefix, suffix), suffix, vars))
+ .then(function(r) {
+ res[suffix] = r;
+ });
+ }))
+ .then(function() {
+ return res;
+ });
+ },
+
+ /**
+ * Store result of object creation for one suffix.
+ *
+ * @protected
+ * @param {String} path Full path of object to create.
+ * @param {String} suffix Suffix of object to create.
+ * @param {Object} res Result of object creation.
+ * @param {Boolean} force Force creation flag.
+ * @returns {Promise * Undefined}
+ */
+ storeCreateResult: function(path, suffix, res, force) {
+ return QFS.exists(path).then(function(exists) {
+ if(exists && !force) {
+ return Q.reject("Already exists '" + path + "'");
+ }
+ // TODO: replace by promisy equivalent
+ bemUtil.mkdirs(PATH.dirname(path));
+ return bemUtil.writeFile(path, res);
+ });
+ },
+
+ /**
+ * Store results of object creation.
+ *
+ * @protected
+ * @param {String} prefix Path prefix of object to create.
+ * @param {Object} res Result of object creation.
+ * @param {Boolean} force Force creation flag.
+ * @returns {Promise * Undefined}
+ */
+ storeCreateResults: function(prefix, res, force) {
+ var _this = this;
+ return Q.when(res, function(res) {
+ return Q.all(_this.getCreateSuffixes().map(function(suffix) {
+ return _this.storeCreateResult(_this.getPath(prefix, suffix), suffix, res[suffix], force);
+ })).get(0);
+ });
+ },
+
+ /**
+ * Read and return content of object identified by specified
+ * prefix for specified suffix.
+ *
+ * @protected
+ * @param {String} path Full path of object to read.
+ * @param {String} suffix Suffix of object to read.
+ * @returns {Promise * String}
+ */
+ readContent: function(path, suffix) {
+ return QFS.exists(path).then(function(exists) {
+ if(!exists) return '';
+
+ return bemUtil.readFile(path);
+ });
+ },
+
+ /**
+ * Read and return content of object identified by specified prefix.
+ *
+ * @param {String} prefix Path prefix of object to read content of.
+ * @returns {Promise * Object}
+ */
+ readAllContent: function(prefix) {
+ var _this = this,
+ res = {};
+
+ return Q
+ .all(this.getCreateSuffixes().map(function(suffix) {
+ return _this.readContent(_this.getPath(prefix, suffix), suffix)
+ .then(function(r) {
+ res[suffix] = r;
+ })
+ }))
+ .then(function() {
+ return res;
+ });
+ },
+
+ /**
+ * Implementation of 'bem build' command.
+ *
+ * @public
+ * @param {Object} decl BEM entities declaration.
+ * @param {Level[]} levels Array of levels.
+ * @param {String} output Path prefix of output.
+ * @param {Object} opts Custom opts.
+ * @returns {Promise * Undefined}
+ */
+ buildByDecl: function(decl, levels, output, opts) {
+
+ return this.storeBuildResults(
+ output,
+ this.getBuildResults(decl, levels, output, opts));
+ },
+
+ /**
+ * Return build result chunk.
+ *
+ * @protected
+ * @param {String} relPath Relative path to source object.
+ * @param {String} path Path to source object.
+ * @param {String} suffix Suffix of source object.
+ * @returns {String} Build result chunk.
+ */
+ getBuildResultChunk: function(relPath, path, suffix) {
+ return relPath + '\n';
+ },
+
+ /**
+ * Build and return result of build of specified prefixes
+ * for specified suffix.
+ *
+ * @protected
+ * @param {Promise * String[]} path Files to build from.
+ * @param {String} suffix Suffix to build result for.
+ * @param {String} output Output prefix of build result.
+ * @param {Object} opts Options.
+ * @returns {Promise * String} Promise for build result.
+ */
+ getBuildResult: function(files, suffix, output, opts) {
+ var _this = this;
+ return Q.all(files.map(function(file) {
+ return _this.getBuildResultChunk(
+ PATH.relative(PATH.dirname(output)+PATH.dirSep, file.absPath), file.absPath, suffix);
+ }));
+ },
+
+ /**
+ * Build and return result of build of specified prefixes.
+ *
+ * @protected
+ * @param {Promise * Object} decl Declaration to build from.
+ * @param {Object[]} levels Array of levels to use.
+ * @param {String} output Output prefix of build result.
+ * @param {Object} opts Custom options
+ * @returns {Promise * Object} Promise for build results object.
+ */
+ getBuildResults: function(decl, levels, output, opts) {
+ var _this = this,
+ res = {},
+ files = this.getBuildPaths(decl, levels);
+
+ return files.then(function(files) {
+ return Q.all(_this.getBuildSuffixes()
+ .map(function(destSuffix) {
+ var filteredFiles = files[destSuffix] || [],
+ file = _this.getPath(output, destSuffix);
+
+ return _this.validate(file, filteredFiles, opts)
+ .then(function(valid) {
+ LOGGER.fverbose('file %s is %s', file, valid?'valid':'not valid');
+ if (!valid) {
+ _this.saveLastUsedData(file, {buildFiles: filteredFiles});
+
+ return _this.getBuildResult(filteredFiles, destSuffix, output, opts)
+ .then(function(r) {
+ res[destSuffix] = r;
+ });
+ }
+ })
+ }))
+ .then(function() {
+ return res;
+ });
+ });
+ },
+
+ /**
+ * Determines is file up to date or not by comparing the list of files to use for build
+ * with such saved list during previous build. Will return false if opts.force is defined.
+ * @param {String} file File which is being built
+ * @param {Object[]} files The list of files which will be used.
+ * @param {Object} opts Custom options.
+ * @return {Promise * boolean}
+ */
+ validate: function(file, files, opts) {
+ if (opts.force) return Q.resolve(false);
+
+ var _this = this;
+
+ return Q.all([_this.getLastUsedData(file),
+ QFS.exists(file)])
+
+ .spread(function(prevFiles, exists) {
+ prevFiles = prevFiles.buildFiles;
+ if (prevFiles && prevFiles.length === files.length)
+ return _this.sameFiles(files, prevFiles) && exists;
+ else
+ return false;
+ });
+ },
+
+ /**
+ * Compares two lists of files.
+ * @param {Object[]} now
+ * @param {Object[]} old
+ * @return {Boolean}
+ */
+ sameFiles: function(now, old) {
+ for(var i = 0; i < now.length; i++) {
+ var n = now[i],
+ o = old[i];
+
+ if (n.absPath !== o.absPath ||
+ n.lastUpdated !== o.lastUpdated) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Loads appropriate meta file for the file which is being built.
+ * @param {String} file File path which is being built.
+ * @return {Object} Object created from the meta file using deserialization.
+ */
+ getLastUsedData: function(file) {
+ var root = this.context.opts.root || '';
+
+ file = PATH.join(
+ root, '.bem', 'cache',
+ PATH.relative(root, file) + '.meta.js');
+
+ return bemUtil
+ .readFile(file)
+ .fail(function() {
+ // meta file does not exist probably
+ return '{}';
+ })
+ .then(function(content) {
+ try {
+ return JSON.parse(content);
+ } catch (err) {
+ LOGGER.fwarn('meta file %s failed to parse. It will be regenerated.', file);
+ return {};
+ }
+ });
+ },
+
+ saveLastUsedData: function(file, data) {
+ var root = this.context.opts.root || '';
+ file = PATH.join(
+ root, '.bem', 'cache',
+ PATH.relative(root, file) + '.meta.js');
+
+ return Q.when(bemUtil.mkdirs(PATH.dirname(file)))
+ .then(function() {
+ return bemUtil.writeFile(file, JSON.stringify(data));
+ });
+ },
+
+ getBuildPaths: function(decl, levels) {
+ var _this = this,
+ res = {},
+ suffixesMap = this.getSuffixesMap();
+
+ return Q.when(decl, function(decl) {
+
+ return Q
+ .all(levels.map(function(level) {
+ return level.scanFiles();
+ }))
+ .then(function() {
+ LOGGER.time('getBuildPaths');
+
+
+ if (decl.deps) for(var d = 0; d < decl.deps.length; d++) {
+ var dep = decl.deps[d];
+
+ for(var l = 0; l < levels.length; l++) {
+ var level = levels[l],
+ files = level.getFileByObjIfExists(dep, _this);
+
+ if (files) {
+ for(var i = 0; i < files.length; i++) {
+ var file = files[i],
+ buildSuffixes = suffixesMap[file.suffix[0] === '.'?file.suffix.substr(1):file.suffix];
+
+ if (buildSuffixes) {
+ for(var bs = 0; bs < buildSuffixes.length; bs++) {
+ var buildSuffix = buildSuffixes[bs];
+ (res[buildSuffix] || (res[buildSuffix] = [])).push(file);
+ };
+ }
+ }
+ }
+
+ }
+ }
+
+ LOGGER.timeEndLevel('silly', 'getBuildPaths');
+ return res;
+ })
+ });
+ },
+
+
+ /**
+ * Store result of build for specified suffix.
+ *
+ * @protected
+ * @param {String} path Path of object to store.
+ * @param {String} suffix Suffix of object to store.
+ * @param {String} res Result of build for specified suffix.
+ * @returns {Promise * Undefined}
+ */
+ storeBuildResult: function(path, suffix, res) {
+ return bemUtil.writeFile(path, res);
+ },
+
+ /**
+ * Store results of build.
+ *
+ * @protected
+ * @param {String} prefix Prefix of object to build.
+ * @param {Promise * String} res Result of build.
+ * @return {Promise * Undefined}
+ */
+ storeBuildResults: function(prefix, res) {
+ var _this = this;
+ return Q.when(res, function(res) {
+ return Q.all(Object.keys(res).map(function(suffix) {
+ return _this.storeBuildResult(_this.getPath(prefix, suffix), suffix, res[suffix]);
+ })).get(0);
+ });
+ },
+
+ /**
+ * Return true if suffix mathes one of tech suffixes.
+ *
+ * @public
+ * @param {String} suffix Suffix to match.
+ * @returns {Boolean}
+ */
+ matchSuffix: function(suffix) {
+ (suffix.substr(0, 1) === '.') && (suffix = suffix.substr(1));
+ return this.getSuffixes().indexOf(suffix) >= 0;
+ },
+
+ _suffixes: null,
+ /**
+ * Return all tech suffixes.
+ *
+ * @public
+ * @returns {String[]}
+ */
+ getSuffixes: function() {
+ if (this._suffixes) return this._suffixes;
+
+ var res = [],
+ map = this.getBuildSuffixesMap();
+
+ Object.keys(map).forEach(function(bs) {
+ res = res.concat(map[bs]);
+ }, this);
+
+ this._suffixes = _.uniq(res);
+
+ return this._suffixes;
+ },
+
+ /**
+ * Return tech suffixes to use in process of bem create.
+ *
+ * @return {String[]}
+ */
+ getCreateSuffixes: function() {
+ return this.getSuffixes();
+ },
+
+ /**
+ * Return tech suffixes to use in process of bem build.
+ *
+ * @return {String[]}
+ */
+ getBuildSuffixes: function() {
+ return Object.keys(this.getBuildSuffixesMap());
+ },
+
+ getBuildSuffixesMap: function() {
+ var res = {};
+ res[this.getTechName()] = [this.getTechName()];
+
+ return res;
+ },
+
+ getSuffixesMap: function() {
+ var buildMap = this.getBuildSuffixesMap(),
+ srcMap = {};
+
+ Object.keys(buildMap).forEach(function(buildSuffix) {
+ buildMap[buildSuffix].forEach(function(srcSuffix) {
+ (srcMap[srcSuffix] || (srcMap[srcSuffix] = [])).push(buildSuffix);
+ }, this);
+ }, this);
+
+ return srcMap;
+ },
+
+ /**
+ * Return path by prefix and suffix.
+ *
+ * @public
+ * @param {String} prefix
+ * @param {String} suffix
+ * @returns {String}
+ */
+ getPath: function(prefix, suffix) {
+ suffix = suffix || this.getTechName();
+ return [prefix, suffix].join('.');
+ },
+
+ /**
+ * Return all paths by prefix.
+ *
+ * @public
+ * @param {String|String[]} prefixes
+ * @param {String|String[]} suffixes
+ * @returns {String[]}
+ */
+ getPaths: function(prefixes, suffixes) {
+ prefixes = Array.isArray(prefixes)? prefixes : [prefixes];
+ suffixes = (!Array.isArray(suffixes) && suffixes)
+ ? [suffixes]
+ : (suffixes || this.getSuffixes());
+
+ var _this = this,
+ paths = [];
+
+ prefixes.forEach(function(p) {
+ suffixes.forEach(function(s) {
+ paths.push(_this.getPath(p, s));
+ });
+ });
+
+ return paths;
+ },
+
+ /**
+ * Return tech name.
+ *
+ * @public
+ * @returns {String}
+ */
+ getTechName: function() {
+ if(this.techName) return this.techName;
+ return bemUtil.stripModuleExt(PATH.basename(this.getTechPath()));
+ },
+
+ /**
+ * Return tech module absolute path.
+ *
+ * @public
+ * @returns {String}
+ */
+ getTechPath: function() {
+ return this.techPath;
+ },
+
+ /**
+ * Return tech module relative path.
+ *
+ * @public
+ * @param {String} from Path to calculate relative path from.
+ * @returns {String}
+ */
+ getTechRelativePath: function(from) {
+ from = PATH.join(from || '.', PATH.dirSep);
+ var absPath = this.getTechPath(),
+ techPath = PATH.relative(PATH.join(__dirname, PATH.unixToOs('../../../')), absPath),
+
+ testDotRe = new RegExp('^[\\.' + PATH.dirSepRe + ']'),
+ testLibRe = new RegExp('^.*?' + PATH.dirSepRe + 'lib'),
+ replaceRe = new RegExp('^.*?' + PATH.dirSepRe);
+
+ // tech from 'bem' module
+ if(!testDotRe.test(techPath) && testLibRe.test(techPath)) {
+ techPath = techPath.replace(replaceRe, PATH.unixToOs('bem/'));
+ } else {
+ // look for tech into node_modules and NODE_PATH env variable
+ var shortestPath = PATH.relative(from, absPath);
+ shortestPath = shortestPath.split(PATH.dirSep);
+ module.paths.concat(bemUtil.getNodePaths()).forEach(function(reqPath) {
+ var relPath = PATH.relative(PATH.join(reqPath, PATH.dirSep), absPath);
+ if(!/^\./.test(relPath)) {
+ relPath = relPath.split(PATH.dirSep);
+ if(relPath.length < shortestPath.length) {
+ shortestPath = relPath;
+ }
+ }
+ });
+
+ techPath = PATH.join.apply(null, shortestPath);
+ // NOTE: could not replace to PATH.join('.', techPath), because of
+ // '.' will be stripped
+ if(!/^\./.test(techPath)) techPath = '.' + PATH.dirSep + techPath;
+ }
+
+ // NOTE: default tech, need to return empty path for it
+ if(techPath === bemUtil.getBemTechPath('default')) return '';
+ return techPath;
+ },
+
+ /**
+ * Return array of tech name dependencies.
+ *
+ * @public
+ * @return {String[]}
+ */
+ getDependencies: function() {
+ return [];
+ }
+
+ }, {
+
+ /**
+ * Set context to use in all tech modules.
+ *
+ * @static
+ * @public
+ * @param {Context} ctx Context instance object.
+ */
+ setContext: function(ctx) {
+ Tech.prototype.context = ctx;
+ require('./../legacy-tech').Tech.setContext(ctx);
+ }
+
+ });
+
+
View
2  lib/techs/bemdecl.js.js
@@ -1,7 +1,7 @@
var FS = require('fs'),
PATH = require('../path'),
INHERIT = require('inherit'),
- Tech = require('../tech').Tech,
+ Tech = require('../tech').TechV2,
bemUtil = require('../util');
exports.Tech = INHERIT(Tech, {
View
1  lib/techs/deps.js.js
@@ -232,7 +232,6 @@ var Deps = exports.Deps = INHERIT({
content = tech.readContentSync(path, tech.getTechName());
if (!content) return;
-
try {
_this.parse(VM.runInThisContext(content, path), item);
} catch(e) {
View
90 lib/techs/v2/bemdecl.js.js
@@ -0,0 +1,90 @@
+var FS = require('fs'),
+ PATH = require('../../path'),
+ INHERIT = require('inherit'),
+ Tech = require('../../tech').TechV2,
+ bemUtil = require('../../util');
+
+exports.Tech = INHERIT(Tech, {
+
+ getCreateResult: function(path, suffix, vars) {
+ var basename = this.getPath(PATH.basename(path, '.' + suffix),
+ 'bemjson.js'),
+ bemjsonPath = PATH.join(PATH.dirname(path), basename),
+ bemjson = bemUtil.isFile(bemjsonPath) ? FS.readFileSync(bemjsonPath) : '[]';
+
+ try {
+ bemjson = require('vm').runInThisContext(bemjson);
+ } catch(e) {
+ console.log(bemjson.toString());
+ throw e;
+ }
+
+ var decl = [];
+ iterateJson(bemjson, getBuilder(decl));
+
+ return 'exports.blocks = ' + JSON.stringify(bemUtil.mergeDecls([], decl), null, 4) + ';\n';
+ },
+
+ storeCreateResult: function(path, suffix, res, force) {
+ bemUtil.mkdirs(PATH.dirname(path));
+ return force?
+ bemUtil.writeFile(path, res) :
+ bemUtil.writeFileIfDiffers(path, res);
+ },
+
+ getDependencies: function() {
+ return ['bemjson.js'];
+ }
+
+});
+
+function isSimple(obj) {
+ var t = typeof obj;
+ return t === 'string' || t === 'number';
+}
+
+function iterateJson(obj, fn) {
+ if(obj && !isSimple(obj))
+ if(Array.isArray(obj)) {
+ var i = 0, l = obj.length;
+ while(i < l) iterateJson(obj[i++], fn);
+ } else fn(obj);
+ return obj;
+}
+
+function getBuilder(decl, block) {
+ return function(obj) {
+ var oldBlock = block;
+
+ block = obj.block || block;
+
+ obj.block && decl.push({ name: block });
+
+ obj.elem && decl.push({ name: block, elems: [{ name: obj.elem }] });
+
+ var mods;
+
+ if(mods = obj.mods)
+ for(var n in mods)
+ if(mods.hasOwnProperty(n))
+ decl.push({
+ name: block,
+ mods: [{ name: n, vals: [ mods[n] ] }]
+ });
+
+ if(obj.elem && (mods = obj.elemMods))
+ for(var n in mods)
+ if(mods.hasOwnProperty(n))
+ decl.push({
+ name: block,
+ elems: [{
+ name: obj.elem,
+ mods: [{ name: n, vals: [ mods[n] ] }]
+ }]
+ });
+
+ iterateJson([obj.mix, obj.content], getBuilder(decl, block));
+
+ block = oldBlock;
+ }
+}
View
34 lib/techs/v2/css.js
@@ -0,0 +1,34 @@
+var INHERIT = require('inherit'),
+ Template = require('../../template'),
+ Tech = require('../../tech').TechV2;
+
+exports.Tech = INHERIT(Tech, {
+
+ getBuildResultChunk: function(relPath, path, suffix) {
+ return '@import url(' + relPath + ');\n';
+ },
+
+ getCreateResult: function(path, suffix, vars) {
+
+ vars.Selector = '.' + vars.BlockName +
+ (vars.ElemName? '__' + vars.ElemName : '') +
+ (vars.ModVal? '_' + vars.ModName + '_' + vars.ModVal : '');
+
+ return Template.process([
+ '{{bemSelector}}',
+ '{',
+ '}'],
+ vars);
+
+ },
+
+ getBuildSuffixesMap: function() {
+ return {
+ 'css': ['css']
+ }
+ },
+
+ getTechName: function() {
+ return 'css';
+ }
+});
View
532 lib/techs/v2/deps.js.js
@@ -0,0 +1,532 @@
+var VM = require('vm'),
+ Q = require('q'),
+ QFS = require('q-fs'),
+ FS = require('fs'),
+ PATH = require('../../path'),
+ U = require('../../util'),
+ LOGGER = require('../../logger'),
+ INHERIT = require('inherit'),
+ Tech = require('../../').TechV2;
+
+exports.Tech = INHERIT(Tech, {
+
+ getTechName: function() {
+ return 'deps.js';
+ },
+
+ getBuildSuffixesMap: function() {
+ return {
+ 'deps.js': 'deps.js'
+ };
+ },
+
+ readContentSync: function(path, suffix) {
+ if (!PATH.existsSync(path)) return '';
+ return FS.readFileSync(path, 'utf8');
+ },
+
+ readContent: function(path, suffix) {
+ return U.readFile(path)
+ .fail(function() {
+ return '';
+ })
+ },
+
+
+ getCreateResult: function(path, suffix, vars) {
+
+ return [
+ '({',
+ ' mustDeps: [],',
+ ' shouldDeps: []',
+ '})'
+ ].join('\n');
+
+ },
+
+ transformBuildDecl: function(decl) {
+
+ return this.expandDeps(decl)
+ .then(function(deps) {
+
+ var d = deps.serialize(),
+ o = {};
+
+ if(d['']) {
+ o.deps = d[''][''];
+ delete d[''][''];
+ }
+ U.isEmptyObject(d) || (o.depsByTechs = d);
+
+ return o;
+
+ });
+
+ },
+
+ expandDeps: function(decl) {
+
+ var _this = this;
+
+ return Q.when(decl)
+ .then(function(decl) {
+ return new Deps(decl.blocks || decl.deps).expandByFS(_this);
+ });
+
+ },
+
+ buildByDecl: function(decl, levels, output, opts) {
+
+ var suffix = this.getTechName(),
+ file = this.getPath(PATH.resolve(output), suffix),
+ valid = Q.defer(),
+ _this = this;
+
+ return Q.all(levels
+ .map(function(level) {
+ return level.scanFiles();
+ }))
+ .then(function(){
+
+ return Q.all([_this.getLastUsedData(file),
+ QFS.lastModified(file)
+ .fail(function() {
+ return -1;
+ })])
+
+ .spread(function(data, lastUpdated) {
+ if (!data || !data.levels || !~lastUpdated) return false;
+
+ return !_this.checkLevelsChanged(levels, data, lastUpdated);
+ })
+ .then(function(valid) {
+ LOGGER.fverbose('%s is %s', file, valid?'valid':'not valid');
+ if (!valid)
+ return _this.storeBuildResult(
+ _this.getPath(output, suffix),
+ suffix,
+ _this.expandDeps(decl)
+ .then(function(deps) {
+ return deps.stringify();
+ }))
+ .then(function() {
+ return _this.saveLastUsedData(file,
+ {
+ levels: levels.map(function(level) {
+ return {
+ dir: level.dir,
+ fileCount: (level.files.files['deps.js'] || []).length
+ }
+ })
+ }
+ )
+ });
+ })
+
+ });
+
+ },
+
+ checkLevelsChanged: function(levels, cache, lastUpdated) {
+ if (levels.count !== cache.levels.count) return true;
+
+ for(var i = 0; i < levels.length; i++) {
+ if (levels[i].dir !== cache.levels[i].dir) {
+ return true;
+ }
+
+ if (((levels[i].files.files['deps.js'] || [])).length !== cache.levels[i].fileCount) {
+ return true;
+ }
+ }
+
+ for(var i = 0; i < levels.length; i++) {
+ var level = levels[i],
+ files = level.files.files['deps.js'] || [];
+
+ for(var file in files) {
+ if (files[file].lastUpdated > lastUpdated) return true;
+ }
+ }
+
+ return false;
+ }
+
+});
+
+var Deps = exports.Deps = INHERIT({
+
+ __constructor: function(deps) {
+ this.items = {};
+ this.itemsByOrder = [];
+ this.uniqExpand = {};
+
+ // Force adding of root item to this.items
+ var rootItem = this.rootItem = new DepsItem({});
+ this.items[rootItem.buildKey()] = rootItem;
+
+ deps && this.parse(deps);
+ },
+
+ add: function(target, depsType, item) {
+ var items = this.items,
+ targetKey = target.buildKey(),
+ itemKey = item.buildKey();
+
+ if(!items[itemKey]) {
+ items[itemKey] = item;
+ this.itemsByOrder.push(itemKey);
+ }
+
+ (items[targetKey] || (items[targetKey] = target))[depsType].push(itemKey);
+ },
+
+ remove: function(target, item) {
+ target = this.items[target.buildKey()];
+ var itemKey = item.buildKey();
+ U.removeFromArray(target.shouldDeps, itemKey);
+ U.removeFromArray(target.mustDeps, itemKey);
+ },
+
+ clone: function(target) {
+ target || (target = new this.__self());
+
+ var items = this.items;
+ for(var i in items) {
+ if(!items.hasOwnProperty(i)) continue;
+ target.items[i] = items[i].clone();
+ }
+
+ target.itemsByOrder = this.itemsByOrder.concat();
+ target.tech = this.tech;
+ target.uniqExpand = this.uniqExpand;
+
+ return target;
+ },
+
+ parse: function(deps, ctx, fn) {
+ fn || (fn = function(i) { this.add(this.rootItem, 'shouldDeps', i) });
+
+ var _this = this,
+
+ forEachItem = function(type, items, ctx) {
+ items && !U.isEmptyObject(items) && (Array.isArray(items) ? items : [items]).forEach(function(item) {
+
+ if(isSimple(item)) {
+ var i = item;
+ (item = {})[type] = i;
+ }
+ item.name && (item[type] = item.name);
+
+ var depsItem = new DepsItem(item, ctx);
+
+ fn.call(_this, depsItem); // _this.add(rootItem, 'shouldDeps', depsItem);
+
+ _this.parse(
+ item.mustDeps,
+ depsItem,