UNPKG

4.29 kBJavaScriptView Raw
1import { DOMTreeConstruction, ConcreteBounds, NewElementBuilder } from '@glimmer/runtime';
2import createHTMLDocument from '@simple-dom/document';
3
4class NodeDOMTreeConstruction extends DOMTreeConstruction {
5 // Hides property on base class
6 constructor(doc) {
7 super(doc || createHTMLDocument());
8 }
9
10 // override to prevent usage of `this.document` until after the constructor
11 setupUselessElement() {}
12 insertHTMLBefore(parent, reference, html) {
13 let raw = this.document.createRawHTMLSection(html);
14 parent.insertBefore(raw, reference);
15 return new ConcreteBounds(parent, raw, raw);
16 }
17
18 // override to avoid SVG detection/work when in node (this is not needed in SSR)
19 createElement(tag) {
20 return this.document.createElement(tag);
21 }
22
23 // override to avoid namespace shenanigans when in node (this is not needed in SSR)
24 setAttribute(element, name, value) {
25 element.setAttribute(name, value);
26 }
27}
28
29const TEXT_NODE = 3;
30const NEEDS_EXTRA_CLOSE = new WeakMap();
31function currentNode(cursor) {
32 let {
33 element,
34 nextSibling
35 } = cursor;
36 if (nextSibling === null) {
37 return element.lastChild;
38 } else {
39 return nextSibling.previousSibling;
40 }
41}
42class SerializeBuilder extends NewElementBuilder {
43 serializeBlockDepth = 0;
44 __openBlock() {
45 let {
46 tagName
47 } = this.element;
48 if (tagName !== 'TITLE' && tagName !== 'SCRIPT' && tagName !== 'STYLE') {
49 let depth = this.serializeBlockDepth++;
50 this.__appendComment(`%+b:${depth}%`);
51 }
52 super.__openBlock();
53 }
54 __closeBlock() {
55 let {
56 tagName
57 } = this.element;
58 super.__closeBlock();
59 if (tagName !== 'TITLE' && tagName !== 'SCRIPT' && tagName !== 'STYLE') {
60 let depth = --this.serializeBlockDepth;
61 this.__appendComment(`%-b:${depth}%`);
62 }
63 }
64 __appendHTML(html) {
65 let {
66 tagName
67 } = this.element;
68 if (tagName === 'TITLE' || tagName === 'SCRIPT' || tagName === 'STYLE') {
69 return super.__appendHTML(html);
70 }
71
72 // Do we need to run the html tokenizer here?
73 let first = this.__appendComment('%glmr%');
74 if (tagName === 'TABLE') {
75 let openIndex = html.indexOf('<');
76 if (openIndex > -1) {
77 let tr = html.slice(openIndex + 1, openIndex + 3);
78 if (tr === 'tr') {
79 html = `<tbody>${html}</tbody>`;
80 }
81 }
82 }
83 if (html === '') {
84 this.__appendComment('% %');
85 } else {
86 super.__appendHTML(html);
87 }
88 let last = this.__appendComment('%glmr%');
89 return new ConcreteBounds(this.element, first, last);
90 }
91 __appendText(string) {
92 let {
93 tagName
94 } = this.element;
95 let current = currentNode(this);
96 if (tagName === 'TITLE' || tagName === 'SCRIPT' || tagName === 'STYLE') {
97 return super.__appendText(string);
98 } else if (string === '') {
99 return this.__appendComment('% %');
100 } else if (current && current.nodeType === TEXT_NODE) {
101 this.__appendComment('%|%');
102 }
103 return super.__appendText(string);
104 }
105 closeElement() {
106 if (NEEDS_EXTRA_CLOSE.has(this.element)) {
107 NEEDS_EXTRA_CLOSE.delete(this.element);
108 super.closeElement();
109 }
110 return super.closeElement();
111 }
112 openElement(tag) {
113 if (tag === 'tr') {
114 if (this.element.tagName !== 'TBODY' && this.element.tagName !== 'THEAD' && this.element.tagName !== 'TFOOT') {
115 this.openElement('tbody');
116 // This prevents the closeBlock comment from being re-parented
117 // under the auto inserted tbody. Rehydration builder needs to
118 // account for the insertion since it is injected here and not
119 // really in the template.
120 NEEDS_EXTRA_CLOSE.set(this.constructing, true);
121 this.flushElement(null);
122 }
123 }
124 return super.openElement(tag);
125 }
126 pushRemoteElement(element, cursorId, insertBefore = null) {
127 let {
128 dom
129 } = this;
130 let script = dom.createElement('script');
131 script.setAttribute('glmr', cursorId);
132 dom.insertBefore(element, script, insertBefore);
133 return super.pushRemoteElement(element, cursorId, insertBefore);
134 }
135}
136function serializeBuilder(env, cursor) {
137 return SerializeBuilder.forInitialRender(env, cursor);
138}
139
140export { NodeDOMTreeConstruction, serializeBuilder };
141//# sourceMappingURL=index.js.map