UNPKG

next

Version:

The React Framework

332 lines (331 loc) • 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { ReactServerPrerenderResult: null, ReactServerResult: null, ReplayableNodeStream: null, createReactServerPrerenderResult: null, createReactServerPrerenderResultFromRender: null, processPrelude: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { ReactServerPrerenderResult: function() { return ReactServerPrerenderResult; }, ReactServerResult: function() { return ReactServerResult; }, ReplayableNodeStream: function() { return ReplayableNodeStream; }, createReactServerPrerenderResult: function() { return createReactServerPrerenderResult; }, createReactServerPrerenderResultFromRender: function() { return createReactServerPrerenderResultFromRender; }, processPrelude: function() { return processPrelude; } }); const _invarianterror = require("../../shared/lib/invariant-error"); function isWebStream(stream) { return typeof stream.tee === 'function'; } class ReactServerResult { constructor(stream){ if (process.env.__NEXT_USE_NODE_STREAMS && !isWebStream(stream)) { this._stream = null; this._replayable = new ReplayableNodeStream(stream); } else { this._stream = stream; this._replayable = null; } } tee() { if (this._replayable) { return this._replayable.createReplayStream(); } if (this._stream === null) { throw Object.defineProperty(new Error('Cannot tee a ReactServerResult that has already been consumed'), "__NEXT_ERROR_CODE", { value: "E106", enumerable: false, configurable: true }); } if (isWebStream(this._stream)) { const tee = this._stream.tee(); this._stream = tee[0]; return tee[1]; } if (process.env.NEXT_RUNTIME === 'edge') { throw Object.defineProperty(new _invarianterror.InvariantError('Node.js Readable cannot be teed in the edge runtime'), "__NEXT_ERROR_CODE", { value: "E1149", enumerable: false, configurable: true }); } else { let Readable; if (process.env.TURBOPACK) { Readable = require('node:stream').Readable; } else { Readable = __non_webpack_require__('node:stream').Readable; } const webStream = Readable.toWeb(this._stream); const tee = webStream.tee(); this._stream = Readable.fromWeb(tee[0]); return Readable.fromWeb(tee[1]); } } consume() { if (this._replayable) { const stream = this._replayable.createReplayStream(); this._replayable.dispose(); this._replayable = null; return stream; } if (this._stream === null) { throw Object.defineProperty(new Error('Cannot consume a ReactServerResult that has already been consumed'), "__NEXT_ERROR_CODE", { value: "E470", enumerable: false, configurable: true }); } const stream = this._stream; this._stream = null; return stream; } } class ReplayableNodeStream { constructor(stream){ this._chunks = []; this._done = false; this._error = null; this._subscribers = new Set(); stream.on('data', (chunk)=>{ const buf = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk); if (this._chunks !== null) { this._chunks.push(buf); } for (const sub of this._subscribers){ sub.onChunk(buf); } }); stream.on('end', ()=>{ this._done = true; for (const sub of this._subscribers){ sub.onEnd(); } this._subscribers.clear(); }); stream.on('error', (err)=>{ this._error = err; for (const sub of this._subscribers){ sub.onError(err); } this._subscribers.clear(); }); } /** * Creates a new Node.js Readable stream that first emits all buffered chunks, * then forwards any new chunks from the source as they arrive. * * Buffered chunks are delivered via _read() (pull-based) rather than pushed * eagerly. This is critical because createReplayStream() is called outside * of AsyncLocalStorage context, and eagerly pushing chunks triggers internal * Node.js stream scheduling (process.nextTick for maybeReadMore) that * captures the empty ALS context. By deferring to _read(), chunks are only * delivered when the consumer reads, which happens inside the correct ALS * scope (e.g. during Fizz's performWork). */ createReplayStream() { if (this._chunks === null) { throw Object.defineProperty(new _invarianterror.InvariantError('Cannot create a replay stream after the ReplayableNodeStream has been disposed.'), "__NEXT_ERROR_CODE", { value: "E1156", enumerable: false, configurable: true }); } let ReadableCtor; if (process.env.NEXT_RUNTIME === 'edge') { throw Object.defineProperty(new _invarianterror.InvariantError('Node.js Readable cannot be teed in the edge runtime'), "__NEXT_ERROR_CODE", { value: "E1149", enumerable: false, configurable: true }); } else { if (process.env.__NEXT_BUNDLER === 'Webpack' || process.env.__NEXT_BUNDLER === 'Rspack') { ReadableCtor = __non_webpack_require__('node:stream').Readable; } else { ReadableCtor = require('node:stream').Readable; } } const bufferedChunks = this._chunks.slice(); let bufferIndex = 0; let bufferDrained = false; const isDone = this._done; const sourceError = this._error; const stream = new ReadableCtor({ read () { if (!bufferDrained) { bufferDrained = true; for(let i = bufferIndex; i < bufferedChunks.length; i++){ this.push(bufferedChunks[i]); } bufferIndex = bufferedChunks.length; if (isDone) { this.push(null); } } } }); if (sourceError) { stream.destroy(sourceError); return stream; } if (isDone) { return stream; } const subscriber = { onChunk: (chunk)=>{ stream.push(chunk); }, onEnd: ()=>{ stream.push(null); }, onError: (err)=>{ stream.destroy(err); } }; this._subscribers.add(subscriber); stream.on('close', ()=>{ this._subscribers.delete(subscriber); }); return stream; } /** * Clears the buffered chunks and all subscriber references. After calling * this, no new replay streams can be created. */ dispose() { this._chunks = null; } } async function createReactServerPrerenderResult(underlying) { const chunks = []; const { prelude } = await underlying; const reader = prelude.getReader(); while(true){ const { done, value } = await reader.read(); if (done) { return new ReactServerPrerenderResult(chunks); } else { chunks.push(value); } } } async function createReactServerPrerenderResultFromRender(underlying) { const chunks = []; if (isWebStream(underlying)) { const reader = underlying.getReader(); while(true){ const { done, value } = await reader.read(); if (done) { break; } else { chunks.push(value); } } } else { for await (const chunk of underlying){ chunks.push(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)); } } return new ReactServerPrerenderResult(chunks); } class ReactServerPrerenderResult { assertChunks(expression) { if (this._chunks === null) { throw Object.defineProperty(new _invarianterror.InvariantError(`Cannot \`${expression}\` on a ReactServerPrerenderResult that has already been consumed.`), "__NEXT_ERROR_CODE", { value: "E593", enumerable: false, configurable: true }); } return this._chunks; } consumeChunks(expression) { const chunks = this.assertChunks(expression); this.consume(); return chunks; } consume() { this._chunks = null; } constructor(chunks){ this._chunks = chunks; } asChunks() { const chunks = this.assertChunks('asChunks()'); return chunks; } asUnclosingStream() { const chunks = this.assertChunks('asUnclosingStream()'); return createUnclosingStream(chunks); } consumeAsUnclosingStream() { const chunks = this.consumeChunks('consumeAsUnclosingStream()'); return createUnclosingStream(chunks); } asStream() { const chunks = this.assertChunks('asStream()'); return createClosingStream(chunks); } consumeAsStream() { const chunks = this.consumeChunks('consumeAsStream()'); return createClosingStream(chunks); } } function createUnclosingStream(chunks) { let i = 0; return new ReadableStream({ async pull (controller) { if (i < chunks.length) { controller.enqueue(chunks[i++]); } // we intentionally keep the stream open. The consumer will clear // out chunks once finished and the remaining memory will be GC'd // when this object goes out of scope } }); } function createClosingStream(chunks) { let i = 0; return new ReadableStream({ async pull (controller) { if (i < chunks.length) { controller.enqueue(chunks[i++]); } else { controller.close(); } } }); } async function processPrelude(unprocessedPrelude) { const [prelude, peek] = unprocessedPrelude.tee(); const reader = peek.getReader(); const firstResult = await reader.read(); reader.cancel(); const preludeIsEmpty = firstResult.done === true; return { prelude, preludeIsEmpty }; } //# sourceMappingURL=app-render-prerender-utils.js.map