EN RU
Forum

Methodology

Technology

Toolbox

Libraries

Tutorials

walk

Tool for traversing a BEM project's file system.

NPM Status

Introduction

Walk traverses the project's file system and returns the following information about found files:

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.

KeySupported valuesnaminglegacy, 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

ParameterTypeDescriptionlevelsstring[]Paths to traverseconfigobjectProject levels

Output data

A readable stream (stream.Readable) that has the following events:

EventDescriptiondataReturns 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" },

    "tech": "bemtree.js",

    "layer": "common"

  },

  "path": "common.blocks/page/page.bemtree.js"

  "level": "common.blocks"

}


Fields:


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.errorGenerated if an error occurred while traversing the levels. Returns an object with the error description.endGenerated when 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: '.' } ],
    ...
}
*/

RunKit live editor.

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: '.' },
    ...
]
*/

RunKit live editor.

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' },
...
]
*/

RunKit live editor.