JSFiddle - React, Tailwind, and code Playground

by Константин Дралюк

HTML

<textarea name="" id="pug" cols="100" rows="15">                +b.about-program
                  +e.title Детокс-программа AntiAge
                  +e.description Разработана нашими специалистами специально для активных женщин, которые хотят чувствовать себя молодо и энергично и оставаться в тонусе, несмотря на стрессы и бешеный ритм жизни мегаполиса.
                  +e.list
                    -
                      var list = [
                        {image: pic('about/program1.jpg'), title: 'Детокс-программа <br>на основе соков и смузи на 3 дня', 'price': '7 470 р', link: v0, isGift: false},
                        {image: pic('about/program2.jpg'), title: 'Фейсфитнес для лица (видео)', 'price': '3 000 р', link: v0, isGift: true},
                        {image: pic('about/program3.jpg'), title: 'Курс медитаций «Внутренняя красота» (аудио)', 'price': '1 000 р', link: v0, isGift: true},
                      ]
                    for item, index in list
                      +e.item(class={'-gift': index !== 0})
                        +e.item-image.image.-rounded(src=item.image)
                        +e.item-title!=item.title
                        +e.item-price.h-serif!=item.price
                        +e.item-link.link(href=item.link) Подробнее</textarea>
            
<textarea name="" id="sass" cols="100" rows="15"></textarea>

JavaScript

const bemto = pug.value

const RE_START_OFFSET = /^\s*/
const RE_BLOCK = /^\s*?\+b\..*/
const RE_ELEMENT = /^\s*?\+e\..*/
const RE_BLOCK_OR_ELEMENT = /^\s*?(\+b\..*|\+e\..*)/
const RE_TYPE = /^\s*?(\+b|\+e)/
const RE_NESTED_ELEMENT = /(\s*?)(\+b\..*|\+e\..*): (\+b\..*|\+e\..*)/g
const RE_TAG = /[^\s*?(\+b\.|\+e\.)]+?[A-Z0-9]\./
const RE_ATTRIBUTES = /\(.*\)/
const RE_ID = /(#(\w|\d|_|-|:)*)/
const RE_CONTENT = /!=.*|(\S)\s.*/

const replaceRecursively = (str, regExp, subStr) => {
  const newStr = str.replace(regExp, subStr)
  if (newStr === str) {
    return newStr
  }
  return newStr.replace(regExp, subStr)
}

const expandBemto = bemto => {
	return replaceRecursively(bemto, RE_NESTED_ELEMENT, '$1$2$1  $3')
}

const removeBemtoChunks = bemto => {
	return bemto.split('\n').filter(line => {
  	return RE_BLOCK_OR_ELEMENT.test(line)
  }).map(line => {
  	return line
      .replace(RE_TAG, '')
    	.replace(RE_ATTRIBUTES, '')
    	.replace(RE_ID, '')
      .replace(RE_CONTENT, '$1')
  }).join('\n')
}

const buildStructure = bemto => {
  return bemto.split('\n').map((line, index) => {
    let name = ''
    let chainableModifiers = []
    let modifiers = []

  	const offset = line.length - line.replace(RE_START_OFFSET, '').length
    const isBlock = RE_BLOCK.test(line)
    const isElement = RE_ELEMENT.test(line)

    const classNames = line.trim().replace(RE_TYPE, '').split(/(?=\.)/g)
    const RE_CHAIN_MODIFIERS = /^\.-/
    const RE_MODIFIERS = new RegExp(`^${name}--`)

    classNames.map((className, index) => {
    	if (index === 0) {
      	name = className
        return
      }
      if (RE_CHAIN_MODIFIERS.test(className)) {
      	chainableModifiers.push(className)
        return
      }
      if (RE_MODIFIERS.test(className)) {
      	modifiers.push(className)
        return
      }
    })

    return {
    	id: ++index,
      name,
      chainableModifiers,
      modifiers,
    	offset,
      isBlock,
      isElement
    }
  })
}


const structure =...