simplify.js 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. var _ = require("underscore");
  2. var ast = require("./ast");
  3. function simplify(nodes) {
  4. return collapse(removeEmpty(nodes));
  5. }
  6. function collapse(nodes) {
  7. var children = [];
  8. nodes.map(collapseNode).forEach(function(child) {
  9. appendChild(children, child);
  10. });
  11. return children;
  12. }
  13. function collapseNode(node) {
  14. return collapsers[node.type](node);
  15. }
  16. var collapsers = {
  17. element: collapseElement,
  18. text: identity,
  19. forceWrite: identity
  20. };
  21. function collapseElement(node) {
  22. return ast.elementWithTag(node.tag, collapse(node.children));
  23. }
  24. function identity(value) {
  25. return value;
  26. }
  27. function appendChild(children, child) {
  28. var lastChild = children[children.length - 1];
  29. if (child.type === "element" && !child.tag.fresh && lastChild && lastChild.type === "element" && child.tag.matchesElement(lastChild.tag)) {
  30. if (child.tag.separator) {
  31. appendChild(lastChild.children, ast.text(child.tag.separator));
  32. }
  33. child.children.forEach(function(grandChild) {
  34. // Mutation is fine since simplifying elements create a copy of the children.
  35. appendChild(lastChild.children, grandChild);
  36. });
  37. } else {
  38. children.push(child);
  39. }
  40. }
  41. function removeEmpty(nodes) {
  42. return flatMap(nodes, function(node) {
  43. return emptiers[node.type](node);
  44. });
  45. }
  46. function flatMap(values, func) {
  47. return _.flatten(_.map(values, func), true);
  48. }
  49. var emptiers = {
  50. element: elementEmptier,
  51. text: textEmptier,
  52. forceWrite: neverEmpty
  53. };
  54. function neverEmpty(node) {
  55. return [node];
  56. }
  57. function elementEmptier(element) {
  58. var children = removeEmpty(element.children);
  59. if (children.length === 0 && !ast.isVoidElement(element)) {
  60. return [];
  61. } else {
  62. return [ast.elementWithTag(element.tag, children)];
  63. }
  64. }
  65. function textEmptier(node) {
  66. if (node.value.length === 0) {
  67. return [];
  68. } else {
  69. return [node];
  70. }
  71. }
  72. module.exports = simplify;