Don't include root directory in HTML tree I've written a function to create an HTML tree from JSON.

async function traverseTree (list, node) {
  const li = document.createElement('li')
  if (node.kids) {
    const
      ul = document.createElement('ul'),
      span = document.createElement('span')
    span.innerText = node.name
    li.appendChild(span)
    li.appendChild(ul)
    list.appendChild(li)
    for (const kid of node.kids) {
      traverse(ul, kid)
    }
    return list
  }
  li.innerText = node.name
  list.appendChild(li)
  return list
}

;(async function(){
  const
    tree = await readJson('./tree.json'),
    list = document.createElement('ul'),
    output = await traverseTree(list, tree)
  console.log(output)
})()


The issue is that I don't want to display the root node in the HTML tree since it is already displayed elsewhere, therefore I modified the code to exclude it.

async function traverseTree (list, node, isNotRoot) { // additional parameter
  const li = document.createElement('li')
  if (node.kids) {
    let ul = list                                     // now let
    if (isNotRoot) {                                  // additional conditional
      const span = document.createElement('span')
      ul = document.createElement('ul')               // additional assignment of value
      span.innerText = node.name
      li.appendChild(span)
      li.appendChild(ul)
      list.appendChild(li)
    }
    for (const kid of node.kids) {
      traverseTree(ul, kid, true)                     // additional argument
    }
    return list
  }
  li.innerText = node.name
  list.appendChild(li)
  return list
}


I'm wondering if there is a better, cleaner way to go about this?
Let's separate it by responsibilities into renderKids, renderName, traverseTree so that:


the main function can start explicitly from where you want
functions become immutable, free of side-effects
less repeated code
a single return is used
hopefully, the intent and flow become more obvious


function renderTree(json) {

  function renderKids(kids) {
    const ul = document.createElement('ul');
    for (const kid of kids) {
      ul.appendChild(traverseTree(kid))
    }
    return ul;
  }

  function renderName(name) {
    const span = document.createElement('span');
    span.textContent = name;
    return span;
  }

  function traverseTree(node) {
    const li = document.createElement('li');
    if (node.kids) {
      li.appendChild(renderName(node.name));
      li.appendChild(renderKids(node.kids));
    } else {
      li.textContent = node.name;
    }
    return li;
  }

  return renderKids(json.kids || []);
}


Usage: const output = renderTree(await readJson('./tree.json'))

Note, traverseTree is not asynchronous so there's no need for async keyword.