User Rating 0.0
Total Usage 0 times
BEMHTML Input
BH Output
Presets:
Is this tool helpful?

Your feedback helps us improve.

About

BEMHTML and BH represent two distinct template engines in the BEM ecosystem. BEMHTML operates on xjst (eXtensible JavaScript Templates) with declarative pattern matching and context modification via apply, applyNext, and applyCtx. BH uses an imperative approach where ctx methods mutate the output directly. The impedance mismatch between these systems means not every BEMHTML construct has a BH equivalent. Incorrect manual conversion leads to silent rendering bugs that surface only in production. This tool parses BEMHTML source into an intermediate representation, maps predicate chains to bh.match selectors, and translates body modes (tag, attrs, js, content, mix, cls, bem) to their BH ctx method equivalents.

Strict mode is enabled by default. The converter accumulates every BH-incompatible feature it encounters and reports them as errors. Constructs like apply with modified context, applyCtx, and custom XJST modes have no direct BH mapping. Disabling strict mode allows best-effort conversion but sacrifices validation reliability. This tool approximates the behavior of the bemhtml-source-convert npm package. It handles common patterns: block/elem/mod predicates, standard modes, object spread in attrs, and JS params merging. Complex nested applyNext chains or runtime-computed predicates require manual review.

bemhtml bh bem template converter bem tools xjst code converter

Formulas

BEMHTML template conversion follows a deterministic mapping from declarative predicates to imperative BH matchers. The core transformation rule maps a BEMHTML predicate chain to a BH selector string:

selector(p) = block + (elem ? "__" + elem : '') + (mod ? "_" + modName + "_" + modVal : '')

Where p is the predicate object extracted from BEMHTML source. block is the block name identifier. elem is an optional element name. mod / modVal represent modifier key-value pairs. Multiple modifiers on the same block generate separate bh.match calls.

Body mode transformation applies the mapping:

transform(mode, value) ctx.mode(value)

For attrs with merge semantics, the pattern becomes:

ctx.attrs(ctx.extend(newAttrs, ctx.attrs()))

For js with conditional merge:

ctx.js(json.js FALSE ? ctx.extend(json.js, params) : FALSE)

Strict mode validation checks each body statement against a compatibility set C = {tag, attrs, js, content, mix, cls, bem}. Any mode m C triggers an incompatibility warning. Any call to apply, applyCtx, or local triggers a strict mode error regardless of context.

Reference Data

BEMHTML ModeBH MethodSignatureNotes
tagctx.tag(value)value: StringDirect mapping. Empty string produces no wrapper tag.
attrsctx.attr(key, val) or ctx.attrs(obj)obj: ObjectBH merges with ctx.extend. BEMHTML replaces entirely.
jsctx.js(value)value: Object | TRUECheck json.js FALSE for merge pattern.
contentctx.content(value)value: BemJsonDirect mapping. Accepts arrays and objects.
mixctx.mix(value)value: Array | ObjectDirect mapping.
clsctx.cls(value)value: StringAdditional CSS classes.
bemctx.bem(value)value: BooleanFALSE disables BEM class generation.
defNo direct equivalent - Requires manual rewrite. applyBase is closest but semantically different.
applyNot supported - Context modification has no BH equivalent. Strict mode error.
applyNextctx.applyBase() - Partial mapping only. BH applyBase re-applies same matcher, not next in chain.
applyCtxNot supported - Replaces current node context. No BH mapping. Strict mode error.
localNot supported - Temporary context modification. No BH mapping.
jsAttrNamebh.setOptionsjsAttrName: StringDefault: "onclick". Alternative: "data-bem".
jsAttrSchemebh.setOptionsjsAttrScheme: StringDefault: "js". Alternative: "json".
Predicate: blockbh.match("blockName") - Direct mapping.
Predicate: block elembh.match("block__elem") - BEM notation with double underscore.
Predicate: mod key valbh.match("block_key_val") - Single underscore separators.
Predicate: elemModbh.match("block__elem_key_val") - Combined elem + mod selector.
Boolean modbh.match("block_key") - No value means boolean modifier.

Frequently Asked Questions

The apply method with modified context, applyCtx, and local have no BH equivalents. These rely on XJST's ability to re-evaluate templates in a temporarily modified context stack, a mechanism BH's imperative architecture does not support. The def mode also lacks a direct mapping. When encountered in strict mode, the converter reports these as incompatibility errors with line references.
Each modifier predicate generates a separate bh.match call with its own selector string. For example, mod "size" "big" and mod "theme" "dark" on block "button" produce two matchers: bh.match("button_size_big", ...) and bh.match("button_theme_dark", ...). Body statements within each modifier scope are scoped to their respective matcher.
Strict mode (default) validates every BEMHTML construct against BH's capability set C. If any incompatible feature is found (apply, applyCtx, local, custom modes), conversion halts and reports all issues. Non-strict mode skips these checks and performs best-effort conversion, silently dropping unsupported constructs. Non-strict output requires manual verification because dropped constructs may alter rendering logic.
In BEMHTML, attrs replaces the entire attribute object. In BH, ctx.attrs merges with existing attributes from BEMJSON. The converter wraps attr values with ctx.extend(newAttrs, ctx.attrs()) to preserve merge semantics consistent with BH's behavior. If your BEMHTML template intentionally replaces all attrs, the converted BH code may behave differently because BH always merges.
No. Custom modes defined via mode("customName") rely on XJST's template matching infrastructure. BH has a fixed set of ctx methods and no mechanism for user-defined modes. In strict mode, any custom mode triggers an incompatibility error. In non-strict mode, the custom mode body is dropped entirely from the output.
The selector follows BEM naming convention: block__elem_modName_modVal. For element "item" of block "menu" with modifier "active" valued "yes", the selector becomes bh.match("menu__item_active_yes", ...). Boolean modifiers omit the value: "menu__item_active".