walk
Tool for traversing a BEM project's file system.
Introduction
Walk traverses the project's file system and returns the following information about found files:
The implementation technology: JS, CSS, etc.
The location in the file system.
Note. If you don't have any BEM projects available to try out the
@bem/sdk.walk
package, the quickest way to create one is to use bem-express.
Try walk
An example is available in the RunKit editor.
Quick start
Attention. To use
@bem/sdk.walk
, you must install Node.js 8.0+.
To run the @bem/sdk.walk
package:
Installing the @bem/sdk.walk
package
To install the @bem/sdk.walk
package, run the following command:
$ npm install --save @bem/sdk.walk
Including the @bem/sdk.walk
package
Create a JavaScript file with any name (for example, app.js) and insert the following:
const walk = require('@bem/sdk.walk');
Note. Use the same file for all of the following steps.
Defining file system levels
Define the project's file system levels in the config
object.
Example:
const config = {
// Project levels.
levels: {
'level1': {
// File naming scheme.
naming: {
preset: 'value'
}
},
'level2': {
// File naming scheme.
naming: {
preset: 'value'
}
},
...
}
};
Specify the file naming scheme for each redefinition level. This lets you get information about BEM entities using their names or using the names of files and directories.
The table shows acceptable values that can be set for the file naming scheme.
naming
legacy
, origin
, two-dashes
, react
, origin-react
Note. For more information about the file naming preset, see @bem/sdk.naming.presets
app.js file:
const walk = require('@bem/sdk.walk');
// Config object with sample value.
const config = {
levels: {
'common.blocks': {
naming: {
preset: 'legacy'
}
}
}
};
Defining paths to traverse
Specify the paths to walk in the levels
object.
Note. You can use relative or absolute paths.
Example:
const levels = [
'common.blocks'
];
app.js file:
const walk = require('@bem/sdk.walk');
// Config object with sample value.
const config = {
levels: {
'common.blocks': {
naming: {
preset: 'legacy'
}
}
}
};
// Levels object with sample value.
const levels = [
'common.blocks'
];
Getting information about found files
Pass the levels
and config
objects to the walk() method.
app.js file:
const walk = require('@bem/sdk.walk');
// Config object with sample value.
const config = {
levels: {
'common.blocks': {
naming: {
preset: 'legacy'
}
}
}
};
// Levels object with sample value.
const levels = [
'common.blocks'
];
(async () => {
console.log(await walk.asArray(levels, config));
})();
The walk.asArray()
function 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, 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.
After that, run your web server using the node app.js
comand, and you will see a result that looks like this:
[
BemFile {
cell: {
entity: { block: 'page', mod: [Object] },
tech: 'bemtree.js',
layer: 'common'
},
path: 'common.blocks/page/_view/page_view_404.bemtree.js',
level: 'common.blocks'
},
BemFile {
cell: {
entity: { block: 'page', mod: [Object] },
tech: 'post.css',
layer: 'common'
},
path: 'common.blocks/page/_view/page_view_404.post.css',
level: 'common.blocks'
},
...
]
API reference
walk()
/**
* Traverse a BEM project's file system.
*
* @param {string[]} levels — paths to traverse
* @param {object} config — project's file system levels
* @return {{cell: {entity: ?BemEntityName, layer: ?string, tech: ?string},
path: ?string, level: ?string}[]} — readable stream
*/
walk(levels, config);
Traverses the directories described in the levels
parameter and returns stream.Readable
.
Parameters
levels
string[]
config
object
Output data
A readable stream (stream.Readable
) that has the following events:
data
walk
method. Objects and keys have sample values.
cell
— BEM cell instance.entity
— BEM entity name instance.tech
— Implementation technology.layer
— Semantic layer.path
— Relative path to the file.level
— File system level.error
end
walk
finishes traversing the levels defined in the levels
object.Parameter tuning
Walk provides a flexible interface for parameter tuning and can be configured to suit different tasks.
This section contains some tips on the possible parameter settings.
Extending config object definitions
If your project's file naming scheme doesn't match the default file system type, you can define it manually.
Example:
/**
* The project's file naming scheme is `legacy`, which matches the `nested` file system type by default.
* Step 1: https://github.com/bem/bem-sdk/blob/master/packages/naming.presets/legacy.js
* Step 2: https://github.com/bem/bem-sdk/blob/master/packages/naming.presets/origin.js
*/
const config = {
levels: {
'common.blocks': {
naming: {
preset: 'legacy',
// Manually defining the project's file system type.
fs: {
scheme: 'mixed'
}
}
}
}
};
Note. For more information about file systems, see @bem/sdk.naming.cell.match
In order to define the default layer, you can use the defaultLayer
field.
Example:
const config = {
levels: {
'common.blocks': {
naming: {
preset: 'legacy',
fs: {
defaultLayer: 'common'
}
}
}
}
};
Automatically defining config objects
Instead of defining the project's levels manually, you can use the @bem/sdk.config package.
Use the levelMapSync()
method which returns the project's file system levels.
Example:
const walk = require('@bem/sdk.walk');
const bemconfig = require('@bem/sdk.config')();
const levelMap = bemconfig.levelMapSync();
const levels = [
'.'
];
const config = {
levels: levelMap
};
(async () => {
console.log(await walk.asArray(levels, config));
})();
Usage examples
Typical tasks that use the resulting JavaScript objects:
Grouping
Grouping found files by block name.
const walk = require('@bem/sdk.walk');
const bemconfig = require('@bem/sdk.config')();
const util = require('util');
const levelMap = bemconfig.levelMapSync();
const levels = [
'.'
];
const config = {
levels: levelMap
};
const groups = {};
(async () => {
const files = await walk.asArray(levels, config);
files.filter(file => {
// Getting the block name for a found file.
const block = file.entity.block;
// Adding information about the found file.
(groups[block] = []).push(file);
});
console.log(util.inspect(groups, {
depth: null
}));
})();
/*
{ page:
[ BemFile { cell:
{ entity: { block: 'page', mod: { name: 'view', val: '404' } },
tech: 'post.css',
layer: 'common' },
path: 'common.blocks/page/_view/page_view_404.post.css',
level: '.' } ],
...
}
*/
Filtering
Finding files for the page
block.
const walk = require('@bem/sdk.walk');
const bemconfig = require('@bem/sdk.config')();
const levelMap = bemconfig.levelMapSync();
const levels = [
'.'
];
const config = {
levels: levelMap
};
const entities = [];
(async () => {
const files = await walk.asArray(levels, config);
files.filter(file => {
// Getting the block name for a found file.
const block = file.entity.block;
// Adding information about the found file.
if (block == 'page') {
entities.push(file);
}
});
console.log(entities);
})();
/*
[ BemFile { cell:
{ entity: { block: 'page' },
tech: 'bemtree.js',
layer: 'common' },
path: 'common.blocks/page/page.bemtree.js',
level: '.' },
BemFile { cell: { entity: { block: 'page' },
tech: 'deps.js', layer: 'common' },
path: 'common.blocks/page/page.deps.js',
level: '.' },
BemFile { cell:
{ entity: { block: 'page' },
tech: 'deps.js',
layer: 'development' },
path: 'development.blocks/page/page.deps.js',
level: '.' },
...
]
*/
Data transformation
Finding BEM files, reading the contents, and creating the new source
property.
const { promisify } = require('util');
const fs = require('fs');
const walk = require('@bem/sdk.walk');
const bemconfig = require('@bem/sdk.config')();
const readFileAsync = promisify(fs.readFile);
const levelMap = bemconfig.levelMapSync();
const levels = [
'.'
];
const config = {
levels: levelMap
};
(async() => {
const files = await walk.asArray(levels, config);
const res = {};
for (const file of files) {
res.file = file;
res.source = await readFileAsync(file.path, 'utf-8');
}
console.log(res);
})();
/*
{ file: BemFile { cell:
{ entity: { block: 'page' }, tech: 'deps.js', layer: 'development' },
path: 'development.blocks/page/page.deps.js',
level: '.' },
source: '({\n shouldDeps: \'livereload\'\n});\n' },
...
]
*/