npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bem/sdk.decl

v0.3.10

Published

Manage declaration of BEM entities

Downloads

201

Readme

decl

A tool for working with declarations in BEM.

NPM Status

Introduction

A declaration is a list of BEM entities (blocks, elements and modifiers) and their technologies that are used on a page.

A build tool uses declaration data to narrow down a list of entities that end up in the final project.

This tool contains a number of methods to work with declarations:

This tool also contains the assign() method. You can use this method to populate empty BEM cell fields with the fields from the scope.

Note. If you don't have any BEM projects available to try out the @bem/sdk.decl package, the quickest way to create one is to use bem-express.

Installation

To install the @bem/sdk.decl package, run the following command:

npm install --save @bem/sdk.decl

Quick start

Attention. To use @bem/sdk.decl, you must install Node.js 8.0+.

Use the following steps after installing the package.

To run the @bem/sdk.decl package:

  1. Load declarations from files
  2. Subtract declarations
  3. Intersect declarations
  4. Merge declarations
  5. Save declarations to a file

Loading declarations from files

Create two files with declarations and insert the following code into them:

set1.bemdecl.js:

exports.blocks = [
    {name: 'a'},
    {name: 'b'},
    {name: 'c'}
];

set2.bemdecl.js:

exports.blocks = [
    {name: 'b'},
    {name: 'e'}
];

In the same directory, create a JavaScript file with any name (for example, app.js), so your work directory will look like:

app/
├── app.js — your application file.
├── set1.bemdecl.js — the first declaration file.
└── set2.bemdecl.js — the second declaration file.

To get the declarations from the created files, use the load() method. Insert the following code into your app.js file:

const bemDecl = require('@bem/sdk.decl');

// Since we are using sets stored in files, we need to load them asynchronously.
async function testDecl() {
    // Wait for the file to load and set the `set1` variable.
    const set1 = await bemDecl.load('set1.bemdecl.js');

    // `set1` is an array of BemCell objects.
    // Convert them to strings using the `map()` method and special `id` property:
    console.log(set1.map(c => c.id));
    // => ['a', 'b', 'c']


    // Load the second set.
    const set2 = await bemDecl.load('set2.bemdecl.js');
    console.log(set2.map(c => c.id));
    // => ['b', 'e']
}

testDecl();

Subtracting declarations

To subtract one set from another, use the subtract() method. Insert this code into your async function in your app.js file:

console.log(bemDecl.subtract(set1, set2).map(c => c.id));
// => ['a', 'c']

The result will be different if we swap arguments:

console.log(bemDecl.subtract(set2, set1).map(c => c.id));
// => ['e']

Intersecting declarations

To calculate the intersection between two sets, use the intersect() method:

console.log(bemDecl.intersect(set1, set2).map(c => c.id));
// => ['b']

Merging declarations

To add elements from one set to another set, use the merge() method:

console.log(bemDecl.merge(set1, set2).map(c => c.id));
// => ['a', 'b', 'c', 'e']

Saving declarations to a file

To save the merged set, use the save() method. Normalize the set before saving:

const mergedSet = bemDecl.normalize(bemDecl.merge(set1, set2));
bemDecl.save('mergedSet.bemdecl.js', mergedSet, { format: 'v1', exportType: 'commonjs' })

The full code of the app.js file will look like this:

const bemDecl = require('@bem/sdk.decl');

// Since we are using sets stored in files, we need to load them asynchronously.
async function testDecl() {
    // Wait for the file to load and set the `set1` variable.
    const set1 = await bemDecl.load('set1.bemdecl.js');

    // `set1` is an array of BemCell objects.
    // Convert them to strings using the `map()` method and special `id` property:
    console.log(set1.map(c => c.id));
    // => ['a', 'b', 'c']


    // Load the second set.
    const set2 = await bemDecl.load('set2.bemdecl.js');
    console.log(set2.map(c => c.id));
    // => ['b', 'e']

    console.log(bemDecl.subtract(set1, set2).map(c => c.id));
    // => ['a', 'c']

    console.log(bemDecl.subtract(set2, set1).map(c => c.id));
    // => ['e']

    console.log(bemDecl.intersect(set1, set2).map(c => c.id));
    // => ['b']

    console.log(bemDecl.merge(set1, set2).map(c => c.id));
    // => ['a', 'b', 'c', 'e']

    const mergedSet = bemDecl.normalize(bemDecl.merge(set1, set2));
    bemDecl.save('mergedSet.bemdecl.js', mergedSet, { format: 'v1', exportType: 'commonjs' })
}

testDecl();

RunKit live example.

Run the app.js file. The mergedSet.bemdecl.js file will be created in the same directory with the following code:

module.exports = {
    format: 'v1',
    blocks: [
        {
            name: 'a'
        },
        {
            name: 'b'
        },
        {
            name: 'c'
        },
        {
            name: 'e'
        }
    ]
};

BEMDECL formats

There are several formats:

  • 'v1' — The old BEMDECL format, also known as exports.blocks = [ /* ... */ ].
  • 'v2' — The format based on deps.js files, also known as exports.decl = [ /* ... */ ]. You can also specify the declaration in the deps field: exports.deps = [ /* ... */ ] like in the 'enb' format.
  • 'enb' — The legacy format for the widely used enb deps reader, also known as exports.deps = [ /* ... */ ]. This format looks like the 'v2' format, but doesn't support syntactic sugar from this format.

Note. bem-decl controls all of them.

API reference

load()

Loads a declaration from the specified file.

This method reads the file and calls the parse() function on its content.

/**
 * @param  {string} filePath — Path to file.
 * @param  {Object|string} opts — Additional options.
 * @return {Promise} — A promise that represents `BemCell[]`.
 */
format(filePath, opts)

You can pass additional options that are used in the readFile() method from the Node.js File System.

The declaration in the file can be described in any format.

parse()

Parses the declaration from a string or JS object to a set of BEM cells.

This method automatically detects the format of the declaration and calls a parse() function for the detected format. Then it normalizes the declaration and converts it to a set of BEM cells.

/**
 * @param {string|Object} bemdecl — String of bemdecl or object.
 * @returns {BemCell[]} — Set of BEM cells.
 */
parse(bemdecl)

RunKit live example.

normalize()

Normalizes the array of entities from a declaration for the specified format. If successful, this method returns the list of BEM cells which represents the declaration.

This method is an alternative to the parse() method. In this method, you pass a format and the declaration contents separately.

/**
 * @param {Array|Object} decl — Declaration.
 * @param {Object} [opts] — Additional options.
 * @param {string} [opts.format='v2'] — Format of the declaration (v1, v2, enb).
 * @param {BemCell} [opts.scope] — A BEM cell to use as the scope to populate the fields of normalized entites. Only for 'v2' format.
 * @returns {BemCell[]}
 */
normalize(decl, opts)

RunKit live example.

subtract()

Calculates the set of BEM cells that occur only in the first passed set and do not exist in the rest. Read more.

/**
 * @param {BemCell[]} set — Original set of BEM cells.
 * @param {...(BemCell[])} removingSet — Set (or sets) with cells that should be removed.
 * @returns {BemCell[]} — Resulting set of cells.
 */
subtract(set, removingSet, ...)

RunKit live example.

intersect()

Calculates the set of BEM cells that exists in each passed set. Read more.

/**
 * @param {BemCell[]} set — Original set of BEM cells.
 * @param {...(BemCell[])} otherSet — Set (or sets) that should be merged into the original one.
 * @returns {BemCell[]} — Resulting set of cells.
 */
intersect(set, otherSet, ...)

RunKit live example.

merge()

Merges multiple sets of BEM cells into one set. Read more

/**
 * @param {BemCell[]} set — Original set of cells.
 * @param {...(BemCell[])} otherSet — Set (or sets) that should be merged into the original one.
 * @returns {BemCell[]} — Resulting set of cells.
 */
merge(set, otherSet, ...)

RunKit live example.

save()

Formats and saves a file with BEM cells from a file in any format.

/**
 * @param   {string} filename — File path to save the declaration.
 * @param   {BemCell[]} cells  — Set of BEM cells to save.
 * @param   {Object} [opts] — Additional options.
 * @param   {string} [opts.format='v2'] — The desired format (v1, v2, enb).
 * @param   {string} [opts.exportType='cjs'] — The desired type for export.
 * @returns {Promise.<undefined>} — A promise resolved when the file is stored.
 */

You can pass additional options that are used in the methods:

Read more about additional options for the writeFile() method in the Node.js File System documentation.

Example:

const decl = [
    new BemCell({ entity: new BemEntityName({ block: 'a' }) })
];
bemDecl.save('set.bemdecl.js', decl, { format: 'enb' })
    .then(() => {
        console.log('saved');
    });

stringify()

Stringifies a set of BEM cells to a specific format.

/**
 * @param {BemCell|BemCell[]} decl — Source declaration.
 * @param {Object} opts — Additional options.
 * @param {string} opts.format — Format of the output declaration (v1, v2, enb).
 * @param {string} [opts.exportType=json5] — Defines how to wrap the result (commonjs, json5, json, es6|es2015).
 * @param {string|Number} [opts.space] — Number of space characters or string to use as white space (exactly as in JSON.stringify).
 * @returns {string} — String representation of the declaration.
 */
stringify(decl, options)

RunKit live example.

format()

Formats a normalized declaration to the target format.

/**
 * @param  {Array|Object} decl — Normalized declaration.
 * @param  {string} opts.format — Target format (v1, v2, enb).
 * @return {Array} — Array with converted declaration.
 */
format(decl, opts)

assign()

Populates empty BEM cell fields with the fields from the scope, except the layer field.

/**
 * @typedef BemEntityNameFields
 * @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 {Object} cell - BEM cell fields, except the `layer` field.
 * @param {BemEntityNameFields} [cell.entity] — Object with fields that specify the BEM entity name.
 *                               This object has the same structure as `BemEntityName`,
 *                               but all properties inside are optional.
 * @param {string} [cell.tech] — BEM cell technology.
 * @param {BemCell} scope — Context (usually the processing entity).
 * @returns {BemCell} — Filled BEM cell with `entity` and `tech` fields.
 */
assign(cell, scope)

RunKit live example.

See another example of assign() usage in the Select all checkboxes section.

Usage examples

Select all checkboxes

Let's say you have a list of checkboxes and you want to implement the "Select all" button, which will mark all checkboxes as checked.

Each checkbox is an element of the checkbox block, and checked is the value of the state modifier.

const bemDecl = require('@bem/sdk.decl');
const bemCell = require('@bem/sdk.cell');

// Sets the 'state' modifier for the entity.
function select(entity) {
    const selectedState = {
        entity: { mod: { name: 'state', val: 'checked'}}
    };
    return bemDecl.assign(selectedState, entity);
};

// Sets the 'state' modifier for the array of entities.
function selectAll(entities) {
    return entities.map(e => select(e));
};

// Let's define BEM cells that represent checkbox entities.
const checkboxes = [
    bemCell.create({ block: 'checkbox', elem: '1', mod: { name: 'state', val: 'unchecked'}}),
    bemCell.create({ block: 'checkbox', elem: '2', mod: { name: 'state', val: 'checked'}}),
    bemCell.create({ block: 'checkbox', elem: '3', mod: { name: 'state'}}),
    bemCell.create({ block: 'checkbox', elem: '4'}),
];

// Select all checkboxes.
selectAll(checkboxes).map(e => e.valueOf());
// => [
//      { entity: { block: 'checkbox', elem: '1', mod: { name: 'state', val: 'checked'}}}
//      { entity: { block: 'checkbox', elem: '2', mod: { name: 'state', val: 'checked'}}}
//      { entity: { block: 'checkbox', elem: '3', mod: { name: 'state', val: 'checked'}}}
//      { entity: { block: 'checkbox', elem: '4', mod: { name: 'state', val: 'checked'}}}
//  ]

RunKit live example.

License

© 2019 Yandex. Code released under Mozilla Public License 2.0.