const {
  isDocumentTypeNode,
  isTextNode,
  isElementNode,
  isCommentNode
} = require('parse5').treeAdapters.default

class Parser {
  constructor ({ root, useTabs = false }) {
    this.root = root
    this.useTabs = useTabs
    this.pug = ''
  }

  get indent () {
    return this.useTabs ? '\t' : '  '
  }

  parse () {
    const walk = this.walk(this.root.childNodes, 0)
    let it

    do {
      it = walk.next()
    } while (!it.done)

    return this.pug.substring(1)
  }

  /**
   * DOM tree traversal
   * Depth-first search (pre-order)
   *
   * @param {DOM} tree - DOM tree or Node
   * @param {Number} level - Current tree level
   */
  * walk (tree, level) {
    if (!tree) {
      return
    }

    for (let i = 0; i < tree.length; i++) {
      const node = tree[i]

      this.pug += `\n${this.parseNode(node, level)}`

      if (
        node.childNodes &&
        node.childNodes.length > 0 &&
        !this.isUniqueTextNode(node)
      ) {
        yield * this.walk(node.childNodes, level + 1)
      }
    }
  }

  parseComment (node, indent) {
    const comment = node.data.split('\n')

    // Differentiate single line to multi-line comments
    if (comment.length > 1) {
      const multiLine = comment.map(line => `${indent}  ${line}`).join('\n')
      return `${indent}//${multiLine}`
    } else {
      return `${indent}//${comment}`
    }
  }

  parseNode (node, level) {
    const indent = this.indent.repeat(level)

    if (isDocumentTypeNode(node)) {
      return `${indent}doctype html`
    }

    if (isTextNode(node)) {
      return `${indent}| ${node.value}`
    } else if (isCommentNode(node)) {
      return this.parseComment(node, indent)
    } else if (isElementNode(node)) {
      let line = `${indent}${this.setAttributes(node)}`

      if (this.isUniqueTextNode(node)) {
        line += ` ${node.childNodes[0].value}`
      }

      return line
    } else {
      return node
    }
  }

  setAttributes (node) {
    const { tagName, attrs: attributes } = node
    const attributeList = []
    let pugNode = tagName

    attributes.forEach(({ name, value }) => {
      let noTag = false

      switch (name) {
        case 'id':
          noTag = true
          pugNode += `#${value}`
          break

        case 'class':
          noTag = true
          pugNode += `.${value.split(' ').join('.')}`
          break

        default:
          // Add escaped single quotes (\') to attribute values
          const val = value.replace(/'/g, "\\'")
          attributeList.push(`${name}='${val}'`)
          break
      }

      // Remove div tag
      if (tagName === 'div' && noTag) {
        pugNode = pugNode.replace('div', '')
      }
    })

    if (attributeList.length) {
      pugNode += `(${attributeList.join(', ')})`
    }

    return pugNode
  }

  isUniqueTextNode (node) {
    return node.childNodes.length === 1 && isTextNode(node.childNodes[0])
  }
}

module.exports = Parser

@@ -1977,20 +1977,8 @@
 ttrs
-: attributes
  %7D =
@@ -2002,20 +2002,17 @@
 ttribute
-List
+s
  = %5B%5D%0A  
@@ -2044,28 +2044,125 @@
     
-attributes.forEach((
+// Add CSS selectors to pug node and append any element attributes to it%0A    for (const attr of attrs) %7B%0A      const 
 %7B na
@@ -2176,38 +2176,272 @@
 ue %7D
-)
  =
-%3E %7B
+ attr%0A
 %0A      
-let noTag = false
+// Remove div tag if a selector is present (shorthand)%0A      // e.g. div#form() -%3E #form()%0A      const hasSelector = name === 'id' %7C%7C name === 'class'%0A      if (tagName === 'div' && hasSelector) %7B%0A        pugNode = pugNode.replace('div', '')%0A      %7D
 %0A%0A  
@@ -2479,39 +2479,16 @@
 e 'id':%0A
-          noTag = true%0A
         
@@ -2520,33 +2520,32 @@
           break%0A
-%0A
         case 'cl
@@ -2554,31 +2554,8 @@
 s':%0A
-          noTag = true%0A
     
@@ -2620,17 +2620,16 @@
   break%0A
-%0A
         
@@ -2769,20 +2769,17 @@
 ttribute
-List
+s
 .push(%60$
@@ -2824,132 +2824,13 @@
   %7D%0A
-%0A      // Remove div tag%0A      if (tagName === 'div' && noTag) %7B%0A        pugNode = pugNode.replace('div', '')%0A      %7D%0A    %7D)
+    %7D
 %0A%0A  
@@ -2844,20 +2844,17 @@
 ttribute
-List
+s
 .length)
@@ -2890,12 +2890,9 @@
 bute
-List
+s
 .joi

Improve shorthand detection and attribute loop in setAttribute method