Skip to content

Commit

Permalink
Configure the requested environment and annotate tasks at boundary be…
Browse files Browse the repository at this point in the history
…tween environments
  • Loading branch information
sebmarkbage committed Jul 25, 2024
1 parent 933b737 commit abb7ae7
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 37 deletions.
139 changes: 102 additions & 37 deletions packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export type Response = {
_debugRootTask?: null | ConsoleTask, // DEV-only
_debugFindSourceMapURL?: void | FindSourceMapURLCallback, // DEV-only
_replayConsole: boolean, // DEV-only
_rootEnvironmentName: string, // DEV-only, the requested environment name.
};

function readChunk<T>(chunk: SomeChunk<T>): T {
Expand Down Expand Up @@ -689,7 +690,15 @@ function createElement(
writable: true,
value: null,
});
let env = response._rootEnvironmentName;
if (enableOwnerStacks) {
if (owner !== null && owner.env != null) {
// Interestingly we don't actually have the environment name of where
// this JSX was created if it doesn't have an owner but if it does
// it must be the same environment as the owner. We could send it separately
// but it seems a bit unnecessary for this edge case.
env = owner.env;
}
let normalizedStackTrace: null | Error = null;
if (stack !== null) {
// We create a fake stack and then create an Error object inside of it.
Expand Down Expand Up @@ -717,7 +726,7 @@ function createElement(
// This owner should ideally have already been initialized to avoid getting
// user stack frames on the stack.
const ownerTask =
owner === null ? null : initializeFakeTask(response, owner);
owner === null ? null : initializeFakeTask(response, owner, env);
if (ownerTask === null) {
const rootTask = response._debugRootTask;
if (rootTask != null) {
Expand Down Expand Up @@ -1320,6 +1329,7 @@ function ResponseInstance(
temporaryReferences: void | TemporaryReferenceSet,
findSourceMapURL: void | FindSourceMapURLCallback,
replayConsole: boolean,
environmentName: void | string,
) {
const chunks: Map<number, SomeChunk<any>> = new Map();
this._bundlerConfig = bundlerConfig;
Expand All @@ -1336,17 +1346,21 @@ function ResponseInstance(
this._rowLength = 0;
this._buffer = [];
this._tempRefs = temporaryReferences;
if (supportsCreateTask) {
// Any stacks that appear on the server need to be rooted somehow on the client
// so we create a root Task for this response which will be the root owner for any
// elements created by the server. We use the "use server" string to indicate that
// this is where we enter the server from the client.
// TODO: Make this string configurable.
this._debugRootTask = (console: any).createTask('"use server"');
}
if (__DEV__) {
const rootEnv = environmentName === undefined ? 'Server' : environmentName;
if (supportsCreateTask) {
// Any stacks that appear on the server need to be rooted somehow on the client
// so we create a root Task for this response which will be the root owner for any
// elements created by the server. We use the "use server" string to indicate that
// this is where we enter the server from the client.
// TODO: Make this string configurable.
this._debugRootTask = (console: any).createTask(
'"use ' + rootEnv.toLowerCase() + '"',
);
}
this._debugFindSourceMapURL = findSourceMapURL;
this._replayConsole = replayConsole;
this._rootEnvironmentName = rootEnv;
}
// Don't inline this call because it causes closure to outline the call above.
this._fromJSON = createFromJSONCallback(this);
Expand All @@ -1361,6 +1375,7 @@ export function createResponse(
temporaryReferences: void | TemporaryReferenceSet,
findSourceMapURL: void | FindSourceMapURLCallback,
replayConsole: boolean,
environmentName: void | string,
): Response {
// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors
return new ResponseInstance(
Expand All @@ -1372,6 +1387,7 @@ export function createResponse(
temporaryReferences,
findSourceMapURL,
replayConsole,
environmentName,
);
}

Expand Down Expand Up @@ -1828,7 +1844,7 @@ function resolveErrorDev(
'An error occurred in the Server Components render but no message was provided',
),
);
const rootTask = response._debugRootTask;
const rootTask = getRootTask(response, env);
if (rootTask != null) {
error = rootTask.run(callStack);
} else {
Expand Down Expand Up @@ -2044,52 +2060,99 @@ function buildFakeCallStack<T>(
return callStack;
}

function getRootTask(
response: Response,
childEnvironmentName: string,
): null | ConsoleTask {
const rootTask = response._debugRootTask;
if (!rootTask) {
return null;
}
if (response._rootEnvironmentName !== childEnvironmentName) {
// If the root most owner component is itself in a different environment than the requested
// environment then we create an extra task to indicate that we're transitioning into it.
// Like if one environment just requests another environment.
const createTaskFn = (console: any).createTask.bind(
console,
'"use ' + childEnvironmentName.toLowerCase() + '"',
);
return rootTask.run(createTaskFn);
}
return rootTask;
}

function initializeFakeTask(
response: Response,
debugInfo: ReactComponentInfo | ReactAsyncInfo,
childEnvironmentName: string,
): null | ConsoleTask {
if (!supportsCreateTask) {
return null;
}
const componentInfo: ReactComponentInfo = (debugInfo: any); // Refined
const cachedEntry = componentInfo.debugTask;
if (cachedEntry !== undefined) {
return cachedEntry;
}

if (debugInfo.stack == null) {
// If this is an error, we should've really already initialized the task.
// If it's null, we can't initialize a task.
return null;
}

const stack = debugInfo.stack;
const env: string =
componentInfo.env == null
? response._rootEnvironmentName
: componentInfo.env;
if (env !== childEnvironmentName) {
// This is the boundary between two environments so we'll annotate the task name.
// That is unusual so we don't cache it.
const ownerTask =
componentInfo.owner == null
? null
: initializeFakeTask(response, componentInfo.owner, env);
return buildFakeTask(
response,
ownerTask,
stack,
'"use ' + childEnvironmentName.toLowerCase() + '"',
env,
);
} else {
const cachedEntry = componentInfo.debugTask;
if (cachedEntry !== undefined) {
return cachedEntry;
}
const ownerTask =
componentInfo.owner == null
? null
: initializeFakeTask(response, componentInfo.owner, env);
// $FlowFixMe[cannot-write]: We consider this part of initialization.
return (componentInfo.debugTask = buildFakeTask(
response,
ownerTask,
stack,
getServerComponentTaskName(componentInfo),
env,
));
}
}

const ownerTask =
componentInfo.owner == null
? null
: initializeFakeTask(response, componentInfo.owner);

const createTaskFn = (console: any).createTask.bind(
console,
getServerComponentTaskName(componentInfo),
);
function buildFakeTask(
response: Response,
ownerTask: null | ConsoleTask,
stack: ReactStackTrace,
taskName: string,
env: string,
): ConsoleTask {
const createTaskFn = (console: any).createTask.bind(console, taskName);
const callStack = buildFakeCallStack(response, stack, createTaskFn);

let componentTask;
if (ownerTask === null) {
const rootTask = response._debugRootTask;
const rootTask = getRootTask(response, env);
if (rootTask != null) {
componentTask = rootTask.run(callStack);
return rootTask.run(callStack);
} else {
componentTask = callStack();
return callStack();
}
} else {
componentTask = ownerTask.run(callStack);
return ownerTask.run(callStack);
}
// $FlowFixMe[cannot-write]: We consider this part of initialization.
componentInfo.debugTask = componentTask;
return componentTask;
}

const createFakeJSXCallStack = {
Expand Down Expand Up @@ -2160,7 +2223,9 @@ function resolveDebugInfo(
// We eagerly initialize the fake task because this resolving happens outside any
// render phase so we're not inside a user space stack at this point. If we waited
// to initialize it when we need it, we might be inside user code.
initializeFakeTask(response, debugInfo);
const env =
debugInfo.env === undefined ? response._rootEnvironmentName : debugInfo.env;
initializeFakeTask(response, debugInfo, env);
initializeFakeStack(response, debugInfo);

const chunk = getChunk(response, id);
Expand Down Expand Up @@ -2209,7 +2274,7 @@ function resolveConsoleEntry(
printToConsole.bind(null, methodName, args, env),
);
if (owner != null) {
const task = initializeFakeTask(response, owner);
const task = initializeFakeTask(response, owner, env);
initializeFakeStack(response, owner);
if (task !== null) {
task.run(callStack);
Expand All @@ -2218,7 +2283,7 @@ function resolveConsoleEntry(
// TODO: Set the current owner so that captureOwnerStack() adds the component
// stack during the replay - if needed.
}
const rootTask = response._debugRootTask;
const rootTask = getRootTask(response, env);
if (rootTask != null) {
rootTask.run(callStack);
return;
Expand Down
1 change: 1 addition & 0 deletions packages/react-noop-renderer/src/ReactNoopFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function read<T>(source: Source, options: ReadOptions): Thenable<T> {
undefined,
options !== undefined ? options.findSourceMapURL : undefined,
true,
undefined,
);
for (let i = 0; i < source.length; i++) {
processBinaryChunk(response, source[i], 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type Options = {
temporaryReferences?: TemporaryReferenceSet,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createResponseFromOptions(options: void | Options) {
Expand All @@ -59,6 +60,9 @@ function createResponseFromOptions(options: void | Options) {
? options.findSourceMapURL
: undefined,
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
}

Expand Down
4 changes: 4 additions & 0 deletions packages/react-server-dom-esm/src/ReactFlightDOMClientNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type Options = {
encodeFormAction?: EncodeFormActionCallback,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createFromNodeStream<T>(
Expand All @@ -70,6 +71,9 @@ function createFromNodeStream<T>(
? options.findSourceMapURL
: undefined,
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
stream.on('data', chunk => {
processBinaryChunk(response, chunk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type Options = {
temporaryReferences?: TemporaryReferenceSet,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createResponseFromOptions(options: void | Options) {
Expand All @@ -58,6 +59,9 @@ function createResponseFromOptions(options: void | Options) {
? options.findSourceMapURL
: undefined,
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export type Options = {
temporaryReferences?: TemporaryReferenceSet,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createResponseFromOptions(options: Options) {
Expand All @@ -88,6 +89,9 @@ function createResponseFromOptions(options: Options) {
? options.findSourceMapURL
: undefined,
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export type Options = {
encodeFormAction?: EncodeFormActionCallback,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createFromNodeStream<T>(
Expand All @@ -79,6 +80,9 @@ function createFromNodeStream<T>(
? options.findSourceMapURL
: undefined,
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
stream.on('data', chunk => {
processBinaryChunk(response, chunk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type Options = {
temporaryReferences?: TemporaryReferenceSet,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createResponseFromOptions(options: void | Options) {
Expand All @@ -58,6 +59,9 @@ function createResponseFromOptions(options: void | Options) {
? options.findSourceMapURL
: undefined,
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export type Options = {
temporaryReferences?: TemporaryReferenceSet,
findSourceMapURL?: FindSourceMapURLCallback,
replayConsoleLogs?: boolean,
environmentName?: string,
};

function createResponseFromOptions(options: Options) {
Expand All @@ -88,6 +89,9 @@ function createResponseFromOptions(options: Options) {
? options.findSourceMapURL
: undefined,
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
__DEV__ && options && options.environmentName
? options.environmentName
: undefined,
);
}

Expand Down
Loading

0 comments on commit abb7ae7

Please sign in to comment.