bem-walk
Tool for traversing a BEM project's file system.
It returns the following information about found files:
- The type of BEM entity (block, element or modifier).
- The implementation technology.
- The location in the file system.
Quick start
Note To use bem-walk
, you must install Node.js 4.0+.
1. Install bem-walk
$ npm install --save @bem/walk
2. Enable bem-walk
Create a JavaScript file with any name and add the following string:
const walk = require('@bem/walk');
Note You will use this JavaScript file for all the following steps.
3. Define file system levels
Define the project's file system levels in the config
object.
const config = {
// project levels
levels: {
'lib/bem-core/common.blocks': {
// `naming` — file naming scheme
naming: 'two-dashes'
},
'common.blocks': {
// `scheme` — file system scheme
scheme: 'nested'
}
}
};
Specify either the file naming scheme or the file system type for each level. This lets you get information about BEM entities using their names or using the names of files and directories.
The table shows the possible values that can be set for each of the schemes.
Key | Scheme | Default value | Possible values |
---|---|---|---|
naming |
File naming. | origin |
origin , two-dashes
|
scheme |
File system. | nested |
nested , flat
|
More information:
Note Instead of defining the project's levels manually, use the bem-config
tool.
const config = require('bem-config')();
const levelMap = config.levelMapSync();
const stream = walk(levels, levelMap);
4. Define the paths to traverse
Specify the paths to walk in the levels
object.
Path options:
-
Relative to the root directory.
const levels = [ 'libs/bem-core/common.blocks', 'common.blocks' ];
-
Absolute.
const levels = [ '/path/to/project/lib/bem-core/common.blocks', '/path/to/project/common.blocks' ];
5. Get information about found files
Pass the walk() method the levels
and config
objects.
Streaming is used for getting data about found files. When a portion of data is received, the data
event is generated and information about the found file is added to the files
array. If an error occurs, bem-walk
stops processing the request and returns a response containing the error ID and description. The end
event occurs when all the data has been received from the stream.
const files = [];
const stream = walk(levels, config);
// adds information about a found file to the end of the "files" array
stream.on('data', file => files.push(file));
stream.on('error', console.error);
stream.on('end', () => console.log(files));
Complete code sample
When all these steps have been completed, the full code of the JavaScript file should look like this:
const walk = require('@bem/walk');
const config = require('bem-config')();
const levels = [
'libs/bem-components/common.blocks',
'common.blocks'
];
const files = [];
const stream = walk(levels, {
levels: config.levelMapSync()
})
.on('data', file => files.push(file))
.on('error', console.error)
.on('end', () => console.log(files));
Note This sample uses the bem-config
package.
API
walk method
walk(levels, config);
Description
Traverses the directories described in the levels
parameter and returns stream.Readable
.
Input parameters
Requirements for the search are defined in the levels
and config
parameters.
Parameter | Type | Description |
---|---|---|
levels | string[] |
Paths to traverse |
config | object |
Project levels |
Output data
Readable stream (stream.Readable
) that has the following events:
Event | Description |
---|---|
'data' | Returns a JavaScript object with information about a found file. The example below shows a JSON interface with elements that are in the response for the walk method. Objects and keys have sample values.Example { "cell": { "entity": { "block": "page" }, "layer": "libs/bem-core/desktop.blocks", "tech": "bemhtml" }, "path": "libs/bem-core/desktop.blocks/page/page.bemhtml.js" } cell — BEM cell instance.entity — BEM entity name instance.layer — Directory path.tech — Implementation technology.path — Relative path to the file. |
'error' | Generated if an error occurred while traversing the levels. Returns an object with the error description. |
'end' | Generated when bem-walk finishes traversing the levels defined in the levels object. |
Usage examples
Typical tasks that use the resulting JavaScript objects:
Grouping
Grouping found files by block name.
const walk = require('@bem/walk');
const config = require('bem-config')();
const util = require('util');
const levels = [
'libs/bem-components/common.blocks',
'common.blocks'
];
const groups = {};
const stream = walk(levels, {
levels: config.levelMapSync()
})
.on('data', file => {
// Getting the block name for a found file.
const block = file.entity.block;
// Adding information about the found file.
(groups[block] = []).push(file);
})
.on('error', console.error)
.on('end', () => console.log(util.inspect(groups, {
depth: null
})));
/*
{ button:
[ BemFile {
cell: BemCell {
entity: BemEntityName { block: 'button', mod: { name: 'togglable', val: 'radio' } },
tech: 'spec.js',
layer: 'libs/bem-components/common.blocks'
},
path: 'libs/bem-components/common.blocks/button/_togglable/
button_togglable_radio.spec.js' } ] },
...
}
*/
Filtering
Finding files for the
popup
block.
const walk = require('@bem/walk');
const config = require('bem-config')();
const levels = [
'libs/bem-components/common.blocks',
'common.blocks'
];
const files = [];
const stream = walk(levels, {
levels: config.levelMapSync()
})
.on('data', file => {
// Getting the block name for a found file.
const block = file.entity.block;
// Adding information about the found file.
if (block == 'popup') {
files.push(file);
}
})
.on('error', console.error)
.on('end', () => console.log(files));
/*
[BemFile {
cell: BemCell {
entity: BemEntityName { block: 'popup', mod: { name: 'target', val: true } },
tech: 'js',
layer: 'libs/bem-components/common.blocks'
},
path: 'libs/bem-components/common.blocks/popup/_target/popup_target.js'
},
...
]
*/
Transformation
Finding BEM files, reading the contents, and creating the new
source
property.
const fs = require('fs');
const walk = require('@bem/walk');
const config = require('bem-config')();
const stringify = require('JSONStream').stringify;
const through2 = require('through2');
const levels = [
'libs/bem-components/common.blocks',
'common.blocks'
];
const stream = walk(levels, {
levels: config.levelMapSync()
})
.pipe(through2.obj(function(file, enc, callback) {
try {
// Certain technologies (for example, `i18n`) might be directories instead of files.
if (fs.statSync(file.path).isFile()) {
// Writing the file content to the `source` field.
file.source = fs.readFileSync(file.path, 'utf-8');
this.push(file);
}
} catch (err) {
callback(err);
}
}))
.pipe(stringify())
.pipe(process.stdout);
/*
[{"cell":{
"entity":{"block":"search","elem":"header"},
"tech":"css",
"layer":"common.blocks"
},
"path":"common.blocks/search/__header/search__header.css",
"source":".search__header {\n\tdisplay: block;\n\tfont-size: 20px;\n\tcolor:
rgba(0,0,0,0.84);\n\tmargin: 0;\n\tpadding: 0 0 16px;\n\n}\n\n"},
...
]
*/