summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorGravatar Bjorn Lu <bjornlu.dev@gmail.com> 2023-11-15 23:40:23 +0800
committerGravatar GitHub <noreply@github.com> 2023-11-15 23:40:23 +0800
commita600c14837fd18c4c4c3330c0195cd47b0b73df9 (patch)
tree26d9ee9ed5fc19752107253c623c9aaf0a57119d /packages
parente63aac94ca8aca96a39785a2f5926827ed18c255 (diff)
downloadastro-a600c14837fd18c4c4c3330c0195cd47b0b73df9.tar.gz
astro-a600c14837fd18c4c4c3330c0195cd47b0b73df9.tar.zst
astro-a600c14837fd18c4c4c3330c0195cd47b0b73df9.zip
Support Svelte 5 (experimental) (#9098)
Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
Diffstat (limited to 'packages')
-rw-r--r--packages/integrations/svelte/README.md2
-rw-r--r--packages/integrations/svelte/client-v5.js43
-rw-r--r--packages/integrations/svelte/package.json10
-rw-r--r--packages/integrations/svelte/server-v5.js42
-rw-r--r--packages/integrations/svelte/src/index.ts19
5 files changed, 107 insertions, 9 deletions
diff --git a/packages/integrations/svelte/README.md b/packages/integrations/svelte/README.md
index 5df61fab0..25513790b 100644
--- a/packages/integrations/svelte/README.md
+++ b/packages/integrations/svelte/README.md
@@ -1,6 +1,6 @@
# @astrojs/svelte 🧡
-This **[Astro integration][astro-integration]** enables server-side rendering and client-side hydration for your [Svelte](https://svelte.dev/) components.
+This **[Astro integration][astro-integration]** enables server-side rendering and client-side hydration for your [Svelte](https://svelte.dev/) components. It supports Svelte 3, 4, and 5 (experimental).
## Installation
diff --git a/packages/integrations/svelte/client-v5.js b/packages/integrations/svelte/client-v5.js
new file mode 100644
index 000000000..b3d2e1964
--- /dev/null
+++ b/packages/integrations/svelte/client-v5.js
@@ -0,0 +1,43 @@
+import { mount } from 'svelte';
+
+export default (element) => {
+ return async (Component, props, slotted) => {
+ if (!element.hasAttribute('ssr')) return;
+
+ let children = undefined;
+ let $$slots = undefined;
+ for (const [key, value] of Object.entries(slotted)) {
+ if (key === 'default') {
+ children = createSlotDefinition(key, value);
+ } else {
+ $$slots ??= {};
+ $$slots[key] = createSlotDefinition(key, value);
+ }
+ }
+
+ const [, destroy] = mount(Component, {
+ target: element,
+ props: {
+ ...props,
+ children,
+ $$slots,
+ },
+ });
+
+ element.addEventListener('astro:unmount', () => destroy(), { once: true });
+ };
+};
+
+function createSlotDefinition(key, children) {
+ /**
+ * @param {Comment} $$anchor A comment node for slots in Svelte 5
+ */
+ return ($$anchor, _$$slotProps) => {
+ const parent = $$anchor.parentNode;
+ const el = document.createElement('div');
+ el.innerHTML = `<astro-slot${
+ key === 'default' ? '' : ` name="${key}"`
+ }>${children}</astro-slot>`;
+ parent.insertBefore(el.children[0], $$anchor);
+ };
+}
diff --git a/packages/integrations/svelte/package.json b/packages/integrations/svelte/package.json
index 8cf1c9d87..65c8522f9 100644
--- a/packages/integrations/svelte/package.json
+++ b/packages/integrations/svelte/package.json
@@ -24,13 +24,17 @@
"./editor": "./dist/editor.cjs",
"./*": "./*",
"./client.js": "./client.js",
+ "./client-v5.js": "./client-v5.js",
"./server.js": "./server.js",
+ "./server-v5.js": "./server-v5.js",
"./package.json": "./package.json"
},
"files": [
"dist",
"client.js",
- "server.js"
+ "client-v5.js",
+ "server.js",
+ "server-v5.js"
],
"scripts": {
"build": "astro-scripts build \"src/index.ts\" && astro-scripts build \"src/editor.cts\" --force-cjs --no-clean-dist && tsc",
@@ -38,7 +42,7 @@
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
- "@sveltejs/vite-plugin-svelte": "^2.4.5",
+ "@sveltejs/vite-plugin-svelte": "^2.5.2",
"svelte2tsx": "^0.6.20"
},
"devDependencies": {
@@ -49,7 +53,7 @@
},
"peerDependencies": {
"astro": "^3.0.0",
- "svelte": "^3.55.0 || ^4.0.0"
+ "svelte": "^3.55.0 || ^4.0.0 || ^5.0.0-next.1"
},
"engines": {
"node": ">=18.14.1"
diff --git a/packages/integrations/svelte/server-v5.js b/packages/integrations/svelte/server-v5.js
new file mode 100644
index 000000000..105b843fb
--- /dev/null
+++ b/packages/integrations/svelte/server-v5.js
@@ -0,0 +1,42 @@
+import { render } from 'svelte/server';
+
+function check(Component) {
+ // Svelte 5 generated components always accept these two props
+ const str = Component.toString();
+ return str.includes('$$payload') && str.includes('$$props');
+}
+
+function needsHydration(metadata) {
+ // Adjust how this is hydrated only when the version of Astro supports `astroStaticSlot`
+ return metadata.astroStaticSlot ? !!metadata.hydrate : true;
+}
+
+async function renderToStaticMarkup(Component, props, slotted, metadata) {
+ const tagName = needsHydration(metadata) ? 'astro-slot' : 'astro-static-slot';
+
+ let children = undefined;
+ let $$slots = undefined;
+ for (const [key, value] of Object.entries(slotted)) {
+ if (key === 'default') {
+ children = () => `<${tagName}>${value}</${tagName}>`;
+ } else {
+ $$slots ??= {};
+ $$slots[key] = () => `<${tagName} name="${key}">${value}</${tagName}>`;
+ }
+ }
+
+ const { html } = render(Component, {
+ props: {
+ ...props,
+ children,
+ $$slots,
+ },
+ });
+ return { html };
+}
+
+export default {
+ check,
+ renderToStaticMarkup,
+ supportsAstroStaticSlot: true,
+};
diff --git a/packages/integrations/svelte/src/index.ts b/packages/integrations/svelte/src/index.ts
index a9d4f37c9..5348f4c93 100644
--- a/packages/integrations/svelte/src/index.ts
+++ b/packages/integrations/svelte/src/index.ts
@@ -1,14 +1,17 @@
import type { Options } from '@sveltejs/vite-plugin-svelte';
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+import { VERSION } from 'svelte/compiler';
import type { AstroIntegration, AstroRenderer } from 'astro';
import { fileURLToPath } from 'node:url';
import type { UserConfig } from 'vite';
+const isSvelte5 = Number.parseInt(VERSION.split('.').at(0)!) >= 5;
+
function getRenderer(): AstroRenderer {
return {
name: '@astrojs/svelte',
- clientEntrypoint: '@astrojs/svelte/client.js',
- serverEntrypoint: '@astrojs/svelte/server.js',
+ clientEntrypoint: isSvelte5 ? '@astrojs/svelte/client-v5.js' : '@astrojs/svelte/client.js',
+ serverEntrypoint: isSvelte5 ? '@astrojs/svelte/server-v5.js' : '@astrojs/svelte/server.js',
};
}
@@ -37,9 +40,15 @@ async function getViteConfiguration({
}: ViteConfigurationArgs): Promise<UserConfig> {
const defaultOptions: Partial<Options> = {
emitCss: true,
- compilerOptions: { dev: isDev, hydratable: true },
+ compilerOptions: { dev: isDev },
};
+ // `hydratable` does not need to be set in Svelte 5 as it's always hydratable by default
+ if (!isSvelte5) {
+ // @ts-ignore ignore Partial type above
+ defaultOptions.compilerOptions.hydratable = true;
+ }
+
// Disable hot mode during the build
if (!isDev) {
defaultOptions.hot = false;
@@ -69,8 +78,8 @@ async function getViteConfiguration({
return {
optimizeDeps: {
- include: ['@astrojs/svelte/client.js'],
- exclude: ['@astrojs/svelte/server.js'],
+ include: [isSvelte5 ? '@astrojs/svelte/client-v5.js' : '@astrojs/svelte/client.js'],
+ exclude: [isSvelte5 ? '@astrojs/svelte/server-v5.js' : '@astrojs/svelte/server.js'],
},
plugins: [svelte(resolvedOptions)],
};