Last active
May 28, 2023 01:53
-
-
Save marvinhagemeister/0672fed6c18a81f759bbd6ebbe9e98c7 to your computer and use it in GitHub Desktop.
Preact SSR collect web components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { options, VNode } from 'preact'; | |
import { renderToString } from 'preact-render-to-string'; | |
// List of web components that were rendered | |
let webComponents = new Set<string>(); | |
// Called when a vnode is rendered | |
let oldDiffedHook = options.diffed; | |
options.diffed = (vnode: VNode) => { | |
if (typeof vnode.type === 'string' && vnode.type.includes('-')) { | |
webComponents.add(vnode.type); | |
} | |
if (oldDiffedHook) oldDiffedHook(vnode); | |
}; | |
// Turn `my-element-name´ into `MyElementName` | |
function toPascalCase(str: string) { | |
return ( | |
str[0].toUpperCase() + | |
str | |
.slice(1) | |
.replace(/\w-\w/g, (m) => m[0] + m[2].toUpperCase() + m.slice(3)) | |
); | |
} | |
// Render a vnode to HTML and add web component scripts | |
function renderWithWc(vnode: VNode) { | |
// RTS is synchronous, so we can rely on the global | |
let html = renderToString(vnode); | |
let rendered = webComponents; | |
// Make sure next render has an empty state to begin with | |
webComponents = new Set(); | |
let scripts = ''; | |
for (const name of rendered.values()) { | |
const pascalCase = toPascalCase(name); | |
scripts += `<script>class ${pascalCase} extends HTMLElement {}</script>\n`; | |
} | |
return scripts + html; | |
} | |
function App() { | |
return ( | |
<my-element> | |
<div> | |
<my-other-element /> | |
</div> | |
</my-element> | |
); | |
} | |
console.log(renderWithWc(<App />)); | |
// Output: | |
// <script>class MyOtherElement extends HTMLElement {}</script> | |
// <script>class MyElement extends HTMLElement {}</script> | |
// <my-element><div><my-other-element></my-other-element></div></my-element> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment