Skip to content

Commit 90f8b63

Browse files
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 <[email protected]> Co-authored-by: Simon Holthausen <[email protected]>
1 parent 268ac95 commit 90f8b63

File tree

5 files changed

+37
-19
lines changed

5 files changed

+37
-19
lines changed

.changeset/three-icons-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: run `onDestroy` callbacks during SSR

packages/svelte/src/internal/server/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ export function assign_payload(p1, p2) {
7979
p1.anchor = p2.anchor;
8080
}
8181

82+
/**
83+
* Array of `onDestroy` callbacks that should be called at the end of the server render function
84+
* @type {Function[]}
85+
*/
86+
export let on_destroy = [];
87+
8288
/**
8389
* @param {(...args: any[]) => void} component
8490
* @param {{ props: Record<string, any>; context?: Map<any, any> }} options
@@ -90,6 +96,8 @@ export function render(component, options) {
9096
const root_head_anchor = create_anchor(payload.head);
9197

9298
set_is_ssr(true);
99+
const prev_on_destroy = on_destroy;
100+
on_destroy = [];
93101
payload.out += root_anchor;
94102

95103
if (options.context) {
@@ -102,6 +110,8 @@ export function render(component, options) {
102110
$.pop();
103111
}
104112
payload.out += root_anchor;
113+
for (const cleanup of on_destroy) cleanup();
114+
on_destroy = prev_on_destroy;
105115
set_is_ssr(false);
106116

107117
return {

packages/svelte/src/main/main-server.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { on_destroy } from '../internal/server/index.js';
2+
13
export {
24
createRoot,
35
createEventDispatcher,
@@ -6,7 +8,6 @@ export {
68
getContext,
79
hasContext,
810
mount,
9-
onDestroy,
1011
setContext,
1112
tick,
1213
untrack
@@ -15,6 +16,11 @@ export {
1516
/** @returns {void} */
1617
export function onMount() {}
1718

19+
/** @param {Function} fn */
20+
export function onDestroy(fn) {
21+
on_destroy.push(fn);
22+
}
23+
1824
/** @returns {void} */
1925
export function beforeUpdate() {}
2026

packages/svelte/tests/runtime-legacy/samples/ondestroy-deep/_config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@ export default test({
1010
assert.deepEqual(destroyed, ['A', 'B', 'C']);
1111

1212
reset();
13+
},
14+
15+
test_ssr({ assert }) {
16+
assert.deepEqual(destroyed, ['A', 'B', 'C']);
1317
}
1418
});

vitest.config.js

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,27 @@ import { configDefaults, defineConfig } from 'vitest/config';
55
const pkg = JSON.parse(fs.readFileSync('packages/svelte/package.json', 'utf8'));
66

77
export default defineConfig({
8-
// We need both a plugin and an alias for some reason
98
resolve: {
109
alias: [
1110
{
1211
find: /^svelte\/?/,
13-
customResolver: (id) => {
14-
// For some reason this turns up as "undefined" instead of "svelte"
15-
const exported = pkg.exports[id.replace('undefined', '.')];
12+
customResolver: (id, importer) => {
13+
// For some reason this turns up as "undefined" instead of "svelte/"
14+
const exported = pkg.exports[id === 'undefined' ? '.' : id.replace('undefined', './')];
1615
if (!exported) return;
1716

18-
return path.resolve('packages/svelte', exported.browser ?? exported.default);
17+
// When running the server version of the Svelte files,
18+
// we also want to use the server export of the Svelte package
19+
return path.resolve(
20+
'packages/svelte',
21+
importer?.includes('_output/server')
22+
? exported.default
23+
: exported.browser ?? exported.default
24+
);
1925
}
2026
}
2127
]
2228
},
23-
plugins: [
24-
{
25-
name: 'resolve-svelte',
26-
resolveId(id) {
27-
if (/^svelte\/?/.test(id)) {
28-
const exported = pkg.exports[id.replace('svelte', '.')];
29-
if (!exported) return;
30-
31-
return path.resolve('packages/svelte', exported.browser ?? exported.default);
32-
}
33-
}
34-
}
35-
],
3629
test: {
3730
dir: '.',
3831
reporters: ['dot'],

0 commit comments

Comments
 (0)