Skip to content

Commit

Permalink
fix: rewrite SSR client-side hydration (#6067)
Browse files Browse the repository at this point in the history
* chore: wip fix hydration errors

* chore: fix spec tests

* chore: added e2e tests

* chore: fix analysis test

* chore: try again

* chore: fixup scoped classes

---------

Co-authored-by: John Jenkins <[email protected]>
  • Loading branch information
johnjenkins and John Jenkins authored Dec 10, 2024
1 parent af13de4 commit ec243c2
Show file tree
Hide file tree
Showing 27 changed files with 1,791 additions and 173 deletions.
6 changes: 6 additions & 0 deletions src/declarations/stencil-private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,12 @@ export interface RenderNode extends HostElement {
*/
['s-nr']?: RenderNode;

/**
* Original Order:
* During SSR; a number representing the order of a slotted node
*/
['s-oo']?: number;

/**
* Scope Id
*/
Expand Down
31 changes: 24 additions & 7 deletions src/mock-doc/serialize-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function serializeNodeToHtml(elm: Node | MockNode, serializationOptions:
? Array.from((elm as MockDocument).body.childNodes)
: opts.outerHtml
? [elm]
: Array.from(elm.childNodes as NodeList);
: Array.from(getChildNodes(elm));

for (let i = 0, ii = children.length; i < ii; i++) {
const child = children[i];
Expand Down Expand Up @@ -130,7 +130,10 @@ function* streamToHtml(
* ToDo(https://github.com/ionic-team/stencil/issues/4111): the shadow root class is `#document-fragment`
* and has no mode attribute. We should consider adding a mode attribute.
*/
if (tag === 'template') {
if (
tag === 'template' &&
(!(node as Element).getAttribute || !(node as Element).getAttribute('shadowrootmode'))
) {
const mode = ` shadowrootmode="open"`;
yield mode;
output.currentLineWidth += mode.length;
Expand Down Expand Up @@ -242,12 +245,13 @@ function* streamToHtml(
yield* streamToHtml(shadowRoot, opts, output);
output.indent = output.indent - (opts.indentSpaces ?? 0);

const childNodes = getChildNodes(node);
if (
opts.newLines &&
(node.childNodes.length === 0 ||
(node.childNodes.length === 1 &&
node.childNodes[0].nodeType === NODE_TYPES.TEXT_NODE &&
node.childNodes[0].nodeValue?.trim() === ''))
(childNodes.length === 0 ||
(childNodes.length === 1 &&
childNodes[0].nodeType === NODE_TYPES.TEXT_NODE &&
childNodes[0].nodeValue?.trim() === ''))
) {
yield '\n';
output.currentLineWidth = 0;
Expand All @@ -262,7 +266,9 @@ function* streamToHtml(
if (opts.excludeTagContent == null || opts.excludeTagContent.includes(tagName) === false) {
const tag = tagName === shadowRootTag ? 'template' : tagName;
const childNodes =
tagName === 'template' ? ((node as any as HTMLTemplateElement).content.childNodes as any) : node.childNodes;
tagName === 'template'
? ((node as any as HTMLTemplateElement).content.childNodes as any)
: getChildNodes(node);
const childNodeLength = childNodes.length;

if (childNodeLength > 0) {
Expand Down Expand Up @@ -525,6 +531,17 @@ function isWithinWhitespaceSensitive(node: Node | MockNode) {
return false;
}

/**
* Normalizes the `childNodes` of a node due to if `experimentalSlotFixes` is enabled, `
* childNodes` will only return 'slotted' / lightDOM nodes
*
* @param node to return `childNodes` from
* @returns a node list of child nodes
*/
function getChildNodes(node: Node | MockNode) {
return ((node as any).__childNodes || node.childNodes) as NodeList;
}

// TODO(STENCIL-1299): Audit this list, remove unsupported/deprecated elements
/*@__PURE__*/ export const NON_ESCAPABLE_CONTENT = new Set([
'STYLE',
Expand Down
Loading

0 comments on commit ec243c2

Please sign in to comment.