aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/solid/src/client.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/solid/src/client.ts')
-rw-r--r--packages/integrations/solid/src/client.ts77
1 files changed, 77 insertions, 0 deletions
diff --git a/packages/integrations/solid/src/client.ts b/packages/integrations/solid/src/client.ts
new file mode 100644
index 000000000..f2020bb56
--- /dev/null
+++ b/packages/integrations/solid/src/client.ts
@@ -0,0 +1,77 @@
+import { Suspense } from 'solid-js';
+import { createStore, reconcile } from 'solid-js/store';
+import { createComponent, hydrate, render } from 'solid-js/web';
+
+const alreadyInitializedElements = new WeakMap<Element, any>();
+
+export default (element: HTMLElement) =>
+ (Component: any, props: any, slotted: any, { client }: { client: string }) => {
+ if (!element.hasAttribute('ssr')) return;
+ const isHydrate = client !== 'only';
+ const bootstrap = isHydrate ? hydrate : render;
+
+ let slot: HTMLElement | null;
+ let _slots: Record<string, any> = {};
+ if (Object.keys(slotted).length > 0) {
+ // hydratable
+ if (client !== 'only') {
+ const iterator = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, (node) => {
+ if (node === element) return NodeFilter.FILTER_SKIP;
+ if (node.nodeName === 'ASTRO-SLOT') return NodeFilter.FILTER_ACCEPT;
+ if (node.nodeName === 'ASTRO-ISLAND') return NodeFilter.FILTER_REJECT;
+ return NodeFilter.FILTER_SKIP;
+ });
+ while ((slot = iterator.nextNode() as HTMLElement | null))
+ _slots[slot.getAttribute('name') || 'default'] = slot;
+ }
+ for (const [key, value] of Object.entries(slotted)) {
+ if (_slots[key]) continue;
+ _slots[key] = document.createElement('astro-slot');
+ if (key !== 'default') _slots[key].setAttribute('name', key);
+ _slots[key].innerHTML = value;
+ }
+ }
+
+ const { default: children, ...slots } = _slots;
+ const renderId = element.dataset.solidRenderId;
+ if (alreadyInitializedElements.has(element)) {
+ // update the mounted component
+ alreadyInitializedElements.get(element)!(
+ // reconcile will make sure to apply as little updates as possible, and also remove missing values w/o breaking reactivity
+ reconcile({
+ ...props,
+ ...slots,
+ children,
+ }),
+ );
+ } else {
+ const [store, setStore] = createStore({
+ ...props,
+ ...slots,
+ children,
+ });
+ // store the function to update the current mounted component
+ alreadyInitializedElements.set(element, setStore);
+
+ const dispose = bootstrap(
+ () => {
+ const inner = () => createComponent(Component, store);
+
+ if (isHydrate) {
+ return createComponent(Suspense, {
+ get children() {
+ return inner();
+ },
+ });
+ } else {
+ return inner();
+ }
+ },
+ element,
+ {
+ renderId,
+ },
+ );
+ element.addEventListener('astro:unmount', () => dispose(), { once: true });
+ }
+ };