graph
The graph of dependencies for BEM entities.
Introduction
Graph allows you to create an ordered dependencies list for the specified BEM entities and technologies.
Try graph
An example is available in the RunKit editor.
Quick start
Attention. To use
@bem/sdk.graph
, you must install Node.js 8.0+.
To run the @bem/sdk.graph
package:
Installing the @bem/sdk.graph
package
To install the @bem/sdk.graph
package, run the following command:
$ npm install --save @bem/sdk.graph
Creating an empty graph
Create a JavaScript file with any name (for example, app.js) and insert the following:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
Note. Use the same file for all of the following steps.
Creating vertices
Create new vertices for the blocks a
and b
:
graph.vertex({ block: 'a'});
graph.vertex({ block: 'b'});
Setting dependencies by using the dependsOn()
function
Assume that block a
depends on block b
. This means that block b
has some code that must be imported before the block a
code.
Let's also say that block b
depends on block c
:
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
graph.vertex({ block: 'b'})
.dependsOn({ block: 'c'});
If you are familiar with the @bem/sdk.deps package,
dependsOn()
adds themustDeps
link.
Getting the dependencies of a block
So block a
depends on block b
, and block b
depends on block c
. If we want to compile block a
, we need to import the code of block c
first, then import the code of block b
, and only then use the code of block a
.
The dependenciesOf()
function will return entity names to us in the correct order:
graph.dependenciesOf({ block: 'a'})
// => [
// { 'entity': { 'block': 'c'}},
// { 'entity': { 'block': 'b'}},
// { 'entity': { 'block': 'a'}}
// ]
Setting dependencies by using the linkWith()
function
Let's say that block b
also depends on block d
, but it doesn't matter when the code from block d
is imported (before or after block b
).
Change the code to set this dependency for the block b
vertex.
app.js:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
graph.vertex({ block: 'b'})
.dependsOn({ block: 'c'})
.linkWith({ block: 'd'});
graph.dependenciesOf({ block: 'a'})
// => [
// { 'entity': { 'block': 'c'}},
// { 'entity': { 'block': 'b'}},
// { 'entity': { 'block': 'a'}},
// { 'entity': { 'block': 'd'}}
// ]
In the dependencies list, block d
will be added to any position randomly.
If you are familiar with the @bem/sdk.deps package,
linkWith()
adds theshouldDeps
link.
API reference
BemGraph.vertex()
Registers a new vertex for the specified BEM entity and technology.
/**
* @typedef BemEntityName
* @property {string} block — Block name.
* @property {string} [elem] — Element name.
* @property {string|Object} [mod] — Modifier name or object with name and value.
* @property {string} mod.name — Modifier name.
* @property {string} [mod.val=true] — Modifier value.
*/
/**
* @param {BemEntityName} entity — Representation of the BEM entity name.
* @param {string} [tech] — Tech of the BEM entity.
* @returns {BemGraph.Vertex} — A created vertex with methods that allow you to link it with other vertices.
*/
BemGraph.vertex(entity, tech)
Example:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'my-block', elem: 'my-element', mod: 'my-modifier'}, 'css');
BemGraph.Vertex.linkWith()
Creates an unordered link between contained and passed vertices.
/**
* @param {BemEntityName} entity — Representation of the BEM entity name.
* @param {string} [tech] — Tech of the BEM entity.
*/
BemGraph.Vertex.linkWith(entity, tech)
Example:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.linkWith({ block: 'b'});
BemGraph.Vertex.dependsOn()
Creates an ordered link between contained and passed vertices.
/**
* @param {BemEntityName} entity — Representation of the BEM entity name.
* @param {string} [tech] — Tech of the BEM entity.
*/
BemGraph.Vertex.dependsOn(entity, tech)
Example:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
BemGraph.dependenciesOf()
Creates an ordered list of the entities and technologies.
For each object passed in the cells
parameter, a new BemCell
object will be created using the create() function from the @bem/sdk.cell
package.
/**
* @param {Object|Array} cells — One or more objects to create BEM cells for and get the dependencies list for.
* @param {string} cells.block — Block name.
* @param {string} cells.elem — Element name.
* @param {string|object} cells.mod — Modifier name or object with name and value.
* @param {string} cells.mod.name — Modifier name.
* @param {string} cells.mod.val — Modifier value.
* @param {string} cells.tech — Tech of cell.
* @return {Array} — Ordered list of the entities and technologies.
*/
BemGraph.dependenciesOf(cells)
Example:
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
graph.dependenciesOf();
BemGraph.naturalize()
Creates "natural" links between registered vertices:
An element should depend on a block.
A block modifier should depend on a block.
An element modifier should depend on an element.
BemGraph.naturalize()
See an example of using this function in the Naturalize graph section.
Parameters tuning
Specify a technology for the created vertex
When you create a new vertex you can specify the technology.
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
graph.vertex({ block: 'a'}, 'css')
.dependsOn({ block: 'c'});
This code means that only block a
with the CSS technology depends on block c
. If you get the dependencies list for block a
with another technology or without any technology, block c
will not be in this list.
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'});
graph.vertex({ block: 'a'}, 'css')
.dependsOn({ block: 'c'});
graph.dependenciesOf({ block: 'a'});
// => [
// { 'entity': { 'block': 'b'}},
// { 'entity': { 'block': 'a'}},
// ]
graph.dependenciesOf({ block: 'a'}, 'js');
// => [
// { 'entity': { 'block': 'b'}, 'tech': 'js'},
// { 'entity': { 'block': 'a'}, 'tech': 'js'}
// ]
graph.dependenciesOf({ block: 'a'}, 'css');
// => [
// { 'entity': { 'block': 'c'}, 'tech': 'css'},
// { 'entity': { 'block': 'b'}, 'tech': 'css'},
// { 'entity': { 'block': 'a'}, 'tech': 'css'}
// ]
Specify a technology for the dependency
When you set a dependency for the created vertex you can specify the technology.
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'}, 'js');
graph.vertex({ block: 'b'}, 'css')
.dependsOn({ block: 'common-css'});
graph.vertex({ block: 'b'}, 'js')
.dependsOn({ block: 'common-js'});
This code means that block a
depends on block b
with the js
technology. The dependencies list for block a
will include the common-js
block, but won't include the common-css
block.
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b'}, 'js');
graph.vertex({ block: 'b'}, 'css')
.dependsOn({ block: 'common-css'});
graph.vertex({ block: 'b'}, 'js')
.dependsOn({ block: 'common-js'});
graph.dependenciesOf({ block: 'a'});
// => [
// { 'entity': { 'block': 'common-js'}, 'tech': 'js'},
// { 'entity': { 'block': 'b'}, 'tech': 'js'},
// { 'entity': { 'block': 'a'}}
// ]
Naturalize graph
Let's say you create a new vertex for the blocks a
and b
and set block a
to depend on the block element b__el
.
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.dependsOn({ block: 'b', elem: 'el'});
graph.vertex({ block: 'b'});
graph.dependenciesOf({block: 'a'});
// => [
// { 'entity': { 'block': 'b', elem: 'el'}},
// { 'entity': { 'block': 'a'}}
// ]
graph.naturalize();
graph.dependenciesOf({block: 'a'});
// => [
// { 'entity': { 'block': 'b'}},
// { 'entity': { 'block': 'b', elem: 'el'}},
// { 'entity': { 'block': 'a'}}
// ]
In this code, calling the graph.naturalize()
function works the same way as the following code:
graph.vertex({ block: 'b', elem: `el` })
.dependsOn({ block: 'b'});
Get dependencies for the list of cells
You can get the dependencies list for multiple cells. To do this, create an array of cells and pass this array to the dependenciesOf()
function.
const { BemGraph } = require('@bem/sdk.graph');
const graph = new BemGraph();
graph.vertex({ block: 'a'})
.linkWith({ block: 'b'});
graph.vertex({ block: 'c'}, 'js')
.dependsOn({ block: 'd'});
const cells = [
{ block: 'a'},
{ block: 'c', tech: 'js'}
]
// Create a BEM cell for each object in the `cells` array and get the dependencies list for these objects.
graph.dependenciesOf(cells);
// => [
// { 'entity': { 'block': 'a'}},
// { 'entity': { 'block': 'd'}},
// { 'entity': { 'block': 'c'}}
// { 'entity': { 'block': 'b'}}
// ]
Usage examples
Create a Header dependencies list
The BEM methodology provides an example of a typical Header.
Let's create a graph and get the dependencies list for the Head block from this example.
const { BemGraph } = require('@bem/sdk.graph');
const BemCell = require('@bem/sdk.cell');
const graph = new BemGraph();
graph.vertex({ block: 'head'})
.dependsOn({ block: 'menu'})
.dependsOn({ block: 'logo'})
.dependsOn({ block: 'search'})
.dependsOn({ block: 'auth'});
graph.vertex({ block: 'search'})
.dependsOn({ block: 'input', mod: 'search-input'})
.dependsOn({ block: 'button', mod: 'search-button'});
graph.vertex({ block: 'menu'})
.dependsOn({ block: 'tab', elem: 'tab1'})
.dependsOn({ block: 'tab', elem: 'tab2'})
.dependsOn({ block: 'tab', elem: 'tab3'})
.dependsOn({ block: 'tab', elem: 'tab4'});
graph.vertex({ block: 'auth'})
.dependsOn({ block: 'input', elem: 'login'})
.dependsOn({ block: 'input', elem: 'password'})
.dependsOn({ block: 'button', mod: 'sign-in'});
// Register remaining vertices to naturalize the graph.
graph.vertex({ block: 'input'});
graph.vertex({ block: 'button'});
graph.vertex({ block: 'tab'});
graph.naturalize();
graph.dependenciesOf({ block: 'head'}).map(c => BemCell.create(c).id).join('\n');
// => tab
// tab__tab1
// tab__tab2
// tab__tab3
// tab__tab4
// menu
// logo
// input
// input_search-input
// button
// button_search-button
// search
// input__login
// input__password
// button_sign-in
// auth
// head