### Configuration Example Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md An example of a build configuration file for @bem-react/pack. ```APIDOC ## Configuration An example configuration: ```js const { resolve } = require('path') const { useCleanUpPlugin } = require('@bem-react/pack/lib/plugins/CleanUpPlugin') const { useCopyAssetsPlugin } = require('@bem-react/pack/lib/plugins/CopyAssetsPlugin') const { useCssPlugin } = require('@bem-react/pack/lib/plugins/CssPlugin') const { useTypeScriptPlugin } = require('@bem-react/pack/lib/plugins/TypescriptPlugin') /** * @type {import('@bem-react/pack/lib/interfaces').Config} */ module.exports = { context: resolve(__dirname, '..'), output: './dist', plugins: [ useCleanUpPlugin(['./dist']), useTypeScriptPlugin({ configPath: './tsconfig.prod.json', }), useCssPlugin({ context: './src', src: './**/*.css', output: ['./dist', './dist/esm'], }), useCopyAssetsPlugin([ { context: './src', src: './**/*.{svg,md,json}', output: ['./dist', './dist/esm'], }, ]), ], } ``` ``` -------------------------------- ### Install @bem-react/core Source: https://github.com/bem/bem-react/blob/master/packages/core/README.md Install the core package using npm. ```bash npm i -S @bem-react/core ``` -------------------------------- ### Install @bem-react/di Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Install the @bem-react/di package using npm. ```bash npm i -S @bem-react/di ``` -------------------------------- ### Install @bem-react/classnames Source: https://github.com/bem/bem-react/blob/master/packages/classnames/README.md Install the package using npm. ```bash npm i -S @bem-react/classnames ``` -------------------------------- ### Start Local Development Server Source: https://github.com/bem/bem-react/blob/master/website/README.md Starts a local development server. Changes are reflected live without a server restart. ```bash npm start ``` -------------------------------- ### Install @bem-react/classname Source: https://github.com/bem/bem-react/blob/master/packages/classname/README.md Install the package using npm. ```bash npm i -S @bem-react/classname ``` -------------------------------- ### Install Dependencies with npm Source: https://github.com/bem/bem-react/blob/master/website/README.md Use this command to install project dependencies. ```bash npm ``` -------------------------------- ### Example @bem-react/pack Build Configuration Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md A comprehensive example of a build configuration file using various plugins like CleanUpPlugin, TypeScriptPlugin, CssPlugin, and CopyAssetsPlugin. This configuration defines the output directory and specifies how different asset types should be processed and copied. ```js const { resolve } = require('path') const { useCleanUpPlugin } = require('@bem-react/pack/lib/plugins/CleanUpPlugin') const { useCopyAssetsPlugin } = require('@bem-react/pack/lib/plugins/CopyAssetsPlugin') const { useCssPlugin } = require('@bem-react/pack/lib/plugins/CssPlugin') const { useTypeScriptPlugin } = require('@bem-react/pack/lib/plugins/TypescriptPlugin') /** * @type {import('@bem-react/pack/lib/interfaces').Config} */ module.exports = { context: resolve(__dirname, '..'), output: './dist', plugins: [ useCleanUpPlugin(['./dist']), useTypeScriptPlugin({ configPath: './tsconfig.prod.json', }), useCssPlugin({ context: './src', src: './**/*.css', output: ['./dist', './dist/esm'], }), useCopyAssetsPlugin([ { context: './src', src: './**/*.{svg,md,json}', output: ['./dist', './dist/esm'], }, ]), ], } ``` -------------------------------- ### Install @bem-react Packages Source: https://github.com/bem/bem-react/blob/master/website/docs/introduction/quick-start.md Install the core, dependency injection, and classname packages for @bem-react. Use the -P flag to save them as production dependencies. ```sh npm i -P @bem-react/core @bem-react/di @bem-react/classname ``` -------------------------------- ### Install @bem-react/pack via yarn Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Install the pack tool as a development dependency using yarn. ```sh yarn add -D @bem-react/pack ``` -------------------------------- ### Pack CLI Usage Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Information on how to install and use the pack CLI tool. ```APIDOC ## Install via npm: ```sh npm i -DE @bem-react/pack ``` via yarn: ```sh yarn add -D @bem-react/pack ``` ## Usage ```sh Runs components build with defined plugins. USAGE $ pack build OPTIONS -c, --config=config [default: build.config.json] The path to a build config file. ``` ``` -------------------------------- ### Install @bem-react/pack via npm Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Install the pack tool as a development dependency using npm. ```sh npm i -DE @bem-react/pack ``` -------------------------------- ### Configure whiteList Option Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/docs/rules/whitelist-levels-imports.md Define allowed import sources for each redefinition level. This example sets up restrictions for 'common', 'desktop', and 'mobile' levels. ```json { "whiteList": { "common": ["common"], "desktop": ["common", "desktop"], "mobile": ["common", "mobile"] } } ``` -------------------------------- ### Configure Specific Component (Button) with Webpack-exp-plugin Source: https://github.com/bem/bem-react/blob/master/packages/webpack-exp-plugin/Readme.md This example shows how to apply an experimental configuration to a single component, 'Button'. This is useful for targeted experimentation without affecting other components. ```javascript const webpackExpPlugin = require('@bem-react/webpack-exp-plugin') module.exports = { entry: './src/index.js', output: './dist', plugins: [new webpackExpPlugin('@yandex/ui', { Button: 'new-button-exp' })], } ``` -------------------------------- ### Clone Bem React Core Repository Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Clone your forked repository to start making changes. Replace `` with your GitHub username. ```bash $ git clone git@github.com:/bem-react-core.git ``` -------------------------------- ### Create a Custom Plugin for @bem-react/pack Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Example of how to create a custom plugin for @bem-react/pack. This involves implementing the Plugin interface and defining behavior for available hooks like onRun. The done callback must be called to signal completion. ```ts import { Plugin, OnDone, HookOptions } from '@bem-raect/pack/lib/interfaces' class MyPlugin implements Plugin { async onRun(done: OnDone, { context, output }: HookOptions) { // Do something stuff. done() } } export function useMyPlugin(): MyPlugin { return new MyPlugin() } ``` -------------------------------- ### Fetch Latest Changes Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Fetch the latest updates from the upstream repository. It's recommended to do this before starting any new changes. ```bash $ git fetch upstream ``` -------------------------------- ### Usage Example Source: https://github.com/bem/bem-react/blob/master/website/docs/guides/lazy.md Demonstrates how to use the `withMod` higher-order component to conditionally load a component. The `mod` prop controls whether the lazily loaded chunk is fetched. ```ts // App.tsx import { Block as BlockPresenter, withMod } from './components/Block/desktop'; const Block = withMod(BlockPresenter); export const App = () => { return ( {/* chunk with DynamicPart not loaded */} {/* chunk with DynamicPart loaded */} ) } ``` -------------------------------- ### Configure ignorePaths Option Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/docs/rules/whitelist-levels-imports.md Specify file path patterns to be excluded from the import checks. This example ignores files ending with '.example.tsx' or '.example.jsx'. ```json { "ignorePath": [ ".example.tsx$", ".example.jsx$" ] } ``` -------------------------------- ### Usage Example with classnames Source: https://github.com/bem/bem-react/blob/master/packages/classnames/README.md Import and use the classnames function to merge CSS class names. Undefined values are ignored. ```typescript import { classnames } from '@bem-react/classnames' classnames('Block', undefined, 'Block2', 'Block') // Block Block2 ``` -------------------------------- ### CleanUpPlugin Usage Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Use the CleanUpPlugin to specify directories that need to be cleaned before the build process starts. This is useful for ensuring a clean output directory. ```js const { useCleanUpPlugin } = require('@bem-react/pack/lib/plugins/CleanUpPlugin') useCleanUpPlugin(['./dist']) ``` -------------------------------- ### Deploy Website to GitHub Pages Source: https://github.com/bem/bem-react/blob/master/website/README.md Builds the website and pushes to the 'gh-pages' branch for GitHub Pages hosting. ```bash GIT_USER= USE_SSH=true npm deploy ``` -------------------------------- ### Usage of Platform-Specific Components Source: https://github.com/bem/bem-react/blob/master/website/docs/guides/structure.md Demonstrates how to import components and hooks from a platform-specific API entry point in another component. Ensure your project is configured for tree shaking. ```ts // src/components/Feature/Feature.tsx import { Button as ButtonDesktop, withViewDefault, useCheckedState, } from 'components/Button/desktop' ``` -------------------------------- ### Build Static Website Content Source: https://github.com/bem/bem-react/blob/master/website/README.md Generates static content into the 'build' directory for hosting. ```bash npm build ``` -------------------------------- ### Configure All Components with Webpack-exp-plugin Source: https://github.com/bem/bem-react/blob/master/packages/webpack-exp-plugin/Readme.md Use this snippet to apply a specific experimental configuration to all components. Ensure the plugin is required and imported correctly. ```javascript const webpackExpPlugin = require('@bem-react/webpack-exp-plugin') module.exports = { entry: './src/index.js', output: './dist', plugins: [new webpackExpPlugin('@yandex/ui', { '*': 'css-modules-exp' })], } ``` -------------------------------- ### Desktop App Version Configuration Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Configure the desktop version of the App by importing desktop-specific components and registering them with the registry. ```tsx import { Registry, withRegistry } from '@bem-react/di' import { App as AppCommon, registryId } from './App' import { Footer } from './Components/Footer/Footer@desktop' import { Header } from './Components/Header/Header@desktop' export const registry = new Registry({ id: registryId }) registry.set('Header', Header) registry.set('Footer', Footer) export const AppDesktop = withRegistry(registry)(AppCommon) ``` -------------------------------- ### Write Own Plugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Information on how to create a custom plugin for the @bem-react/pack tool. ```APIDOC ## 🏗 Write own plugin The plugin can perform an action on one of the available hook `onBeforeRun`, `onRun` and `onAfterRun`. ### Example ```ts import { Plugin, OnDone, HookOptions } from '@bem-raect/pack/lib/interfaces' class MyPlugin implements Plugin { async onRun(done: OnDone, { context, output }: HookOptions) { // Do something stuff. done() } } export function useMyPlugin(): MyPlugin { return new MyPlugin() } ``` ``` -------------------------------- ### Build Components with @bem-react/pack Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Command to run the component build process using defined plugins. Use the -c flag to specify a custom build configuration file. ```sh pack build OPTIONS -c, --config=config [default: build.config.json] The path to a build config file. ``` -------------------------------- ### Configure Multiple Specific Components (Button, Select) with Webpack-exp-plugin Source: https://github.com/bem/bem-react/blob/master/packages/webpack-exp-plugin/Readme.md Configure multiple specific components, such as 'Button' and 'Select', with their respective experimental settings. This allows for more complex, component-level experimental rollouts. ```javascript const webpackExpPlugin = require('@bem-react/webpack-exp-plugin') module.exports = { entry: './src/index.js', output: './dist', plugins: [ new webpackExpPlugin('@yandex/ui', { Button: 'new-button-exp', Select: 'better-select-exp' }), ], } ``` -------------------------------- ### Configure Custom Naming Presets Source: https://github.com/bem/bem-react/blob/master/packages/classname/README.md Customize the naming convention using `withNaming` for custom prefixes and separators. ```javascript import { withNaming } from '@bem-react/classname' const cn = withNaming({ n: 'ns-', e: '__', m: '_', v: '_' }) cn('block', 'elem')({ theme: 'default' }) // ns-block__elem_theme_default ``` -------------------------------- ### Desktop Level Imports Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/docs/rules/whitelist-levels-imports.md Illustrates valid and invalid imports at the 'desktop' redefinition level, adhering to the 'whiteList' rules. ```javascript // Button@desktop.tsx import { Icon } from '../Icon/Icon'; // Good import { Icon } from '../Icon/Icon@common'; // Good import { Icon } from '../Icon/Icon@desktop'; // Good import { Icon } from '../Icon/Icon@mobile'; // No, it's wrong! ``` -------------------------------- ### Configure `whitelist-levels-imports` ESLint Rule Source: https://context7.com/bem/bem-react/llms.txt Set up the `whitelist-levels-imports` ESLint rule to enforce BEM redefinition-level import boundaries. This prevents files at a lower BEM level (e.g., `common`) from importing components from higher levels (e.g., `desktop`), maintaining platform bundle integrity. ```json // .eslintrc { "plugins": ["@bem-react"], "rules": { "@bem-react/whitelist-levels-imports": [ "error", { "defaultLevel": "common", "whiteList": { "common": ["common"], "desktop": ["common", "desktop"], "mobile": ["common", "mobile"] }, "ignorePaths": ["\.example\.tsx$"] } ] } } ``` -------------------------------- ### Export App Version with Registry Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Wrap a common App component with a specific registry using `withRegistry` to create a versioned application. ```typescript export const AppNewVersion = withRegistry(registry)(AppCommon) ``` -------------------------------- ### Importing Different App Versions Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Import and utilize different versions of the application (e.g., desktop and mobile) based on the configured registries. ```ts import { AppDesktop } from './path-to/App@desktop' import { AppMobile } from './path-to/App@mobile' ``` -------------------------------- ### Common Level Imports Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/docs/rules/whitelist-levels-imports.md Demonstrates correct and incorrect imports at the 'common' redefinition level according to the 'whiteList' configuration. ```javascript // Button@common.tsx import { Icon } from '../Icon/Icon'; // Good import { Icon } from '../Icon/Icon@common'; // Good import { Icon } from '../Icon/Icon@mobile'; // No, it's wrong! import { Icon } from '../Icon/Icon@desktop'; // No, it's wrong! ``` -------------------------------- ### CleanUpPlugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Plugin for cleaning up directories. It runs at the `beforeRun` step. ```APIDOC ### CleanUpPlugin Plugin for cleanuping directories. _(Run at `beforeRun` step)._ #### Usage ```js const { useCleanUpPlugin } = require('@bem-react/pack/lib/plugins/CleanUpPlugin') useCleanUpPlugin(['./dist']) ``` #### Declaration ```ts /** * A list of directories which need to be cleaned. */ type Sources = string[] export declare function useCleanUpPlugin(sources: Sources): CleanUpPlugin ``` ``` -------------------------------- ### Replace Component with New Implementation Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Use `withRegistry` to wrap your application with a new registry that replaces a specific component. This is useful for A/B testing or introducing experimental features. ```typescript import { Registry, withRegistry } from '@bem-react/di' import { AppDesktop, registryId } from './App@desktop' import { HeaderExperimental } from './experiments/Components/Header/Header' const expRegistry = new Registry({ id: registryId }) // replacing original Header with HeaderExperimental expRegistry.set('Header', HeaderExperimental) // AppDesktopExperimental will call App with HeaderExperimental as 'Header' export const AppDesktopExperimental = withRegistry(expRegistry)(AppDesktop) ``` -------------------------------- ### Component Registry and Context with `@bem-react/di` Source: https://context7.com/bem/bem-react/llms.txt Utilize `Registry` as a key-value store for components and `withRegistry` to provide this registry via React context. This pattern facilitates platform-specific or experimental component swapping without altering consumer imports. ```tsx import React from 'react' import { Registry, withRegistry, useRegistry } from '@bem-react/di' import { cn } from '@bem-react/classname' // --- App.tsx (base, platform-agnostic) --- export const cnApp = cn('App') export const App = () => { const { Header, Footer } = useRegistry(cnApp()) return (
) } // --- App@desktop.tsx --- import { HeaderDesktop } from './Components/Header/Header@desktop' import { FooterDesktop } from './Components/Footer/Footer@desktop' const desktopRegistry = new Registry({ id: cnApp() }) desktopRegistry.fill({ Header: HeaderDesktop, Footer: FooterDesktop }) export const AppDesktop = withRegistry(desktopRegistry)(App) // AppDesktop renders Header/Footer desktop variants // --- App@mobile.tsx --- import { HeaderMobile } from './Components/Header/Header@mobile' import { FooterMobile } from './Components/Footer/Footer@mobile' const mobileRegistry = new Registry({ id: cnApp() }) mobileRegistry.fill({ Header: HeaderMobile, Footer: FooterMobile }) export const AppMobile = withRegistry(mobileRegistry)(App) // --- Entry point (e.g. server-side rendering) --- const isMobile = true const RootApp = isMobile ? AppMobile : AppDesktop // ReactDOM.render(, …) ``` -------------------------------- ### Fill Registry with Multiple Components Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Register multiple components at once using the `fill` method with an object mapping keys to component references. ```typescript registry.fill({ Header, Footer, }) ``` -------------------------------- ### Mobile App Version Configuration Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Configure the mobile version of the App by importing mobile-specific components and registering them with the registry. ```tsx import { Registry, withRegistry } from '@bem-react/di' import { App as AppCommon, registryId } from './App' import { Footer } from './Components/Footer/Footer@mobile' import { Header } from './Components/Header/Header@mobile' export const registry = new Registry({ id: registryId }) registry.set('Header', Header) registry.set('Footer', Footer) export const AppMobile = withRegistry(registry)(AppCommon) ``` -------------------------------- ### Add Plugin to ESLint Configuration Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/README.md Include '@bem-react' in the plugins section of your .eslintrc file to enable the plugin. ```json { "plugins": ["@bem-react"] } ``` -------------------------------- ### CssPlugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md A plugin that copies CSS files and processes them using PostCSS. It runs at the `run` step. ```APIDOC ### CssPlugin A plugin that copies css files and makes processing using postcss on demand. _(Run at `run` step)._ #### Usage ```js const { useCssPlugin } = require('@bem-react/pack/lib/plugins/CssPlugin') useCssPlugin({ context: './src', src: './**/*.css', }) ``` #### Declaration ```ts type Options = { /** * A path that determines how to interpret the `src` path. */ context?: string /** * Glob or path from where we сopy files. */ src: string /** * Output paths. */ output: string[] /** * Paths to files that will be ignored when copying and processing. */ ignore?: string[] /** * A path to postcss config. */ postcssConfigPath?: string } export declare function useCssPlugin(options: Options): CssPlugin ``` ``` -------------------------------- ### withNaming Function Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/withNaming.md The `withNaming` function creates a new `ClassNameInitializer` with a passed preset. It accepts a `preset` object which defines the naming conventions for elements and modifiers. The function returns a `ClassNameInitializer` that can be used to generate BEM class names. ```APIDOC ## `withNaming` Creates a new `ClassNameInitilizer` with passed [preset](Preset). ### Arguments 1. `preset` (Preset): Shape with naming preset. ### Returns `ClassNameInitilizer` ### Examples React naming preset: ```ts import { withNaming } from '@bem-react/classname' const preset = { e: '-', m: '_' } const cn = withNaming(preset) cn('Block', 'Elem')({ theme: 'default' }) // -> Block-Elem_theme_default ``` Origin naming preset: ```ts import { withNaming } from '@bem-react/classname' const preset = { e: '__', m: '_', v: '_' } const cn = withNaming(preset) cn('block', 'elem')({ theme: 'default' }) // -> block__elem_theme_default ``` ``` -------------------------------- ### Register Components in a Registry Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Register individual component versions within the registry using descriptive keys. These keys should be consistent across different registry versions. ```typescript registry.set('Header', Header) registry.set('Footer', Footer) ``` -------------------------------- ### Common Component Implementation Source: https://github.com/bem/bem-react/blob/master/website/docs/guides/structure.md Defines the base implementation of a component, typically used across all platforms. Ensure this file exists for basic functionality. ```tsx // src/components/Button/Button.tsx import React from 'react' import './Button.css' export const Button = ({ children }) => ``` -------------------------------- ### Define Public API for Platform Source: https://github.com/bem/bem-react/blob/master/website/docs/guides/structure.md Exports components and hooks intended for public use from a platform-specific entry point. This is crucial for tree shaking and managing dependencies. ```ts // src/components/Button/desktop.ts export * from './Button@desktop' export * from './_view/Button_view_default' export * from './hooks/useCheckedState' ``` -------------------------------- ### CopyAssetsPlugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Plugin for copying assets. It runs at the `afterRun` step. ```APIDOC ### CopyAssetsPlugin Plugin for copying assets. _(Run at `afterRun` step)._ #### Usage ```js const { useCopyAssetsPlugin } = require('@bem-react/pack/lib/plugins/CopyAssetsPlugin') useCopyAssetsPlugin([ { context: './src', src: './**/*.{svg,md,json}', output: ['./dist', './dist/esm'], }, ]) ``` #### Declaration ```ts type Rule = { /** * Glob or path from where we сopy files. */ src: string /** * Output paths. */ output: string[] /** * A path that determines how to interpret the `src` path. */ context?: string /** * Paths to files that will be ignored when copying. */ ignore?: string[] } type Rules = Rule | Rule[] function useCopyAssetsPlugin(rules: Rules): CopyAssetsPlugin ``` ``` -------------------------------- ### Add Upstream Remote Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Add the main repository as a remote named 'upstream' to fetch the latest changes. Navigate into the cloned repository directory first. ```bash $ cd bem-react-core $ git remote add upstream git@github.com:bem/bem-react-core.git ``` -------------------------------- ### Create a Registry with a Specific ID Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Initialize a new Registry instance with a unique identifier, typically derived from a BEM classname. ```typescript const registry = new Registry({ id: cnApp() }) ``` -------------------------------- ### PackageJsonPlugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md A plugin that copies the package.json file and modifies its content. It runs at the `onFinish` step. ```APIDOC ### PackageJsonPlugin A plugin that copy package.json and modify content. _(Run at `onFinish` step)._ #### Usage ```js const { usePackageJsonPlugin } = require('@bem-react/pack/lib/plugins/PackageJsonPlugin') usePackageJsonPlugin({ scripts: {}, }) ``` ``` -------------------------------- ### Consuming Registry Components with Hooks and Render Props Source: https://context7.com/bem/bem-react/llms.txt Consume registered components using either the `useRegistry` hook (recommended for React 16.8+) or the `RegistryConsumer` render-prop component. Both patterns provide access to components registered within a specific registry ID. ```tsx import React from 'react' import { RegistryConsumer, useRegistry } from '@bem-react/di' const cnWidget = cn('Widget') // --- Hook pattern (recommended) --- export const WidgetHook = () => { const { Title, Body, Footer } = useRegistry(cnWidget()) return (
<Body /> <Footer /> </section> ) } // --- Render-prop pattern --- export const WidgetConsumer = () => ( <RegistryConsumer id={cnWidget()}> {({ Title, Body, Footer }) => ( <section className={cnWidget()}> <Title /> <Body /> <Footer /> </section> )} </RegistryConsumer> ) ``` -------------------------------- ### Enforce BEM Import Boundaries with `whitelist-levels-imports` Source: https://context7.com/bem/bem-react/llms.txt Illustrates correct and incorrect import patterns based on BEM levels enforced by the `whitelist-levels-imports` ESLint rule. Ensure imports stay within allowed levels to maintain platform separation. ```ts // Button@common.tsx import { Icon } from '../Icon/Icon@common' // ✅ same level import { Icon } from '../Icon/Icon@desktop' // ❌ ESLint error — desktop not allowed at common level // Button@desktop.tsx import { Icon } from '../Icon/Icon@common' // ✅ import { Icon } from '../Icon/Icon@desktop' // ✅ import { Icon } from '../Icon/Icon@mobile' // ❌ ESLint error ``` -------------------------------- ### TypescriptPlugin Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md A plugin that processes TypeScript files and creates CJS and ESM copies of the build. It runs at the `run` step. ```APIDOC ### TypescriptPlugin A plugin that process ts and creates two copies of the build (cjs and esm). _(Run at `run` step)._ #### Usage ```js const { useTypeScriptPlugin } = require('@bem-react/pack/lib/plugins/TypescriptPlugin') useTypeScriptPlugin({ configPath: './tsconfig.prod.json', }) ``` #### Declaration ```ts type Options = { /** * A path to typescript config. */ configPath?: string /** * A callback for when creating side effects. */ onCreateSideEffects: (path: string) => string[] | boolean | undefined } function useTypeScriptPlugin(options: Options): TypeScriptPlugin ``` ``` -------------------------------- ### Platform-Specific Component Implementation Source: https://github.com/bem/bem-react/blob/master/website/docs/guides/structure.md Provides an implementation for a specific platform (e.g., desktop). It re-exports the common implementation and imports platform-specific styles. ```tsx // src/components/Button/Button@desktop.tsx export * from './Button' import './Button@desktop.css' ``` -------------------------------- ### compose Source: https://context7.com/bem/bem-react/llms.txt Combines multiple `withBemMod` higher-order components onto a single base component. The order of arguments determines CSS cascade order and, when modifiers produce different layouts, which layout takes precedence. All simple (CSS-only) modifiers are automatically batched into a single optimized wrapper. ```APIDOC ## compose Combines multiple `withBemMod` higher-order components onto a single base component. The order of arguments determines CSS cascade order and, when modifiers produce different layouts, which layout takes precedence. All simple (CSS-only) modifiers are automatically batched into a single optimized wrapper. ### Example: Composing multiple Button modifiers ```tsx import React, { FC } from 'react' import { compose } from '@bem-react/core' import { Button as ButtonPresenter } from './Button/Button' import { withButtonThemeAction } from './Button/_theme/Button_theme_action' import { withButtonThemeDefault } from './Button/_theme/Button_theme_default' import { withButtonTypeLink } from './Button/_type/Button_type_link' import { withButtonSizeM } from './Button/_size/Button_size_m' // Compose all variants. Import order = CSS specificity order. const Button = compose( withButtonThemeAction, withButtonThemeDefault, withButtonTypeLink, withButtonSizeM, )(ButtonPresenter) export const App: FC = () => ( <> <Button>Basic</Button> {/* → <button class="Button">Basic</button> */} <Button theme="action">Action</Button> {/* → <button class="Button Button_theme_action">Action</button> */} <Button type="link" href="/home">Link</Button> {/* → <a href="/home" class="Button Button_type_link">Link</a> */} <Button theme="action" type="link" size="m" href="/go">All</Button> {/* → <a href="/go" class="Button Button_theme_action Button_type_link Button_size_m">All</a> */} </> ) ``` ``` -------------------------------- ### ClassNameInitilizer Function Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/ClassNameInitilizer.md Initializes a class name formatter. It takes the block name and an optional element name to create a formatter function. ```APIDOC ## ClassNameInitilizer ### Description Initializes a class name formatter. It takes the block name and an optional element name to create a formatter function. ### Signature ```ts type ClassNameInitilizer = (blockName: string, elemName?: string) => ClassNameFormatter ``` ### Parameters #### blockName - **blockName** (string) - Required - The name of the BEM block. #### elemName - **elemName** (string) - Optional - The name of the BEM element. If provided, the formatter will be scoped to this element. ### Returns - **ClassNameFormatter** - A function that formats class names based on the provided block and element names. ``` -------------------------------- ### Mobile Level Imports Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/docs/rules/whitelist-levels-imports.md Shows allowed and disallowed imports at the 'mobile' redefinition level based on the 'whiteList' settings. ```javascript // Button@mobile.tsx import { Icon } from '../Icon/Icon'; // Good import { Icon } from '../Icon/Icon@common'; // Good import { Icon } from '../Icon/Icon@mobile'; // Good import { Icon } from '../Icon/Icon@desktop'; // No, it's wrong! ``` -------------------------------- ### Compose Button Variants with BEM React Source: https://github.com/bem/bem-react/blob/master/packages/core/README.md Compose different button variants using `compose` and `composeU` from @bem-react/core. Ensure correct import order for CSS rules. Use `composeU` for modifiers with different values. ```tsx import React, { FC } from 'react'; import { compose, composeU } from '@bem-react/core'; import { Button as ButtonPresenter } from './Components/Button/Button'; import { withButtonTypeLink } from './Components/Button/_type/Button_type_link'; import { withButtonThemeAction } from './Components/Button/_theme/Button_theme_action'; import { withButtonThemeDefault } from './Components/Button/_theme/Button_theme_default'; import './App.css'; const Button = compose( composeU(withButtonThemeAction, withButtonThemeDefault), withButtonTypeLink, )(ButtonPresenter); export const App: FC = () => ( <div className="App"> <Button>I'm basic</Button> // Renders into HTML as: <button class="Button">I'm Basic</button> <Button type="link" href="#stub">I'm type link</Button> // Renders into HTML as: <a href="#stub" class="Button Button_type_link">I'm type link</a> <Button theme="action">I'm theme action</Button> // Renders into HTML as: <button class="Button Button_theme_action">I'm theme action</button> <Button theme="action" type="link">I'm all together</Button> // Renders into HTML as: <a class="Button Button_theme_action Button_type_link">I'm all together</a> </div> ); ``` ```tsx export const Button = compose( withButtonThemeAction, withButtonTypeLink, )(ButtonPresenter) ``` -------------------------------- ### Basic Button Component Implementation Source: https://github.com/bem/bem-react/blob/master/packages/core/README.md Implement the basic Button component that renders if no modifiers are set. It uses cnButton for BEM class generation. ```tsx import React, { FC } from 'react' import { IButtonProps, cnButton } from './index' export const Button: FC<IButtonProps> = ({ children, className, as: Component = 'button', ...props }) => ( <Component {...props} className={cnButton({}, [className])}> {children} </Component> ) ``` -------------------------------- ### Generate ClassName for Element (Alternative) Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/cn.md An alternative way to create a formatter for a BEM element by passing both block and element names to `cn` directly. ```typescript import { cn } from '@bem-react/classname' const buttonTextCn = cn('Button', 'Text') buttonTextCn() // -> Button-Text buttonTextCn({ size: 'm' }) // Button-Text Button-Text_size_m ``` -------------------------------- ### `@bem-react/classname` — `withNaming` Source: https://context7.com/bem/bem-react/llms.txt Creates a custom BEM class name initializer with a user-defined naming preset. This is useful for projects that follow conventions other than the default React naming, such as classic BEM (`Block__Elem--Mod`). ```APIDOC ## `withNaming` ### Description Creates a custom BEM class name initializer with a user-defined naming preset. Useful when a project follows classic BEM (`Block__Elem--Mod`) or any other convention instead of the React default. ### Usage ```ts import { withNaming } from '@bem-react/classname' // Classic BEM: Block__Elem--Mod--Val const cn = withNaming({ e: '__', m: '--', v: '--' }) const cnMenu = cn('Menu') cnMenu() // 'Menu' cnMenu({ theme: 'dark' }) // 'Menu Menu--theme--dark' cnMenu('Item') // 'Menu__Item' cnMenu('Item', { active: true }) // 'Menu__Item Menu__Item--active' // With global namespace prefix const cnNs = withNaming({ n: 'ns-', e: '__', m: '_', v: '_' }) const cnCard = cnNs('Card', 'Body') cnCard({ size: 'l' }) // 'ns-Card__Body ns-Card__Body_size_l' ``` ``` -------------------------------- ### Fill Registry with Custom IDs Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Use the `fill` method to register components with custom string identifiers instead of direct variable names. ```typescript registry.fill({ 'id-1': Header, 'id-2': Footer, }) ``` -------------------------------- ### Push Changes to Origin Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Push your feature branch to your forked repository on GitHub. This makes your changes available for a pull request. ```bash $ git push -u origin issue-<issue number> ``` -------------------------------- ### React Naming Preset with withNaming Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/withNaming.md Use `withNaming` to create a `ClassNameInitializer` with a React-style naming preset. This preset uses a hyphen for element separators and an underscore for modifier separators. It's useful for projects following common React BEM patterns. ```typescript import { withNaming } from '@bem-react/classname' const preset = { e: '-', m: '_' } const cn = withNaming(preset) cn('Block', 'Elem')({ theme: 'default' }) // -> Block-Elem_theme_default ``` -------------------------------- ### Configure ESLint Rules for BEM React Source: https://github.com/bem/bem-react/blob/master/packages/eslint-plugin/README.md Specify the rules you wish to enable and their severity levels within the rules section of your .eslintrc configuration. ```json { "rules": { "@bem-react/no-classname-runtime": "warn", "@bem-react/whitelist-levels-imports": [ "error", { "defaultLevel": "common", "whiteList": { "common": ["common"], "desktop": ["common", "desktop"], "mobile": ["common", "mobile"] } } ] } } ``` -------------------------------- ### Extend Component with New Functionality Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Use the `extends` method on a registry to wrap an existing component with new logic or UI elements. This allows for incremental changes without rewriting the original component. ```tsx import { Registry, withRegistry, withBase } from '@bem-react/di' import { AppDesktop, registryId } from './App@desktop' const expRegistry = new Registry({ id: registryId }) // extends original Header expRegistry.extends('Header', (BaseHeader) => (props) => ( <div> <BaseHeader height={200} color={red} /> </div> )) // AppDesktopExperimental will call App with extended 'Header' export const AppDesktopExperimental = withRegistry(expRegistry)(AppDesktop) ``` -------------------------------- ### Types Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/api.md This section describes the types used within the Classname library, which are helpful for understanding data structures and function signatures. ```APIDOC ## Types These types define the structure of data and configurations used within the Classname library: - **Preset**: Represents a predefined naming convention. - **ClassNameInitilizer**: Defines the signature for initializing class name generation. - **ClassNameFormatter**: Specifies the format for generated class names. ``` -------------------------------- ### Origin Naming Preset with withNaming Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/withNaming.md Configure `withNaming` with an origin naming preset that uses double underscores for element separators and single underscores for modifiers and variants. This is suitable for projects adhering to a more traditional BEM origin naming scheme. ```typescript import { withNaming } from '@bem-react/classname' const preset = { e: '__', m: '_', v: '_' } const cn = withNaming(preset) cn('block', 'elem')({ theme: 'default' }) // -> block__elem_theme_default ``` -------------------------------- ### Inject Dependencies using RegistryConsumer Source: https://github.com/bem/bem-react/blob/master/packages/di/README.md Use the `RegistryConsumer` component to access registered components within the component tree. This avoids direct imports of Header and Footer. ```tsx import React from 'react' import { cn } from '@bem-react/classname' import { RegistryConsumer } from '@bem-react/di' // No Header or Footer imports const cnApp = cn('App') export const App = () => ( <RegistryConsumer id={cnApp()}> {({ Header, Footer, }) => ( <> <Header /> <Footer /> </> )} </RegistryConsumer> ) ``` -------------------------------- ### Button Variant: Theme Action Source: https://github.com/bem/bem-react/blob/master/packages/core/README.md Create a Button variant for 'action' theme using withBemMod. This variant is applied when the 'theme' prop is 'action'. ```tsx import { withBemMod } from '@bem-react/core' import { cnButton } from '../index' export interface IButtonThemeActionProps { theme?: 'action' } export const withButtonThemeAction = withBemMod<IButtonThemeActionProps>(cnButton(), { theme: 'action', }) ``` -------------------------------- ### Configure `no-classname-runtime` ESLint Rule Source: https://context7.com/bem/bem-react/llms.txt Enable the `no-classname-runtime` rule in your ESLint configuration to warn when `cn(...)` is called within the render function. This encourages pre-computing class names at module scope for performance. ```json // .eslintrc { "plugins": ["@bem-react"], "rules": { "@bem-react/no-classname-runtime": "warn" } } ``` -------------------------------- ### Rebase and Pull Changes Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Before pushing, rebase your branch onto the latest upstream master to integrate recent changes and resolve conflicts. ```bash $ git pull --rebase upstream master ``` -------------------------------- ### Basic Usage of cn Source: https://github.com/bem/bem-react/blob/master/packages/classname/README.md Use the `cn` function to create BEM class names for a single block with modifiers and elements. ```javascript import { cn } from '@bem-react/classname' const cat = cn('Cat') cat() // Cat cat({ size: 'm' }) // Cat Cat_size_m cat('Tail') // Cat-Tail cat('Tail', { length: 'small' }) // Cat-Tail Cat-Tail_length_small ``` ```javascript const dogPaw = cn('Dog', 'Paw') dogPaw() // Dog-Paw dogPaw({ color: 'black', exists: true }) // Dog-Paw Dog-Paw_color_black Dog-Paw_exists ``` -------------------------------- ### Top-Level Exports Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/api.md These are the main functions and components exported by the Classname library that users can directly import and utilize. ```APIDOC ## Top-Level Exports This section lists the primary functions and components available for direct use: - **cn**: A function for composing class names. - **withNaming**: A higher-order component for applying naming conventions. ``` -------------------------------- ### Plugin Interface Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md The Plugin interface defines optional hook functions that can be provided to customize BEM React's behavior at different stages of its execution. ```APIDOC ## Plugin Interface ### Description The `Plugin` interface allows users to hook into various stages of the BEM process, such as starting, before running, during running, after running, and finishing. ### Interface Definition ```ts interface Plugin { /** * Run hook at start. */ onStart?: HookFn /** * Run hook before run. */ onBeforeRun?: HookFn /** * Run hook at run. */ onRun?: HookFn /** * Run hook after run. */ onAfterRun?: HookFn /** * Run hook at finish. */ onFinish?: HookFn } // Type definitions for hooks: type OnDone = () => void type HookOptions = { context: string; output: string } type HookFn = (done: OnDone, options: HookOptions) => Promise<void> ``` ### Hooks - **onStart**: Executed when the process begins. - **onBeforeRun**: Executed just before the main run logic. - **onRun**: Executed during the main run logic. - **onAfterRun**: Executed immediately after the main run logic. - **onFinish**: Executed when the process completes. ``` -------------------------------- ### Conventional Commits Source: https://github.com/bem/bem-react/blob/master/CONTRIBUTING.md Record your changes using Git commits that follow the Conventional Commits specification. This helps in automating changelog generation and understanding commit history. ```bash $ git commit -m "<type>[optional scope]: <description>" ``` -------------------------------- ### Generate ClassName for Block Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/cn.md Use this to create a formatter for a BEM block. Call the formatter with modifiers to generate class names. ```typescript import { cn } from '@bem-react/classname' const buttonCn = cn('Button') buttonCn() // -> Button buttonCn({ size: 'm' }) // Button Button_size_m ``` -------------------------------- ### `@bem-react/classname` — `cn` (React naming preset) Source: https://context7.com/bem/bem-react/llms.txt Generates a BEM entity class name formatter using the React naming convention (`Block-Elem_mod_val`). The formatter accepts optional modifiers and mixes (extra class names to append). ```APIDOC ## `cn` (React naming preset) ### Description Creates a BEM entity class name formatter using the React naming convention (`Block-Elem_mod_val`). The returned formatter accepts optional modifiers and mixes (extra class names to append). ### Usage ```ts import { cn } from '@bem-react/classname' const cnButton = cn('Button') cnButton() // 'Button' cnButton({ theme: 'action' }) // 'Button Button_theme_action' cnButton({ theme: 'action', disabled: true }) // 'Button Button_theme_action Button_disabled' cnButton('Text') // 'Button-Text' cnButton('Text', { size: 's' }) // 'Button-Text Button-Text_size_s' // Mixes — append arbitrary class names cnButton(null, ['MyExtra']) // 'Button MyExtra' cnButton({ theme: 'action' }, ['MyExtra']) // 'Button Button_theme_action MyExtra' // Nested element with mixes const cnIcon = cn('Icon') cnButton('Icon', [cnIcon()]) // 'Button-Icon Icon' // React component usage import React, { FC } from 'react' import { IClassNameProps } from '@bem-react/core' interface IButtonProps extends IClassNameProps { theme?: 'action' | 'default' disabled?: boolean } export const Button: FC<IButtonProps> = ({ className, theme, disabled, children }) => ( <button className={cnButton({ theme, disabled }, [className])}> {children} </button> ) // <Button theme="action" /> → <button class="Button Button_theme_action">…</button> ``` ``` -------------------------------- ### CssPlugin Usage Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Employ the CssPlugin to copy and process CSS files. It can handle PostCSS processing and output the processed CSS to specified directories. Ensure the 'src' path correctly targets your CSS files. ```js const { useCssPlugin } = require('@bem-react/pack/lib/plugins/CssPlugin') useCssPlugin({ context: './src', src: './**/*.css', }) ``` -------------------------------- ### Preset Type Definition Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classname/Preset.md Defines the structure of a Preset object, which can include global namespace, element delimiter, modifier delimiter, and modifier value delimiter. ```APIDOC ## `Preset` Type ### Description The `Preset` type is a configuration object used to define custom delimiters and namespaces for BEM. ### Properties - **n** (string) - Optional - Global namespace. - **e** (string) - Optional - Element delimiter. - **m** (string) - Optional - Modifier delimiter. - **v** (string) - Optional - Modifier value delimiter. ### Type Definition ```ts type Preset = { /** * Global namespace. */ n?: string /** * Element delimiter. */ e?: string /** * Modifier delimiter. */ m?: string /** * Modifier value delimiter. */ v?: string } ``` ``` -------------------------------- ### TypeScriptPlugin Usage Source: https://github.com/bem/bem-react/blob/master/packages/pack/README.md Integrate the TypeScriptPlugin to process TypeScript files and generate CommonJS (cjs) and ES Module (esm) outputs. Specify the path to your tsconfig.json file for custom configurations. ```js const { useTypeScriptPlugin } = require('@bem-react/pack/lib/plugins/TypescriptPlugin') useTypeScriptPlugin({ configPath: './tsconfig.prod.json', }) ``` -------------------------------- ### Button Variant: Type Link Source: https://github.com/bem/bem-react/blob/master/packages/core/README.md Create a Button variant for 'link' type using withBemMod. This variant renders an 'a' tag and is applied when the 'type' prop is 'link'. ```tsx import React from 'react' import { withBemMod } from '@bem-react/core' import { IButtonProps, cnButton } from '../index' export interface IButtonTypeLinkProps { type?: 'link' href?: string } export const withButtonTypeLink = withBemMod<IButtonTypeLinkProps, IButtonProps>( cnButton(), { type: 'link' }, (Button) => (props) => <Button {...props} as="a" />, ) ``` -------------------------------- ### classnames Source: https://github.com/bem/bem-react/blob/master/website/docs/api/classnames/api.md The primary function exported by the classnames utility. It allows for conditionally joining class names together. ```APIDOC ## classnames ### Description Conditionally joins class names together. Accepts an arbitrary number of arguments and returns a string of all the "truthy" class names. ### Usage ```javascript classnames('foo', 'bar', { baz: true, qux: false }) // => 'foo bar baz' ``` ### Parameters - **...args** (any) - Accepts strings, objects, or arrays of class names. Only truthy values will be included in the output string. ```