UNPKG

next

Version:

The React Framework

314 lines (313 loc) • 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readableStreamTee = readableStreamTee; exports.pipeTo = pipeTo; exports.pipeThrough = pipeThrough; exports.chainStreams = chainStreams; exports.streamFromArray = streamFromArray; exports.streamToString = streamToString; exports.encodeText = encodeText; exports.decodeText = decodeText; exports.createTransformStream = createTransformStream; exports.createBufferedTransformStream = createBufferedTransformStream; exports.createFlushEffectStream = createFlushEffectStream; exports.renderToInitialStream = renderToInitialStream; exports.continueFromInitialStream = continueFromInitialStream; exports.renderToStream = renderToStream; exports.createSuffixStream = createSuffixStream; exports.createPrefixStream = createPrefixStream; exports.createInlineDataStream = createInlineDataStream; function readableStreamTee(readable) { const transformStream = new TransformStream(); const transformStream2 = new TransformStream(); const writer = transformStream.writable.getWriter(); const writer2 = transformStream2.writable.getWriter(); const reader = readable.getReader(); function read() { reader.read().then(({ done , value })=>{ if (done) { writer.close(); writer2.close(); return; } writer.write(value); writer2.write(value); read(); }); } read(); return [ transformStream.readable, transformStream2.readable ]; } function pipeTo(readable, writable, options) { let resolver; const promise = new Promise((resolve)=>resolver = resolve ); const reader = readable.getReader(); const writer = writable.getWriter(); function process() { reader.read().then(({ done , value })=>{ if (done) { if (options === null || options === void 0 ? void 0 : options.preventClose) { writer.releaseLock(); } else { writer.close(); } resolver(); } else { writer.write(value); process(); } }); } process(); return promise; } function pipeThrough(readable, transformStream) { pipeTo(readable, transformStream.writable); return transformStream.readable; } function chainStreams(streams) { const { readable , writable } = new TransformStream(); let promise = Promise.resolve(); for(let i = 0; i < streams.length; ++i){ promise = promise.then(()=>pipeTo(streams[i], writable, { preventClose: i + 1 < streams.length }) ); } return readable; } function streamFromArray(strings) { // Note: we use a TransformStream here instead of instantiating a ReadableStream // because the built-in ReadableStream polyfill runs strings through TextEncoder. const { readable , writable } = new TransformStream(); const writer = writable.getWriter(); strings.forEach((str)=>writer.write(encodeText(str)) ); writer.close(); return readable; } async function streamToString(stream) { const reader = stream.getReader(); let bufferedString = ''; while(true){ const { done , value } = await reader.read(); if (done) { return bufferedString; } bufferedString += decodeText(value); } } function encodeText(input) { return new TextEncoder().encode(input); } function decodeText(input, textDecoder) { return textDecoder ? textDecoder.decode(input, { stream: true }) : new TextDecoder().decode(input); } function createTransformStream({ flush , transform }) { const source = new TransformStream(); const sink = new TransformStream(); const reader = source.readable.getReader(); const writer = sink.writable.getWriter(); const controller = { enqueue (chunk) { writer.write(chunk); }, error (reason) { writer.abort(reason); reader.cancel(); }, terminate () { writer.close(); reader.cancel(); }, get desiredSize () { return writer.desiredSize; } }; (async ()=>{ try { while(true){ const { done , value } = await reader.read(); if (done) { const maybePromise = flush === null || flush === void 0 ? void 0 : flush(controller); if (maybePromise) { await maybePromise; } writer.close(); return; } if (transform) { const maybePromise = transform(value, controller); if (maybePromise) { await maybePromise; } } else { controller.enqueue(value); } } } catch (err) { writer.abort(err); } })(); return { readable: sink.readable, writable: source.writable }; } function createBufferedTransformStream() { let bufferedString = ''; let pendingFlush = null; const flushBuffer = (controller)=>{ if (!pendingFlush) { pendingFlush = new Promise((resolve)=>{ setTimeout(()=>{ controller.enqueue(encodeText(bufferedString)); bufferedString = ''; pendingFlush = null; resolve(); }, 0); }); } return pendingFlush; }; const textDecoder = new TextDecoder(); return createTransformStream({ transform (chunk, controller) { bufferedString += decodeText(chunk, textDecoder); flushBuffer(controller); }, flush () { if (pendingFlush) { return pendingFlush; } } }); } function createFlushEffectStream(handleFlushEffect) { return createTransformStream({ async transform (chunk, controller) { const extraChunk = await handleFlushEffect(); // those should flush together at once controller.enqueue(encodeText(extraChunk + decodeText(chunk))); } }); } async function renderToInitialStream({ ReactDOMServer , element }) { return await ReactDOMServer.renderToReadableStream(element); } async function continueFromInitialStream({ suffix , dataStream , generateStaticHTML , flushEffectHandler , renderStream }) { const closeTag = '</body></html>'; const suffixUnclosed = suffix ? suffix.split(closeTag)[0] : null; if (generateStaticHTML) { await renderStream.allReady; } const transforms = [ createBufferedTransformStream(), flushEffectHandler ? createFlushEffectStream(flushEffectHandler) : null, suffixUnclosed != null ? createPrefixStream(suffixUnclosed) : null, dataStream ? createInlineDataStream(dataStream) : null, suffixUnclosed != null ? createSuffixStream(closeTag) : null, ].filter(Boolean); return transforms.reduce((readable, transform)=>pipeThrough(readable, transform) , renderStream); } async function renderToStream({ ReactDOMServer , element , suffix , dataStream , generateStaticHTML , flushEffectHandler }) { const closeTag = '</body></html>'; const suffixUnclosed = suffix ? suffix.split(closeTag)[0] : null; const renderStream = await ReactDOMServer.renderToReadableStream(element); if (generateStaticHTML) { await renderStream.allReady; } const transforms = [ createBufferedTransformStream(), flushEffectHandler ? createFlushEffectStream(flushEffectHandler) : null, suffixUnclosed != null ? createPrefixStream(suffixUnclosed) : null, dataStream ? createInlineDataStream(dataStream) : null, suffixUnclosed != null ? createSuffixStream(closeTag) : null, ].filter(Boolean); return transforms.reduce((readable, transform)=>pipeThrough(readable, transform) , renderStream); } function createSuffixStream(suffix) { return createTransformStream({ flush (controller) { if (suffix) { controller.enqueue(encodeText(suffix)); } } }); } function createPrefixStream(prefix) { let prefixFlushed = false; let prefixPrefixFlushFinished = null; return createTransformStream({ transform (chunk, controller) { controller.enqueue(chunk); if (!prefixFlushed && prefix) { prefixFlushed = true; prefixPrefixFlushFinished = new Promise((res)=>{ // NOTE: streaming flush // Enqueue prefix part before the major chunks are enqueued so that // prefix won't be flushed too early to interrupt the data stream setTimeout(()=>{ controller.enqueue(encodeText(prefix)); res(); }); }); } }, flush (controller) { if (prefixPrefixFlushFinished) return prefixPrefixFlushFinished; if (!prefixFlushed && prefix) { prefixFlushed = true; controller.enqueue(encodeText(prefix)); } } }); } function createInlineDataStream(dataStream) { let dataStreamFinished = null; return createTransformStream({ transform (chunk, controller) { controller.enqueue(chunk); if (!dataStreamFinished) { const dataStreamReader = dataStream.getReader(); // NOTE: streaming flush // We are buffering here for the inlined data stream because the // "shell" stream might be chunkenized again by the underlying stream // implementation, e.g. with a specific high-water mark. To ensure it's // the safe timing to pipe the data stream, this extra tick is // necessary. dataStreamFinished = new Promise((res)=>setTimeout(async ()=>{ try { while(true){ const { done , value } = await dataStreamReader.read(); if (done) { return res(); } controller.enqueue(value); } } catch (err) { controller.error(err); } res(); }, 0) ); } }, flush () { if (dataStreamFinished) { return dataStreamFinished; } } }); } //# sourceMappingURL=node-web-streams-helper.js.map