Skip to content

Commit 807f04a

Browse files
fix(plugin-runtime): streaming ssr split chunk so that can't match SHELL_STREAM_END_MARK (#3719)
* fix(plugin-runtime): streaming ssr split chunk so that cant match SHELL_STREAM_END_MARK * fix: update test && handler worker streaming ssr
1 parent a4e40df commit 807f04a

File tree

6 files changed

+47
-38
lines changed

6 files changed

+47
-38
lines changed

.changeset/tame-sloths-look.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@modern-js/runtime': patch
3+
---
4+
5+
fix(plugin-runtime): streaming ssr split chunk so that can't match `SHELL_STREAM_END_MARK`
6+
fix(plugin-runtime): streaming ssr chunk 进行分割导致无法匹配 `SHELL_STREAM_END_MARK`

packages/runtime/plugin-runtime/src/router/runtime/DeferredDataScripts.node.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ const DeferredDataScript = ({
181181
<ErrorDeferredDataScript routeId={routeId} dataKey={dataKey} />
182182
}
183183
>
184-
{data => (
184+
{(data: any) => (
185185
<script
186186
async
187187
suppressHydrationWarning

packages/runtime/plugin-runtime/src/ssr/serverRender/renderToStream/renderToPipe.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ import { getTemplates } from './template';
77
export type Pipe<T extends Writable> = (output: T) => Promise<T | string>;
88

99
enum ShellChunkStatus {
10-
IDLE = 0,
11-
START = 1,
12-
FINIESH = 2,
10+
START = 0,
11+
FINIESH = 1,
1312
}
1413

1514
function renderToPipe(
1615
rootElement: React.ReactElement,
1716
context: RuntimeContext,
1817
options?: RenderToPipeableStreamOptions,
1918
) {
20-
let shellChunkStatus = ShellChunkStatus.IDLE;
19+
let shellChunkStatus = ShellChunkStatus.START;
2120

2221
const { ssrContext } = context;
22+
const chunkVec: string[] = [];
2323
const forUserPipe: Pipe<Writable> = stream => {
2424
return new Promise(resolve => {
2525
let renderToPipeableStream;
@@ -40,28 +40,24 @@ function renderToPipe(
4040
transform(chunk, _encoding, callback) {
4141
try {
4242
if (shellChunkStatus !== ShellChunkStatus.FINIESH) {
43-
let concatedChunk = chunk.toString();
44-
if (shellChunkStatus === ShellChunkStatus.IDLE) {
45-
concatedChunk = `${shellBefore}${concatedChunk}`;
46-
shellChunkStatus = ShellChunkStatus.START;
47-
}
43+
chunkVec.push(chunk.toString());
44+
4845
/**
4946
* The shell content of App may be splitted by multiple chunks to transform,
5047
* when any node value's size is larger than the React limitation, refer to:
5148
* https://github.com/facebook/react/blob/v18.2.0/packages/react-server/src/ReactServerStreamConfigNode.js#L53.
5249
* So we use the `SHELL_STREAM_END_MARK` to mark the shell content' tail.
5350
*/
54-
if (
55-
shellChunkStatus === ShellChunkStatus.START &&
56-
concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)
57-
) {
51+
let concatedChunk = chunkVec.join('');
52+
if (concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)) {
5853
concatedChunk = concatedChunk.replace(
5954
ESCAPED_SHELL_STREAM_END_MARK,
60-
shellAfter,
55+
'',
6156
);
57+
6258
shellChunkStatus = ShellChunkStatus.FINIESH;
59+
this.push(`${shellBefore}${concatedChunk}${shellAfter}`);
6360
}
64-
this.push(concatedChunk);
6561
} else {
6662
this.push(chunk);
6763
}

packages/runtime/plugin-runtime/src/ssr/serverRender/renderToStream/renderToPipe.worker.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import { getTemplates } from './template';
1010
export type Pipe<T extends Writable> = (output: T) => Promise<T | string>;
1111

1212
enum ShellChunkStatus {
13-
IDLE = 0,
14-
START = 1,
15-
FINIESH = 2,
13+
START = 0,
14+
FINIESH = 1,
1615
}
1716

1817
function renderToPipe(
1918
rootElement: React.ReactElement,
2019
context: RuntimeContext,
2120
options?: RenderToReadableStreamOptions,
2221
) {
23-
let shellChunkStatus = ShellChunkStatus.IDLE;
22+
let shellChunkStatus = ShellChunkStatus.START;
23+
const chunkVec: string[] = [];
2424

2525
const { ssrContext } = context;
2626
const forUserPipe = async () => {
@@ -56,20 +56,24 @@ function renderToPipe(
5656
return;
5757
}
5858
if (shellChunkStatus !== ShellChunkStatus.FINIESH) {
59-
let concatedChunk = new TextDecoder().decode(value);
60-
if (shellChunkStatus === ShellChunkStatus.IDLE) {
61-
concatedChunk = `${shellBefore}${concatedChunk}`;
62-
shellChunkStatus = ShellChunkStatus.START;
63-
}
64-
if (
65-
shellChunkStatus === ShellChunkStatus.START &&
66-
concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)
67-
) {
59+
const chunk = new TextDecoder().decode(value);
60+
61+
chunkVec.push(chunk);
62+
63+
let concatedChunk = chunkVec.join('');
64+
if (concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)) {
6865
concatedChunk = concatedChunk.replace(
6966
ESCAPED_SHELL_STREAM_END_MARK,
70-
shellAfter!,
67+
'',
7168
);
69+
7270
shellChunkStatus = ShellChunkStatus.FINIESH;
71+
72+
controller.enqueue(
73+
encodeForWebStream(
74+
`${shellBefore}${concatedChunk}${shellAfter}`,
75+
),
76+
);
7377
}
7478
controller.enqueue(encodeForWebStream(concatedChunk));
7579
} else {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export type InjectTemplate = {
2-
shellBefore?: string;
3-
shellAfter?: string;
2+
shellBefore: string;
3+
shellAfter: string;
44
};

packages/runtime/plugin-runtime/tests/ssr/fixtures/streaming-ssr/App.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ const Home = lazy(() => import('./Home'));
44

55
const App: React.FunctionComponent = () => {
66
return (
7-
<div>
8-
<div>App Layout</div>
9-
<Suspense fallback={<div>loading home...</div>}>
10-
<Home />
11-
</Suspense>
12-
</div>
7+
<>
8+
<div>
9+
<div>App Layout</div>
10+
<Suspense fallback={<div>loading home...</div>}>
11+
<Home />
12+
</Suspense>
13+
</div>
14+
{'<!--<?- SHELL_STREAM_END ?>-->'}
15+
</>
1316
);
1417
};
1518

0 commit comments

Comments
 (0)