@bleedingdev/modern-js-render 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/rsc.mjs +100 -68
- package/dist/esm/rscWorker.mjs +104 -72
- package/dist/esm/server~0.mjs +81 -48
- package/dist/esm/ssr.mjs +7 -6
- package/dist/types/rsc.worker.d.ts +1 -1
- package/dist/types/server/ssr/ssr.d.ts +3 -1
- package/package.json +4 -5
- package/dist/types/server/rsc/rsc.worker.d.ts +0 -12
package/dist/esm/rsc.mjs
CHANGED
|
@@ -1,63 +1,96 @@
|
|
|
1
|
-
import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
|
|
2
1
|
import { decodeReply as server_node_decodeReply, loadServerAction as server_node_loadServerAction, registerClientReference, registerServerReference, renderToReadableStream } from "react-server-dom-rspack/server.node";
|
|
3
2
|
const server_encoder = new TextEncoder();
|
|
4
3
|
const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
|
|
5
|
-
function
|
|
6
|
-
if (injectClosingTags) return injectRSCPayload(rscStream);
|
|
4
|
+
function injectRSCPayload(rscStream, { injectClosingTags = true }) {
|
|
7
5
|
const decoder = new TextDecoder();
|
|
8
|
-
let
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
let pendingHtml = '';
|
|
7
|
+
let closingBody = false;
|
|
8
|
+
let closingHtml = false;
|
|
9
|
+
let closingProbe = '';
|
|
10
|
+
let flightReader;
|
|
11
|
+
let flightPromise;
|
|
12
|
+
function startFlight(controller) {
|
|
13
|
+
if (!flightPromise) {
|
|
14
|
+
flightReader = rscStream.getReader();
|
|
15
|
+
flightPromise = writeRSCStream(flightReader, controller).finally(()=>{
|
|
16
|
+
flightReader = void 0;
|
|
17
|
+
});
|
|
18
|
+
flightPromise.catch((error)=>controller.error(error));
|
|
19
|
+
}
|
|
20
|
+
return flightPromise;
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
async function writeHtml(value, controller) {
|
|
23
|
+
let html = pendingHtml + value;
|
|
24
|
+
pendingHtml = '';
|
|
25
|
+
if (!closingBody) {
|
|
26
|
+
const bodyIndex = html.search(/<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?$/i);
|
|
27
|
+
if (-1 !== bodyIndex) {
|
|
28
|
+
controller.enqueue(server_encoder.encode(html.slice(0, bodyIndex)));
|
|
29
|
+
await startFlight(controller);
|
|
30
|
+
closingBody = true;
|
|
31
|
+
html = html.slice(bodyIndex);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (closingBody) {
|
|
35
|
+
if (!injectClosingTags) {
|
|
36
|
+
pendingHtml = html;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
closingHtml ||= /<\/html>/i.test(closingProbe + html);
|
|
40
|
+
closingProbe = (closingProbe + html).slice(-6);
|
|
41
|
+
} else {
|
|
42
|
+
const tagIndex = html.lastIndexOf('<');
|
|
43
|
+
if (tagIndex > html.lastIndexOf('>')) {
|
|
44
|
+
pendingHtml = html.slice(tagIndex);
|
|
45
|
+
html = html.slice(0, tagIndex);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (html) {
|
|
49
|
+
controller.enqueue(server_encoder.encode(html));
|
|
50
|
+
startFlight(controller);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const transformer = {
|
|
24
54
|
transform (chunk, controller) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
flushBufferedChunks(controller);
|
|
29
|
-
if (!startedRSC) {
|
|
30
|
-
startedRSC = true;
|
|
31
|
-
writeRSCStream(rscStream, controller).catch((err)=>controller.error(err)).then(()=>resolveFlightDataPromise());
|
|
32
|
-
}
|
|
33
|
-
}, 0);
|
|
55
|
+
return writeHtml(decoder.decode(chunk, {
|
|
56
|
+
stream: true
|
|
57
|
+
}), controller);
|
|
34
58
|
},
|
|
35
59
|
async flush (controller) {
|
|
36
|
-
await
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
await writeHtml(decoder.decode(), controller);
|
|
61
|
+
await startFlight(controller);
|
|
62
|
+
const tail = injectClosingTags ? pendingHtml : pendingHtml.replace(closingTagsPattern, '');
|
|
63
|
+
if (tail) controller.enqueue(server_encoder.encode(tail));
|
|
64
|
+
if (injectClosingTags && !closingHtml) controller.enqueue(server_encoder.encode(closingBody ? '</html>' : '</body></html>'));
|
|
65
|
+
},
|
|
66
|
+
cancel (reason) {
|
|
67
|
+
return flightReader ? flightReader.cancel(reason) : rscStream.cancel(reason);
|
|
44
68
|
}
|
|
45
|
-
}
|
|
69
|
+
};
|
|
70
|
+
return new TransformStream(transformer);
|
|
46
71
|
}
|
|
47
|
-
async function writeRSCStream(
|
|
72
|
+
async function writeRSCStream(reader, controller) {
|
|
48
73
|
const decoder = new TextDecoder('utf-8', {
|
|
49
|
-
fatal: true
|
|
74
|
+
fatal: true,
|
|
75
|
+
ignoreBOM: true
|
|
50
76
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
try {
|
|
78
|
+
while(true){
|
|
79
|
+
const { done, value } = await reader.read();
|
|
80
|
+
if (done) return;
|
|
81
|
+
let chunk;
|
|
82
|
+
try {
|
|
83
|
+
chunk = JSON.stringify(decoder.decode(value));
|
|
84
|
+
} catch {
|
|
85
|
+
let binary = '';
|
|
86
|
+
for (const byte of value)binary += String.fromCharCode(byte);
|
|
87
|
+
chunk = `Uint8Array.from(atob(${JSON.stringify(btoa(binary))}), m => m.codePointAt(0))`;
|
|
88
|
+
}
|
|
89
|
+
writeChunk(chunk, controller);
|
|
90
|
+
}
|
|
91
|
+
} finally{
|
|
92
|
+
reader.releaseLock();
|
|
58
93
|
}
|
|
59
|
-
const remaining = decoder.decode();
|
|
60
|
-
if (remaining.length) writeChunk(JSON.stringify(remaining), controller);
|
|
61
94
|
}
|
|
62
95
|
function writeChunk(chunk, controller) {
|
|
63
96
|
controller.enqueue(server_encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}</script>`));
|
|
@@ -76,35 +109,34 @@ function injectCSS(cssFiles, { injectClosingTags = true } = {}) {
|
|
|
76
109
|
});
|
|
77
110
|
const decoder = new TextDecoder();
|
|
78
111
|
const headTrailer = '</head>';
|
|
79
|
-
|
|
80
|
-
let timeout = null;
|
|
112
|
+
let pendingHtml = '';
|
|
81
113
|
let cssInjected = false;
|
|
82
114
|
const cssLinks = cssFiles.map((css)=>`<link href="${css}" rel="stylesheet" />`).join('');
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
function writeHtml(value, controller, final = false) {
|
|
116
|
+
let html = pendingHtml + value;
|
|
117
|
+
pendingHtml = '';
|
|
118
|
+
if (!cssInjected) {
|
|
119
|
+
const headIndex = html.toLowerCase().indexOf(headTrailer);
|
|
120
|
+
if (-1 !== headIndex) {
|
|
121
|
+
html = html.slice(0, headIndex) + cssLinks + html.slice(headIndex);
|
|
88
122
|
cssInjected = true;
|
|
123
|
+
} else if (!final) {
|
|
124
|
+
let retain = Math.min(headTrailer.length - 1, html.length);
|
|
125
|
+
while(retain && !html.toLowerCase().endsWith(headTrailer.slice(0, retain)))retain--;
|
|
126
|
+
pendingHtml = html.slice(html.length - retain);
|
|
127
|
+
html = html.slice(0, html.length - retain);
|
|
89
128
|
}
|
|
90
|
-
controller.enqueue(server_encoder.encode(buf));
|
|
91
129
|
}
|
|
92
|
-
|
|
93
|
-
timeout = null;
|
|
130
|
+
if (html) controller.enqueue(server_encoder.encode(html));
|
|
94
131
|
}
|
|
95
132
|
return new TransformStream({
|
|
96
133
|
transform (chunk, controller) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
flushBufferedChunks(controller);
|
|
101
|
-
}, 0);
|
|
134
|
+
writeHtml(decoder.decode(chunk, {
|
|
135
|
+
stream: true
|
|
136
|
+
}), controller);
|
|
102
137
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
clearTimeout(timeout);
|
|
106
|
-
flushBufferedChunks(controller);
|
|
107
|
-
}
|
|
138
|
+
flush (controller) {
|
|
139
|
+
writeHtml(decoder.decode(), controller, true);
|
|
108
140
|
if (!cssInjected) controller.enqueue(server_encoder.encode(cssLinks));
|
|
109
141
|
if (injectClosingTags) controller.enqueue(server_encoder.encode('</body></html>'));
|
|
110
142
|
}
|
|
@@ -125,7 +157,7 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
|
|
|
125
157
|
});
|
|
126
158
|
const stream = htmlStream.pipeThrough(injectCSS(cssFiles, {
|
|
127
159
|
injectClosingTags: false
|
|
128
|
-
})).pipeThrough(
|
|
160
|
+
})).pipeThrough(injectRSCPayload(rscPayloadStream, {
|
|
129
161
|
injectClosingTags: true
|
|
130
162
|
}));
|
|
131
163
|
return new Response(stream, {
|
package/dist/esm/rscWorker.mjs
CHANGED
|
@@ -1,63 +1,96 @@
|
|
|
1
1
|
import { decodeReply as server_edge_decodeReply, loadServerAction as server_edge_loadServerAction, registerClientReference, registerServerReference, renderToReadableStream } from "react-server-dom-rspack/server.edge";
|
|
2
|
-
import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
|
|
3
2
|
const server_encoder = new TextEncoder();
|
|
4
3
|
const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
|
|
5
|
-
function
|
|
6
|
-
if (injectClosingTags) return injectRSCPayload(rscStream);
|
|
4
|
+
function injectRSCPayload(rscStream, { injectClosingTags = true }) {
|
|
7
5
|
const decoder = new TextDecoder();
|
|
8
|
-
let
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
let pendingHtml = '';
|
|
7
|
+
let closingBody = false;
|
|
8
|
+
let closingHtml = false;
|
|
9
|
+
let closingProbe = '';
|
|
10
|
+
let flightReader;
|
|
11
|
+
let flightPromise;
|
|
12
|
+
function startFlight(controller) {
|
|
13
|
+
if (!flightPromise) {
|
|
14
|
+
flightReader = rscStream.getReader();
|
|
15
|
+
flightPromise = writeRSCStream(flightReader, controller).finally(()=>{
|
|
16
|
+
flightReader = void 0;
|
|
17
|
+
});
|
|
18
|
+
flightPromise.catch((error)=>controller.error(error));
|
|
19
|
+
}
|
|
20
|
+
return flightPromise;
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
async function writeHtml(value, controller) {
|
|
23
|
+
let html = pendingHtml + value;
|
|
24
|
+
pendingHtml = '';
|
|
25
|
+
if (!closingBody) {
|
|
26
|
+
const bodyIndex = html.search(/<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?$/i);
|
|
27
|
+
if (-1 !== bodyIndex) {
|
|
28
|
+
controller.enqueue(server_encoder.encode(html.slice(0, bodyIndex)));
|
|
29
|
+
await startFlight(controller);
|
|
30
|
+
closingBody = true;
|
|
31
|
+
html = html.slice(bodyIndex);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (closingBody) {
|
|
35
|
+
if (!injectClosingTags) {
|
|
36
|
+
pendingHtml = html;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
closingHtml ||= /<\/html>/i.test(closingProbe + html);
|
|
40
|
+
closingProbe = (closingProbe + html).slice(-6);
|
|
41
|
+
} else {
|
|
42
|
+
const tagIndex = html.lastIndexOf('<');
|
|
43
|
+
if (tagIndex > html.lastIndexOf('>')) {
|
|
44
|
+
pendingHtml = html.slice(tagIndex);
|
|
45
|
+
html = html.slice(0, tagIndex);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (html) {
|
|
49
|
+
controller.enqueue(server_encoder.encode(html));
|
|
50
|
+
startFlight(controller);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const transformer = {
|
|
24
54
|
transform (chunk, controller) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
flushBufferedChunks(controller);
|
|
29
|
-
if (!startedRSC) {
|
|
30
|
-
startedRSC = true;
|
|
31
|
-
writeRSCStream(rscStream, controller).catch((err)=>controller.error(err)).then(()=>resolveFlightDataPromise());
|
|
32
|
-
}
|
|
33
|
-
}, 0);
|
|
55
|
+
return writeHtml(decoder.decode(chunk, {
|
|
56
|
+
stream: true
|
|
57
|
+
}), controller);
|
|
34
58
|
},
|
|
35
59
|
async flush (controller) {
|
|
36
|
-
await
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
await writeHtml(decoder.decode(), controller);
|
|
61
|
+
await startFlight(controller);
|
|
62
|
+
const tail = injectClosingTags ? pendingHtml : pendingHtml.replace(closingTagsPattern, '');
|
|
63
|
+
if (tail) controller.enqueue(server_encoder.encode(tail));
|
|
64
|
+
if (injectClosingTags && !closingHtml) controller.enqueue(server_encoder.encode(closingBody ? '</html>' : '</body></html>'));
|
|
65
|
+
},
|
|
66
|
+
cancel (reason) {
|
|
67
|
+
return flightReader ? flightReader.cancel(reason) : rscStream.cancel(reason);
|
|
44
68
|
}
|
|
45
|
-
}
|
|
69
|
+
};
|
|
70
|
+
return new TransformStream(transformer);
|
|
46
71
|
}
|
|
47
|
-
async function writeRSCStream(
|
|
72
|
+
async function writeRSCStream(reader, controller) {
|
|
48
73
|
const decoder = new TextDecoder('utf-8', {
|
|
49
|
-
fatal: true
|
|
74
|
+
fatal: true,
|
|
75
|
+
ignoreBOM: true
|
|
50
76
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
try {
|
|
78
|
+
while(true){
|
|
79
|
+
const { done, value } = await reader.read();
|
|
80
|
+
if (done) return;
|
|
81
|
+
let chunk;
|
|
82
|
+
try {
|
|
83
|
+
chunk = JSON.stringify(decoder.decode(value));
|
|
84
|
+
} catch {
|
|
85
|
+
let binary = '';
|
|
86
|
+
for (const byte of value)binary += String.fromCharCode(byte);
|
|
87
|
+
chunk = `Uint8Array.from(atob(${JSON.stringify(btoa(binary))}), m => m.codePointAt(0))`;
|
|
88
|
+
}
|
|
89
|
+
writeChunk(chunk, controller);
|
|
90
|
+
}
|
|
91
|
+
} finally{
|
|
92
|
+
reader.releaseLock();
|
|
58
93
|
}
|
|
59
|
-
const remaining = decoder.decode();
|
|
60
|
-
if (remaining.length) writeChunk(JSON.stringify(remaining), controller);
|
|
61
94
|
}
|
|
62
95
|
function writeChunk(chunk, controller) {
|
|
63
96
|
controller.enqueue(server_encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}</script>`));
|
|
@@ -76,35 +109,34 @@ function injectCSS(cssFiles, { injectClosingTags = true } = {}) {
|
|
|
76
109
|
});
|
|
77
110
|
const decoder = new TextDecoder();
|
|
78
111
|
const headTrailer = '</head>';
|
|
79
|
-
|
|
80
|
-
let timeout = null;
|
|
112
|
+
let pendingHtml = '';
|
|
81
113
|
let cssInjected = false;
|
|
82
114
|
const cssLinks = cssFiles.map((css)=>`<link href="${css}" rel="stylesheet" />`).join('');
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
function writeHtml(value, controller, final = false) {
|
|
116
|
+
let html = pendingHtml + value;
|
|
117
|
+
pendingHtml = '';
|
|
118
|
+
if (!cssInjected) {
|
|
119
|
+
const headIndex = html.toLowerCase().indexOf(headTrailer);
|
|
120
|
+
if (-1 !== headIndex) {
|
|
121
|
+
html = html.slice(0, headIndex) + cssLinks + html.slice(headIndex);
|
|
88
122
|
cssInjected = true;
|
|
123
|
+
} else if (!final) {
|
|
124
|
+
let retain = Math.min(headTrailer.length - 1, html.length);
|
|
125
|
+
while(retain && !html.toLowerCase().endsWith(headTrailer.slice(0, retain)))retain--;
|
|
126
|
+
pendingHtml = html.slice(html.length - retain);
|
|
127
|
+
html = html.slice(0, html.length - retain);
|
|
89
128
|
}
|
|
90
|
-
controller.enqueue(server_encoder.encode(buf));
|
|
91
129
|
}
|
|
92
|
-
|
|
93
|
-
timeout = null;
|
|
130
|
+
if (html) controller.enqueue(server_encoder.encode(html));
|
|
94
131
|
}
|
|
95
132
|
return new TransformStream({
|
|
96
133
|
transform (chunk, controller) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
flushBufferedChunks(controller);
|
|
101
|
-
}, 0);
|
|
134
|
+
writeHtml(decoder.decode(chunk, {
|
|
135
|
+
stream: true
|
|
136
|
+
}), controller);
|
|
102
137
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
clearTimeout(timeout);
|
|
106
|
-
flushBufferedChunks(controller);
|
|
107
|
-
}
|
|
138
|
+
flush (controller) {
|
|
139
|
+
writeHtml(decoder.decode(), controller, true);
|
|
108
140
|
if (!cssInjected) controller.enqueue(server_encoder.encode(cssLinks));
|
|
109
141
|
if (injectClosingTags) controller.enqueue(server_encoder.encode('</body></html>'));
|
|
110
142
|
}
|
|
@@ -125,7 +157,7 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
|
|
|
125
157
|
});
|
|
126
158
|
const stream = htmlStream.pipeThrough(injectCSS(cssFiles, {
|
|
127
159
|
injectClosingTags: false
|
|
128
|
-
})).pipeThrough(
|
|
160
|
+
})).pipeThrough(injectRSCPayload(rscPayloadStream, {
|
|
129
161
|
injectClosingTags: true
|
|
130
162
|
}));
|
|
131
163
|
return new Response(stream, {
|
|
@@ -231,15 +263,15 @@ const createHandleAction = ({ decodeReply, loadServerAction, renderRsc })=>async
|
|
|
231
263
|
});
|
|
232
264
|
}
|
|
233
265
|
};
|
|
234
|
-
const
|
|
266
|
+
const rsc_renderRsc = (options)=>{
|
|
235
267
|
const readable = renderToReadableStream(options.element);
|
|
236
268
|
return readable;
|
|
237
269
|
};
|
|
238
|
-
const renderCSRWithRSC = createRenderCSRWithRSC(rsc_worker_renderRsc);
|
|
239
270
|
const handleAction = createHandleAction({
|
|
240
271
|
decodeReply: server_edge_decodeReply,
|
|
241
272
|
loadServerAction: server_edge_loadServerAction,
|
|
242
|
-
renderRsc:
|
|
273
|
+
renderRsc: rsc_renderRsc
|
|
243
274
|
});
|
|
275
|
+
const renderCSRWithRSC = createRenderCSRWithRSC(rsc_renderRsc);
|
|
244
276
|
export { createFromReadableStream } from "react-server-dom-rspack/client.edge";
|
|
245
|
-
export { handleAction, registerClientReference, registerServerReference, renderCSRWithRSC, renderToReadableStream,
|
|
277
|
+
export { handleAction, registerClientReference, registerServerReference, renderCSRWithRSC, renderToReadableStream, rsc_renderRsc as renderRsc };
|
package/dist/esm/server~0.mjs
CHANGED
|
@@ -1,62 +1,95 @@
|
|
|
1
|
-
import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
|
|
2
1
|
const encoder = new TextEncoder();
|
|
3
2
|
const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
|
|
4
|
-
function
|
|
5
|
-
if (injectClosingTags) return injectRSCPayload(rscStream);
|
|
3
|
+
function injectRSCPayload(rscStream, { injectClosingTags = true }) {
|
|
6
4
|
const decoder = new TextDecoder();
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
let pendingHtml = '';
|
|
6
|
+
let closingBody = false;
|
|
7
|
+
let closingHtml = false;
|
|
8
|
+
let closingProbe = '';
|
|
9
|
+
let flightReader;
|
|
10
|
+
let flightPromise;
|
|
11
|
+
function startFlight(controller) {
|
|
12
|
+
if (!flightPromise) {
|
|
13
|
+
flightReader = rscStream.getReader();
|
|
14
|
+
flightPromise = writeRSCStream(flightReader, controller).finally(()=>{
|
|
15
|
+
flightReader = void 0;
|
|
16
|
+
});
|
|
17
|
+
flightPromise.catch((error)=>controller.error(error));
|
|
18
|
+
}
|
|
19
|
+
return flightPromise;
|
|
21
20
|
}
|
|
22
|
-
|
|
21
|
+
async function writeHtml(value, controller) {
|
|
22
|
+
let html = pendingHtml + value;
|
|
23
|
+
pendingHtml = '';
|
|
24
|
+
if (!closingBody) {
|
|
25
|
+
const bodyIndex = html.search(/<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?$/i);
|
|
26
|
+
if (-1 !== bodyIndex) {
|
|
27
|
+
controller.enqueue(encoder.encode(html.slice(0, bodyIndex)));
|
|
28
|
+
await startFlight(controller);
|
|
29
|
+
closingBody = true;
|
|
30
|
+
html = html.slice(bodyIndex);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (closingBody) {
|
|
34
|
+
if (!injectClosingTags) {
|
|
35
|
+
pendingHtml = html;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
closingHtml ||= /<\/html>/i.test(closingProbe + html);
|
|
39
|
+
closingProbe = (closingProbe + html).slice(-6);
|
|
40
|
+
} else {
|
|
41
|
+
const tagIndex = html.lastIndexOf('<');
|
|
42
|
+
if (tagIndex > html.lastIndexOf('>')) {
|
|
43
|
+
pendingHtml = html.slice(tagIndex);
|
|
44
|
+
html = html.slice(0, tagIndex);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (html) {
|
|
48
|
+
controller.enqueue(encoder.encode(html));
|
|
49
|
+
startFlight(controller);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const transformer = {
|
|
23
53
|
transform (chunk, controller) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
flushBufferedChunks(controller);
|
|
28
|
-
if (!startedRSC) {
|
|
29
|
-
startedRSC = true;
|
|
30
|
-
writeRSCStream(rscStream, controller).catch((err)=>controller.error(err)).then(()=>resolveFlightDataPromise());
|
|
31
|
-
}
|
|
32
|
-
}, 0);
|
|
54
|
+
return writeHtml(decoder.decode(chunk, {
|
|
55
|
+
stream: true
|
|
56
|
+
}), controller);
|
|
33
57
|
},
|
|
34
58
|
async flush (controller) {
|
|
35
|
-
await
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
await writeHtml(decoder.decode(), controller);
|
|
60
|
+
await startFlight(controller);
|
|
61
|
+
const tail = injectClosingTags ? pendingHtml : pendingHtml.replace(closingTagsPattern, '');
|
|
62
|
+
if (tail) controller.enqueue(encoder.encode(tail));
|
|
63
|
+
if (injectClosingTags && !closingHtml) controller.enqueue(encoder.encode(closingBody ? '</html>' : '</body></html>'));
|
|
64
|
+
},
|
|
65
|
+
cancel (reason) {
|
|
66
|
+
return flightReader ? flightReader.cancel(reason) : rscStream.cancel(reason);
|
|
43
67
|
}
|
|
44
|
-
}
|
|
68
|
+
};
|
|
69
|
+
return new TransformStream(transformer);
|
|
45
70
|
}
|
|
46
|
-
async function writeRSCStream(
|
|
71
|
+
async function writeRSCStream(reader, controller) {
|
|
47
72
|
const decoder = new TextDecoder('utf-8', {
|
|
48
|
-
fatal: true
|
|
73
|
+
fatal: true,
|
|
74
|
+
ignoreBOM: true
|
|
49
75
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
try {
|
|
77
|
+
while(true){
|
|
78
|
+
const { done, value } = await reader.read();
|
|
79
|
+
if (done) return;
|
|
80
|
+
let chunk;
|
|
81
|
+
try {
|
|
82
|
+
chunk = JSON.stringify(decoder.decode(value));
|
|
83
|
+
} catch {
|
|
84
|
+
let binary = '';
|
|
85
|
+
for (const byte of value)binary += String.fromCharCode(byte);
|
|
86
|
+
chunk = `Uint8Array.from(atob(${JSON.stringify(btoa(binary))}), m => m.codePointAt(0))`;
|
|
87
|
+
}
|
|
88
|
+
writeChunk(chunk, controller);
|
|
89
|
+
}
|
|
90
|
+
} finally{
|
|
91
|
+
reader.releaseLock();
|
|
57
92
|
}
|
|
58
|
-
const remaining = decoder.decode();
|
|
59
|
-
if (remaining.length) writeChunk(JSON.stringify(remaining), controller);
|
|
60
93
|
}
|
|
61
94
|
function writeChunk(chunk, controller) {
|
|
62
95
|
controller.enqueue(encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}</script>`));
|
|
@@ -64,4 +97,4 @@ function writeChunk(chunk, controller) {
|
|
|
64
97
|
function escapeScript(script) {
|
|
65
98
|
return script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1');
|
|
66
99
|
}
|
|
67
|
-
export {
|
|
100
|
+
export { injectRSCPayload };
|
package/dist/esm/ssr.mjs
CHANGED
|
@@ -16,13 +16,13 @@ function wrapStream(stream, originalStream) {
|
|
|
16
16
|
return wrappedStream;
|
|
17
17
|
}
|
|
18
18
|
const renderSSRStream = async (children, options)=>{
|
|
19
|
-
const { rscManifest, rscRoot, routes } = options;
|
|
19
|
+
const { rscManifest, rscRoot, routes, wrapHtmlRoot, ...reactOptions } = options;
|
|
20
20
|
const clientManifest = rscManifest?.clientManifest;
|
|
21
21
|
const serverConsumerModuleMap = rscManifest?.serverConsumerModuleMap;
|
|
22
22
|
const entryCssFiles = rscManifest?.entryCssFiles;
|
|
23
23
|
const hasRoutes = Boolean(routes && routes.length > 0);
|
|
24
|
-
if (!clientManifest || !serverConsumerModuleMap) return renderToReadableStream(children, {
|
|
25
|
-
...
|
|
24
|
+
if (!clientManifest || !serverConsumerModuleMap) return renderToReadableStream(wrapHtmlRoot ? wrapHtmlRoot(children) : children, {
|
|
25
|
+
...reactOptions,
|
|
26
26
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
27
27
|
});
|
|
28
28
|
try {
|
|
@@ -39,12 +39,13 @@ const renderSSRStream = async (children, options)=>{
|
|
|
39
39
|
const elements = createFromReadableStream(rscElementStream);
|
|
40
40
|
let cssFiles = [];
|
|
41
41
|
if (!hasRoutes && entryCssFiles) cssFiles = Object.values(entryCssFiles).flat();
|
|
42
|
-
const
|
|
42
|
+
const htmlRoot = /*#__PURE__*/ react.createElement(ServerElementsProvider, {
|
|
43
43
|
elements: elements
|
|
44
44
|
}, /*#__PURE__*/ react.createElement(CSSLinks, {
|
|
45
45
|
cssFiles: cssFiles
|
|
46
|
-
}), children)
|
|
47
|
-
|
|
46
|
+
}), children);
|
|
47
|
+
const htmlStream = await renderToReadableStream(wrapHtmlRoot ? wrapHtmlRoot(htmlRoot) : htmlRoot, {
|
|
48
|
+
...reactOptions,
|
|
48
49
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
49
50
|
});
|
|
50
51
|
const stream = htmlStream.pipeThrough(injectRSCPayload(rscPayloadStream, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './server/rsc/
|
|
1
|
+
export * from './server/rsc/index.js';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ClientManifest } from '@modern-js/types/server';
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { type ReactNode } from 'react';
|
|
3
3
|
import { renderToReadableStream } from 'react-dom/server.edge';
|
|
4
4
|
type Options = {
|
|
5
5
|
request: Request;
|
|
6
6
|
routes?: unknown[];
|
|
7
|
+
/** Wrap only the HTML render tree, never the input serialized by Flight. */
|
|
8
|
+
wrapHtmlRoot?: (root: ReactNode) => ReactNode;
|
|
7
9
|
rscManifest?: {
|
|
8
10
|
clientManifest?: ClientManifest;
|
|
9
11
|
serverConsumerModuleMap?: unknown;
|
package/package.json
CHANGED
|
@@ -17,19 +17,18 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.9.0-ultramodern.
|
|
20
|
+
"version": "3.9.0-ultramodern.6",
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"hook.d.ts"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@modern-js/
|
|
27
|
-
"@modern-js/
|
|
28
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.4",
|
|
26
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.6",
|
|
27
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.6",
|
|
29
28
|
"@swc/helpers": "^0.5.23"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
32
|
-
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.
|
|
31
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.6",
|
|
33
32
|
"@rslib/core": "1.0.0",
|
|
34
33
|
"@scripts/build": "2.66.0",
|
|
35
34
|
"@scripts/rstest-config": "2.66.0",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export { renderToReadableStream } from 'react-server-dom-rspack/server.edge';
|
|
2
|
-
export { createFromReadableStream } from 'react-server-dom-rspack/client.edge';
|
|
3
|
-
export { registerClientReference, registerServerReference, } from 'react-server-dom-rspack/server.edge';
|
|
4
|
-
type RenderRscOptions = {
|
|
5
|
-
element: React.ReactElement;
|
|
6
|
-
};
|
|
7
|
-
export declare const renderRsc: (options: RenderRscOptions) => ReadableStream<Uint8Array<ArrayBufferLike>>;
|
|
8
|
-
export declare const renderCSRWithRSC: (options: {
|
|
9
|
-
html: string;
|
|
10
|
-
rscRoot: import("react").ReactElement;
|
|
11
|
-
}) => Promise<Response>;
|
|
12
|
-
export declare const handleAction: (req: Request) => Promise<Response>;
|