Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
// const { get } = require('lodash')
// const log = require('fliplog')
const { toUniverseView, dot } = require('./modules/chain-able')

const set = dot.set
/* eslint-disable brace-style */
// https://mdn.mozillademos.org/files/3665/css%20syntax%20-%20declaration.png
class Selector {

  // & ~ + * [=""] : ,
}
//             lowercase-or-numbers
Selector.className = /^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/
// same as above, but starts with `.` ends with `{`
Selector.match = /^.([a-z][a-z0-9]*)(-[a-z0-9]+)*/


const isStarOrDoubleSlash = line => line.startsWith('/*') || line.startsWith('//')
// const isStarOrDoubleSlash = char => char === '*' || char === '//'
// const isComment = line => line.charAt(0) === '/' && isStarOrDoubleSlash(line.charAt(1))
const isComment = line => line.startsWith('/') && isStarOrDoubleSlash(line)

const not = fn => x => !fn(x)
const isNotComment = not(isComment)
const trim = x => x.trim()
const isNotEmpty = line => line !== ''


const removeFirst = current => {
  current = current.split('')
  current.shift()
  current = current.join('')
  return current
}
const removeLast = (current, num = 1) => {
  current = current.split('')

  while (num > 0) {
    num -= 1
    current.pop()
  }
  current = current.join('')
  return current
}

const reverse = x => x.split('').reverse().join('')

const removeIndentWhile = str => {
  let current = str.replace(/\t\r\n/gmi, '')
  while (current.charAt(0) === ' ') {
    current = removeFirst(current)
  }

  return current
}

const removeIndents = str => {
  str = removeIndentWhile(str)
  str = reverse(str)
  str = removeIndentWhile(str)
  str = reverse(str)
  return str
}

const isSelector = line => Selector.match.test(line)

// isSelector(line) &&
const isMultiLineSelector = line => line.endsWith(',')
// isSelector(line) &&
const isSelectorEnd = line => line.endsWith('{')
const isRuleEnd = line => line.endsWith('}')

function toPath(line) {
  line = line
    // esc
    // .replace(/\./gmi, '\\.')
    // .split(' ').join('.')
    .split('.').join('\\.')
    // .split('\\.\\.').join('\\.')

    // .map(l => l === '.')
    // ---
    // .replace(/\.\s/gim, '')
    // .replace(/\./gim, ' ')
    // .replace(/\s+/gim, '.')
    // .replace(/ /gmi, '')
    // .replace(/(\s|\t|\n|\r)+/gmi, '')
    .trim()

  // return removeLast(line)
  if (line.endsWith('\\.')) {
    return removeLast(line, 2)
  }

  // replace first dot if it exists
  return line.charAt(0) === '.'
    ? line.replace('.', '')
    : line
}

class Declaration {
  constructor(property, value) {
    this.property = property
    this.value = value
  }
}

class Parser {
  constructor(css) {
    this.css = css

    // this.level = 0
    this.store = new Map()
    this.obj = {}

    this.selectorPath = []

    this.selectorGroupStack = false
    // this.history = []
    // this.stack = []
    // this.path = []

    this.mapLine = this.mapLine.bind(this)
  }

  get selector() {
    return this.selectorPath.join(' ')
  }

  get(selector) {
    return this.store.get(selector)
  }
  set(selector) {
    if (this.store.has(selector)) {
      return
    }

    const line = {
      selectors: [selector],
      rules: [],
    }

    this.store.set(selector, line)
    // this.selectorPath.push(selector)
  }

  last() {
    return this.selectorPath[this.selectorPath.length - 1]
  }
  lastWasMultiLineSelector() {
    let last = this.last()
    if (!last) return false
    return isMultiLineSelector(last)
  }
  add(line) {
    line = line.replace(/\&/gmi, '').trim()

    this.selectorPath.push(line)

    this.set(this.selector)

    const path = toPath(this.selector)
    set(this.obj, path, this.get(this.selector).rules)

    toUniverseView(this.obj)
  }
  pop(line) {
    this.selectorPath.pop()
  }

  mapLine(line) {
    if (isMultiLineSelector(line)) {
      line = removeLast(line)

      if (this.selectorGroupStack) {
        this.selectorGroupStack.push(line)
      }
      else {
        this.selectorGroupStack = [line]
      }
    }

    if (isSelectorEnd(line)) {
      line = removeLast(line)
      // line = line.trim()

      // this is the annoying part...
      if (this.selectorGroupStack) {
        const selector = this.selector
        const selectorGroupStack = this.selectorGroupStack.concat([line])
        this.selectorGroupStack = false

        selectorGroupStack.forEach((nestedSelector, index) => {
          const isLast = index === selectorGroupStack.length - 1
          let nested = nestedSelector
          if (isLast) {
            nested = selector + nestedSelector
            nested += ','
          }
          else {
            nested = selector + nestedSelector
          }

          this.add(nested)
        })
      }
      else {
        this.add(line)
      }
    }
    else if (isRuleEnd(line)) {
      this.pop()
    }
    else {
      // console.log(this.selector, line)
      const group = this.get(this.selector)

      // console.log(group, this.selector)
      group.rules.push(line)
    }

    // console.log(this.selectorPath)
  }
  parse(css) {
    const mapped = css
      .split('\n')
      .map(removeIndents)
      .filter(isNotComment)
      // .filter(x => isComment(x))
      .filter(isNotEmpty)
      .map(this.mapLine)

    Array.from(this.store.keys()).forEach(name => {
      console.log('\n')
      const value = this.get(name)

      // log.fmtobj({ [name]: value.rules }).echo()
      // log.bold(name).echo()
      // log.blue('selectors').data(value.selectors).echo()
      // log.yellow('rules').data(value.rules).echo()
      // console.log({ [name]: value })
      console.log('\n')
    })

    // log.fmtobj(this.obj.toFlat(false)).echo()
    // log.quick((this.obj))
    // log.quick(toUniverseView(this.obj, true))
    // console.log(this.selector)
    return css
  }
}

function parse(css) {
  return new Parser(css).parse(css)
}

module.exports = parse