Skip to content

Commit 47f8845

Browse files
authored
feat(hydrate): add serializeShadowroot to hydrateDocument (#6259)
* feat(hydrate): extended hydrateDocument to include serializeShadowRoot opt * refactor(hydrate): removed serializeShadowRoot from SerializeDocumentOptions
1 parent 992a687 commit 47f8845

File tree

3 files changed

+62
-28
lines changed

3 files changed

+62
-28
lines changed

src/declarations/stencil-public-compiler.ts

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,34 @@ export interface HydrateDocumentOptions {
902902
* Sets `navigator.userAgent`
903903
*/
904904
userAgent?: string;
905+
/**
906+
* Configure how Stencil serializes the components shadow root.
907+
* - If set to `declarative-shadow-dom` the component will be rendered within a Declarative Shadow DOM.
908+
* - If set to `scoped` Stencil will render the contents of the shadow root as a `scoped: true` component
909+
* and the shadow DOM will be created during client-side hydration.
910+
* - Alternatively you can mix and match the two by providing an object with `declarative-shadow-dom` and `scoped` keys,
911+
* the value arrays containing the tag names of the components that should be rendered in that mode.
912+
*
913+
* Examples:
914+
* - `{ 'declarative-shadow-dom': ['my-component-1', 'another-component'], default: 'scoped' }`
915+
* Render all components as `scoped` apart from `my-component-1` and `another-component`
916+
* - `{ 'scoped': ['an-option-component'], default: 'declarative-shadow-dom' }`
917+
* Render all components within `declarative-shadow-dom` apart from `an-option-component`
918+
* - `'scoped'` Render all components as `scoped`
919+
* - `false` disables shadow root serialization
920+
*
921+
* *NOTE* `true` has been deprecated in favor of `declarative-shadow-dom` and `scoped`
922+
* @default 'declarative-shadow-dom'
923+
*/
924+
serializeShadowRoot?:
925+
| 'declarative-shadow-dom'
926+
| 'scoped'
927+
| {
928+
'declarative-shadow-dom'?: string[];
929+
scoped?: string[];
930+
default: 'declarative-shadow-dom' | 'scoped';
931+
}
932+
| boolean;
905933
}
906934

907935
export interface SerializeDocumentOptions extends HydrateDocumentOptions {
@@ -945,34 +973,6 @@ export interface SerializeDocumentOptions extends HydrateDocumentOptions {
945973
* Remove HTML comments. Defaults to `true`.
946974
*/
947975
removeHtmlComments?: boolean;
948-
/**
949-
* Configure how Stencil serializes the components shadow root.
950-
* - If set to `declarative-shadow-dom` the component will be rendered within a Declarative Shadow DOM.
951-
* - If set to `scoped` Stencil will render the contents of the shadow root as a `scoped: true` component
952-
* and the shadow DOM will be created during client-side hydration.
953-
* - Alternatively you can mix and match the two by providing an object with `declarative-shadow-dom` and `scoped` keys,
954-
* the value arrays containing the tag names of the components that should be rendered in that mode.
955-
*
956-
* Examples:
957-
* - `{ 'declarative-shadow-dom': ['my-component-1', 'another-component'], default: 'scoped' }`
958-
* Render all components as `scoped` apart from `my-component-1` and `another-component`
959-
* - `{ 'scoped': ['an-option-component'], default: 'declarative-shadow-dom' }`
960-
* Render all components within `declarative-shadow-dom` apart from `an-option-component`
961-
* - `'scoped'` Render all components as `scoped`
962-
* - `false` disables shadow root serialization
963-
*
964-
* *NOTE* `true` has been deprecated in favor of `declarative-shadow-dom` and `scoped`
965-
* @default 'declarative-shadow-dom'
966-
*/
967-
serializeShadowRoot?:
968-
| 'declarative-shadow-dom'
969-
| 'scoped'
970-
| {
971-
'declarative-shadow-dom'?: string[];
972-
scoped?: string[];
973-
default: 'declarative-shadow-dom' | 'scoped';
974-
}
975-
| boolean;
976976
/**
977977
* The `fullDocument` flag determines the format of the rendered output. Set it to true to
978978
* generate a complete HTML document, or false to render only the component.

src/hydrate/runner/render.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ export function hydrateDocument(
7070
asStream?: boolean,
7171
): Promise<HydrateResults> | Readable {
7272
const opts = normalizeHydrateOptions(options);
73+
/**
74+
* Defines whether we render the shadow root as a declarative shadow root or as scoped shadow root.
75+
*/
76+
opts.serializeShadowRoot =
77+
typeof opts.serializeShadowRoot === 'undefined' ? 'declarative-shadow-dom' : opts.serializeShadowRoot;
7378

7479
let win: MockWindow | null = null;
7580
const results = generateHydrateResults(opts);

test/end-to-end/src/declarative-shadow-dom/test.e2e.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type HydrateModule = typeof import('../../hydrate');
3030
let renderToString: HydrateModule['renderToString'];
3131
let streamToString: HydrateModule['streamToString'];
3232
let hydrateDocument: HydrateModule['hydrateDocument'];
33+
let createWindowFromHtml: HydrateModule['createWindowFromHtml'];
3334

3435
describe('renderToString', () => {
3536
beforeAll(async () => {
@@ -38,6 +39,7 @@ describe('renderToString', () => {
3839
renderToString = mod.renderToString;
3940
streamToString = mod.streamToString;
4041
hydrateDocument = mod.hydrateDocument;
42+
createWindowFromHtml = mod.createWindowFromHtml;
4143
});
4244

4345
it('resolves to a Promise<HydrateResults> by default', async () => {
@@ -379,4 +381,31 @@ describe('renderToString', () => {
379381
</nested-cmp-child>
380382
</nested-cmp-parent>`);
381383
});
384+
385+
describe('hydrateDocument', () => {
386+
it('can hydrate components with open shadow dom by default', async () => {
387+
const template = `<another-car-detail></another-car-detail>`;
388+
const fullHTML = `<html><head></head><body>${template}</body></html>`;
389+
const win = createWindowFromHtml(fullHTML, Math.random().toString());
390+
const document = win.document;
391+
await hydrateDocument(document);
392+
const html = document.documentElement.outerHTML;
393+
394+
expect(html).toContain('shadowrootmode="open"');
395+
});
396+
397+
it('can hydrate components with scoped shadow dom', async () => {
398+
const template = `<another-car-detail></another-car-detail>`;
399+
const fullHTML = `<html><head></head><body>${template}</body></html>`;
400+
const win = createWindowFromHtml(fullHTML, Math.random().toString());
401+
const document = win.document;
402+
await hydrateDocument(document, {
403+
serializeShadowRoot: 'scoped',
404+
});
405+
const html = document.documentElement.outerHTML;
406+
407+
expect(html).not.toContain('shadowrootmode="open"');
408+
expect(html).toContain('sc-');
409+
});
410+
});
382411
});

0 commit comments

Comments
 (0)