Skip to content

Commit

Permalink
fix(mock-doc): avoid double hydration of components (#6003)
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann authored Oct 2, 2024
1 parent c5a8ea9 commit dbc2f58
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 1 deletion.
24 changes: 23 additions & 1 deletion src/mock-doc/serialize-node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { CONTENT_REF_ID, ORG_LOCATION_ID, SLOT_NODE_ID, TEXT_NODE_ID, XLINK_NS } from '../runtime/runtime-constants';
import {
CONTENT_REF_ID,
HYDRATE_ID,
ORG_LOCATION_ID,
SLOT_NODE_ID,
TEXT_NODE_ID,
XLINK_NS,
} from '../runtime/runtime-constants';
import { cloneAttributes } from './attribute';
import { NODE_TYPES } from './constants';
import { type MockDocument } from './document';
Expand Down Expand Up @@ -274,6 +281,21 @@ function* streamToHtml(
}

for (let i = 0; i < childNodeLength; i++) {
/**
* In cases where a user would pass in a declarative shadow dom of a
* Stencil component, we want to skip over the template tag as we
* will be parsing the shadow root of the component again.
*
* We know it is a hydrated Stencil component by checking if the `HYDRATE_ID`
* is set on the node.
*/
const sId = (node as HTMLElement).attributes.getNamedItem(HYDRATE_ID);
const isStencilDeclarativeShadowDOM = childNodes[i].nodeName.toLowerCase() === 'template' && sId;
if (isStencilDeclarativeShadowDOM) {
yield `\n${' '.repeat(output.indent)}<!--r.${sId.value}-->`;
continue;
}

yield* streamToHtml(childNodes[i], opts, output);
}

Expand Down
26 changes: 26 additions & 0 deletions test/end-to-end/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ export namespace Components {
"someMethodWithArgs": (unit: string, value: number) => Promise<string>;
"someProp": number;
}
interface NestedCmpChild {
}
interface NestedCmpParent {
}
interface PathAliasCmp {
}
interface PrerenderCmp {
Expand Down Expand Up @@ -334,6 +338,18 @@ declare global {
prototype: HTMLMethodCmpElement;
new (): HTMLMethodCmpElement;
};
interface HTMLNestedCmpChildElement extends Components.NestedCmpChild, HTMLStencilElement {
}
var HTMLNestedCmpChildElement: {
prototype: HTMLNestedCmpChildElement;
new (): HTMLNestedCmpChildElement;
};
interface HTMLNestedCmpParentElement extends Components.NestedCmpParent, HTMLStencilElement {
}
var HTMLNestedCmpParentElement: {
prototype: HTMLNestedCmpParentElement;
new (): HTMLNestedCmpParentElement;
};
interface HTMLPathAliasCmpElement extends Components.PathAliasCmp, HTMLStencilElement {
}
var HTMLPathAliasCmpElement: {
Expand Down Expand Up @@ -427,6 +443,8 @@ declare global {
"import-assets": HTMLImportAssetsElement;
"listen-cmp": HTMLListenCmpElement;
"method-cmp": HTMLMethodCmpElement;
"nested-cmp-child": HTMLNestedCmpChildElement;
"nested-cmp-parent": HTMLNestedCmpParentElement;
"path-alias-cmp": HTMLPathAliasCmpElement;
"prerender-cmp": HTMLPrerenderCmpElement;
"prop-cmp": HTMLPropCmpElement;
Expand Down Expand Up @@ -507,6 +525,10 @@ declare namespace LocalJSX {
interface MethodCmp {
"someProp"?: number;
}
interface NestedCmpChild {
}
interface NestedCmpParent {
}
interface PathAliasCmp {
}
interface PrerenderCmp {
Expand Down Expand Up @@ -564,6 +586,8 @@ declare namespace LocalJSX {
"import-assets": ImportAssets;
"listen-cmp": ListenCmp;
"method-cmp": MethodCmp;
"nested-cmp-child": NestedCmpChild;
"nested-cmp-parent": NestedCmpParent;
"path-alias-cmp": PathAliasCmp;
"prerender-cmp": PrerenderCmp;
"prop-cmp": PropCmp;
Expand Down Expand Up @@ -609,6 +633,8 @@ declare module "@stencil/core" {
"import-assets": LocalJSX.ImportAssets & JSXBase.HTMLAttributes<HTMLImportAssetsElement>;
"listen-cmp": LocalJSX.ListenCmp & JSXBase.HTMLAttributes<HTMLListenCmpElement>;
"method-cmp": LocalJSX.MethodCmp & JSXBase.HTMLAttributes<HTMLMethodCmpElement>;
"nested-cmp-child": LocalJSX.NestedCmpChild & JSXBase.HTMLAttributes<HTMLNestedCmpChildElement>;
"nested-cmp-parent": LocalJSX.NestedCmpParent & JSXBase.HTMLAttributes<HTMLNestedCmpParentElement>;
"path-alias-cmp": LocalJSX.PathAliasCmp & JSXBase.HTMLAttributes<HTMLPathAliasCmpElement>;
"prerender-cmp": LocalJSX.PrerenderCmp & JSXBase.HTMLAttributes<HTMLPrerenderCmpElement>;
"prop-cmp": LocalJSX.PropCmp & JSXBase.HTMLAttributes<HTMLPropCmpElement>;
Expand Down
15 changes: 15 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/nested-child-cmp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, h } from '@stencil/core';

@Component({
tag: 'nested-cmp-child',
shadow: true,
})
export class NestedCmpChild {
render() {
return (
<div class="some-other-class">
<slot></slot>
</div>
);
}
}
15 changes: 15 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/parent-cmp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, h } from '@stencil/core';

@Component({
tag: 'nested-cmp-parent',
shadow: true,
})
export class NestedCmpParent {
render() {
return (
<div class="some-class">
<slot></slot>
</div>
);
}
}
39 changes: 39 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/test.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,43 @@ describe('renderToString', () => {
expect(color).toBe('rgb(0, 0, 0)');
});
});

it('does not render the shadow root twice', async () => {
const { html } = await renderToString(
`
<nested-cmp-parent>
<nested-cmp-child custom-hydrate-flag="" s-id="3">
<template shadowrootmode="open">
<div c-id="3.0.0.0" class="some-other-class">
<slot c-id="3.1.1.0"></slot>
</div>
</template>
<!--r.3-->
Hello World
</nested-cmp-child>
</nested-cmp-parent>
`,
{
fullDocument: false,
prettyHtml: true,
},
);
expect(html).toBe(`<nested-cmp-parent custom-hydrate-flag="" s-id="1">
<template shadowrootmode="open">
<div c-id="1.0.0.0" class="some-class">
<slot c-id="1.1.1.0"></slot>
</div>
</template>
<!--r.1-->
<nested-cmp-child custom-hydrate-flag="" s-id="2">
<template shadowrootmode="open">
<div c-id="2.0.0.0" class="some-other-class">
<slot c-id="2.1.1.0"></slot>
</div>
</template>
<!--r.2-->
Hello World
</nested-cmp-child>
</nested-cmp-parent>`);
});
});

0 comments on commit dbc2f58

Please sign in to comment.