including-modules
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
||||
import { InvariantError } from '../../shared/lib/invariant-error';
|
||||
import { createAtomicTimerGroup } from './app-render-scheduling';
|
||||
import { DANGEROUSLY_runPendingImmediatesAfterCurrentTask, expectNoPendingImmediates } from '../node-environment-extensions/fast-set-immediate.external';
|
||||
/**
|
||||
* This is a utility function to make scheduling sequential tasks that run back to back easier.
|
||||
* We schedule on the same queue (setTimeout) at the same time to ensure no other events can sneak in between.
|
||||
*/ export function prerenderAndAbortInSequentialTasks(prerender, abort) {
|
||||
if (process.env.NEXT_RUNTIME === 'edge') {
|
||||
throw Object.defineProperty(new InvariantError('`prerenderAndAbortInSequentialTasks` should not be called in edge runtime.'), "__NEXT_ERROR_CODE", {
|
||||
value: "E538",
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject)=>{
|
||||
const scheduleTimeout = createAtomicTimerGroup();
|
||||
let pendingResult;
|
||||
scheduleTimeout(()=>{
|
||||
try {
|
||||
DANGEROUSLY_runPendingImmediatesAfterCurrentTask();
|
||||
pendingResult = prerender();
|
||||
pendingResult.catch(()=>{});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
scheduleTimeout(()=>{
|
||||
try {
|
||||
expectNoPendingImmediates();
|
||||
abort();
|
||||
resolve(pendingResult);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Like `prerenderAndAbortInSequentialTasks`, but with another task between `prerender` and `abort`,
|
||||
* which allows us to move a part of the render into a separate task.
|
||||
*/ export function prerenderAndAbortInSequentialTasksWithStages(prerender, advanceStage, abort) {
|
||||
if (process.env.NEXT_RUNTIME === 'edge') {
|
||||
throw Object.defineProperty(new InvariantError('`prerenderAndAbortInSequentialTasksWithStages` should not be called in edge runtime.'), "__NEXT_ERROR_CODE", {
|
||||
value: "E778",
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject)=>{
|
||||
const scheduleTimeout = createAtomicTimerGroup();
|
||||
let pendingResult;
|
||||
scheduleTimeout(()=>{
|
||||
try {
|
||||
DANGEROUSLY_runPendingImmediatesAfterCurrentTask();
|
||||
pendingResult = prerender();
|
||||
pendingResult.catch(()=>{});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
scheduleTimeout(()=>{
|
||||
try {
|
||||
DANGEROUSLY_runPendingImmediatesAfterCurrentTask();
|
||||
advanceStage();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
scheduleTimeout(()=>{
|
||||
try {
|
||||
expectNoPendingImmediates();
|
||||
abort();
|
||||
resolve(pendingResult);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// React's RSC prerender function will emit an incomplete flight stream when using `prerender`. If the connection
|
||||
// closes then whatever hanging chunks exist will be errored. This is because prerender (an experimental feature)
|
||||
// has not yet implemented a concept of resume. For now we will simulate a paused connection by wrapping the stream
|
||||
// in one that doesn't close even when the underlying is complete.
|
||||
export class ReactServerResult {
|
||||
constructor(stream){
|
||||
this._stream = stream;
|
||||
}
|
||||
tee() {
|
||||
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
|
||||
});
|
||||
}
|
||||
const tee = this._stream.tee();
|
||||
this._stream = tee[0];
|
||||
return tee[1];
|
||||
}
|
||||
consume() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
export 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function createReactServerPrerenderResultFromRender(underlying) {
|
||||
const chunks = [];
|
||||
const reader = underlying.getReader();
|
||||
while(true){
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
} else {
|
||||
chunks.push(value);
|
||||
}
|
||||
}
|
||||
return new ReactServerPrerenderResult(chunks);
|
||||
}
|
||||
export class ReactServerPrerenderResult {
|
||||
assertChunks(expression) {
|
||||
if (this._chunks === null) {
|
||||
throw Object.defineProperty(new 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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
export 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
|
||||
Reference in New Issue
Block a user