JSFiddle - React, Tailwind, and code Playground

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

HTML

<textarea name="" id="pug" cols="100" rows="15">+b.about
  +e.container.container--new.-test.-test2.-test_1.container
  +e.container.container.-test2231.-test
    .col-two-third
      +e.header
        +b.about-header.about-header--new.-test
          +e.H2.title Anti Age
            +e.subtitle Новая летняя детокс-программа
          +e.description Фантастическое омоложение за 3 дня!</textarea>
            
<textarea name="" id="sass" cols="100" rows="15"></textarea>

JavaScript

const getStartOffset = line => {
  return line.length - line.trimStart().length
}

const removeOffset = text => {
	const lines = text.split('\n')
	const firstLine = lines.find(line => /\b/.test(line))

  return lines.map(line => line.slice(getStartOffset(firstLine))).join('\n')
}

const removeLinesChunks = lines => {
	return lines.filter(line => {
  	return /^\+b|\+e/.test(line.trim())
  })
}

const textToLines = text => {
	const lines = text.split('\n')
  return removeLinesChunks(lines)
}

const buildBlocks = lines => {
	const blocks = []
	const items = []

  const clearLine = line => {
  	let clearedLine = line.trim().replace(/\(.*\)/, '').replace(/( |!).*$/, '')

    return clearedLine
  }

  const buildItem = line => {
  	let lineArray = clearLine(line).split('.')
    
    const hasTag = lineArray[1].toUpperCase() === lineArray[1]
    
    if (hasTag) {
    	lineArray = [...lineArray.slice(0, 1), ...lineArray.slice(2)]
    }

   	const type = /^\+b/.test(line.trim()) ? 'block' : 'element'
    const name = lineArray[1]

    const modifiers = lineArray.filter(prop => {
    	const RE = new RegExp(`^-|${name}--`)
      return RE.test(prop)
    })

    return {type, name, line, modifiers}
  }

	const buildItems = lines.map(line => {
    const item = buildItem(line)
		
    const parent = [...items].reverse().find(item => 
    	item.type === 'block' &&
    	getStartOffset(line) > getStartOffset(item.line)
    )

    item.parent = parent ? parent.name : null

		const firstInstance = items.find(_item => _item.name === item.name && (parent && _item.parent === item.parent))

		if (firstInstance) {
    	firstInstance.modifiers.push(...item.modifiers)
    }

    items.push(item)

    return line
  })
  
  const getModifiers = (item) => {
    return [...new Set(item.modifiers)].sort((current, next) => {
      if (/^-/.test(current)) {
        return -1
      } else {
        return 1
      }
    }).map(modifier => {
      if (/^-/.test(modifier)) {
        return...