├── .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 '';
29 | yield* shadowContents;
30 | yield '';
31 | }
32 | yield* instance.renderLight();
33 | yield `${tag}>`;
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 |
--------------------------------------------------------------------------------