fix: run `onDestroy` cleanup during SSR (#10297)

fixes #10296
Also make sure to use the server export conditions when resolving Svelte imports from inside the server compiler output

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/10416/head
Rich Harris 5 months ago committed by GitHub
parent 268ac95fde
commit 90f8b63bee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: run `onDestroy` callbacks during SSR

@ -79,6 +79,12 @@ export function assign_payload(p1, p2) {
p1.anchor = p2.anchor;
}
/**
* Array of `onDestroy` callbacks that should be called at the end of the server render function
* @type {Function[]}
*/
export let on_destroy = [];
/**
* @param {(...args: any[]) => void} component
* @param {{ props: Record<string, any>; context?: Map<any, any> }} options
@ -90,6 +96,8 @@ export function render(component, options) {
const root_head_anchor = create_anchor(payload.head);
set_is_ssr(true);
const prev_on_destroy = on_destroy;
on_destroy = [];
payload.out += root_anchor;
if (options.context) {
@ -102,6 +110,8 @@ export function render(component, options) {
$.pop();
}
payload.out += root_anchor;
for (const cleanup of on_destroy) cleanup();
on_destroy = prev_on_destroy;
set_is_ssr(false);
return {

@ -1,3 +1,5 @@
import { on_destroy } from '../internal/server/index.js';
export {
createRoot,
createEventDispatcher,
@ -6,7 +8,6 @@ export {
getContext,
hasContext,
mount,
onDestroy,
setContext,
tick,
untrack
@ -15,6 +16,11 @@ export {
/** @returns {void} */
export function onMount() {}
/** @param {Function} fn */
export function onDestroy(fn) {
on_destroy.push(fn);
}
/** @returns {void} */
export function beforeUpdate() {}

@ -10,5 +10,9 @@ export default test({
assert.deepEqual(destroyed, ['A', 'B', 'C']);
reset();
},
test_ssr({ assert }) {
assert.deepEqual(destroyed, ['A', 'B', 'C']);
}
});

@ -5,34 +5,27 @@ import { configDefaults, defineConfig } from 'vitest/config';
const pkg = JSON.parse(fs.readFileSync('packages/svelte/package.json', 'utf8'));
export default defineConfig({
// We need both a plugin and an alias for some reason
resolve: {
alias: [
{
find: /^svelte\/?/,
customResolver: (id) => {
// For some reason this turns up as "undefined" instead of "svelte"
const exported = pkg.exports[id.replace('undefined', '.')];
customResolver: (id, importer) => {
// For some reason this turns up as "undefined" instead of "svelte/"
const exported = pkg.exports[id === 'undefined' ? '.' : id.replace('undefined', './')];
if (!exported) return;
return path.resolve('packages/svelte', exported.browser ?? exported.default);
// When running the server version of the Svelte files,
// we also want to use the server export of the Svelte package
return path.resolve(
'packages/svelte',
importer?.includes('_output/server')
? exported.default
: exported.browser ?? exported.default
);
}
}
]
},
plugins: [
{
name: 'resolve-svelte',
resolveId(id) {
if (/^svelte\/?/.test(id)) {
const exported = pkg.exports[id.replace('svelte', '.')];
if (!exported) return;
return path.resolve('packages/svelte', exported.browser ?? exported.default);
}
}
}
],
test: {
dir: '.',
reporters: ['dot'],

Loading…
Cancel
Save