diff --git a/packages/next/src/server/stream-utils/node-web-streams-helper.ts b/packages/next/src/server/stream-utils/node-web-streams-helper.ts index f979638a47341..e8c032a9e9323 100644 --- a/packages/next/src/server/stream-utils/node-web-streams-helper.ts +++ b/packages/next/src/server/stream-utils/node-web-streams-helper.ts @@ -202,6 +202,7 @@ function createHeadInsertionTransformStream( controller.enqueue(chunk) freezing = true } else { + // TODO (@Ethan-Arrowood): Replace the generic `indexOfUint8Array` method with something finely tuned for the subset of things actually being checked for. const index = indexOfUint8Array(chunk, ENCODED_TAGS.CLOSED.HEAD) if (index !== -1) { const insertedHeadContent = new Uint8Array( @@ -444,10 +445,22 @@ export function createRootLayoutValidatorStream(): TransformStream< > { let foundHtml = false let foundBody = false - let content = new Uint8Array(0) + let chunks: Uint8Array[] = [] + let size = 0 return new TransformStream({ async transform(chunk, controller) { - content = concatUint8Arrays(content, chunk) + chunks.push(chunk) + size += chunk.length + controller.enqueue(chunk) + }, + flush(controller) { + const content = new Uint8Array(size) + let offset = 0 + for (const chunk of chunks) { + content.set(chunk, offset) + offset += chunk.length + } + // Peek into the streamed chunk to see if the tags are present. if (!foundHtml || !foundBody) { if ( @@ -463,9 +476,7 @@ export function createRootLayoutValidatorStream(): TransformStream< foundBody = true } } - controller.enqueue(chunk) - }, - flush(controller) { + const missingTags: typeof window.__next_root_layout_missing_tags = [] if (!foundHtml) missingTags.push('html') if (!foundBody) missingTags.push('body') diff --git a/packages/next/src/server/stream-utils/uint8array-helpers.ts b/packages/next/src/server/stream-utils/uint8array-helpers.ts index 7293269b09c18..fba63ef276c86 100644 --- a/packages/next/src/server/stream-utils/uint8array-helpers.ts +++ b/packages/next/src/server/stream-utils/uint8array-helpers.ts @@ -2,7 +2,8 @@ * Find the starting index of Uint8Array `b` within Uint8Array `a`. */ export function indexOfUint8Array(a: Uint8Array, b: Uint8Array) { - if (a.length === 0 || b.length === 0 || b.length > a.length) return -1 + if (b.length === 0) return 0 + if (a.length === 0 || b.length > a.length) return -1 // start iterating through `a` for (let i = 0; i <= a.length - b.length; i++) { @@ -28,7 +29,13 @@ export function indexOfUint8Array(a: Uint8Array, b: Uint8Array) { * Check if two Uint8Arrays are strictly equivalent. */ export function isEquivalentUint8Arrays(a: Uint8Array, b: Uint8Array) { - return a.length === b.length && a.every((v, i) => v === b[i]) + if (a.length !== b.length) return false + + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false + } + + return true } /** @@ -40,6 +47,7 @@ export function isEquivalentUint8Arrays(a: Uint8Array, b: Uint8Array) { */ export function removeFromUint8Array(a: Uint8Array, b: Uint8Array) { const tagIndex = indexOfUint8Array(a, b) + if (tagIndex === 0) return a.subarray(b.length) if (tagIndex > -1) { const removed = new Uint8Array(a.length - b.length) removed.set(a.slice(0, tagIndex))