-
-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathstream-node.js
84 lines (76 loc) · 1.78 KB
/
stream-node.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { PassThrough } from 'node:stream';
import { renderToChunks } from './lib/chunked.js';
/**
* @typedef {object} RenderToPipeableStreamOptions
* @property {() => void} [onShellReady]
* @property {() => void} [onAllReady]
* @property {(error) => void} [onError]
*/
/**
* @typedef {object} PipeableStream
* @property {() => void} abort
* @property {(writable: import('stream').Writable) => void} pipe
*/
/**
* @param {import('preact').VNode} vnode
* @param {RenderToPipeableStreamOptions} options
* @param {any} [context]
* @returns {PipeableStream}
*/
export function renderToPipeableStream(vnode, options, context) {
const encoder = new TextEncoder('utf-8');
const controller = new AbortController();
const stream = new PassThrough();
renderToChunks(vnode, {
context,
abortSignal: controller.signal,
onError: (error) => {
if (options.onError) {
options.onError(error);
}
controller.abort(error);
},
onWrite(s) {
stream.write(encoder.encode(s));
}
})
.then(() => {
options.onAllReady && options.onAllReady();
stream.end();
})
.catch((error) => {
stream.destroy();
if (options.onError) {
options.onError(error);
} else {
throw error;
}
});
Promise.resolve().then(() => {
options.onShellReady && options.onShellReady();
});
return {
/**
* @param {unknown} [reason]
*/
abort(
reason = new Error(
'The render was aborted by the server without a reason.'
)
) {
// Remix/React-Router will always call abort after a timeout, even on success
if (stream.closed) return;
controller.abort();
stream.destroy();
if (options.onError) {
options.onError(reason);
}
},
/**
* @param {import("stream").Writable} writable
*/
pipe(writable) {
stream.pipe(writable, { end: true });
}
};
}