Задача от Facebook

Итак, чат, я тут намедни сходил на on-site в Facebook. Среди задач была одна довольно стандартная (вроде бы): написать DOMRenderer. То есть на вход подается объект вида

const app = {
  type: "div",
  props: {
    id: "app",
    children: [
      {
        type: "h1",
        props: {
          class: "header",
          children: "Hello, World!"
        }
      },    
    ],
  },
};

вот так он должен вызываться:

const renderer = new DOMRenderer();
renderer.render(app, document.body);

вот такой должен быть результат:

<div id="app">
  <h1 class="header">Hello, World!</h1>
</div>

Я был уверен, что как раз с этой задачей справился норм, но есть информация, что там целая куча багов.

Можете попробовать решить сами, но если хотите сразу помочь с баг-хантингом, то

Решение под катом
class DOMRenderer {
  render(data, rootNode) { // transform data into DOM and render it inside the rootNode
    rootNode.appendChild(this.createDomNode(data));
  }
  
  createDomNode(rootDataElement, rootNode) {
    const node = document.createElement(rootDataElement.type);
    
    Object.keys(rootDataElement.props).forEach(propName => {
      if (propName === 'children') return;
      
      node.addAttr(propName, rootDataElement[propName]);
    });
    
    if (!rootDataElement.props.children]) {
      return node;
    }
    
    if (typeof rootDataElement.props.children === 'string') {
      node.textContent(rootDataElement.children);
    }
    
    rootDataElement.props.children.forEach(child => {
      const childNode = this.createDomNode(child);
      
      node.appendChild(childNode);
    });
    
    return node;
  } 
}

Disclaimer: решение не вылизанное, а прям копипаста с live-coding, так что там наверняка куча проблем из-за того, что я забыл DOM API. Но меня интересуют, в первую очередь, более глобальные баги и пропущенные эдж-кейсы

2 симпатии

Кажется не покрыт случай когда в children есть массив из текста и узлов.

const app = {
	type: "div",
	props: {
		id: "ppp",
		children: [
			"mama",
			{
				type: "span",
				props: {
					children: ["mila"]
				}
			},
			"ramu"
		]
	}
}

Чтобы покрыть случай, нужно вот эту логику (я бы переписал ее через создание текстового узла)

if (typeof rootDataElement.props.children === 'string') {
      node.textContent(rootDataElement.children);
    }

делать внутри цикла пробегающего по детям.

rootDataElement.props.children.forEach( ... )

тогда получится решение в общем виде. Может даже приводить одного текстового ребенка к массиву из одного текстового ребенка.


Не вижу покрытия случая когда все приложение это просто текстовый узел. По идее что один child что рутовый узел приложения - должны быть одного типа (текст или объект).


Задача интересная, по свободе запилю свой вариант решения, тогда смогу лучше понять какие кейсы упущены. Так будет проще обсудить твой подход.

1 симпатия

а кемелкейсы (data- атрибуты) и стили не должно обрабатывать?
props: { dataAria: '', style: { backgroundImage: "" } }

1 симпатия

Возможно. Скорее всего этот случай задачей не предусмотрен, но если его упомянуть, то наверняка будет плюс.

Да, действительно, этот кейс не покрыт 👍
Тогда в рекурсивном вызове он попытается создать новый элемент из строки и выкинет ошибку.

Итак, я немного переписал и теперь даже все работает.
Комментарии? Follow-up? Где-то можно упростить (у меня есть некоторые вопросы к читабельности, но я хз, как сделать лучше)? Упущенные юзкейсы?

С точки зрения читабельности я бы сделал так: Code

1 симпатия