Finaly a Tool to generate reusable BEM

This commit is contained in:
reverend 2021-01-17 15:57:17 +01:00
parent fac6fb4da5
commit 08e20dfcb3
2 changed files with 192 additions and 275 deletions

View File

@ -1,151 +1,203 @@
// Storing the currents block properties and attributes
// to generate the block afterwards
/* /*
* This is set of mixins to help generating * A method for iterating throug a
* CSS classes according to the Block__Element--Modifier pattern. * list/array safely since this language sucks
* It also helps nesting those classes in any fashion imaganible * realy bad https://github.com/stylus/stylus/issues/1440
* and still produces clean BEM CSS
*/ */
@import './_laneHandling' foreach(list, callback)
for entry in list
if entry != ()
callback(entry)
_css-prefix = 'RV' create_list()
return () ()
_block = {}
reset_block()
_block.elements = create_list()
_block.block_modifiers = create_list()
_block.alias = {
map: {}
stash: {
RV-Element: create_list()
RV-Element__Modifier: create_list()
RV-Block__Modifier: create_list()
}
}
_block.modifier_stash = create_list()
_block.element_stash = create_list()
// Calling the function to initiate the block
// for first use
reset_block()
_bem_mixins = 'RV-Block','RV-Element','RV-Modifier','RV-Element__Modifier','RV-Block__Modifier'
/* /*
* Generates a CSS class for an * Checks if the caller of this funcion is a direct
* Block according to BEM * descendant of RV-Block. Checks for BEM-Mixins only.
* If any other function or mixin is between the last
* block and the caller, it will still count as an direct
* descendant of RV-Block
*/ */
RV-Block(block_name) is_direct_descendant_of_block()
_pop_lane('element_aliases') block_index = index(called-from, 'RV-Block') - 1
b = @block{
if block_index <= 0
return true
for i in (1..block_index)
if called-from[i] in _bem_mixins
return false
return true
/*
* Taking the currently stashed names, mapping
* them to the given name under the called parent
* (using called-from) and returning the stashed blocks
*/
generate_aliases(actual_name)
level_type = split(' ', called-from)[0]
if actual_name == null
if length(_block.alias.stash[level_type]) > 2
actual_name = _block.alias.stash[level_type][-1].name
else
warn('No name found for '+called-from)
blocks = ()
foreach(_block.alias.stash[level_type], @(alias){
_block.alias.map[level_type+':'+alias.name] = actual_name
push(blocks, alias.block)
})
_block.alias.stash[level_type] = create_list()
return {
name: actual_name
blocks: blocks
}
/*
* Resolving the alias according to the level type
* (using called-from). Returning the given name
* when no alias is found
*/
resolve_alias(name)
level_type = split(' ', called-from)[0]
if level_type+':'+name in _block.alias.map
return _block.alias.map[level_type+':'+name]
else
return name
render_element_modifier(modifier)
&--{resolve_alias(modifier.name)}
foreach(modifier.blocks, @(block){
{block} {block}
& .{block_name}{ })
for element_block in _pop_stack('elements_to_generate'){
{element_block}
}
}
&.{block_name}{ render_element(element)
for modifier_block in _pop_stack('block_modifiers_to_generate'){ &__{resolve_alias(element.name)}
{modifier_block} foreach(element.blocks, @(block){
} {block}
} })
foreach(element.modifiers, @(modifier){
render_element_modifier(modifier)
})
for modifier_block in _pop_stack('after_element_modifiers_to_generate'){ render_block_modifier(modifier)
{modifier_block} &--{resolve_alias(modifier.name)}
} foreach(modifier.blocks, @(block){
_reset_last_name('RV-Element') {block}
_reset_last_name('RV-Element__Modifier') })
_reset_last_name('RV-Block_Modifier')
}
if 'RV-Block' in called-from
{b}
else
.{block_name}
{b}
_reset_aliases()
/*
* Generates a CSS class for an block modifier
* according to BEM.
*/
RV-Block__Modifier(block_modifier_name)
_push_onto_stack(block)
//Detect if the selector will be generated
//by an earlier call of RV-Block__Modifier
if not 'RV-Block__Modifier' in called-from
// Saving accumulated modifiers and blocks
if 'RV-Block' in called-from
elements = _pop_stack('block_modifier_elements_to_generate')
modifier_block = @block{
&--{block_modifier_name}{
for sub_block in _pop_stack(){
{sub_block}
}
& ^[-2..-2]{ & ^[-2..-2]{
for element in elements{ foreach(modifier.elements, @(element){
{element} render_element(element)
})
} }
}
}
}
_push_onto_stack(modifier_block, 'block_modifiers_to_generate')
/* /*
* Generates an CSS class for an element * Stashing the given block under the given
* according to BEM. Also minds the element modifiers * name to be aliased by a late level
* For more see RV-Element___Modifier * (See generate_aliases)
*/ */
RV-Element(element_name) RV-Element--name(name)
_push_onto_stack(block) push(_block.alias.stash.RV-Element, {
name: name
block: block
})
//Detect if the selector will be generated RV-Element__Modifier--name(name)
//by an earlier call of RV-Element push(_block.alias.stash.RV-Element__Modifier, {
if not 'RV-Element' in called-from name: name
// Saving accumulated modifiers and blocks block: block
for alias in _pop_stack('element_aliases') })
_set_alias(alias, element_name)
stack_name = 'elements_to_generate'
if 'RV-Block__Modifier' in called-from
stack_name = 'block_modifier_elements_to_generate'
element_name = _get_alias(element_name) RV-Block__Modifier--name(name)
element_block = @block{ push(_block.alias.stash.RV-Block__Modifier, {
&__{element_name}{ name: name
for sub_block in _pop_stack(){ block: block
{sub_block} })
}
&^[-3..-3]__{element_name}{
for modifier_block in _pop_stack('element_modifiers_to_generate'){
{modifier_block}
}
}
}
}
_push_onto_stack(element_block, stack_name)
_set_last_name(element_name) RV-Block(block_name)
& .{block_name}
{block}
foreach(_block.block_modifiers, @(modifier){
render_block_modifier(modifier)
})
foreach(_block.elements, @(element){
render_element(element)
})
RV-Block__Modifier(modifier_name=null)
modifier_name = resolve_alias(modifier_name)
blocks = generate_aliases(modifier_name)
modifier_name = blocks.name
blocks = blocks.blocks
push(blocks, block)
modifier = {
name: modifier_name
blocks: blocks
elements: _block.element_stash
}
_block.element_stash = create_list()
push(_block.block_modifiers, modifier)
RV-Element(element_name=null)
element_name = resolve_alias(element_name)
blocks = generate_aliases(element_name)
element_name = blocks.name
blocks = blocks.blocks
push(blocks, block)
element = {
name: element_name
blocks: blocks
modifiers: _block.modifier_stash
}
_block.modifier_stash = create_list()
if is_direct_descendant_of_block()
push(_block.elements, element)
else else
_push_onto_stack(element_name, 'element_aliases') push(_block.element_stash, element)
/*
* Generates a CSS class for an element modifier
* according to BEM. These are ment to be part of the
* content block of an element and since child-mixins
* are called first there is no way of knowing what the
* current element is named. To solve this, we store
* a callback for the modifier and add the element name
* from RV-Element
*/
RV-Element__Modifier(element_modifier_name)
_push_onto_stack(block)
//Is this modifier beeing included by another modifier
if not 'RV-Element__Modifier' in called-from
//No, store all blocks under the give name
//Is this modifier beeing called in in- or postfix
if 'RV-Element' in called-from
//Infix, store for handling by element
modifier_block = @block{
&--{element_modifier_name}{
for current_block in _pop_stack(){
{current_block}
}
}
}
_push_onto_stack(modifier_block, 'element_modifiers_to_generate') RV-Element__Modifier(modifier_name=null)
else modifier_name = resolve_alias(modifier_name)
//Postfix, append to existing element blocks = generate_aliases(modifier_name)
last_element_name = _get_last_name('RV-Element') modifier_name = blocks.name
modifier_block = @block { blocks = blocks.blocks
& .{block_name}__{last_element_name}.{block_name}__{last_element_name}--{element_modifier_name}{ push(blocks, block)
for current_block in _pop_stack(){
{current_block}
}
}
}
_push_onto_stack(modifier_block, 'after_element_modifiers_to_generate')
modifier = {
name: modifier_name
blocks: blocks
}
push(_block.modifier_stash, modifier)

View File

@ -1,135 +0,0 @@
_block = {
stack: {}
elements: {}
block_modifiers: {}
element_stack: () ()
element_modifier_stack: () ()
block_modifier_stack: () ()
current_elements: () ()
current_element_modifiers: () ()
current_block_modifiers: () ()
}
reset_block()
_block.stack = {}
_block.elements = {}
_block.block_modifiers = {}
_block.element_stack = {}
_block.element_modifier_stack = {}
_block.block_modifier_stack = {}
_block.current_elements = () ()
_block.current_element_modifiers = () ()
_block.current_block_modifiers = () ()
_include = {
elements: {}
element_modifires: {}
block_modifiers: {}
element_stack: {}
element_modifier_stack: {}
block_modifier_stack: {}
}
reset_include()
_aliases.element_aliases = {}
_aliases.element_modifires_aliases = {}
_aliases.block_modifiers_aliases = {}
_aliases.element_alias_stack = {}
_aliases.element_modifires_alias_stack = {}
_aliases.block_modifiers_alias_stack = {}
_block_map = {}
RV-Include(block_name, alias = null)
RV-Squash()
{block}
RV-Block(block_name)
{block}
push(_block.elements, _block.current_elements)
push(_block.block_modifiers, _block.current_block_modifiers)
_block_map[block_name] = _block
reset_block()
RV-Element(element_name)
if 'RV-Include' in called-from
if 'RV-Element' in called-from
push(_include.element_stack, element_name)
else
for alias in _include.element_stack
_include.elements[alias] = element_name
_include.elements[element_name] = element_name
if 'RV-Element' in called-from
push(_state.element_stack, block)
else
element = {
type: 'RV-Element'
name: element_name
blocks: (block)
modifiers: _block.current_element_modifiers
}
_block.current_element_modifiers = () ()
for child in _block.element_stack
if child != ()
push(element.blocks, child)
_block.element_stack = () ()
if 'RV-Block__Modifier' in called-from
push(_block.current_elements, element)
else
_block.elements[elemante_name] = element
RV-Element__Modifier(modifier_name)
if 'RV-Element__Modifier' in called-from
push(_state.element_modifier_stack, block)
else
modifier = {
type: 'RV-Element__Modifier'
name: modifier_name
blocks: (block)
}
for child in _block.element_modifier_stack
if child != ()
push(modifier.blocks, child)
_block.element_modifier_stack = () ()
push(_block.current_element_modifiers, modifier)
RV-Block__Modifier(modifier_name)
if 'RV-Block__Modifier' in called-from
push(_block.block_modifier_stack, block)
else
modifier = {
type: 'RV-Block__Modifier'
name: modifier_name
blocks: (block)
elements: _block.current_elements
}
_block.current_elements = () ()
for child in _block.block_modifier_stack
if child != ()
push(modifier.blocks, child)
_block.block_modifier_stack = () ()
_block.block_modifiers[modifier_name] = modifier