aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/svelte/src/client.svelte.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/svelte/src/client.svelte.ts')
-rw-r--r--packages/integrations/svelte/src/client.svelte.ts85
1 files changed, 85 insertions, 0 deletions
diff --git a/packages/integrations/svelte/src/client.svelte.ts b/packages/integrations/svelte/src/client.svelte.ts
new file mode 100644
index 000000000..273c9753a
--- /dev/null
+++ b/packages/integrations/svelte/src/client.svelte.ts
@@ -0,0 +1,85 @@
+import { createRawSnippet, hydrate, mount, unmount } from 'svelte';
+
+const existingApplications = new WeakMap<HTMLElement, ReturnType<typeof createComponent>>();
+
+export default (element: HTMLElement) => {
+ return async (
+ Component: any,
+ props: Record<string, any>,
+ slotted: Record<string, any>,
+ { client }: Record<string, string>,
+ ) => {
+ if (!element.hasAttribute('ssr')) return;
+
+ let children = undefined;
+ let _$$slots: Record<string, any> | undefined = undefined;
+ let renderFns: Record<string, any> = {};
+
+ for (const [key, value] of Object.entries(slotted)) {
+ // Legacy slot support
+ _$$slots ??= {};
+ if (key === 'default') {
+ _$$slots.default = true;
+ children = createRawSnippet(() => ({
+ render: () => `<astro-slot>${value}</astro-slot>`,
+ }));
+ } else {
+ _$$slots[key] = createRawSnippet(() => ({
+ render: () => `<astro-slot name="${key}">${value}</astro-slot>`,
+ }));
+ }
+ // @render support for Svelte ^5.0
+ if (key === 'default') {
+ renderFns.children = createRawSnippet(() => ({
+ render: () => `<astro-slot>${value}</astro-slot>`,
+ }));
+ } else {
+ renderFns[key] = createRawSnippet(() => ({
+ render: () => `<astro-slot name="${key}">${value}</astro-slot>`,
+ }));
+ }
+ }
+
+ const resolvedProps = {
+ ...props,
+ children,
+ $$slots: _$$slots,
+ ...renderFns,
+ };
+ if (existingApplications.has(element)) {
+ existingApplications.get(element)!.setProps(resolvedProps);
+ } else {
+ const component = createComponent(Component, element, resolvedProps, client !== 'only');
+ existingApplications.set(element, component);
+ element.addEventListener('astro:unmount', () => component.destroy(), { once: true });
+ }
+ };
+};
+
+function createComponent(
+ Component: any,
+ target: HTMLElement,
+ props: Record<string, any>,
+ shouldHydrate: boolean,
+) {
+ let propsState = $state(props);
+ const bootstrap = shouldHydrate ? hydrate : mount;
+ if (!shouldHydrate) {
+ target.innerHTML = '';
+ }
+ const component = bootstrap(Component, { target, props: propsState });
+ return {
+ setProps(newProps: Record<string, any>) {
+ Object.assign(propsState, newProps);
+ // Remove props in `propsState` but not in `newProps`
+ for (const key in propsState) {
+ if (!(key in newProps)) {
+ delete propsState[key];
+ }
+ }
+ },
+ destroy() {
+ unmount(component);
+ },
+ };
+}