rofrischmann/fela

View on GitHub
packages/fela-identifier/src/index.js

Summary

Maintainability
A
2 hrs
Test Coverage
import { objectReduce } from 'fast-loops'

const defaultGenerator = (identifierName, index) => index.toString()

const isRenderer = (renderer) => renderer && renderer.renderRule

export default function identifier(config = {}) {
  const { prefix = 'fela-identifier' } = config
  const { generator = defaultGenerator } = config

  const idRepository = {}
  let index = 0
  let enhanced = false

  return (identifierName) => {
    if (isRenderer(identifierName)) {
      enhanced = true
      const renderer = identifierName
      const existingFilterClassName = renderer.filterClassName.bind(renderer)
      const existingRenderRule = renderer.renderRule.bind(renderer)

      renderer.filterClassName = (className) =>
        existingFilterClassName(className) && !idRepository[className]

      renderer.renderRule = (rule, props) => {
        const idClassNames = []
        const style = rule(props, renderer)
        const cleanedStyle = objectReduce(
          style,
          (newStyle, value, key) => {
            const identifierKey = key.replace(/^--/, '')

            if (idRepository[identifierKey]) {
              idClassNames.push(idRepository[identifierKey].className)
              return newStyle
            }

            return {
              ...newStyle,
              [key]: value,
            }
          },
          {}
        )

        const modifiedRule = () => cleanedStyle
        Object.assign(modifiedRule, rule)

        return [...idClassNames, existingRenderRule(modifiedRule)].join(' ')
      }

      return renderer
    }

    if (!enhanced) {
      throw new Error(
        'You are trying to create a new identifier before you used the identifier as an enhancer. ' +
          'Please add the identifier as an enhancer in the configuration of the renderer.'
      )
    }

    const identifierKey = [
      prefix,
      identifierName,
      generator(identifierName, index++),
    ]
      .filter((chunk) => chunk)
      .join('-')

    const identifierRule = () => ({
      [`--${identifierKey}`]: '',
    })

    identifierRule.className = identifierKey
    identifierRule.toString = () => identifierKey

    idRepository[identifierKey] = identifierRule

    return identifierRule
  }
}