mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
This lets you click a stack frame on the client and see the Server source code inline. <img width="871" alt="Screenshot 2024-06-01 at 11 44 24 PM" src="https://github.com/facebook/react/assets/63648/581281ce-0dce-40c0-a084-4a6d53ba1682"> <img width="840" alt="Screenshot 2024-06-01 at 11 43 37 PM" src="https://github.com/facebook/react/assets/63648/00dc77af-07c1-4389-9ae0-cf1f45199efb"> We could do some logic on the server that sends a source map url for every stack frame in the RSC payload. That would make the client potentially config free. However regardless we need the config to describe what url scheme to use since that’s not built in to the bundler config. In practice you likely have a common pattern for your source maps so no need to send data over and over when we can just have a simple function configured on the client. The server must return a source map, even if the file is not actually compiled since the fake file is still compiled. The source mapping strategy can be one of two models depending on if the server’s stack traces (`new Error().stack`) are source mapped back to the original (`—enable-source-maps`) or represents the location in compiled code (like in the browser). If it represents the location in compiled code it’s actually easier. You just serve the source map generated for that file by the tooling. If it is already source mapped it has to generate a source map where everything points to the same location (as if not compiled) ideally with a segment per logical ast node.
139 lines
3.3 KiB
JavaScript
139 lines
3.3 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {Thenable} from 'shared/ReactTypes.js';
|
|
|
|
import type {
|
|
Response as FlightResponse,
|
|
FindSourceMapURLCallback,
|
|
} from 'react-client/src/ReactFlightClient';
|
|
|
|
import type {ReactServerValue} from 'react-client/src/ReactFlightReplyClient';
|
|
|
|
import {
|
|
createResponse,
|
|
getRoot,
|
|
reportGlobalError,
|
|
processBinaryChunk,
|
|
close,
|
|
} from 'react-client/src/ReactFlightClient';
|
|
|
|
import {
|
|
processReply,
|
|
createServerReference,
|
|
} from 'react-client/src/ReactFlightReplyClient';
|
|
|
|
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
|
|
|
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
|
|
|
export type {TemporaryReferenceSet};
|
|
|
|
type CallServerCallback = <A, T>(string, args: A) => Promise<T>;
|
|
|
|
export type Options = {
|
|
callServer?: CallServerCallback,
|
|
temporaryReferences?: TemporaryReferenceSet,
|
|
findSourceMapURL?: FindSourceMapURLCallback,
|
|
};
|
|
|
|
function createResponseFromOptions(options: void | Options) {
|
|
return createResponse(
|
|
null,
|
|
null,
|
|
options && options.callServer ? options.callServer : undefined,
|
|
undefined, // encodeFormAction
|
|
undefined, // nonce
|
|
options && options.temporaryReferences
|
|
? options.temporaryReferences
|
|
: undefined,
|
|
__DEV__ && options && options.findSourceMapURL
|
|
? options.findSourceMapURL
|
|
: undefined,
|
|
);
|
|
}
|
|
|
|
function startReadingFromStream(
|
|
response: FlightResponse,
|
|
stream: ReadableStream,
|
|
): void {
|
|
const reader = stream.getReader();
|
|
function progress({
|
|
done,
|
|
value,
|
|
}: {
|
|
done: boolean,
|
|
value: ?any,
|
|
...
|
|
}): void | Promise<void> {
|
|
if (done) {
|
|
close(response);
|
|
return;
|
|
}
|
|
const buffer: Uint8Array = (value: any);
|
|
processBinaryChunk(response, buffer);
|
|
return reader.read().then(progress).catch(error);
|
|
}
|
|
function error(e: any) {
|
|
reportGlobalError(response, e);
|
|
}
|
|
reader.read().then(progress).catch(error);
|
|
}
|
|
|
|
function createFromReadableStream<T>(
|
|
stream: ReadableStream,
|
|
options?: Options,
|
|
): Thenable<T> {
|
|
const response: FlightResponse = createResponseFromOptions(options);
|
|
startReadingFromStream(response, stream);
|
|
return getRoot(response);
|
|
}
|
|
|
|
function createFromFetch<T>(
|
|
promiseForResponse: Promise<Response>,
|
|
options?: Options,
|
|
): Thenable<T> {
|
|
const response: FlightResponse = createResponseFromOptions(options);
|
|
promiseForResponse.then(
|
|
function (r) {
|
|
startReadingFromStream(response, (r.body: any));
|
|
},
|
|
function (e) {
|
|
reportGlobalError(response, e);
|
|
},
|
|
);
|
|
return getRoot(response);
|
|
}
|
|
|
|
function encodeReply(
|
|
value: ReactServerValue,
|
|
options?: {temporaryReferences?: TemporaryReferenceSet},
|
|
): Promise<
|
|
string | URLSearchParams | FormData,
|
|
> /* We don't use URLSearchParams yet but maybe */ {
|
|
return new Promise((resolve, reject) => {
|
|
processReply(
|
|
value,
|
|
'',
|
|
options && options.temporaryReferences
|
|
? options.temporaryReferences
|
|
: undefined,
|
|
resolve,
|
|
reject,
|
|
);
|
|
});
|
|
}
|
|
|
|
export {
|
|
createFromFetch,
|
|
createFromReadableStream,
|
|
encodeReply,
|
|
createServerReference,
|
|
};
|