Skip to content

Loading…

Ленивое вычисление уровня в BlockNode и его потомках #341

Closed
wants to merge 18 commits into from

3 participants

@narqo
Block, Element, Modifier member

Из-за того, что в BlockNode уровень вычисляется на этапе конструктора, невозможно собирать уровни, которых «еще» нет на файловой структуре, поскольку технологии описысанные в конфиге уровня не будут разрезолвленны. Вместо них, на этапе «холодного» запуска bem-make будут всегда использоваться технологии из bem-tools.

Задача собирать уровень которого еще нет, актуальна для сборки примеров БЭМ-сущностей, см. проект bem-pr.

\cc @arikon @scf2k

arikon and others added some commits
@arikon arikon Add Level.matchPath() method to recursively match fs paths to array o…
…f BEM-entity objects; add path.split() helper function
d126494
@arikon arikon Level.createIntrospector(): add `stopFilter` callback to `opts`, `cre…
…ator` callback now receive `path` as a 3rd argument
01a8791
@arikon arikon Add ability to create full project structure using `bem create` commands
- Add `project` level prototype
- Add `project ` techs that should be used to create projects as a level with `project` level prototype
- Add `level` tech module to be used as a base for the techs to create levels
- Add `blocks` and `bundles` tech modules that should be used to create blocks and bundles level respectively
- Add `Level.getTypes()` function that should return array of level type strings
- Refactor `util.findLevel()` to respect `Level.getTypes()` result
c0476aa
@arikon arikon Merge branch 'master' into introspect 8aa8940
@arikon arikon Create `examples.js` level prototype in `project` tech; add `examples…
…` tech to `blocks.js` level prototype
97f3320
@arikon arikon Add `util.serializeBemPath()` and `util.deserializeBemPath()` methods 6e40ec6
@arikon arikon bem create: Ability to customize default techs list based on BEM-entity 95b31d5
@narqo narqo A typo in logging message 93e2e22
@narqo narqo Another typo fix 68c8505
@arikon arikon Merge branch 'master' into introspect bad0749
@arikon arikon Merge branch 'master' into introspect
Conflicts:
	lib/nodes/lib.js
3dcd08c
@arikon arikon Merge branch 'master' into introspect 8a728a2
@arikon arikon Add Level.resolveBemPath() and Level.resolveBemRelPath() methods and …
…a basic test for Level.resolveBemRelPath()
7f911d2
@arikon arikon Add a NOTE to Level.resolveBemPath() 61f2b8a
@narqo
Block, Element, Modifier member

You define var _this = this below, so this line will fail with TypeError: Cannot read property 'target' of undefined

@arikon
Block, Element, Modifier member

Будет в очередной версии 0.6.x

@arikon arikon closed this
@arikon arikon added a commit that referenced this pull request
@arikon arikon BlockNode: Lazy level init in node constructor
See #341 for more info
9d6eb87
@arikon arikon deleted the blocknode-lazy-level-resolving branch
@arikon
Block, Element, Modifier member

bem@0.6.12

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 26, 2012
  1. @arikon

    Add Level.matchPath() method to recursively match fs paths to array o…

    arikon committed
    …f BEM-entity objects; add path.split() helper function
  2. @arikon

    Level.createIntrospector(): add `stopFilter` callback to `opts`, `cre…

    arikon committed
    …ator` callback now receive `path` as a 3rd argument
  3. @arikon

    Add ability to create full project structure using `bem create` commands

    arikon committed
    - Add `project` level prototype
    - Add `project ` techs that should be used to create projects as a level with `project` level prototype
    - Add `level` tech module to be used as a base for the techs to create levels
    - Add `blocks` and `bundles` tech modules that should be used to create blocks and bundles level respectively
    - Add `Level.getTypes()` function that should return array of level type strings
    - Refactor `util.findLevel()` to respect `Level.getTypes()` result
  4. @arikon
Commits on Oct 27, 2012
  1. @arikon

    Create `examples.js` level prototype in `project` tech; add `examples…

    arikon committed
    …` tech to `blocks.js` level prototype
Commits on Oct 29, 2012
  1. @arikon
  2. @arikon
Commits on Oct 31, 2012
  1. @narqo

    A typo in logging message

    narqo committed
  2. @narqo

    Another typo fix

    narqo committed
Commits on Nov 8, 2012
  1. @arikon
Commits on Nov 13, 2012
  1. @arikon

    Merge branch 'master' into introspect

    arikon committed
    Conflicts:
    	lib/nodes/lib.js
  2. @arikon
  3. @arikon

    Add Level.resolveBemPath() and Level.resolveBemRelPath() methods and …

    arikon committed
    …a basic test for Level.resolveBemRelPath()
Commits on Nov 14, 2012
  1. @arikon
Commits on Nov 19, 2012
  1. @narqo

    Fix: "Cannot read property 'target' of undefined"

    narqo committed
    see #commitcomment-2158996
Commits on Nov 23, 2012
  1. @scf2k
Commits on Jan 16, 2013
  1. @narqo
  2. @narqo
View
13 lib/commands/create.js
@@ -86,18 +86,21 @@ module.exports = function() {
eachModVal = function(itemBase, val) {
items.push(bemUtil.extend({ val: val }, itemBase));
- },
-
- techs = context.getDefaultTechs();
-
- if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+ };
opts.block.forEach(eachBlock);
return Q.all(items.map(function(item) {
+
+ var techs = context.getDefaultTechs(item);
+
+ if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, ' +
+ '-T or --add-tech, -t options');
+
return Q.all(techs.map(function(t) {
return context.getTech(t).createByDecl(item, opts.level, addOpts);
})).get(0);
+
})).get(0);
});
View
14 lib/commands/create/block.js
@@ -23,15 +23,19 @@ module.exports = function() {
.act(function(opts, args) {
var context = new Context(opts.level, opts),
- addOpts = { args: args.raw || [], force: opts.force },
- techs = context.getDefaultTechs();
-
- if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+ addOpts = { args: args.raw || [], force: opts.force };
return Q.all(args.names.map(function(name) {
+
+ var item = { block: name },
+ techs = context.getDefaultTechs(item);
+
+ if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+
return Q.all(techs.map(function(t) {
- return context.getTech(t).createByDecl({ block: name }, opts.level, addOpts);
+ return context.getTech(t).createByDecl(item, opts.level, addOpts);
})).get(0);
+
})).get(0);
});
View
14 lib/commands/create/elem.js
@@ -28,15 +28,19 @@ module.exports = function() {
.act(function(opts, args) {
var context = new Context(opts.level, opts),
- addOpts = { args: args.raw || [], force: opts.force },
- techs = context.getDefaultTechs();
-
- if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+ addOpts = { args: args.raw || [], force: opts.force };
return Q.all(args.names.map(function(name) {
+
+ var item = { block: opts.blockName, elem: name },
+ techs = context.getDefaultTechs(item);
+
+ if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+
return Q.all(techs.map(function(t) {
- return context.getTech(t).createByDecl({ block: opts.blockName, elem: name }, opts.level, addOpts);
+ return context.getTech(t).createByDecl(item, opts.level, addOpts);
})).get(0);
+
})).get(0);
});
View
12 lib/commands/create/mod.js
@@ -38,18 +38,20 @@ module.exports = function() {
var context = new Context(opts.level, opts),
addOpts = { args: args.raw || [], force: opts.force },
- itemBase = { block: opts.blockName },
- techs = context.getDefaultTechs();
-
- if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+ itemBase = { block: opts.blockName };
if (opts.elemName) itemBase.elem = opts.elemName;
return Q.all(args.names.map(function(name) {
return Q.all((opts.modVal || ['']).map(function(val) {
- var item = bemUtil.extend({}, itemBase, { mod: name });
+ var item = bemUtil.extend({}, itemBase, { mod: name }),
+ techs;
if (val) item.val = val;
+ techs = context.getDefaultTechs(item);
+
+ if (!techs.length) return Q.reject('You should specify techs to create using --force-tech, -T or --add-tech, -t options');
+
return Q.all(techs.map(function(t) {
return context.getTech(t).createByDecl(item, opts.level, addOpts);
})).get(0);
View
6 lib/context.js
@@ -72,10 +72,10 @@ exports.Context = INHERIT({
return this.opts.tech;
},
- getDefaultTechs: function() {
- // Список технологий по умолчанию с последнего уровня переопределения
+ getDefaultTechs: function(item) {
+ // List of default techs from the level of definition
var defaultTechs = (!this.opts.forceTech && this.getLevel())?
- this.getLevel().getDefaultTechs() : [],
+ this.getLevel().getDefaultTechs(item) : [],
opts = this.opts;
opts.forceTech && defaultTechs.push.apply(defaultTechs, opts.forceTech);
View
115 lib/level.js
@@ -20,7 +20,7 @@ var PATH = require('./path'),
* @param {String} path Path to level directory.
* @return {Level} Level object.
*/
-exports.createLevel = function(path) {
+var createLevel = exports.createLevel = function(path) {
// NOTE: в директории .bem внутри уровня переопределения
// лежит модуль-конфиг для уровня переопределения
return new (getLevelClass(PATH.resolve(path, '.bem', 'level.js'), true))(path);
@@ -45,6 +45,17 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
},
/**
+ * Return level type.
+ *
+ * Default is `['level']`.
+ *
+ * @return {String[]}
+ */
+ getTypes: function() {
+ return ['level'];
+ },
+
+ /**
* Place to store uncommon level configurations
*
* @return {Object}
@@ -156,9 +167,14 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
* Returns all declared techs in `defaultTechs` property or keys of result
* of `getTech()` method if `defaultTechs` is undefined.
*
+ * @param {Object} item BEM-entity object.
+ * @param {String} item.block Block name.
+ * @param {String} [item.elem] Element name.
+ * @param {String} [item.mod] Modifier name.
+ * @param {String} [item.val] Modifier value.
* @return {String[]} Array of tech names.
*/
- getDefaultTechs: function() {
+ getDefaultTechs: function(item) {
return this.defaultTechs || Object.keys(this.getTechs());
},
@@ -621,6 +637,89 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
},
/**
+ * Matches path recursively and returns array of BEM-enitity objects
+ * in case of positive match.
+ *
+ * @param {String} path Path to match.
+ * @return {Object[]|Boolean} Array of BEM-entity objects or false
+ */
+ matchPath: function(path) {
+
+ if (PATH.isAbsolute(path)) path = PATH.relative(this.dir, path);
+
+ var res = false;
+
+ PATH.split(path).forEach(function(part, i, parts) {
+
+ if (res) return;
+
+ var workPath = parts.slice(0, i + 1).join(PATH.dirSep),
+ nextPath = parts.slice(i + 1).join(PATH.dirSep),
+ levelPath = PATH.join(this.dir, workPath),
+ match = this.matchAny(workPath),
+ nextRes;
+
+ if (match && match.tech) {
+
+ res = [match];
+
+ if (nextPath && bemUtil.isLevel(levelPath)) {
+ nextRes = createLevel(levelPath).matchPath(nextPath);
+ res = nextRes? res.concat(nextRes) : false;
+ }
+
+ }
+
+ }, this);
+
+ return res;
+
+ },
+
+ /**
+ * @param {Object[]} bemPath Array of BEM-entity objects.
+ * @return {String[]|Boolean} Absolute paths on filesystem or false.
+ *
+ * TODO: think of always returning paths, even when level dirs are not exists
+ */
+ resolveBemPath: function(bemPath) {
+
+ var part = bemPath.slice(0, 1).shift(),
+ path;
+
+ if (part && part.tech) {
+ // NOTE: We assume that tech which represents a level
+ // - map to only one path
+ // - its suffix is equals to tech name
+ path = this.getPathByObj(part, part.tech);
+
+ if (bemPath.length > 1) {
+ if (bemUtil.isLevel(path)) {
+ return createLevel(path).resolveBemPath(bemPath.slice(1));
+ }
+ } else {
+ return this.getTech(part.tech).getPaths(this.getByObj(part));
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * @param {Object[]} bemPath Array of BEM-entity objects.
+ * @return {String[]|Boolean} Paths on filesystem, relative to this level or false.
+ */
+ resolveBemRelPath: function(bemPath) {
+
+ return this.resolveBemPath(bemPath)
+ .map(function(path) {
+ return PATH.relative(this.dir, path);
+ }, this);
+
+ },
+
+ /**
* Get declaration for block.
*
* @param {String} blockName Block name to get declaration for.
@@ -675,6 +774,7 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
* @param {String} [opts.from] Relative path to subdirectory of level directory to start introspection from.
* @param {Function} [opts.init] Function to return initial value of introspection.
* @param {Function} [opts.filter] Function to filter paths to introspect, must return {Boolean}.
+ * @param {Function} [opts.stopFilter] Function to filter out paths from recursive scanning, must return {Boolean}.
* @param {Function} [opts.matcher] Function to perform match of paths, must return introspected value.
* @param {Function} [opts.creator] Function to modify introspection object with matched value, must return new introspection.
* @return {Function} Introspection function.
@@ -699,13 +799,18 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
return !this.isIgnorablePath(path);
});
+ // stop recursion filter
+ opts.stopFilter || (opts.stopFilter = function(res, match, path) {
+ return false;
+ });
+
// matcher function
opts.matcher || (opts.matcher = function(path) {
return this.matchAny(path);
});
// result creator function
- opts.creator || (opts.creator = function(res, match) {
+ opts.creator || (opts.creator = function(res, match, path) {
if (match && match.tech) res.push(match);
return res;
});
@@ -723,7 +828,9 @@ var Level = exports.Level = INHERIT(/** @lends Level.prototype */{
res || (res = opts.init.call(level));
bemUtil.fsWalkTree(from, function(path) {
- res = opts.creator.call(level, res, opts.matcher.call(level, path));
+ var match = opts.matcher.call(level, path);
+ res = opts.creator.call(level, res, match, path);
+ return opts.stopFilter.call(level, res, match, path);
},
opts.filter,
level);
View
14 lib/levels/project.js
@@ -0,0 +1,14 @@
+exports.baseLevelPath = require.resolve('./simple');
+
+exports.getTypes = function() {
+ return ['project'].concat(this.__base());
+};
+
+exports.getTechs = function() {
+
+ return {
+ 'blocks': 'blocks',
+ 'bundles': 'bundles'
+ };
+
+};
View
12 lib/nodes/block.js
@@ -1,7 +1,7 @@
var INHERIT = require('inherit'),
PATH = require('../path'),
U = require('../util'),
- createLevel = require('../index').createLevel,
+ createLevel = require('../level').createLevel,
registry = require('../nodesregistry'),
MagicNode = require('./magic').MagicNodeName,
@@ -18,11 +18,15 @@ registry.decl(BlockNodeName, MagicNode, {
__constructor: function(o) {
- this.level = typeof o.level === 'string'?
- createLevel(PATH.resolve(o.root, o.level)) :
- o.level;
+ this._level = typeof o.level === 'string'? o.level : o.level.dir;
this.item = o.item || o;
+ // resolved Level object
+ var level;
+ this.__defineGetter__('level', function() {
+ return level || (level = createLevel(PATH.resolve(this.root, this._level)));
+ });
+
this.__base(U.extend({ path: this.__self.createPath(o) }, o));
},
View
2 lib/nodes/lib.js
@@ -55,7 +55,7 @@ registry.decl(LibraryNodeName, Node, /** @lends LibraryNode.prototype */ {
installLibraryDependencies: function() {
if (this.npmPackages === false) {
- LOGGER.finfo('npmPacakges config variable is set to false, skip installing npm dependencies for the %s library', this.target);
+ LOGGER.finfo('npmPackages config variable is set to false, skip installing npm dependencies for the %s library', this.target);
return;
}
View
10 lib/path.js
@@ -18,6 +18,16 @@ exports.isRoot = function(path) {
};
/**
+ * Split path by directory separator.
+ *
+ * @param {String} path
+ * @return {String[]}
+ */
+exports.split = function(path) {
+ return path.split(dirSep);
+};
+
+/**
* Extension to the PATH.relative() function to add `./` to the
* start of relative path if `dot` flag equals to `true`.
*
View
8 lib/tech.js
@@ -98,10 +98,10 @@ var Q = require('qq'),
* 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 {String} item.block BEM entity block name.
+ * @param {String} [item.elem] BEM entity elem name.
+ * @param {String} [item.mod] BEM entity modifier name.
+ * @param {String} [item.val] BEM entity modifier value.
* @param {Level} level Level instance object.
* @param {Object} opts Additional options.
* @returns {Promise * Undefined}
View
15 lib/techs/blocks.js
@@ -0,0 +1,15 @@
+var U = require('../util'),
+ PATH = require('path');
+
+exports.baseTechPath = require.resolve('./level.js');
+
+exports.techMixin = {
+
+ createLevel: function(opts, names) {
+
+ opts.level = PATH.resolve(U.findLevel(opts.outputDir, 'project'), '.bem/levels/blocks.js');
+ return this.__base(opts, names);
+
+ }
+
+};
View
15 lib/techs/bundles.js
@@ -0,0 +1,15 @@
+var U = require('../util'),
+ PATH = require('path');
+
+exports.baseTechPath = require.resolve('./level.js');
+
+exports.techMixin = {
+
+ createLevel: function(opts, names) {
+
+ opts.level = PATH.resolve(U.findLevel(opts.outputDir, 'project'), '.bem/levels/bundles.js');
+ return this.__base(opts, names);
+
+ }
+
+};
View
15 lib/techs/examples.js
@@ -0,0 +1,15 @@
+var U = require('../util'),
+ PATH = require('path');
+
+exports.baseTechPath = require.resolve('./level.js');
+
+exports.techMixin = {
+
+ createLevel: function(opts, names) {
+
+ opts.level = PATH.resolve(U.findLevel(opts.outputDir, 'project'), '.bem/levels/examples.js');
+ return this.__base(opts, names);
+
+ }
+
+};
View
18 lib/techs/level.js
@@ -0,0 +1,18 @@
+var BEM = require('../..');
+
+exports.techMixin = {
+
+ createByDecl: function(item, level, opts) {
+
+ return this.createLevel({
+ outputDir: level.dir,
+ force: opts.force
+ }, [this.getPath(level.getRelByObj(item))]);
+
+ },
+
+ createLevel: function(opts, names) {
+ return BEM.api.create.level(opts, { names: names });
+ }
+
+};
View
77 lib/techs/project.js
@@ -0,0 +1,77 @@
+var U = require('../util'),
+ Q = require('q'),
+ PATH = require('path');
+
+exports.baseTechPath = require.resolve('./level.js');
+
+exports.techMixin = {
+
+ createByDecl: function(item, level, opts) {
+
+ var _this = this,
+ projectName = item.block;
+
+ return this.createLevel({
+ outputDir: level.dir,
+ force: opts.force,
+ level: 'project'
+ }, [projectName])
+ .then(function() {
+ return _this.createProject(PATH.join(level.dir, projectName), opts);
+ });
+
+ },
+
+ /**
+ * Create the following project structure
+ *
+ * .bem/
+ * levels/
+ * blocks.js
+ * bundles.js
+ * techs/
+ * level.js
+ *
+ * @param {String} path Absolute path to the project directory
+ * @param {Object} opts Options to the `bem create` command
+ * @return {Promise * Undefined}
+ */
+ createProject: function(path, opts) {
+
+ var bemDir = PATH.join(path, '.bem'),
+ levels = PATH.join(bemDir, 'levels'),
+
+ // .bem/levels/create blocks.js level prototype
+ blocks = this.createLevel({
+ forceTech: ['examples'],
+ outputDir: levels,
+ force: opts.force
+ }, ['blocks.js']),
+
+ // create .bem/levels/bundles.js level prototype
+ bundles = this.createLevel({
+ outputDir: levels,
+ force: opts.force
+ }, ['bundles.js']),
+
+ // create .bem/levels/examples.js level prototype
+ examples = bundles.then(function() {
+ return this.createLevel({
+ level: PATH.resolve(levels, 'bundles.js'),
+ outputDir: levels,
+ force: opts.force
+ }, ['examples.js'])
+ }.bind(this)),
+
+ // create .bem/techs directory
+ techs = U.mkdirp(PATH.join(bemDir, 'techs')),
+
+ // run `npm link bem` command
+ linkBem = U.exec('npm link bem', { cwd: path, env: process.env });
+
+ return Q.all([blocks, bundles, examples, techs, linkBem])
+ .then(function() {});
+
+ }
+
+};
View
73 lib/util.js
@@ -6,7 +6,8 @@ var ASSERT = require('assert'),
VM = require('vm'),
UTIL = require('util'),
ENV = require('./env'),
- MKDIRP = require('mkdirp');
+ MKDIRP = require('mkdirp'),
+ _ = require('underscore');
exports.chdirOptParse = function() {
return this.opt()
@@ -260,11 +261,31 @@ exports.isLevel = function(path) {
exports.isFile(PATH.join(path, '.bem', 'level.js'));
};
-exports.findLevel = function(path, startPath) {
+/**
+ * Search for the nearest level recursivelt from the specified
+ * directory to the filesystem root.
+ *
+ * @param {String} path Path to start search from.
+ * @param {String[]|String|Undefined} [types] Level type to search.
+ * @param {String} [startPath]
+ * @return {String} Found level path or specified path if not found.
+ */
+exports.findLevel = function(path, types, startPath) {
+
+ var createLevel = require('./level').createLevel;
+
+ if (types && !Array.isArray(types)) types = [types];
startPath = startPath || path;
- if (exports.isLevel(path)) return path;
+
+ // Check for level and level type if applicable
+ if (exports.isLevel(path) &&
+ (!types || exports.containsAll(createLevel(path).getTypes(), types))) return path;
+
+ // Check for fs root
if (PATH.isRoot(path)) return startPath;
- return exports.findLevel(PATH.dirname(path), startPath);
+
+ return exports.findLevel(PATH.dirname(path), types, startPath);
+
};
/**
@@ -343,8 +364,7 @@ exports.fsWalkTree = function(root, fileCb, filterCb, ctx) {
while (files.length > 0) {
var path = PATH.join(root, files.shift());
if(filterCb && !filterCb.call(ctx, path)) continue;
- fileCb.call(ctx, path);
- if(exports.isDirectory(path)) exports.fsWalkTree(path, fileCb, filterCb, ctx);
+ if(!fileCb.call(ctx, path) && exports.isDirectory(path)) exports.fsWalkTree(path, fileCb, filterCb, ctx);
}
};
@@ -652,6 +672,30 @@ exports.bemParseKey = function(key) {
};
+var bemPathSep = ':';
+
+/**
+ * Serialize array of BEM-entity objects into colon separated string
+ * of BEM-entity keys.
+ *
+ * @param {Object[]} arr Array of BEM-entity object to serialize.
+ * @return {String} Serialized path.
+ */
+exports.serializeBemPath = function(arr) {
+ return arr.map(exports.bemFullKey).join(bemPathSep);
+};
+
+/**
+ * Deserialize string of colon separated BEM-entity keys into
+ * array of BEM-entity objects.
+ *
+ * @param {String} path Path to deserialize.
+ * @return {Object[]} Array of BEM-entity objects.
+ */
+exports.deserializeBemPath = function(path) {
+ return path.split(bemPathSep).map(exports.bemParseKey);
+};
+
/**
* Result of the uniq() can be used as a callback to [].reduce().
*
@@ -700,6 +744,23 @@ exports.removeFromArray = function(arr, o) {
false
};
+/**
+ * Return true if all of `needles` are found in `arr`.
+ *
+ * @param {Array} arr Array to search.
+ * @param {String[]|String} needles Needles to search.
+ * @return {Boolean}
+ */
+exports.containsAll = function(arr, needles) {
+
+ Array.isArray(needles) || (needles = [needles]);
+
+ return _.all(needles, function(i) {
+ return _.contains(arr, i);
+ });
+
+};
+
var getNodePrefix = exports.getNodePrefix = function(level, item) {
return PATH.join(
PATH.relative(ENV.getEnv('root'), level.dir),
View
1 test/data/level-project/.bem/level.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('bem/lib/levels/project');
View
7 test/data/level-project/.bem/levels/blocks.js
@@ -0,0 +1,7 @@
+exports.getTechs = function() {
+ var techs;
+ techs = {
+ 'examples': 'bem/lib/techs/examples.js'
+ };
+ return techs;
+};
View
0 test/data/level-project/.bem/levels/bundles.js
No changes.
View
1 test/data/level-project/.bem/levels/examples.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('./bundles.js');
View
1 test/data/level-project/pages.bundles/.bem/level.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('../../.bem/levels/bundles.js');
View
1 test/data/level-project/pages.bundles/page/page.blocks/.bem/level.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('../../../../.bem/levels/blocks.js');
View
1 test/data/level-project/pages.bundles/page/page.blocks/block/block.bundles/.bem/level.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('../../../../../../.bem/levels/bundles.js');
View
1 ...t/pages.bundles/page/page.blocks/block/block.bundles/example/example.blocks/.bem/level.js
@@ -0,0 +1 @@
+exports.baseLevelPath = require.resolve('../../../../../../../../.bem/levels/blocks.js');
View
3 ...pages.bundles/page/page.blocks/block/block.bundles/example/example.blocks/block/block.css
@@ -0,0 +1,3 @@
+.block
+{
+}
View
15 test/level.js
@@ -235,6 +235,21 @@ describe('level', function() {
});
+ describe(".resolveBemRelPath()", function() {
+ it("resolves to correct path", function() {
+ var level = createLevel(absolute('data/level-project')),
+ paths = level.resolveBemRelPath([
+ { block: 'pages', suffix: '.bundles', tech: 'bundles' },
+ { block: 'page', suffix: '.blocks', tech: 'blocks' },
+ { block: 'block', suffix: '.bundles', tech: 'bundles' },
+ { block: 'example', suffix: '.blocks', tech: 'blocks' },
+ { block: 'block', suffix: '.css', tech: 'css' }
+ ]);
+
+ assert.deepEqual(paths, ['pages.bundles/page/page.blocks/block/block.bundles/example/example.blocks/block/block.css']);
+ });
+ });
+
});
});
Something went wrong with that request. Please try again.