diff --git a/packages/cli/builder/src/index.ts b/packages/cli/builder/src/index.ts index 3e77c36ae0c1..00515b74fe0b 100644 --- a/packages/cli/builder/src/index.ts +++ b/packages/cli/builder/src/index.ts @@ -31,6 +31,7 @@ export { export { RUNTIME_CHUNK_NAME, + RUNTIME_CHUNK_REGEX, SERVICE_WORKER_ENVIRONMENT_NAME, isHtmlDisabled, castArray, diff --git a/packages/cli/builder/src/plugins/runtimeChunk.ts b/packages/cli/builder/src/plugins/runtimeChunk.ts index 4b3ca832cd59..b5d40630b859 100644 --- a/packages/cli/builder/src/plugins/runtimeChunk.ts +++ b/packages/cli/builder/src/plugins/runtimeChunk.ts @@ -1,5 +1,5 @@ import type { RsbuildPlugin } from '@rsbuild/core'; -import { RUNTIME_CHUNK_NAME } from '../shared/utils'; +import { RUNTIME_CHUNK_NAME, RUNTIME_CHUNK_REGEX } from '../shared/utils'; export const pluginRuntimeChunk = ( disableInlineRuntimeChunk?: boolean, @@ -30,12 +30,8 @@ export const pluginRuntimeChunk = ( return; } - // RegExp like /bundler-runtime([.].+)?\.js$/ - // matches bundler-runtime.js and bundler-runtime.123456.js - const regexp = new RegExp(`${RUNTIME_CHUNK_NAME}([.].+)?\\.js$`); - if (!config.output.inlineScripts) { - config.output.inlineScripts = regexp; + config.output.inlineScripts = RUNTIME_CHUNK_REGEX; } }); }, diff --git a/packages/cli/builder/src/shared/utils.ts b/packages/cli/builder/src/shared/utils.ts index 754e7b497e26..0c19b5be402a 100644 --- a/packages/cli/builder/src/shared/utils.ts +++ b/packages/cli/builder/src/shared/utils.ts @@ -3,6 +3,11 @@ import browserslist from 'browserslist'; export const RUNTIME_CHUNK_NAME = 'builder-runtime'; +// RegExp like /builder-runtime([.].+)?\.js$/ +export const RUNTIME_CHUNK_REGEX = new RegExp( + `${RUNTIME_CHUNK_NAME}([.].+)?\\.js$`, +); + export const SERVICE_WORKER_ENVIRONMENT_NAME = 'workerSSR'; export const NODE_MODULES_REGEX = /[\\/]node_modules[\\/]/; diff --git a/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.worker.ts b/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.worker.ts index 5f3ca9bb0e6a..00dfc8a5245e 100644 --- a/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.worker.ts +++ b/packages/runtime/plugin-runtime/src/core/server/stream/createReadableStream.worker.ts @@ -68,9 +68,38 @@ export const createReadableStreamFromElement: CreateReadableStreamFromElement = const stream = new ReadableStream({ start(controller) { const pendingScripts: string[] = []; + let isClosed = false; + + const safeEnqueue = (chunk: Uint8Array | unknown) => { + if (isClosed) return; + try { + controller.enqueue(chunk as Uint8Array); + } catch { + isClosed = true; + } + }; + + const closeController = () => { + if (!isClosed) { + isClosed = true; + try { + controller.close(); + } catch { + // Controller already closed + } + } + }; + + const flushPendingScripts = () => { + for (const s of pendingScripts) { + safeEnqueue(encodeForWebStream(s)); + } + pendingScripts.length = 0; + }; + const enqueueScript = (script: string) => { if (shellChunkStatus === ShellChunkStatus.FINISH) { - controller.enqueue(encodeForWebStream(script)); + safeEnqueue(encodeForWebStream(script)); } else { pendingScripts.push(script); } @@ -78,7 +107,6 @@ export const createReadableStreamFromElement: CreateReadableStreamFromElement = const storageContext = storage.useContext?.(); const activeDeferreds = storageContext?.activeDeferreds; - /** * activeDeferreds is injected into storageContext by @modern-js/runtime. * @see packages/toolkit/runtime-utils/src/browser/nestedRoutes.tsx @@ -89,51 +117,58 @@ export const createReadableStreamFromElement: CreateReadableStreamFromElement = : []; if (entries.length > 0) { - enqueueFromEntries(entries, config.nonce, (s: string) => - enqueueScript(s), - ); + enqueueFromEntries(entries, config.nonce, enqueueScript); } async function push() { - const { done, value } = await reader.read(); - if (done) { - controller.close(); - return; - } - if (shellChunkStatus !== ShellChunkStatus.FINISH) { - const chunk = new TextDecoder().decode(value); - - chunkVec.push(chunk); - - let concatedChunk = chunkVec.join(''); - if (concatedChunk.includes(ESCAPED_SHELL_STREAM_END_MARK)) { - concatedChunk = concatedChunk.replace( - ESCAPED_SHELL_STREAM_END_MARK, - '', - ); - - shellChunkStatus = ShellChunkStatus.FINISH; - - controller.enqueue( - encodeForWebStream( - `${shellBefore}${concatedChunk}${shellAfter}`, - ), - ); - // Flush any pending