├── .gitignore ├── CustomElementRenderer.js ├── README.md ├── astro.js ├── client-shim.min.js ├── package-lock.json ├── package.json ├── server-shim.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | .DS_Store -------------------------------------------------------------------------------- /CustomElementRenderer.js: -------------------------------------------------------------------------------- 1 | import { ElementRenderer } from '@lit-labs/ssr/lib/element-renderer.js'; 2 | import { escapeHtml } from '@lit-labs/ssr/lib/util/escape-html.js'; 3 | 4 | export class CustomElementRender extends ElementRenderer { 5 | constructor(tagName) { 6 | super(tagName); 7 | this.element = new (customElements.get(tagName))(); 8 | this._attributes = {}; 9 | } 10 | setAttribute(name, value) { 11 | this._attributes[name] = value; 12 | this.element.setAttribute(name, value); 13 | } 14 | *renderAttributes() { 15 | for (const [name, value] of Object.entries(this._attributes)) { 16 | if (value === '' || value === undefined || value === null) { 17 | yield ` ${name}`; 18 | } 19 | else { 20 | yield ` ${name}="${escapeHtml(value)}"`; 21 | } 22 | } 23 | } 24 | async connectedCallback() { 25 | this.element?.connectedCallback?.(); 26 | await this.element?.updateComplete || Promise.resolve(); 27 | } 28 | attributeChangedCallback() { } 29 | *renderLight() { 30 | yield this.element.innerHTML; 31 | } 32 | *renderShadow() { 33 | yield this.element.shadowRoot.innerHTML; 34 | } 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # custom-elements-ssr 2 | 3 | This package contains an Astro SSR integration to render vanilla custom elements on the server, as well as a [@lit-labs/ssr](https://www.npmjs.com/package/@lit-labs/ssr) compatible `ElementRenderer` for usage with [@lit-labs/ssr](https://www.npmjs.com/package/@lit-labs/ssr). 4 | 5 | Enables server-side rendering and client-side hydration for your [Lit](https://lit.dev/) custom elements. 6 | 7 | > Try it on [Stackblitz](https://stackblitz.com/edit/github-hcspcv-ccu1fd?file=src/pages/index.astro) 8 | 9 | ## Differences with lit SSR 10 | 11 | It could be the case that you were hoping the Lit SSR package would also support vanilla Custom Elements, and were surprised to find that it didnt. The reason for this is that to render custom elements on the server side, we need some browser APIs to be available in a Node.js environment. Lit however, makes surprisingly little use of browser APIs to be able to do efficient rendering. This means that the DOM shim that Lit SSR requires is really, really minimal, and doesn't include a bunch of things, like for example querySelectors. This package instead makes use of [linkedom]() to shim browser functionality on the server, which does include the required browser APIs to render custom elements on the server. 12 | 13 | Additionally the `ElementRenderer` for vanilla custom elements is a little bit different from Lit elements. 14 | 15 | ## Limitations 16 | 17 | Linkedom has decent support for custom elements, but there is some functionality missing, like for example HTMLSlotElements `assignedNodes()` method. There is an open issue [here](https://github.com/WebReflection/linkedom/issues/131). 18 | 19 | ## @lit-labs/ssr usage 20 | 21 | Import the ElementRenderer: 22 | 23 | ``` 24 | import { CustomElementRenderer } from 'custom-elements-ssr/CustomElementRenderer.js'; 25 | ``` 26 | 27 | ## Astro Usage 28 | 29 | ### Configuration 30 | 31 | In your Astro SSR-enabled project, you'll need to install the integration and the required polyfill: 32 | 33 | ``` 34 | npm i custom-elements-ssr @webcomponents/template-shadowroot 35 | ``` 36 | 37 | Add the integration to your `astro.config.mjs`: 38 | ```diff 39 | import { defineConfig } from 'astro/config'; 40 | + import customElements from 'custom-elements-ssr/astro.js'; 41 | 42 | // https://astro.build/config 43 | export default defineConfig({ 44 | + integrations: [customElements()] 45 | }); 46 | ``` 47 | 48 | ### Usage 49 | 50 | Create a custom element in your project. 51 | 52 | `src/components/my-element.js`: 53 | ```js 54 | class MyElement extends HTMLElement { 55 | constructor() { 56 | super(); 57 | this.attachShadow({ mode: 'open' }); 58 | } 59 | 60 | connectedCallback() { 61 | this.shadowRoot.innerHTML = '

Hello World

'; 62 | } 63 | } 64 | 65 | // Make sure to export the `tagName`, without it, Astro will error 66 | export const tagName = 'my-element'; 67 | customElements.define(tagName, MyElement); 68 | ``` 69 | 70 | And then use it in your Astro pages: 71 | `index.astro`: 72 | ```astro 73 | --- 74 | import '../components/my-element.js'; 75 | --- 76 | 77 | ``` 78 | 79 | ## Example 80 | 81 | You can find an example here: 82 | - [github source](https://github.com/thepassle/astro-custom-element-example) 83 | - [netlify demo](https://vanilla-ssr-deploy-test.netlify.app/) -------------------------------------------------------------------------------- /astro.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "node:fs"; 2 | 3 | function getViteConfiguration() { 4 | return { 5 | optimizeDeps: { 6 | include: [ 7 | "custom-elements-ssr/client-shim.min.js", 8 | "@webcomponents/template-shadowroot/template-shadowroot.js", 9 | ], 10 | exclude: [ 11 | "custom-elements-ssr/server.js", 12 | ] 13 | }, 14 | ssr: { 15 | external: [ 16 | "linkedom", 17 | "custom-elements-ssr/server.js" 18 | ] 19 | } 20 | }; 21 | } 22 | 23 | /** 24 | * Astro's webapi already polyfills `CustomEvent`. When creating CustomEvent in linkedom, 25 | * it does a check to see if `typeof CustomEvent === 'function'` which it is, because 26 | * Astro already polyfills it. It then uses Astro's version of `CustomEvent`, which conflicts 27 | * with Linkedom's `Event` and errors. 28 | * 29 | * https://github.com/WebReflection/linkedom/issues/130 30 | */ 31 | Object.assign(globalThis, { 32 | CustomEvent: null 33 | }); 34 | 35 | export default function customElements() { 36 | return { 37 | name: "custom-elements-ssr", 38 | hooks: { 39 | "astro:config:setup": ({ updateConfig, addRenderer, injectScript }) => { 40 | injectScript("head-inline", readFileSync(new URL("./client-shim.min.js", import.meta.url), { encoding: "utf-8" })); 41 | 42 | addRenderer({ 43 | name: "custom-elements-ssr", 44 | serverEntrypoint: "custom-elements-ssr/server.js" 45 | }); 46 | 47 | updateConfig({ 48 | vite: getViteConfiguration() 49 | }); 50 | } 51 | } 52 | }; 53 | } -------------------------------------------------------------------------------- /client-shim.min.js: -------------------------------------------------------------------------------- 1 | /** @license Copyright 2020 Google LLC (BSD-3-Clause) */ 2 | /** Bundled JS generated from "@astrojs/lit/client-shim.js" */ 3 | var N = Object.defineProperty; 4 | var i = (t, n) => () => (t && (n = t((t = 0))), n); 5 | var b = (t, n) => { 6 | for (var a in n) N(t, a, { get: n[a], enumerable: !0 }); 7 | }; 8 | function s() { 9 | if (d === void 0) { 10 | let t = document.createElement('div'); 11 | (t.innerHTML = '
'), 12 | (d = !!t.firstElementChild.shadowRoot); 13 | } 14 | return d; 15 | } 16 | var d, 17 | m = i(() => { }); 18 | var p, 19 | c, 20 | f, 21 | u = i(() => { 22 | (p = (t) => t.parentElement === null), 23 | (c = (t) => t.tagName === 'TEMPLATE'), 24 | (f = (t) => t.nodeType === Node.ELEMENT_NODE); 25 | }); 26 | var h, 27 | E = i(() => { 28 | m(); 29 | u(); 30 | h = (t) => { 31 | var n; 32 | if (s()) return; 33 | let a = [], 34 | e = t.firstElementChild; 35 | for (; e !== t && e !== null;) 36 | if (c(e)) a.push(e), (e = e.content); 37 | else if (e.firstElementChild !== null) e = e.firstElementChild; 38 | else if (f(e) && e.nextElementSibling !== null) e = e.nextElementSibling; 39 | else { 40 | let o; 41 | for (; e !== t && e !== null;) 42 | if (p(e)) { 43 | o = a.pop(); 44 | let r = o.parentElement, 45 | l = o.getAttribute('shadowroot'); 46 | if (((e = o), l === 'open' || l === 'closed')) { 47 | let y = o.hasAttribute('shadowrootdelegatesfocus'); 48 | try { 49 | r.attachShadow({ mode: l, delegatesFocus: y }).append(o.content); 50 | } catch { } 51 | } else o = void 0; 52 | } else { 53 | let r = e.nextElementSibling; 54 | if (r != null) { 55 | (e = r), o !== void 0 && o.parentElement.removeChild(o); 56 | break; 57 | } 58 | let l = 59 | (n = e.parentElement) === null || n === void 0 ? void 0 : n.nextElementSibling; 60 | if (l != null) { 61 | (e = l), o !== void 0 && o.parentElement.removeChild(o); 62 | break; 63 | } 64 | (e = e.parentElement), o !== void 0 && (o.parentElement.removeChild(o), (o = void 0)); 65 | } 66 | } 67 | }; 68 | }); 69 | var w = i(() => { 70 | E(); 71 | }); 72 | var v = {}; 73 | b(v, { hasNativeDeclarativeShadowRoots: () => s, hydrateShadowRoots: () => h }); 74 | var S = i(() => { 75 | m(); 76 | w(); 77 | }); 78 | async function g() { 79 | let { hydrateShadowRoots: t } = await Promise.resolve().then(() => (S(), v)); 80 | t(document.body); 81 | } 82 | var x = new DOMParser() 83 | .parseFromString('

', 'text/html', { 84 | includeShadowRoots: !0, 85 | }) 86 | .querySelector('p'); 87 | (!x || !x.shadowRoot) && g(); 88 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astrojs/lit", 3 | "version": "0.1.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@astrojs/lit", 9 | "version": "0.1.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@lit-labs/ssr": "^2.1.0", 13 | "linkedom": "^0.14.7" 14 | }, 15 | "peerDependencies": { 16 | "@webcomponents/template-shadowroot": "^0.1.0" 17 | } 18 | }, 19 | "node_modules/@lit-labs/ssr": { 20 | "version": "2.1.0", 21 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-2.1.0.tgz", 22 | "integrity": "sha512-Tnz/S99G57QKQkI+5QhpfOyVxdHM/IbSa3DZmbF5aeIugivONjurHOuMn6AHzzgdteae3ihcGV2eehKPJuG4/w==", 23 | "dependencies": { 24 | "@lit-labs/ssr-client": "^1.0.0", 25 | "@lit/reactive-element": "^1.1.0", 26 | "@types/node": "^16.0.0", 27 | "lit": "^2.1.0", 28 | "lit-element": "^3.1.0", 29 | "lit-html": "^2.1.0", 30 | "node-fetch": "^2.6.0", 31 | "parse5": "^6.0.1", 32 | "resolve": "^1.10.1" 33 | }, 34 | "engines": { 35 | "node": ">=13.9.0" 36 | } 37 | }, 38 | "node_modules/@lit-labs/ssr-client": { 39 | "version": "1.0.1", 40 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr-client/-/ssr-client-1.0.1.tgz", 41 | "integrity": "sha512-rr/UVhxbKWNUr+3qRyvZk+glC7v7ph8Gk/W0z96YG64COJKf9ilnWY6JGW77TRqhrRMmS2nsvAXOyQgcF+4jrA==", 42 | "dependencies": { 43 | "@lit/reactive-element": "^1.0.0", 44 | "lit": "^2.0.0", 45 | "lit-html": "^2.0.0" 46 | } 47 | }, 48 | "node_modules/@lit/reactive-element": { 49 | "version": "1.3.1", 50 | "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.1.tgz", 51 | "integrity": "sha512-nOJARIr3pReqK3hfFCSW2Zg/kFcFsSAlIE7z4a0C9D2dPrgD/YSn3ZP2ET/rxKB65SXyG7jJbkynBRm+tGlacw==" 52 | }, 53 | "node_modules/@types/node": { 54 | "version": "16.11.27", 55 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz", 56 | "integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==" 57 | }, 58 | "node_modules/@types/trusted-types": { 59 | "version": "2.0.2", 60 | "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", 61 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" 62 | }, 63 | "node_modules/@webcomponents/template-shadowroot": { 64 | "version": "0.1.0", 65 | "resolved": "https://registry.npmjs.org/@webcomponents/template-shadowroot/-/template-shadowroot-0.1.0.tgz", 66 | "integrity": "sha512-ry84Vft6xtRBbd4M/ptRodbOLodV5AD15TYhyRghCRgIcJJKmYmJ2v2BaaWxygENwh6Uq3zTfGPmlckKT/GXsQ==", 67 | "peer": true 68 | }, 69 | "node_modules/boolbase": { 70 | "version": "1.0.0", 71 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 72 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 73 | }, 74 | "node_modules/css-select": { 75 | "version": "4.3.0", 76 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", 77 | "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", 78 | "dependencies": { 79 | "boolbase": "^1.0.0", 80 | "css-what": "^6.0.1", 81 | "domhandler": "^4.3.1", 82 | "domutils": "^2.8.0", 83 | "nth-check": "^2.0.1" 84 | }, 85 | "funding": { 86 | "url": "https://github.com/sponsors/fb55" 87 | } 88 | }, 89 | "node_modules/css-what": { 90 | "version": "6.1.0", 91 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 92 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", 93 | "engines": { 94 | "node": ">= 6" 95 | }, 96 | "funding": { 97 | "url": "https://github.com/sponsors/fb55" 98 | } 99 | }, 100 | "node_modules/cssom": { 101 | "version": "0.5.0", 102 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", 103 | "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" 104 | }, 105 | "node_modules/dom-serializer": { 106 | "version": "1.4.1", 107 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", 108 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", 109 | "dependencies": { 110 | "domelementtype": "^2.0.1", 111 | "domhandler": "^4.2.0", 112 | "entities": "^2.0.0" 113 | }, 114 | "funding": { 115 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" 116 | } 117 | }, 118 | "node_modules/domelementtype": { 119 | "version": "2.3.0", 120 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 121 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", 122 | "funding": [ 123 | { 124 | "type": "github", 125 | "url": "https://github.com/sponsors/fb55" 126 | } 127 | ] 128 | }, 129 | "node_modules/domhandler": { 130 | "version": "4.3.1", 131 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", 132 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", 133 | "dependencies": { 134 | "domelementtype": "^2.2.0" 135 | }, 136 | "engines": { 137 | "node": ">= 4" 138 | }, 139 | "funding": { 140 | "url": "https://github.com/fb55/domhandler?sponsor=1" 141 | } 142 | }, 143 | "node_modules/domutils": { 144 | "version": "2.8.0", 145 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", 146 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", 147 | "dependencies": { 148 | "dom-serializer": "^1.0.1", 149 | "domelementtype": "^2.2.0", 150 | "domhandler": "^4.2.0" 151 | }, 152 | "funding": { 153 | "url": "https://github.com/fb55/domutils?sponsor=1" 154 | } 155 | }, 156 | "node_modules/entities": { 157 | "version": "2.2.0", 158 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", 159 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", 160 | "funding": { 161 | "url": "https://github.com/fb55/entities?sponsor=1" 162 | } 163 | }, 164 | "node_modules/function-bind": { 165 | "version": "1.1.1", 166 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 167 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 168 | }, 169 | "node_modules/has": { 170 | "version": "1.0.3", 171 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 172 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 173 | "dependencies": { 174 | "function-bind": "^1.1.1" 175 | }, 176 | "engines": { 177 | "node": ">= 0.4.0" 178 | } 179 | }, 180 | "node_modules/html-escaper": { 181 | "version": "3.0.3", 182 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", 183 | "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" 184 | }, 185 | "node_modules/htmlparser2": { 186 | "version": "7.2.0", 187 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", 188 | "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", 189 | "funding": [ 190 | "https://github.com/fb55/htmlparser2?sponsor=1", 191 | { 192 | "type": "github", 193 | "url": "https://github.com/sponsors/fb55" 194 | } 195 | ], 196 | "dependencies": { 197 | "domelementtype": "^2.0.1", 198 | "domhandler": "^4.2.2", 199 | "domutils": "^2.8.0", 200 | "entities": "^3.0.1" 201 | } 202 | }, 203 | "node_modules/htmlparser2/node_modules/entities": { 204 | "version": "3.0.1", 205 | "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", 206 | "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", 207 | "engines": { 208 | "node": ">=0.12" 209 | }, 210 | "funding": { 211 | "url": "https://github.com/fb55/entities?sponsor=1" 212 | } 213 | }, 214 | "node_modules/is-core-module": { 215 | "version": "2.9.0", 216 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 217 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 218 | "dependencies": { 219 | "has": "^1.0.3" 220 | }, 221 | "funding": { 222 | "url": "https://github.com/sponsors/ljharb" 223 | } 224 | }, 225 | "node_modules/linkedom": { 226 | "version": "0.14.7", 227 | "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.7.tgz", 228 | "integrity": "sha512-3S0iNZDXWbKAqrtc0SU4UdhRDRm+8Fq1y15zAhcVnHWaGh+6Qnej9BF2wFJjpgdwrmF2E8nxb5axYSKLpMM3Pw==", 229 | "dependencies": { 230 | "css-select": "^4.3.0", 231 | "cssom": "^0.5.0", 232 | "html-escaper": "^3.0.3", 233 | "htmlparser2": "^7.2.0", 234 | "uhyphen": "^0.1.0" 235 | } 236 | }, 237 | "node_modules/lit": { 238 | "version": "2.2.2", 239 | "resolved": "https://registry.npmjs.org/lit/-/lit-2.2.2.tgz", 240 | "integrity": "sha512-eN3+2QRHn/erxYB88AXiiRgQA6RltE9MhzySCwX+ACOxA/MLWN3VdXvcbZD9PN09zmUwlgzDvW3T84YWj2Sa0A==", 241 | "dependencies": { 242 | "@lit/reactive-element": "^1.3.0", 243 | "lit-element": "^3.2.0", 244 | "lit-html": "^2.2.0" 245 | } 246 | }, 247 | "node_modules/lit-element": { 248 | "version": "3.2.0", 249 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", 250 | "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", 251 | "dependencies": { 252 | "@lit/reactive-element": "^1.3.0", 253 | "lit-html": "^2.2.0" 254 | } 255 | }, 256 | "node_modules/lit-html": { 257 | "version": "2.2.2", 258 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.2.tgz", 259 | "integrity": "sha512-cJofCRXuizwyaiGt9pJjJOcauezUlSB6t87VBXsPwRhbzF29MgD8GH6fZ0BuZdXAAC02IRONZBd//VPUuU8QbQ==", 260 | "dependencies": { 261 | "@types/trusted-types": "^2.0.2" 262 | } 263 | }, 264 | "node_modules/node-fetch": { 265 | "version": "2.6.7", 266 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 267 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 268 | "dependencies": { 269 | "whatwg-url": "^5.0.0" 270 | }, 271 | "engines": { 272 | "node": "4.x || >=6.0.0" 273 | }, 274 | "peerDependencies": { 275 | "encoding": "^0.1.0" 276 | }, 277 | "peerDependenciesMeta": { 278 | "encoding": { 279 | "optional": true 280 | } 281 | } 282 | }, 283 | "node_modules/nth-check": { 284 | "version": "2.0.1", 285 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", 286 | "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", 287 | "dependencies": { 288 | "boolbase": "^1.0.0" 289 | }, 290 | "funding": { 291 | "url": "https://github.com/fb55/nth-check?sponsor=1" 292 | } 293 | }, 294 | "node_modules/parse5": { 295 | "version": "6.0.1", 296 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", 297 | "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" 298 | }, 299 | "node_modules/path-parse": { 300 | "version": "1.0.7", 301 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 302 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 303 | }, 304 | "node_modules/resolve": { 305 | "version": "1.22.0", 306 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 307 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 308 | "dependencies": { 309 | "is-core-module": "^2.8.1", 310 | "path-parse": "^1.0.7", 311 | "supports-preserve-symlinks-flag": "^1.0.0" 312 | }, 313 | "bin": { 314 | "resolve": "bin/resolve" 315 | }, 316 | "funding": { 317 | "url": "https://github.com/sponsors/ljharb" 318 | } 319 | }, 320 | "node_modules/supports-preserve-symlinks-flag": { 321 | "version": "1.0.0", 322 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 323 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 324 | "engines": { 325 | "node": ">= 0.4" 326 | }, 327 | "funding": { 328 | "url": "https://github.com/sponsors/ljharb" 329 | } 330 | }, 331 | "node_modules/tr46": { 332 | "version": "0.0.3", 333 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 334 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" 335 | }, 336 | "node_modules/uhyphen": { 337 | "version": "0.1.0", 338 | "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.1.0.tgz", 339 | "integrity": "sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==" 340 | }, 341 | "node_modules/webidl-conversions": { 342 | "version": "3.0.1", 343 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 344 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" 345 | }, 346 | "node_modules/whatwg-url": { 347 | "version": "5.0.0", 348 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 349 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", 350 | "dependencies": { 351 | "tr46": "~0.0.3", 352 | "webidl-conversions": "^3.0.0" 353 | } 354 | } 355 | }, 356 | "dependencies": { 357 | "@lit-labs/ssr": { 358 | "version": "2.1.0", 359 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-2.1.0.tgz", 360 | "integrity": "sha512-Tnz/S99G57QKQkI+5QhpfOyVxdHM/IbSa3DZmbF5aeIugivONjurHOuMn6AHzzgdteae3ihcGV2eehKPJuG4/w==", 361 | "requires": { 362 | "@lit-labs/ssr-client": "^1.0.0", 363 | "@lit/reactive-element": "^1.1.0", 364 | "@types/node": "^16.0.0", 365 | "lit": "^2.1.0", 366 | "lit-element": "^3.1.0", 367 | "lit-html": "^2.1.0", 368 | "node-fetch": "^2.6.0", 369 | "parse5": "^6.0.1", 370 | "resolve": "^1.10.1" 371 | } 372 | }, 373 | "@lit-labs/ssr-client": { 374 | "version": "1.0.1", 375 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr-client/-/ssr-client-1.0.1.tgz", 376 | "integrity": "sha512-rr/UVhxbKWNUr+3qRyvZk+glC7v7ph8Gk/W0z96YG64COJKf9ilnWY6JGW77TRqhrRMmS2nsvAXOyQgcF+4jrA==", 377 | "requires": { 378 | "@lit/reactive-element": "^1.0.0", 379 | "lit": "^2.0.0", 380 | "lit-html": "^2.0.0" 381 | } 382 | }, 383 | "@lit/reactive-element": { 384 | "version": "1.3.1", 385 | "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.1.tgz", 386 | "integrity": "sha512-nOJARIr3pReqK3hfFCSW2Zg/kFcFsSAlIE7z4a0C9D2dPrgD/YSn3ZP2ET/rxKB65SXyG7jJbkynBRm+tGlacw==" 387 | }, 388 | "@types/node": { 389 | "version": "16.11.27", 390 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz", 391 | "integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==" 392 | }, 393 | "@types/trusted-types": { 394 | "version": "2.0.2", 395 | "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", 396 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" 397 | }, 398 | "@webcomponents/template-shadowroot": { 399 | "version": "0.1.0", 400 | "resolved": "https://registry.npmjs.org/@webcomponents/template-shadowroot/-/template-shadowroot-0.1.0.tgz", 401 | "integrity": "sha512-ry84Vft6xtRBbd4M/ptRodbOLodV5AD15TYhyRghCRgIcJJKmYmJ2v2BaaWxygENwh6Uq3zTfGPmlckKT/GXsQ==", 402 | "peer": true 403 | }, 404 | "boolbase": { 405 | "version": "1.0.0", 406 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 407 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 408 | }, 409 | "css-select": { 410 | "version": "4.3.0", 411 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", 412 | "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", 413 | "requires": { 414 | "boolbase": "^1.0.0", 415 | "css-what": "^6.0.1", 416 | "domhandler": "^4.3.1", 417 | "domutils": "^2.8.0", 418 | "nth-check": "^2.0.1" 419 | } 420 | }, 421 | "css-what": { 422 | "version": "6.1.0", 423 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 424 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" 425 | }, 426 | "cssom": { 427 | "version": "0.5.0", 428 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", 429 | "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" 430 | }, 431 | "dom-serializer": { 432 | "version": "1.4.1", 433 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", 434 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", 435 | "requires": { 436 | "domelementtype": "^2.0.1", 437 | "domhandler": "^4.2.0", 438 | "entities": "^2.0.0" 439 | } 440 | }, 441 | "domelementtype": { 442 | "version": "2.3.0", 443 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 444 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" 445 | }, 446 | "domhandler": { 447 | "version": "4.3.1", 448 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", 449 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", 450 | "requires": { 451 | "domelementtype": "^2.2.0" 452 | } 453 | }, 454 | "domutils": { 455 | "version": "2.8.0", 456 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", 457 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", 458 | "requires": { 459 | "dom-serializer": "^1.0.1", 460 | "domelementtype": "^2.2.0", 461 | "domhandler": "^4.2.0" 462 | } 463 | }, 464 | "entities": { 465 | "version": "2.2.0", 466 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", 467 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" 468 | }, 469 | "function-bind": { 470 | "version": "1.1.1", 471 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 472 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 473 | }, 474 | "has": { 475 | "version": "1.0.3", 476 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 477 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 478 | "requires": { 479 | "function-bind": "^1.1.1" 480 | } 481 | }, 482 | "html-escaper": { 483 | "version": "3.0.3", 484 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", 485 | "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" 486 | }, 487 | "htmlparser2": { 488 | "version": "7.2.0", 489 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", 490 | "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", 491 | "requires": { 492 | "domelementtype": "^2.0.1", 493 | "domhandler": "^4.2.2", 494 | "domutils": "^2.8.0", 495 | "entities": "^3.0.1" 496 | }, 497 | "dependencies": { 498 | "entities": { 499 | "version": "3.0.1", 500 | "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", 501 | "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" 502 | } 503 | } 504 | }, 505 | "is-core-module": { 506 | "version": "2.9.0", 507 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 508 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 509 | "requires": { 510 | "has": "^1.0.3" 511 | } 512 | }, 513 | "linkedom": { 514 | "version": "0.14.7", 515 | "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.7.tgz", 516 | "integrity": "sha512-3S0iNZDXWbKAqrtc0SU4UdhRDRm+8Fq1y15zAhcVnHWaGh+6Qnej9BF2wFJjpgdwrmF2E8nxb5axYSKLpMM3Pw==", 517 | "requires": { 518 | "css-select": "^4.3.0", 519 | "cssom": "^0.5.0", 520 | "html-escaper": "^3.0.3", 521 | "htmlparser2": "^7.2.0", 522 | "uhyphen": "^0.1.0" 523 | } 524 | }, 525 | "lit": { 526 | "version": "2.2.2", 527 | "resolved": "https://registry.npmjs.org/lit/-/lit-2.2.2.tgz", 528 | "integrity": "sha512-eN3+2QRHn/erxYB88AXiiRgQA6RltE9MhzySCwX+ACOxA/MLWN3VdXvcbZD9PN09zmUwlgzDvW3T84YWj2Sa0A==", 529 | "requires": { 530 | "@lit/reactive-element": "^1.3.0", 531 | "lit-element": "^3.2.0", 532 | "lit-html": "^2.2.0" 533 | } 534 | }, 535 | "lit-element": { 536 | "version": "3.2.0", 537 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", 538 | "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", 539 | "requires": { 540 | "@lit/reactive-element": "^1.3.0", 541 | "lit-html": "^2.2.0" 542 | } 543 | }, 544 | "lit-html": { 545 | "version": "2.2.2", 546 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.2.tgz", 547 | "integrity": "sha512-cJofCRXuizwyaiGt9pJjJOcauezUlSB6t87VBXsPwRhbzF29MgD8GH6fZ0BuZdXAAC02IRONZBd//VPUuU8QbQ==", 548 | "requires": { 549 | "@types/trusted-types": "^2.0.2" 550 | } 551 | }, 552 | "node-fetch": { 553 | "version": "2.6.7", 554 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 555 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 556 | "requires": { 557 | "whatwg-url": "^5.0.0" 558 | } 559 | }, 560 | "nth-check": { 561 | "version": "2.0.1", 562 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", 563 | "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", 564 | "requires": { 565 | "boolbase": "^1.0.0" 566 | } 567 | }, 568 | "parse5": { 569 | "version": "6.0.1", 570 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", 571 | "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" 572 | }, 573 | "path-parse": { 574 | "version": "1.0.7", 575 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 576 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 577 | }, 578 | "resolve": { 579 | "version": "1.22.0", 580 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 581 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 582 | "requires": { 583 | "is-core-module": "^2.8.1", 584 | "path-parse": "^1.0.7", 585 | "supports-preserve-symlinks-flag": "^1.0.0" 586 | } 587 | }, 588 | "supports-preserve-symlinks-flag": { 589 | "version": "1.0.0", 590 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 591 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" 592 | }, 593 | "tr46": { 594 | "version": "0.0.3", 595 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 596 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" 597 | }, 598 | "uhyphen": { 599 | "version": "0.1.0", 600 | "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.1.0.tgz", 601 | "integrity": "sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==" 602 | }, 603 | "webidl-conversions": { 604 | "version": "3.0.1", 605 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 606 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" 607 | }, 608 | "whatwg-url": { 609 | "version": "5.0.0", 610 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 611 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", 612 | "requires": { 613 | "tr46": "~0.0.3", 614 | "webidl-conversions": "^3.0.0" 615 | } 616 | } 617 | } 618 | } 619 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-elements-ssr", 3 | "version": "0.0.11", 4 | "description": "Custom elements renderer for ssr", 5 | "type": "module", 6 | "author": "open-wc", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/thepassle/custom-elements-ssr" 11 | }, 12 | "keywords": [ 13 | "astro-component", 14 | "astro", 15 | "renderer", 16 | "ssr", 17 | "lit", 18 | "custom-element", 19 | "webcomponent" 20 | ], 21 | "bugs": "https://github.com/thepassle/custom-elements-ssr", 22 | "homepage": "", 23 | "scripts": { 24 | }, 25 | "dependencies": { 26 | "@lit-labs/ssr": "^2.1.0", 27 | "linkedom": "^0.14.7" 28 | }, 29 | "peerDependencies": { 30 | "@webcomponents/template-shadowroot": "^0.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server-shim.js: -------------------------------------------------------------------------------- 1 | import { parseHTML } from 'linkedom'; 2 | 3 | /** 4 | * Astro's webapi already polyfills `CustomEvent`. When creating CustomEvent in linkedom, 5 | * it does a check to see if `typeof CustomEvent === 'function'` which it is, because 6 | * Astro already polyfills it. It then uses Astro's version of `CustomEvent`, which conflicts 7 | * with Linkedom's `Event` and errors. 8 | * 9 | * https://github.com/WebReflection/linkedom/issues/130 10 | */ 11 | Object.assign(globalThis, { 12 | CustomEvent: null 13 | }); 14 | 15 | const { 16 | window, 17 | document, 18 | customElements, 19 | HTMLElement, 20 | Event, 21 | CustomEvent 22 | } = parseHTML(`Hello SSR`); 23 | 24 | const originalDefine = customElements.define.bind(customElements); 25 | 26 | customElements.define = (name, klass) => { 27 | try { 28 | originalDefine(name, klass); 29 | } catch(e) { 30 | customElements.registry.delete(name); 31 | originalDefine(name, class extends klass {}) 32 | } 33 | } 34 | 35 | Object.assign(globalThis, { 36 | document, 37 | window, 38 | customElements, 39 | HTMLElement, 40 | Event, 41 | CustomEvent 42 | }); 43 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import './server-shim.js'; 2 | import { DOMParser } from 'linkedom'; 3 | import { CustomElementRender } from './CustomElementRenderer.js'; 4 | 5 | async function check(tag) { 6 | return !!customElements?.get?.(tag); 7 | } 8 | 9 | async function* render(tag, attrs, children) { 10 | const instance = new CustomElementRender(tag); 11 | 12 | Object.entries(attrs).forEach(([k, v]) => { 13 | instance.setAttribute(k, v); 14 | }); 15 | 16 | if (children) { 17 | const nodes = new DOMParser().parseFromString(children, 'text/html').childNodes; 18 | instance.element.append(...nodes); 19 | } 20 | 21 | instance?.connectedCallback?.(); 22 | 23 | yield `<${tag}`; 24 | yield* instance.renderAttributes(); 25 | yield `>`; 26 | const shadowContents = instance.renderShadow(); 27 | if (shadowContents !== undefined) { 28 | yield ''; 31 | } 32 | yield* instance.renderLight(); 33 | yield ``; 34 | } 35 | 36 | async function renderToStaticMarkup(tag, attrs, children) { 37 | 38 | let html = ''; 39 | for await (let chunk of render(tag, attrs, children)) { 40 | html += chunk; 41 | } 42 | 43 | return { html }; 44 | } 45 | 46 | export default { check, renderToStaticMarkup }; 47 | --------------------------------------------------------------------------------