diff --git a/sites/svelte-5-preview/src/lib/Bundler.js b/sites/svelte-5-preview/src/lib/Bundler.js index e20946de7f02..b078be7d6e6c 100644 --- a/sites/svelte-5-preview/src/lib/Bundler.js +++ b/sites/svelte-5-preview/src/lib/Bundler.js @@ -50,16 +50,18 @@ export default class Bundler { /** * * @param {import('./types').File[]} files + * @param { string} mode * @returns */ - bundle(files) { + bundle(files, mode) { return new Promise((fulfil) => { this.handlers.set(uid, fulfil); this.worker.postMessage({ uid, type: 'bundle', - files + files, + mode }); uid += 1; diff --git a/sites/svelte-5-preview/src/lib/Output/Viewer.svelte b/sites/svelte-5-preview/src/lib/Output/Viewer.svelte index 759e72cfd834..d816d72b5d47 100644 --- a/sites/svelte-5-preview/src/lib/Output/Viewer.svelte +++ b/sites/svelte-5-preview/src/lib/Output/Viewer.svelte @@ -21,7 +21,7 @@ /** @type {'light' | 'dark'} */ export let theme; - const { bundle } = get_repl_context(); + const { bundle, compile_options } = get_repl_context(); /** @type {import('./console/console').Log[]} */ let logs = []; @@ -100,6 +100,7 @@ * @param {import('$lib/types').Bundle | null} $bundle */ async function apply_bundle($bundle) { + if (!$bundle) return; try { @@ -129,31 +130,49 @@ window._svelteTransitionManager = null; } - const __repl_exports = ${$bundle.client?.code}; + const __repl_server_exports = ${$bundle.server?.code}; + const __repl_client_exports = ${$bundle.client?.code}; { - const { mount, unmount, App, untrack } = __repl_exports; - - const console_methods = ['log', 'error', 'trace', 'assert', 'warn', 'table', 'group']; + const { mount, unmount, App: ClientApp, untrack, hydrate } = __repl_client_exports; + const {render, App: ServerApp} = __repl_server_exports; - // The REPL hooks up to the console to provide a virtual console. However, the implementation - // needs to stringify the console to pass over a MessageChannel, which means that the object - // can get deeply read and tracked by accident when using the console. We can avoid this by - // ensuring we untrack the main console methods. + const serverOperation = () => { + const {html, head} = render(ServerApp, {}); + document.head.innerHTML = head; + document.body.innerHTML = html; + } + const clientOperation = (hydrateApp) => { + const console_methods = ['log', 'error', 'trace', 'assert', 'warn', 'table', 'group']; + // The REPL hooks up to the console to provide a virtual console. However, the implementation + // needs to stringify the console to pass over a MessageChannel, which means that the object + // can get deeply read and tracked by accident when using the console. We can avoid this by + // ensuring we untrack the main console methods. - const original = {}; + const original = {}; - for (const method of console_methods) { - original[method] = console[method]; - console[method] = function (...v) { - return untrack(() => original[method].apply(this, v)); - } - } - const component = mount(App, { target: document.body }); - window.__unmount_previous = () => { for (const method of console_methods) { - console[method] = original[method]; + original[method] = console[method]; + console[method] = function (...v) { + return untrack(() => original[method].apply(this, v)); + } + } + const component = ${$compile_options.generate === 'hydrate' ? 'hydrate' : 'mount'}(ClientApp, { target: document.body, hydrate: hydrateApp }); + window.__unmount_previous = () => { + for (const method of console_methods) { + console[method] = original[method]; + } + unmount(component); } - unmount(component); + } + if (${$compile_options.generate} === 'server') { + serverOperation() + } else if (${$compile_options.generate} === 'client') { + clientOperation(false); + } else { + serverOperation(); + setTimeout(() => { + clientOperation(true) + }, 3000) } } //# sourceURL=playground:output diff --git a/sites/svelte-5-preview/src/lib/Repl.svelte b/sites/svelte-5-preview/src/lib/Repl.svelte index 80a2b7ff6110..9c17a8dbe5e7 100644 --- a/sites/svelte-5-preview/src/lib/Repl.svelte +++ b/sites/svelte-5-preview/src/lib/Repl.svelte @@ -76,7 +76,7 @@ /** @type {import('svelte/compiler').CompileOptions} */ const DEFAULT_COMPILE_OPTIONS = { - generate: 'client', + generate: 'hydrate', dev: false }; @@ -152,7 +152,7 @@ $bundling = new Promise((resolve) => { resolver = resolve; }); - const result = await $bundler?.bundle($files); + const result = await $bundler?.bundle($files, DEFAULT_COMPILE_OPTIONS.generate); if (result && token === current_token) $bundle = result; resolver(); } diff --git a/sites/svelte-5-preview/src/lib/workers/bundler/index.js b/sites/svelte-5-preview/src/lib/workers/bundler/index.js index 5a289fff7dcf..addf100d708c 100644 --- a/sites/svelte-5-preview/src/lib/workers/bundler/index.js +++ b/sites/svelte-5-preview/src/lib/workers/bundler/index.js @@ -59,7 +59,7 @@ self.addEventListener( case 'bundle': { await ready; - const { uid, files } = event.data; + const { uid, files, mode } = event.data; if (files.length === 0) return; @@ -68,7 +68,7 @@ self.addEventListener( setTimeout(async () => { if (current_id !== uid) return; - const result = await bundle({ uid, files }); + const result = await bundle({ uid, files, mode }); if (JSON.stringify(result.error) === JSON.stringify(ABORT)) return; if (result && uid === current_id) postMessage(result); @@ -392,8 +392,9 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { } else if (id.endsWith('.svelte')) { result = svelte.compile(code, { filename: name + '.svelte', - generate: 'client', - dev: true + generate: mode === 'client' ? 'dom' : 'ssr', + dev: true, + hydratable: mode === 'server' ? true : false }); if (result.css) { @@ -408,8 +409,9 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { } else if (id.endsWith('.svelte.js')) { result = svelte.compileModule(code, { filename: name + '.js', - generate: 'client', - dev: true + generate: mode === 'client' ? 'dom' : 'ssr', + dev: true, + hydratable: mode === 'server' ? true : false }); if (!result) { return null; @@ -478,7 +480,7 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { * @param {{ uid: number; files: import('$lib/types.js').File[] }} param0 * @returns */ -async function bundle({ uid, files }) { +async function bundle({ uid, files, mode }) { if (!DEV) { console.clear(); console.log(`running Svelte compiler version %c${svelte.VERSION}`, 'font-weight: bold'); @@ -490,7 +492,8 @@ async function bundle({ uid, files }) { lookup.set('./__entry.js', { name: '__entry', source: ` - export { mount, unmount, untrack } from 'svelte'; + export { mount, unmount, untrack, hydrate } from 'svelte'; + export {render} from 'svelte/server'; export {default as App} from './App.svelte'; `, type: 'js', @@ -513,15 +516,18 @@ async function bundle({ uid, files }) { cached.client = client.cache; - const client_result = ( - await client.bundle?.generate({ - format: 'iife', - exports: 'named' - // sourcemap: 'inline' - }) - )?.output[0]; - - const server = false // TODO how can we do SSR? + const client_result = + mode === 'hydrate' || mode === 'client' + ? ( + await client.bundle?.generate({ + format: 'iife', + exports: 'named' + // sourcemap: 'inline' + }) + )?.output[0] + : null; + + const server = mode === 'server' || mode === 'hydrate' // TODO how can we do SSR? ? await get_bundle(uid, 'server', cached.server, lookup) : null; @@ -536,7 +542,7 @@ async function bundle({ uid, files }) { ? ( await server.bundle?.generate({ format: 'iife', - name: 'SvelteComponent', + // name: 'SvelteComponent', exports: 'named' // sourcemap: 'inline' })