Skip to content

Commit 90dd32b

Browse files
thunkify
1 parent 2ec34a2 commit 90dd32b

File tree

4 files changed

+52
-24
lines changed

4 files changed

+52
-24
lines changed

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

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as e from './errors.js';
66
import * as devalue from 'devalue';
77
import { get_stack } from './dev.js';
88
import { DEV } from 'esm-env';
9+
import { deferred } from '../shared/utils.js';
910

1011
/**
1112
* @template T
@@ -23,8 +24,10 @@ export function hydratable(key, fn) {
2324
let entry = hydratable.lookup.get(key);
2425

2526
if (entry !== undefined) {
26-
if (DEV) {
27-
compare(key, entry, encode(key, fn(), []));
27+
if (DEV && entry.dev) {
28+
const comparison = compare(key, entry, encode(key, fn(), []));
29+
comparison.catch(() => {});
30+
hydratable.comparisons.push(comparison);
2831
}
2932

3033
return /** @type {T} */ (entry.value);
@@ -49,12 +52,25 @@ function encode(key, value, values, unresolved) {
4952
const entry = { value, index: -1 };
5053

5154
if (DEV) {
52-
entry.stack = get_stack(`hydratable"`)?.stack;
55+
entry.dev = {
56+
serialized: undefined,
57+
serialize_work: [],
58+
stack: get_stack('hydratable')?.stack
59+
};
5360
}
5461

62+
let needs_thunk = false;
5563
let serialized = devalue.uneval(entry.value, (value, uneval) => {
5664
if (value instanceof Promise) {
57-
const serialize_promise = value.then((v) => `r(${uneval(v)})`);
65+
needs_thunk = true;
66+
/** @param {string} val */
67+
const scoped_uneval = (val) => {
68+
const raw = `r(${uneval(val)})`;
69+
const result = needs_thunk ? `()=>(${raw})` : raw;
70+
needs_thunk = false;
71+
return result;
72+
};
73+
const serialize_promise = value.then(scoped_uneval);
5874
unresolved?.set(serialize_promise, key);
5975
serialize_promise.finally(() => unresolved?.delete(serialize_promise));
6076

@@ -66,11 +82,12 @@ function encode(key, value, values, unresolved) {
6682
// of a given hydratable are identical with a simple string comparison
6783
const result = DEV ? `d("${index}")` : `d(${index})`;
6884

69-
if (DEV) {
70-
(entry.promises ??= []).push(
85+
if (DEV && entry.dev) {
86+
const { dev } = entry;
87+
dev.serialize_work.push(
7188
serialize_promise.then((s) => {
7289
serialized = serialized.replace(result, s);
73-
entry.serialized = serialized;
90+
dev.serialized = serialized;
7491
})
7592
);
7693
}
@@ -79,7 +96,8 @@ function encode(key, value, values, unresolved) {
7996
}
8097
});
8198

82-
entry.index = values.push(serialized) - 1;
99+
entry.index = values.push(needs_thunk ? `()=>(${serialized})` : serialized) - 1;
100+
needs_thunk = false;
83101

84102
return entry;
85103
}
@@ -90,21 +108,22 @@ function encode(key, value, values, unresolved) {
90108
* @param {HydratableLookupEntry} b
91109
*/
92110
async function compare(key, a, b) {
93-
for (const p of a.promises ?? []) {
111+
// note: these need to be loops (as opposed to Promise.all) because
112+
// additional promises can get pushed to them while we're awaiting
113+
// an earlier one
114+
for (const p of a.dev?.serialize_work ?? []) {
94115
await p;
95116
}
96117

97-
for (const p of b.promises ?? []) {
118+
for (const p of b.dev?.serialize_work ?? []) {
98119
await p;
99120
}
100121

101-
if (a.serialized !== b.serialized) {
102-
// TODO right now this causes an unhandled rejection — it
103-
// needs to happen somewhere else
122+
if (a.dev?.serialized !== b.dev?.serialized) {
104123
e.hydratable_clobbering(
105124
key,
106-
a.stack ?? '<missing stack trace>',
107-
b.stack ?? '<missing stack trace>'
125+
a.dev?.stack ?? '<missing stack trace>',
126+
b.dev?.stack ?? '<missing stack trace>'
108127
);
109128
}
110129
}

packages/svelte/src/internal/server/render-context.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export async function with_render_context(fn) {
5858
hydratable: {
5959
lookup: new Map(),
6060
values: [],
61+
comparisons: [],
6162
unresolved_promises: new Map()
6263
}
6364
};

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,16 @@ export class Renderer {
583583
// serialize it, so we're blocking the response on useless content.
584584
w.unresolved_hydratable(
585585
key,
586-
DEV ? ctx.lookup.get(key)?.stack ?? 'unavailable' : 'unavailable in production builds',
586+
ctx.lookup.get(key)?.dev?.stack ?? '<missing stack trace>',
587587
await promise
588588
);
589589
}
590590

591+
for (const comparison of ctx.comparisons) {
592+
// these reject if there's a mismatch
593+
await comparison;
594+
}
595+
591596
return await Renderer.#hydratable_block(ctx, []);
592597
}
593598

@@ -666,8 +671,11 @@ export class Renderer {
666671
<script>
667672
{
668673
const r = (v) => Promise.resolve(v);
669-
const v = [${values.map((v) => `() => (${v})`).join(',')}];
670-
function d(i) { return v[i]() };
674+
const v = [${values.join(',')}];
675+
function d(i) {
676+
const value = v[i];
677+
return typeof value === 'function' ? value() : value;
678+
};
671679
const sv = window.__svelte ??= {};${Renderer.#used_hydratables(ctx.lookup)}${Renderer.#unused_hydratables(unused_keys)}
672680
}
673681
</script>`;

packages/svelte/src/internal/server/types.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ export interface SSRContext {
1818
export interface HydratableLookupEntry {
1919
value: unknown;
2020
index: number;
21-
/** dev-only */
22-
promises?: Array<Promise<void>>;
23-
/** dev-only */
24-
serialized?: string;
25-
/** dev-only */
26-
stack?: string;
21+
dev?: {
22+
serialize_work: Array<Promise<void>>;
23+
serialized: string | undefined;
24+
stack: string | undefined;
25+
};
2726
}
2827

2928
export interface HydratableContext {
3029
lookup: Map<string, HydratableLookupEntry>;
3130
values: MaybePromise<string>[];
31+
comparisons: Promise<void>[];
3232
unresolved_promises: Map<Promise<string>, string>;
3333
}
3434

0 commit comments

Comments
 (0)