Skip to content

Commit

Permalink
fix(ssr): stop stripping comment nodes (#6123)
Browse files Browse the repository at this point in the history
* fix(ssr): stop stripping comment nodes

* Update test/wdio/ssr-hydration/cmp.test.tsx

Co-authored-by: Christian Bromann <[email protected]>

---------

Co-authored-by: John Jenkins <[email protected]>
Co-authored-by: Christian Bromann <[email protected]>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent eb11d25 commit 5a7ab24
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 18 deletions.
7 changes: 0 additions & 7 deletions src/runtime/client-hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,6 @@ export const initializeClientHydrate = (
for (rnIdex; rnIdex < rnLen; rnIdex++) {
shadowRoot.appendChild(shadowRootNodes[rnIdex] as any);
}

// Tidy up left-over / unnecessary comments to stop frameworks complaining about DOM mismatches
Array.from(hostElm.childNodes).forEach((node) => {
if (node.nodeType === NODE_TYPE.CommentNode && typeof (node as d.RenderNode)['s-sn'] !== 'string') {
node.parentNode.removeChild(node);
}
});
}

hostRef.$hostElement$ = hostElm;
Expand Down
66 changes: 55 additions & 11 deletions src/runtime/test/hydrate-shadow-child.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ describe('hydrate, shadow child', () => {
);
}
}
// @ts-ignore

const serverHydrated = await newSpecPage({
components: [CmpA, CmpB, CmpC],
html: `
Expand All @@ -467,6 +467,7 @@ describe('hydrate, shadow child', () => {
`,
hydrateServerSide: true,
});

expect(serverHydrated.root).toEqualHtml(`
<cmp-a class="hydrated" s-id="1">
<!--r.1-->
Expand Down Expand Up @@ -522,35 +523,78 @@ describe('hydrate, shadow child', () => {
`);
});

it('test shadow root innerHTML', async () => {
it('preserves all nodes', async () => {
@Component({
tag: 'cmp-a',
shadow: true,
})
class CmpA {
render() {
return <div>Shadow Content</div>;
return <slot>Shadow Content</slot>;
}
}

const page = await newSpecPage({
const serverHydrated = await newSpecPage({
components: [CmpA],
html: `
<cmp-a>
Light Content
A text node
<!-- a comment -->
<div>An element</div>
<!-- another comment -->
Another text node
</cmp-a>
`,
hydrateServerSide: true,
});

expect(page.root).toEqualHtml(`
<cmp-a>
expect(serverHydrated.root).toEqualHtml(`
<cmp-a class=\"hydrated\" s-id=\"1\">
<!--r.1-->
<!--o.0.1.-->
<!--o.0.2.-->
<!--o.0.4.-->
<!--o.0.6.-->
<!--o.0.7.-->
<slot-fb c-id=\"1.0.0.0\" hidden=\"\" s-sn=\"\">
<!--t.1.1.1.0-->
Shadow Content
</slot-fb>
<!--t.0.1-->
A text node
<!--c.0.2-->
<!-- a comment -->
<div c-id=\"0.4\" s-sn=\"\">
An element
</div>
<!--c.0.6-->
<!-- another comment -->
<!--t.0.7-->
Another text node
</cmp-a>
`);

const clientHydrated = await newSpecPage({
components: [CmpA],
html: serverHydrated.root.outerHTML,
hydrateClientSide: true,
});

expect(clientHydrated.root).toEqualHtml(`
<cmp-a class=\"hydrated\">
<mock:shadow-root>
<div>
<slot>
Shadow Content
</div>
</slot>
</mock:shadow-root>
Light Content
</cmp-a>
A text node
<!-- a comment -->
<div>
An element
</div>
<!-- another comment -->
Another text node
</cmp-a>
`);
});
});
1 change: 1 addition & 0 deletions src/runtime/test/hydrate-shadow-in-shadow.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('hydrate, shadow in shadow', () => {
<mock:shadow-root>
<slot></slot>
</mock:shadow-root>
<!---->
<slot></slot>
</cmp-b>
</mock:shadow-root>
Expand Down
32 changes: 32 additions & 0 deletions src/runtime/test/shadow.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,36 @@ describe('shadow', () => {
expect(page.root).toEqualHtml(expected);
expect(page.root).toEqualLightHtml(expected);
});

it('test shadow root innerHTML', async () => {
@Component({
tag: 'cmp-a',
shadow: true,
})
class CmpA {
render() {
return <div>Shadow Content</div>;
}
}

const page = await newSpecPage({
components: [CmpA],
html: `
<cmp-a>
Light Content
</cmp-a>
`,
});

expect(page.root).toEqualHtml(`
<cmp-a>
<mock:shadow-root>
<div>
Shadow Content
</div>
</mock:shadow-root>
Light Content
</cmp-a>
`);
});
});
59 changes: 59 additions & 0 deletions test/wdio/ssr-hydration/cmp.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { renderToString } from '../hydrate/index.mjs';

describe('ssr-shadow-cmp', () => {
function getNodeNames(chidNodes: NodeListOf<ChildNode>) {
return Array.from(chidNodes)
.flatMap((node) => {
if (node.nodeType === 3) {
if (node.textContent?.trim()) {
return 'text';
} else {
return [];
}
} else if (node.nodeType === 8) {
return 'comment';
} else {
return node.nodeName.toLowerCase();
}
})
.join(' ');
}

it('verifies all nodes are preserved during hydration', async () => {
if (!document.querySelector('#stage')) {
const { html } = await renderToString(
`
<ssr-shadow-cmp>
A text node
<!-- a comment -->
<div>An element</div>
<!-- another comment -->
Another text node
</ssr-shadow-cmp>
`,
{
fullDocument: true,
serializeShadowRoot: true,
constrainTimeouts: false,
},
);
const stage = document.createElement('div');
stage.setAttribute('id', 'stage');
stage.setHTMLUnsafe(html);
document.body.appendChild(stage);
}

// @ts-expect-error resolved through WDIO
const { defineCustomElements } = await import('/dist/loader/index.js');
defineCustomElements().catch(console.error);

// wait for Stencil to take over and reconcile
await browser.waitUntil(async () => customElements.get('ssr-shadow-cmp'));
expect(typeof customElements.get('ssr-shadow-cmp')).toBe('function');
await expect(getNodeNames(document.querySelector('ssr-shadow-cmp').childNodes)).toBe(
`text comment div comment text`,
);

document.querySelector('#stage')?.remove();
});
});
15 changes: 15 additions & 0 deletions test/wdio/ssr-hydration/cmp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, h } from '@stencil/core';

@Component({
tag: 'ssr-shadow-cmp',
shadow: true,
})
export class SsrShadowCmp {
render() {
return (
<div>
<slot />
</div>
);
}
}

0 comments on commit 5a7ab24

Please sign in to comment.