Home Reference Source

src/node.js

  1. function attrEscape (str) {
  2. return str.replace(/&/g, '&')
  3. .replace(/</g, '&lt;')
  4. .replace(/"/g, '&quot;')
  5. .replace(/\t/g, '&#x9;')
  6. .replace(/\n/g, '&#xA;')
  7. .replace(/\r/g, '&#xD;');
  8. }
  9. function escape (str) {
  10. return str.replace(/&/g, '&amp;')
  11. .replace(/</g, '&lt;')
  12. .replace(/>/g, '&gt;')
  13. .replace(/\r/g, '&#xD;');
  14. }
  15.  
  16. export const HEAD = Symbol('head');
  17.  
  18. export function props (...keys) {
  19. return (target) => {
  20. for (const key of keys) {
  21. target.elements.push({
  22. key,
  23. kind: 'method',
  24. placement: 'prototype',
  25. descriptor: {
  26. get () {
  27. if (this.attributes) {
  28. return this.attributes[key];
  29. }
  30. },
  31. set (value) {
  32. if (this.attributes === undefined) {
  33. this.attributes = {};
  34. }
  35. this.attributes[key] = value;
  36. },
  37. configurable: true,
  38. enumerable: true
  39. }
  40. });
  41. }
  42. return target;
  43. };
  44. }
  45.  
  46. export class Node {
  47. constructor (attributes = {}, children = [], name) {
  48. for (const key of Object.keys(attributes)) {
  49. this[key] = attributes[key];
  50. }
  51. this.children = children;
  52. this.__name = name || this.constructor.name.substring(1);
  53. }
  54. render () {
  55. function walk (tree) {
  56. const name = tree.__name;
  57. const { attributes, children } = tree;
  58. const tokens = [];
  59.  
  60. if (tree[HEAD]) {
  61. tokens.push(tree[HEAD]);
  62. }
  63. tokens.push(`<${name}`);
  64.  
  65. for (const key of Object.keys(attributes || {})) {
  66. let v = attributes[key];
  67. if (v === undefined) continue;
  68. if (typeof v === 'string') {
  69. v = attrEscape(v);
  70. }
  71. if (typeof v === 'boolean') {
  72. v = v ? 1 : 0;
  73. }
  74. tokens.push(` ${key}="${v}"`);
  75. }
  76.  
  77. if (!children.length) {
  78. tokens.push('/>');
  79. return tokens;
  80. }
  81. tokens.push('>');
  82. for (const child of children) {
  83. if (child instanceof Node) {
  84. tokens.push(child.render());
  85. } else if (typeof child === 'string') {
  86. tokens.push(escape(child));
  87. } else {
  88. tokens.push(child.toString());
  89. }
  90. }
  91. tokens.push(`</${name}>`);
  92. return tokens;
  93. }
  94. return walk(this).join('');
  95. }
  96. }