Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@bem/naming

bem-sdk3.3kMPL-2.02.0.0-6

Manage naming of BEM entities

bem, naming, entity, name, representation, parse, stringify, conventions, origin, react, two-dashes

readme

bem-naming

Tool for working with BEM entity representations: allows you to parse string representation and stringify object representation.

Supports various naming conventions: origin, two-dashes, react and allows to create your convention.

NPM Status Travis Status Coverage Status Dependency Status

Install

$ npm install --save @bem/naming

Usage

const bemNaming = require('@bem/naming');

bemNaming.parse('button__text'); // BemEntityName { block: 'button', elem: 'text' }
bemNaming.stringify({ block: 'button', mod: 'checked' }); // String 'button_checked'

Table of Contents

BEM Entity representation

With BEM entity representation you can define block, element, block modifier and element modifier.

The representation can include name of block, name of element, name of modifier and value of modifier.

BEM entity can be represented using Object or String.

Object representation

The BemEntityName class describes the representation of a BEM entity name.

String representation

To define BEM entities, we often use a special string format that allows us to define exactly which entity is represented.

According to the original BEM naming convention, it looks like this:

'block[_block-mod-name[_block-mod-val]][__elem-name[_elem-mod-name[_elem-mod-val]]]'

(Parameters within square brackets are optional)

Delimiters

The names are separated from each other by means of special delimiters.

The original naming uses the following delimiters:

  • __ — to separate an element from a block
  • _ — to separate a modifier name from a block or element and to separate a modifier value from a modifier name

Examples

BEM Entity Type String Representation
Block block-name
Block modifier block-scope_mod-name_mod-val
Element block-scope__elem-name
Element modifier block-scope__elem-scope_mod-name_mod-val

The simple modifier doesn't have value. Therefore, in the string representation the value should be omitted.

BEM Entity Type String Representation
Block modifier block-scope_mod-name
Element modifier block-scope__elem-scope_mod-name

Common misconceptions

The BEM methodology uses a flat structure inside blocks. This means that a BEM entity can't be represented as an element of another element, and the following string representation will be invalid:

'block__some-elem__sub-elem'

For more information, see the FAQ:

Why doesn't BEM recommend using elements within elements (block__elem1__elem2)?

Also, a BEM entity can't be a block modifier and an element modifier simultaneously, so the following string representation will be invalid:

'block_block-mod-name_block-mod-val__elem-name_elem-mod-name_elem-mod-val'

Naming conventions

The main idea of the naming convention is to make names of BEM entities as informative and clear as possible.

Read more in the section naming convention of the BEM methodology.

The BEM methodology provides an idea for creating naming rules and implements that idea in its canonical naming convention: origin naming convention.

However, a number of alternative schemes based on the BEM principles also exist in the world of web development:

In addition, you can invent your naming convention. How to do this, see the Custom naming convention section.

Origin naming convention

According to this convention elements are delimited with two underscores (__), modifiers and values of modifiers are delimited by one underscore (_).

Read more in the section naming convention of the BEM methodology.

Example:

const originNaming = require('@bem/naming')('origin');

originNaming.parse('block__elem');    // BemEntityName { block: 'block', elem: 'elem' }
originNaming.parse('block_mod_val');  // BemEntityName { block: 'block',
                                      //                 mod: { name: 'mod', val: 'val' } }

originNaming.stringify({
    block: 'block',
    elem: 'elem',
    mod: 'mod'
});

// ➜ block__elem_mod

Harry Roberts' naming convention

According to this convention elements are delimited with two underscores (__), modifiers are delimited by two hyphens (--), and values of modifiers are delimited by one underscore (_).

Read more in the Guidelines.

Example:

const twoDashesNaming = require('@bem/naming')('two-dashes');

twoDashesNaming.parse('block__elem');    // { block: 'block', elem: 'elem' }
twoDashesNaming.parse('block--mod_val'); // { block: 'block',
                                         //   mod: { name: 'mod', val: 'val' } }

twoDashesNaming.stringify({
    block: 'block',
    elem: 'elem',
    mod: 'mod'
});

// ➜ block__elem--mod

React naming convention

According to this convention elements are delimited with one hyphen (-), modifiers are delimited by one underscore (_), and values of modifiers are delimited by one underscore (_).

You can explore this convention at bem-react-components.

Example:

const reactNaming = require('@bem/naming')('react');

reactNaming.parse('Block-Elem');    // BemEntityName { block: 'Block', elem: 'Elem' }
reactNaming.parse('Block_Mod_Val'); // BemEntityName { block: 'Block',
                                    //                 mod: { name: 'Mod', val: 'Val' } }

reactNaming.stringify({
    block: 'Block',
    elem: 'Elem',
    mod: 'Mod'
});

// ➜ Block-Elem_Mod

Custom naming convention

To create an instance where you can manage your own naming convention use the bemNaming function.

Example:

const createBemNaming = require('@bem/naming');

const myNaming = createBemNaming({
    delims: {
        elem: '-',
        mod: { name: '--', val: '_' }
    },
    wordPattern: '[a-zA-Z0-9]+'   // because element and modifier's separators include
});                               // hyphen in it, we need to exclude it from block,
                                  // element and modifier's name

myNaming.parse('block--mod_val'); // BemEntityName
                                  // { block: 'block',
                                  //   mod: { name: 'mod', val: 'val' } }

const BemEntityName = require('@bem/entity-name');

myNaming.stringify(new BemEntityName({
    block: 'blockName',
    elem: 'elemName',
    mod: 'simpleElemMod'
});

// ➜ blockName-elemName--simpleElemMod

API

bemNaming({ delims: {elem, mod}, wordPattern })

Parameter Type Description Default
delims object Defines delimeters for elem and/or mods
delims.elem string Separates element's name from block. __
delims.mod string, { name: string, val: string } Separates modifier from block or element. _
delims.mod.name string Separates a modifier name from a block or an element. _
delims.mod.val string Separates the value of a modifier from the modifier name. _
wordPattern string Defines which characters can be used in names of blocks, elements, and modifiers. [a-z0-9]+(?:-[a-z0-9]+)*

parse(str)

Parameter Type Description
str string BEM entity name representation.

Parses the string into an instance of BemEntityName.

Example:

const bemNaming = require('@bem/naming');

bemNaming.parse('block__elem_mod_val');

// ➜ BemEntityName {
//     block: 'block',
//     elem: 'elem',
//     mod: { name: 'mod', val: 'val' }
// }

stringify(entityName)

Parameter Type Description
entityName BemEntityName, object BEM entity name representation.

Forms a string from the instance of BemEntityName.

Example:

const bemNaming = require('@bem/naming');
const BemEntityName = require('@bem/entity-name');

bemNaming.stringify(new BemEntityName({
    block: 'block',
    elem: 'elem',
    mod: { name: 'mod', val: 'val' }
});

// ➜ block__elem_mod_val

delims

Strings to separate names of bem entities.

Type: Object

delims.elem

String to separate an element from a block.

Type: String

Default: __

delims.mod.name

String to separate a modifier name from a block or element.

Type: String

Default: _

delims.mod.val

String to separate a modifier value from the name of the modifier.

Type: String

Default: _

License

Code and documentation copyright 2014 YANDEX LLC. Code released under the Mozilla Public License 2.0.

changelog

Changelog

2.0.0

BEM SDK

The bem-naming became part of the BEM SDK. In this regard, there have been several changes for consistency with other packages of BEM SDK.

Now BEM SDK modules are used in assembly systems and bem-tools plugins. Therefore, the modules support Node.js only.

  • Removed support of YModules and AMD (@blond #138).
  • Stopped publishing to Bower (@blond #118).

If it becomes necessary to use BEM SDK in browsers or other environments we'll figure out a system solution for all modules.

API

According to the principles of BEM SDK each module solves only one problem.

The bem-naming module did more than just parse and stringify BEM names.

Removed typeOf method (#98)

To work with BEM entities there are packages @bem/entity and @bem/entity-name.

API v1.x.x

const bemNaming = require('bem-naming');

// get type by string
bemNaming.typeOf('button'); // block

// get type by entity object
bemNaming.typeOf({ block: 'button', modName: 'focused' }); // blockMod

API v2.x.x

// get type by string
const parseBemName = require('@bem/naming').parse;
const blockName = parseBemName('button');

blockName.type // block

// get type by entity object
const BemEntityName = require('@bem/entity-name');
const modName = new BemEntityName({ block: 'button', mod: 'focused' });

modName.type; // blockMod

Removed validate method (#147)

Use parse method instead.

API v1.x.x

const validate = require('bem-naming').validate;

validate('block-name'); // true
validate('^*^');        // false

API v2.x.x

const parse = require('@bem/naming').parse;

Boolean(parse('block-name')); // true
Boolean(parse('^*^'));        // false

The parse method returns BemEntityName object (#126).

It will allow to use helpers of BemEntityName.

Important: in BemEntityName the modName and modVal fields are deprecated. Use the mod field instead (#95).

API v1.x.x

const parse = require('bem-naming').parse;

const entityName = parse('button_disabled');

entityName.modName; // disabled
entityName.modVal;  // true

console.log(entityName); // { block: 'button', modName: 'disabled', modVal: true }

API v2.x.x

const parse = require('@bem/naming').parse;

const entityName = parse('button_disabled');

entityName.mod;  // { name: 'disabled', val: true }
entityName.id;   // button_disabled
entityName.type; // mod

console.log(entityName); // BemEntityName { block: 'button', mod: { name: 'disabled', val: true } }

The stringify method supports BemEntityName instance (#152).

Important: in BemEntityName the modName and modVal fields are deprecated. Use the mod field instead (#95).

API v1.x.x

const stringify = require('bem-naming').stringify;

stringify({ block: 'button', modName: 'disabled', modVal: true });

// ➜ button_disabled

API v2.x.x

const stringify = require('@bem/naming').stringify;
const BemEntityName = require('@bem/entity-name');

const entityName = new BemEntityName({ block: 'button', mod: 'disabled' });

stringify(entityName);

// ➜ button_disabled

The bem-naming constructor signature for custom-naming was changed (#160).

{ elem: '…', mod: '…' }{ delims: { elem: '…', mod: '…' } }

API v1.x.x

const bemNaming = require('bem-naming');

const myNaming = bemNaming({
    elem: '-',
    mod: { name: '--', val: '_' }
    wordPattern: '[a-zA-Z0-9]+'
});

myNaming.parse('block--mod_val'); // { block: 'block'
                                  //   modName: 'mod',
                                  //   modVal: 'val' }

API v2.x.x

const bemNaming = require('@bem/naming');

const myNaming = bemNaming({
    delims: {
        elem: '-',
        mod: { name: '--', val: '_' }
    },
    wordPattern: '[a-zA-Z0-9]+'
});

myNaming.parse('block--mod_val'); // BemEntityName
                                  // { block: 'block',
                                  //   mod: { name: 'mod', val: 'val' } }

Important: now if the delimiter of modifier value is not specified it doesn't inherit from delimiter of modifier name and falls back to default bemNaming.modValDelim (#169).

API v1.x.x

const bemNaming = require('bem-naming');

// myNaming1 is equal myNaming2
const myNaming1 = bemNaming({ mod: { name: '--' } });
const myNaming2 = bemNaming({ mod: { name: '--', val: '--' } });

API v2.x.x

const bemNaming = require('@bem/naming');

// myNaming1 is equal myNaming2
const myNaming1 = bemNaming({ delims: { mod: '--' } });
const myNaming2 = bemNaming({ delims: { mod: { name: '--', val: '--' } } });

// but myNaming1 is not equal myNaming3
const myNaming3 = bemNaming({ delims: { mod: { name: '--' } } });
// because myNaming3 is equal myNaming4
const myNaming4 = bemNaming({ delims: { mod: { name: '--', val: bemNaming.modValDelim } } });

Delims field (#167).

Added delims field instead of elemDelim, modDelim and modValDelim for consistency with bemNaming function.

API v1.x.x

const bemNaming = require('bem-naming');

bemNaming.elemDelim
bemNaming.modDelim
bemNaming.modValDelim

API v2.x.x

const bemNaming = require('@bem/naming');

bemNaming.delims.elem
bemNaming.delims.mod.name
bemNaming.delims.mod.val

NPM

Now BEM SDK modules are published in @bem scope, so the bem-naming module was renamed to @bem/naming (@blond #158).

Read more about scopes in NPM Documentation.

To install 1.x version of the module you need to run the command:

$ npm i bem-naming

To install 2.x version of the module you need to run the command:

$ npm i @bem/naming

Presets

  • Added react preset (@yeti-or #161).

Performance

  • Accelerated initialization for origin naming (@tadatuta #134).

Chore

  • Moved the package to bem-sdk organization (@blond b22dfc5).
  • Removed Russian docs (@blond #142).
  • Updated docs (@blond #153).
  • Run tests in Node.js v6 (@blond #114).

1.0.1

Bug fixes

  • Functions not working without context (#91).

    Example:

    
    var stringifyEntity = require('bem-naming').stringify;
    
    stringifyEntity({ block: 'button', modName: 'size', modVal: 's' });
    
    // Uncaught TypeError: Cannot read property 'modDelim' of undefined

Commits

1.0.0

Modifier Delimiters (#76)

Added support to separate value of modifier from name of modifier with specified string.

Before one could only specify a string to separate name of a modifier from name of a block or an element. It string used to separate value of modifier from name of modifier.

Before:

var myNaming = bemNaming({
    mod: '--'
});

var obj = {
    block: 'block',
    modName: 'mod',
    modVal: 'val'
};

myNaming.stringify(obj); // 'block--mod--val'

Now:

var myNaming = bemNaming({
    mod: { name: '--', val: '_' }
});

var obj = {
    block: 'block',
    modName: 'mod',
    modVal: 'val'
};

myNaming.stringify(obj); // 'block--mod_val'

Also added the modValDelim field.

Presets (#81)

Added naming presets:

  • origin (by default) — Yandex convention (block__elem_mod_val).
  • two-dashesHarry Roberts convention (block__elem--mod_val).

It is nessesary not to pass all options every time you use the convention by Harry Roberts.

var bemNaming = require('bem-naming');

// with preset
var myNaming = bemNaming('two-dashes');

Bug fixes

  • Functions for custom naming not working without context(#72).

    Example:

    
    var bemNaming = require('bem-naming');
    
    var myNaming = bemNaming({ mod: '--' });
    
    ['block__elem', 'block--mod'].map(myNaming.parse); // The `parse` function requires context of `myNaming` object.
                                                       // To correct work Usage of bind (myNaming.parse.bind(myNaming)) // was necessary.
  • this was used instead of global object. (#86).

Removed deprecated

  • The BEMNaming filed removed (#74).

    Use bemNaming function to create custom naming:

    var bemNaming = require('bemNaming');
    
    var myNaming = bemNaming({ elem: '__', mod: '--' });
  • The elemSeparator, modSeparator and literal options removed (#75).

    Use elem, mod and wordPattern instead.

  • The bem-naming.min.js file removed.

Other

  • The stringify method should return undefined for invalid objects, but not throw errror (#71).

    It will be easier to check for an empty string than use try..catch.

    Before:

    try {
        var str = bemNaming.stringify({ elem: 'elem' });
    } catch(e) { /* ... */ }

    Now:

    var str = bemNaming.stringify({ elem: 'elem' });
    
    if (str) {
        /* ... */
    }

Commits

0.5.1

  • Implemented caching for BEMNaming instances (#53).
  • stringify method is speeded up by 2,5 times (#57).
  • parse method is speeded up on 15% (#58).
  • typeOf method is speeded up by 2,25 times (#59).

0.5.0

  • API: delimiters provided (#48).

0.4.0

  • Simplified API for custom naming convention (#37).
  • Added method typeOf (#35).
  • Added support for CamelCase (#34).
  • Added license.

0.3.0

  • Option elemSeparator is deprecated, use elem instead.
  • Option modSeparator is deprecated, use mod instead.
  • Option literal is deprecated, use wordPattern instead.

0.2.1

  • Fixed package.json file.

0.2.0

  • Added ability to use BEM-naming object without modVal field.
  • Added minified version.
  • Fixed bug with is* methods for invalid strings.
  • Fixed bug with bemNaming for IE6-8.

0.1.0

  • Methods validate, isBlock, isElem, isBlockMod, isElemMod were added.
  • Generated string will not get modifier if modVal field of BEM-naming object is undefined.
  • stringify method throws error if invalid BEM-naming object is specified.
  • parse method was fixed: BEM-naming object does not contain explicit undefined fields.