(context: HydrationContext, node: RNode)
| 536 | * serialization. |
| 537 | */ |
| 538 | export function processTextNodeBeforeSerialization(context: HydrationContext, node: RNode) { |
| 539 | // Handle cases where text nodes can be lost after DOM serialization: |
| 540 | // 1. When there is an *empty text node* in DOM: in this case, this |
| 541 | // node would not make it into the serialized string and as a result, |
| 542 | // this node wouldn't be created in a browser. This would result in |
| 543 | // a mismatch during the hydration, where the runtime logic would expect |
| 544 | // a text node to be present in live DOM, but no text node would exist. |
| 545 | // Example: `<span>{{ name }}</span>` when the `name` is an empty string. |
| 546 | // This would result in `<span></span>` string after serialization and |
| 547 | // in a browser only the `span` element would be created. To resolve that, |
| 548 | // an extra comment node is appended in place of an empty text node and |
| 549 | // that special comment node is replaced with an empty text node *before* |
| 550 | // hydration. |
| 551 | // 2. When there are 2 consecutive text nodes present in the DOM. |
| 552 | // Example: `<div>Hello <ng-container *ngIf="true">world</ng-container></div>`. |
| 553 | // In this scenario, the live DOM would look like this: |
| 554 | // <div>#text('Hello ') #text('world') #comment('container')</div> |
| 555 | // Serialized string would look like this: `<div>Hello world<!--container--></div>`. |
| 556 | // The live DOM in a browser after that would be: |
| 557 | // <div>#text('Hello world') #comment('container')</div> |
| 558 | // Notice how 2 text nodes are now "merged" into one. This would cause hydration |
| 559 | // logic to fail, since it'd expect 2 text nodes being present, not one. |
| 560 | // To fix this, we insert a special comment node in between those text nodes, so |
| 561 | // serialized representation is: `<div>Hello <!--ngtns-->world<!--container--></div>`. |
| 562 | // This forces browser to create 2 text nodes separated by a comment node. |
| 563 | // Before running a hydration process, this special comment node is removed, so the |
| 564 | // live DOM has exactly the same state as it was before serialization. |
| 565 | |
| 566 | // Collect this node as required special annotation only when its |
| 567 | // contents is empty. Otherwise, such text node would be present on |
| 568 | // the client after server-side rendering and no special handling needed. |
| 569 | const el = node as HTMLElement; |
| 570 | const corruptedTextNodes = context.corruptedTextNodes; |
| 571 | if (el.textContent === '') { |
| 572 | corruptedTextNodes.set(el, TextNodeMarker.EmptyNode); |
| 573 | } else if (el.nextSibling?.nodeType === Node.TEXT_NODE) { |
| 574 | corruptedTextNodes.set(el, TextNodeMarker.Separator); |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | export function convertHydrateTriggersToJsAction( |
| 579 | triggers: Map<DeferBlockTrigger, HydrateTriggerDetails | null> | null, |
no test coverage detected
searching dependent graphs…