-
Notifications
You must be signed in to change notification settings - Fork 788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(runtime): support declarative shadow DOM #5792
Conversation
|
Path | Error Count |
---|---|
src/dev-server/index.ts | 37 |
src/dev-server/server-process.ts | 32 |
src/compiler/prerender/prerender-main.ts | 22 |
src/runtime/vdom/vdom-render.ts | 22 |
src/runtime/client-hydrate.ts | 20 |
src/runtime/vdom/test/patch.spec.ts | 19 |
src/runtime/vdom/test/util.spec.ts | 19 |
src/screenshot/connector-base.ts | 19 |
src/testing/puppeteer/puppeteer-element.ts | 19 |
src/dev-server/request-handler.ts | 15 |
src/compiler/prerender/prerender-optimize.ts | 14 |
src/compiler/sys/stencil-sys.ts | 14 |
src/runtime/connected-callback.ts | 14 |
src/sys/node/node-sys.ts | 14 |
src/compiler/prerender/prerender-queue.ts | 13 |
src/compiler/sys/in-memory-fs.ts | 13 |
src/runtime/set-value.ts | 13 |
src/compiler/output-targets/output-www.ts | 12 |
src/compiler/transformers/test/parse-vdom.spec.ts | 12 |
src/compiler/transformers/transform-utils.ts | 12 |
Our most common errors
Typescript Error Code | Count |
---|---|
TS2322 | 336 |
TS2345 | 322 |
TS18048 | 185 |
TS18047 | 99 |
TS2722 | 27 |
TS2532 | 23 |
TS2531 | 19 |
TS2790 | 11 |
TS2454 | 10 |
TS2352 | 9 |
TS2769 | 8 |
TS2416 | 7 |
TS2538 | 4 |
TS2493 | 3 |
TS18046 | 2 |
TS2684 | 1 |
TS2430 | 1 |
Unused exports report
There are 15 unused exports on this PR. That's the same number of errors on main, so at least we're not creating new ones!
Unused exports
File | Line | Identifier |
---|---|---|
src/runtime/bootstrap-lazy.ts | 21 | setNonce |
src/screenshot/screenshot-fs.ts | 18 | readScreenshotData |
src/testing/testing-utils.ts | 198 | withSilentWarn |
src/utils/index.ts | 145 | CUSTOM |
src/utils/index.ts | 245 | NODE_TYPES |
src/utils/index.ts | 269 | normalize |
src/utils/index.ts | 7 | escapeRegExpSpecialCharacters |
src/compiler/app-core/app-data.ts | 25 | BUILD |
src/compiler/app-core/app-data.ts | 116 | Env |
src/compiler/app-core/app-data.ts | 118 | NAMESPACE |
src/compiler/fs-watch/fs-watch-rebuild.ts | 123 | updateCacheFromRebuild |
src/compiler/types/validate-primary-package-output-target.ts | 82 | satisfies |
src/compiler/types/validate-primary-package-output-target.ts | 82 | Record |
src/testing/puppeteer/puppeteer-declarations.ts | 485 | WaitForEventOptions |
src/compiler/sys/fetch/write-fetch-success.ts | 7 | writeFetchSuccessSync |
PR built and packed!Download the tarball here: https://github.com/ionic-team/stencil/actions/runs/9569595832/artifacts/1613920781 If your browser saves files to
|
ae78727
to
b7d4a3b
Compare
feat(runtime): enhance renderToString to support serializeShadowRootAsDeclarativeShadowRoot flag make esm hydrate script make test work add unit test fix prettier wip minor tweaks apply more changes from #5787 get unit tests working prettier remove import fix test eslint fix use dynamic import minor e2e fixes prettier fix cspell adjust tests prettier allow to run headless make streaming work fix tests prettier remove obsolete file this should fix pre-render test prettier finally get it right prettier
b7d4a3b
to
532557e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ran into one issue when testing this out. I setup a test app that used the hydrate output from a Stencil starter project. Only change I made to the starter was adding a slot
element in the render
function return value for my-component
. Then, if I added the following snippet to my test server, it would result in 3 instances of the component getting rendered:
const res = await renderToString(
'<my-component>Jimmy</my-component><my-component first="Bob"></my-component>'
);
response.writeHead(200);
response.write(res.html);
response.end();
The first two elements that get rendered are correct, but the third is an instance without any slot content or input property values. Same thing happens when using streamToString
. I checked with a published version of Stencil and didn't see this behavior, so something we might wanna look into. I'm sure it's an issue client-side when the runtime takes over since the server wasn't spitting out any additional elements in the strings it returned.
Also, code looks good, but would be good to cut down the number of new SNC violations this introduces :)
Co-authored-by: Tanner Reits <[email protected]>
@tanner-reits thanks for reviewing! I am struggling reproducing the issue you are describing. I've set-up a new Stencil project with the following component: import { Component, Prop, h } from '@stencil/core';
import { format } from '../../utils/utils';
@Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true,
})
export class MyComponent {
/**
* The first name
*/
@Prop() first: string;
/**
* The middle name
*/
@Prop() middle: string;
/**
* The last name
*/
@Prop() last: string;
private getText(): string {
return format(this.first, this.middle, this.last);
}
render() {
return <div>
Hello, World! I'm {this.getText()}
<slot></slot>
</div>;
}
} When creating an hydration script and calling this script: const { renderToString } = require('./hydrate');
(async () => {
console.log(await renderToString('<my-component>Jimmy</my-component><my-component first="Bob"></my-component>', {
prettyHtml: true,,
serializeShadowRoot: true
}));
})() I correctly get the following output: <!doctype html>
<html class="hydrated" data-stencil-build="u56d8u2z">
<head>
<meta charset="utf-8">
</head>
<body>
<my-component class="hydrated sc-my-component-h" s-id="1">
<template shadowrootmode="open">
<style sty-id="sc-my-component">
/*!@:host*/.sc-my-component-h{display:block}
</style>
<div c-id="1.0.0.0" class="sc-my-component">
<!--t.1.1.1.0-->
Hello, World! I'm
<slot c-id="1.2.1.1" class="sc-my-component"></slot>
</div>
</template>
<!--r.1-->
Jimmy
</my-component>
<my-component class="hydrated sc-my-component-h" first="Bob" s-id="2">
<template shadowrootmode="open">
<style sty-id="sc-my-component">
/*!@:host*/.sc-my-component-h{display:block}
</style>
<div c-id="2.0.0.0" class="sc-my-component">
<!--t.2.1.1.0-->
Hello, World! I'm Bob
<slot c-id="2.2.1.1" class="sc-my-component"></slot>
</div>
</template>
<!--r.2-->
</my-component>
</body>
</html> Which just shows two elements. Mind sharing your example? |
@christian-bromann The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! renderToString
and streamToString
are both emitting the expected HTML and the browser is rendering correctly!
@christian-bromann I tried your PR build in a more advanced use case (nested components, multiple named slots) and with With With The components that have duplicated rendering, some elements inside are missing c-id or html comments: Tested with |
@mayerraphael thanks so much for providing feedback 🙏 is there any chance you can create a minimal reproducible example? I will try to reproduce this myself but haven't come across this behavior. |
@christian-bromann I invited you to my repository. Its the same repo as before. Disable javascript in the browser to see the correct rendering (but with missing c-ids in the I hope the example helps :) |
@mayerraphael thanks for reporting, I proposed a fix and made a dev release ( |
@christian-bromann Thanks, that was fast. Fixes some duplicate rendering, but not all. Also got some missing s-ids on top level with multiple components. Will try to replicate in my repository again. Edit: I updated my repository again. I couldn't replicate the missing s-ids. But i got another bug with s-ids beeing set on child-components. From our demo page we still get some duplicated rendering and missing c-id/s-ids: |
### Release Notes <details> <summary>ionic-team/stencil (@​stencil/core)</summary> ### [`v4.19.0`](https://togithub.com/ionic-team/stencil/blob/HEAD/CHANGELOG.md#-4190-2024-06-26) [Compare Source](https://togithub.com/ionic-team/stencil/compare/v4.18.3...v4.19.0) ### Bug Fixes * **compiler:** support rollup's external input option ([#3227](ionic-team/stencil#3227)) ([2c68849](ionic-team/stencil@2c68849)), fixes [#3226](ionic-team/stencil#3226) * **emit:** don't emit test files ([#5789](ionic-team/stencil#5789)) ([50892f1](ionic-team/stencil@50892f1)), fixes [#5788](ionic-team/stencil#5788) * **hyrdate:** support vdom annotation in nested dsd structures ([#5856](ionic-team/stencil#5856)) ([61bb5e3](ionic-team/stencil@61bb5e3)) * label attribute not toggling input ([#3474](ionic-team/stencil#3474)) ([13db920](ionic-team/stencil@13db920)), fixes [#3473](ionic-team/stencil#3473) * **mock-doc:** expose ShadowRoot and DocumentFragment globals ([#5827](ionic-team/stencil#5827)) ([98bbd7c](ionic-team/stencil@98bbd7c)), fixes [#3260](ionic-team/stencil#3260) * **runtime:** allow watchers to fire w/ no Stencil members ([#5855](ionic-team/stencil#5855)) ([850ad4f](ionic-team/stencil@850ad4f)), fixes [#5854](ionic-team/stencil#5854) * **runtime:** catch errors in async lifecycle methods ([#5826](ionic-team/stencil#5826)) ([87e5b33](ionic-team/stencil@87e5b33)), fixes [#5824](ionic-team/stencil#5824) * **runtime:** don't register listener before connected to DOM ([#5844](ionic-team/stencil#5844)) ([9d7021f](ionic-team/stencil@9d7021f)), fixes [#4067](ionic-team/stencil#4067) * **runtime:** properly assign style declarations ([#5838](ionic-team/stencil#5838)) ([5c10ebf](ionic-team/stencil@5c10ebf)) * **testing:** allow to re-use pages across it blocks ([#5830](ionic-team/stencil#5830)) ([561eab4](ionic-team/stencil@561eab4)), fixes [#3720](ionic-team/stencil#3720) * **typescript:** remove unsupported label property ([#5840](ionic-team/stencil#5840)) ([d26ea2b](ionic-team/stencil@d26ea2b)), fixes [#3473](ionic-team/stencil#3473) ### Features * **cli:** support generation of sass and less files ([#5857](ionic-team/stencil#5857)) ([1883812](ionic-team/stencil@1883812)), closes [#2155](ionic-team/stencil#2155) * **compiler:** generate export maps on build ([#5809](ionic-team/stencil#5809)) ([b6d2404](ionic-team/stencil@b6d2404)) * **complier:** support type import aliasing ([#5836](ionic-team/stencil#5836)) ([7ffb25d](ionic-team/stencil@7ffb25d)), closes [#2335](ionic-team/stencil#2335) * **runtime:** support declarative shadow DOM ([#5792](ionic-team/stencil#5792)) ([c837063](ionic-team/stencil@c837063)), closes [#4010](ionic-team/stencil#4010) * **testing:** add `toHaveLastReceivedEventDetail` event spy matcher ([#5829](ionic-team/stencil#5829)) ([63491de](ionic-team/stencil@63491de)), closes [#2488](ionic-team/stencil#2488) * **testing:** allow to disable network error logging via 'logFailingNetworkRequests' option ([#5839](ionic-team/stencil#5839)) ([dac3e33](ionic-team/stencil@dac3e33)), closes [#2572](ionic-team/stencil#2572) * **testing:** expose captureBeyondViewport in pageCompareScreenshot ([#5828](ionic-team/stencil#5828)) ([cf6a450](ionic-team/stencil@cf6a450)), closes [#3188](ionic-team/stencil#3188) </details>
@christian-bromann I was working from babe807, which correctly hydrated my components, however none of the releases on NPM will hydrate the components, they instead seem not to recognize them. In my use-case: const { html } = await renderToString(
'<ch-oklch-picker lightness="0.43" chroma="0.4" hue="256"></ch-oklch-picker>'
{
serializeShadowRoot: true,
fullDocument: false,
}); should return what babe807 returned: <ch-oklch-picker lightness="0.43" chroma="0.4" hue="256" role="group" class="hydrated" s-id="1"><!--r.1--><label id="hue-0329" c-id="1.0.0.0"><!--t.1.1.1.0-->Hue (0–360)</label><input property="hue" aria-labelledby="hue-0329" type="number" min="0" max="360" step="1" value="256" c-id="1.2.0.1"><input property="hue" aria-labelledby="hue-0329" type="range" min="0" max="360" step="1" value="256" c-id="1.3.0.2"><label id="chroma-66e5" c-id="1.4.0.3"><!--t.1.5.1.0-->Chroma (0–0.4)</label><input property="chroma" aria-labelledby="chroma-66e5" type="number" min="0.000" max="0.400" step="0.004" value="0.4" c-id="1.6.0.4"><input property="chroma" aria-labelledby="chroma-66e5" type="range" min="0.000" max="0.400" step="0.004" value="0.4" c-id="1.7.0.5"><label id="lightness-4fd3" c-id="1.8.0.6"><!--t.1.9.1.0-->Lightness (0–1)</label><input property="lightness" aria-labelledby="lightness-4fd3" type="number" min="0.00" max="1.00" step="0.01" value="0.43" c-id="1.10.0.7"><input property="lightness" aria-labelledby="lightness-4fd3" type="range" min="0.00" max="1.00" step="0.01" value="0.43" c-id="1.11.0.8"></ch-oklch-picker> However 4.19.0 and any of the prereleases just return the input string. Did the API change, or is there something I should be doing to configure the hydrate app properly? My prototype Astro integration which uses the hydrate app is here: https://github.com/ch-ui-dev/ch-ui/blob/thure/feat-astro/packages/astro-stencil/src/server.ts |
I’ve posted a comparison PR for the hydrate outputs of babe807 and 4.19.0 here: Could one of these differences account for the component not hydrating? |
No, we've build it to be backward compatible. Let me take a look. |
@thure it seems like setting up your project and the branch and running this script: import { renderToString } from './packages/elements-hydrate-temp/index.mjs'
const { html } = await renderToString(
'<ch-oklch-picker lightness="0.43" chroma="0.4" hue="256"></ch-oklch-picker>',
{
serializeShadowRoot: true,
fullDocument: false,
}
);
console.log(html); gives me above mentioned hydrated string. Can you provide some concrete steps I can walk through to properly reproduce what you see? |
@mayerraphael thanks again for your feedback, I check out your repository, updated Stencil to <my-component last-page="5" class="sc-my-component-h hydrated" s-id="1">
<template shadowrootmode="open">
<style sty-id="sc-my-component">
/*!@:host*/
.sc-my-component-h {
display: block
}
</style>
<div class="sc-my-component" c-id="1.0.0.0">
<div class="pagination sc-my-component" c-id="1.1.1.0">
<div class="pagination-pages pagination-notation sc-my-component" c-id="1.2.2.0">
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="1.3.3.0" s-id="2">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="2.0.0.0">
<!--t.2.1.1.0-->0
</div>
</template>
<!--r.2-->
</my-other-component>
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="1.4.3.1" s-id="3">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="3.0.0.0">
<!--t.3.1.1.0-->1
</div>
</template>
<!--r.3-->
</my-other-component>
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="1.5.3.2" s-id="4">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="4.0.0.0">
<!--t.4.1.1.0-->2
</div>
</template>
<!--r.4-->
</my-other-component>
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="1.6.3.3" s-id="5">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="5.0.0.0">
<!--t.5.1.1.0-->3
</div>
</template>
<!--r.5-->
</my-other-component>
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="1.7.3.4" s-id="6">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="6.0.0.0">
<!--t.6.1.1.0-->4
</div>
</template>
<!--r.6-->
</my-other-component>
</div>
</div>
</div>
</template>
<!--r.1-->
</my-component>
<div>
<my-other-component label="2" class="sc-my-other-component-h hydrated" s-id="7">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="7.0.0.0">
<!--t.7.1.1.0-->2
</div>
</template>
<!--r.7-->
</my-other-component>
</div>
<my-component last-page="2" class="sc-my-component-h hydrated" s-id="8">
<template shadowrootmode="open">
<style sty-id="sc-my-component">
/*!@:host*/
.sc-my-component-h {
display: block
}
</style>
<div class="sc-my-component" c-id="8.0.0.0">
<div class="pagination sc-my-component" c-id="8.1.1.0">
<div class="pagination-pages pagination-notation sc-my-component" c-id="8.2.2.0">
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="8.3.3.0" s-id="9">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="9.0.0.0">
<!--t.9.1.1.0-->0
</div>
</template>
<!--r.9-->
</my-other-component>
<my-other-component class="sc-my-component sc-my-other-component-h hydrated" c-id="8.4.3.1" s-id="10">
<template shadowrootmode="open">
<style sty-id="sc-my-other-component">
/*!@:host*/
.sc-my-other-component-h {
display: block
}
</style>
<div class="pagination-item sc-my-other-component" c-id="10.0.0.0">
<!--t.10.1.1.0-->1
</div>
</template>
<!--r.10-->
</my-other-component>
</div>
</div>
</div>
</template>
<!--r.8-->
</my-component>
<script type="module">
import {
defineCustomElements
} from "./static/loader/index.js";
defineCustomElements().catch(console.error);
</script> I can see all |
@christian-bromann Yes, for sure:
If you then |
I should note that |
No!
Unfortunately I am getting this error:
|
@thure I think I was able to reproduce this using internal test infrastructure. |
@thure false alarm 🙈 it does seem with our current tests that scoped components hydrate just fine. Mind providing more minimalistic example of what you experience? |
@christian-bromann I’ve set up a Codesandbox with the hydrate apps produced by both babe807 and 4.19.0 — these are the same files as the diff I provided earlier. If you have the I can isolate @ch-ui/elements if that would help, will just need some time to do so, let me know. |
... </script> ``` Sorry those patternlib components are some internal ones. In the debugger it looks like Exception has occurred: TypeError: Cannot read properties of undefined (reading 'addEventListener')
at Object.ael
at hydrate\index.js:1712:11
at Array.map (<anonymous>)
at addHostEventListeners (\hydrate\index.js:1708:15)
at hydrateComponent (\hydrate\index.js:2071:7)
at connectElement2 (\hydrate\index.js:2030:18) I am not sure why @christian-bromann I updated my example with an The That was a tough one :) Repo: https://github.com/mayerraphael/stencil-dsd-ssr-playground Edit: This also affects the "old" |
I issued individual tickets as i found more problems with the new Version: |
@mayerraphael thanks a lot! I will take a look. |
What is the current behavior?
This patch introduces support for declarative shadow DOM in Stencil 🎉
GitHub Issue Number: #4010
What is the new behavior?
This patch includes:
renderToString
method:fullDocument
: allows to define whether to return a full document or just the componentserializeShadowRoot
: defines whether a component marked withshadow: true
is being rendered in a declarative shadow DOM:streamToString
function that returns aReadable
object that can be passed into a server response.isPromise
helper in the coderenderToString(
<car-detail car=${JSON.stringify({ year: 1234 })}>)
renderToString
andhyrdrateDocument
serializeToHtml
tostreamToHtml
which uses generatorssetTimeout
orsetInterval
in mock doc in order to enable support for using the hydrate script in the browserDocumentation
ToDo missing
Does this introduce a breaking change?
Testing
Added e2e tests for using the hydration script in the browser as well as part of the e2e test suite.
Other information
n/a