mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[Flight Parcel] Implement findSourceMapURL (#32294)
This implements `findSourceMapURL` in react-server-dom-parcel, enabling source maps for replayed server errors on the client. It utilizes a new endpoint in the Parcel dev server that returns the source map for a given bundle/file. The error overlay UI has also been updated to handle these stacks. See https://github.com/parcel-bundler/parcel/pull/10082 Also updated the fixture to the latest Parcel canary. A few APIs have changed. We do have a higher level library wrapper now (`@parcel/rsc` added in https://github.com/parcel-bundler/parcel/pull/10074) but I left the fixture using the lower level APIs directly here since it is easier to see how react-server-dom-parcel is used.
This commit is contained in:
parent
0a82580bfc
commit
f82c662b8d
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "@parcel/config-default",
|
|
||||||
"runtimes": ["...", "@parcel/runtime-rsc"]
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "flight-parcel",
|
"name": "flight-parcel",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"source": "src/server.tsx",
|
||||||
"examples/*"
|
|
||||||
],
|
|
||||||
"server": "dist/server.js",
|
"server": "dist/server.js",
|
||||||
"targets": {
|
"targets": {
|
||||||
"server": {
|
"server": {
|
||||||
"source": "src/server.tsx",
|
|
||||||
"context": "react-server",
|
"context": "react-server",
|
||||||
"outputFormat": "commonjs",
|
|
||||||
"includeNodeModules": {
|
"includeNodeModules": {
|
||||||
"express": false
|
"express": false
|
||||||
}
|
}
|
||||||
|
|
@ -18,18 +14,11 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"predev": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
"predev": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||||
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
|
||||||
"dev": "concurrently \"npm run dev:watch\" \"sleep 2 && npm run dev:start\"",
|
"dev": "parcel",
|
||||||
"dev:watch": "NODE_ENV=development parcel watch",
|
|
||||||
"dev:start": "NODE_ENV=development node dist/server.js",
|
|
||||||
"build": "parcel build",
|
"build": "parcel build",
|
||||||
"start": "node dist/server.js"
|
"start": "node dist/server.js"
|
||||||
},
|
},
|
||||||
"@parcel/resolver-default": {
|
|
||||||
"packageExports": true
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@parcel/config-default": "2.0.0-dev.1795",
|
|
||||||
"@parcel/runtime-rsc": "2.13.3-dev.3418",
|
|
||||||
"@types/parcel-env": "^0.0.6",
|
"@types/parcel-env": "^0.0.6",
|
||||||
"@types/express": "*",
|
"@types/express": "*",
|
||||||
"@types/node": "^22.10.1",
|
"@types/node": "^22.10.1",
|
||||||
|
|
@ -37,15 +26,11 @@
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
"concurrently": "^7.3.0",
|
"concurrently": "^7.3.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"parcel": "2.0.0-dev.1793",
|
"parcel": "canary",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"react": "experimental",
|
"react": "experimental",
|
||||||
"react-dom": "experimental",
|
"react-dom": "experimental",
|
||||||
"react-server-dom-parcel": "experimental",
|
"react-server-dom-parcel": "experimental",
|
||||||
"rsc-html-stream": "^0.0.4",
|
"rsc-html-stream": "^0.0.4"
|
||||||
"ws": "^8.8.1"
|
|
||||||
},
|
|
||||||
"@parcel/bundler-default": {
|
|
||||||
"minBundleSize": 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import './client';
|
import './client';
|
||||||
import './Todos.css';
|
import './Todos.css';
|
||||||
import {Resources} from '@parcel/runtime-rsc';
|
|
||||||
import {Dialog} from './Dialog';
|
import {Dialog} from './Dialog';
|
||||||
import {TodoDetail} from './TodoDetail';
|
import {TodoDetail} from './TodoDetail';
|
||||||
import {TodoCreate} from './TodoCreate';
|
import {TodoCreate} from './TodoCreate';
|
||||||
|
|
@ -13,7 +12,6 @@ export async function Todos({id}: {id?: number}) {
|
||||||
<html style={{colorScheme: 'dark light'}}>
|
<html style={{colorScheme: 'dark light'}}>
|
||||||
<head>
|
<head>
|
||||||
<title>Todos</title>
|
<title>Todos</title>
|
||||||
<Resources />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ async function render(
|
||||||
return ReactClient.use(data);
|
return ReactClient.use(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
let htmlStream = await renderHTMLToReadableStream(<Content />);
|
let htmlStream = await renderHTMLToReadableStream(<Content />, {
|
||||||
|
bootstrapScriptContent: (Todos as any).bootstrapScript,
|
||||||
|
});
|
||||||
let response = htmlStream.pipeThrough(injectRSCPayload(s2));
|
let response = htmlStream.pipeThrough(injectRSCPayload(s2));
|
||||||
Readable.fromWeb(response as NodeReadableStream).pipe(res);
|
Readable.fromWeb(response as NodeReadableStream).pipe(res);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -31,6 +31,17 @@ import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryR
|
||||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||||
export type {TemporaryReferenceSet};
|
export type {TemporaryReferenceSet};
|
||||||
|
|
||||||
|
function findSourceMapURL(filename: string, environmentName: string) {
|
||||||
|
const devServer = parcelRequire.meta.devServer;
|
||||||
|
if (devServer != null) {
|
||||||
|
const qs = new URLSearchParams();
|
||||||
|
qs.set('filename', filename);
|
||||||
|
qs.set('env', environmentName);
|
||||||
|
return devServer + '/__parcel_source_map?' + qs.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
type CallServerCallback = <A, T>(id: string, args: A) => Promise<T>;
|
type CallServerCallback = <A, T>(id: string, args: A) => Promise<T>;
|
||||||
|
|
||||||
let callServer: CallServerCallback | null = null;
|
let callServer: CallServerCallback | null = null;
|
||||||
|
|
@ -57,6 +68,9 @@ export function createServerReference<A: Iterable<any>, T>(
|
||||||
return createServerReferenceImpl(
|
return createServerReferenceImpl(
|
||||||
id + '#' + exportName,
|
id + '#' + exportName,
|
||||||
callCurrentServerCallback,
|
callCurrentServerCallback,
|
||||||
|
undefined,
|
||||||
|
findSourceMapURL,
|
||||||
|
exportName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +121,7 @@ export function createFromReadableStream<T>(
|
||||||
options && options.temporaryReferences
|
options && options.temporaryReferences
|
||||||
? options.temporaryReferences
|
? options.temporaryReferences
|
||||||
: undefined,
|
: undefined,
|
||||||
undefined, // TODO: findSourceMapUrl
|
__DEV__ ? findSourceMapURL : undefined,
|
||||||
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
||||||
__DEV__ && options && options.environmentName
|
__DEV__ && options && options.environmentName
|
||||||
? options.environmentName
|
? options.environmentName
|
||||||
|
|
@ -131,7 +145,7 @@ export function createFromFetch<T>(
|
||||||
options && options.temporaryReferences
|
options && options.temporaryReferences
|
||||||
? options.temporaryReferences
|
? options.temporaryReferences
|
||||||
: undefined,
|
: undefined,
|
||||||
undefined, // TODO: findSourceMapUrl
|
__DEV__ ? findSourceMapURL : undefined,
|
||||||
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
__DEV__ ? (options ? options.replayConsoleLogs !== false : true) : false, // defaults to true
|
||||||
__DEV__ && options && options.environmentName
|
__DEV__ && options && options.environmentName
|
||||||
? options.environmentName
|
? options.environmentName
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,17 @@ import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryR
|
||||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||||
export type {TemporaryReferenceSet};
|
export type {TemporaryReferenceSet};
|
||||||
|
|
||||||
|
function findSourceMapURL(filename: string, environmentName: string) {
|
||||||
|
const devServer = parcelRequire.meta.devServer;
|
||||||
|
if (devServer != null) {
|
||||||
|
const qs = new URLSearchParams();
|
||||||
|
qs.set('filename', filename);
|
||||||
|
qs.set('env', environmentName);
|
||||||
|
return devServer + '/__parcel_source_map?' + qs.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function noServerCall() {
|
function noServerCall() {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Server Functions cannot be called during initial render. ' +
|
'Server Functions cannot be called during initial render. ' +
|
||||||
|
|
@ -42,7 +53,13 @@ export function createServerReference<A: Iterable<any>, T>(
|
||||||
id: string,
|
id: string,
|
||||||
exportName: string,
|
exportName: string,
|
||||||
): (...A) => Promise<T> {
|
): (...A) => Promise<T> {
|
||||||
return createServerReferenceImpl(id + '#' + exportName, noServerCall);
|
return createServerReferenceImpl(
|
||||||
|
id + '#' + exportName,
|
||||||
|
noServerCall,
|
||||||
|
undefined,
|
||||||
|
findSourceMapURL,
|
||||||
|
exportName,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type EncodeFormActionCallback = <A>(
|
type EncodeFormActionCallback = <A>(
|
||||||
|
|
@ -69,7 +86,7 @@ function createResponseFromOptions(options?: Options) {
|
||||||
options && options.temporaryReferences
|
options && options.temporaryReferences
|
||||||
? options.temporaryReferences
|
? options.temporaryReferences
|
||||||
: undefined,
|
: undefined,
|
||||||
undefined, // TODO: findSourceMapUrl
|
__DEV__ ? findSourceMapURL : undefined,
|
||||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||||
__DEV__ && options && options.environmentName
|
__DEV__ && options && options.environmentName
|
||||||
? options.environmentName
|
? options.environmentName
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,17 @@ import {
|
||||||
|
|
||||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||||
|
|
||||||
|
function findSourceMapURL(filename: string, environmentName: string) {
|
||||||
|
const devServer = parcelRequire.meta.devServer;
|
||||||
|
if (devServer != null) {
|
||||||
|
const qs = new URLSearchParams();
|
||||||
|
qs.set('filename', filename);
|
||||||
|
qs.set('env', environmentName);
|
||||||
|
return devServer + '/__parcel_source_map?' + qs.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function noServerCall() {
|
function noServerCall() {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Server Functions cannot be called during initial render. ' +
|
'Server Functions cannot be called during initial render. ' +
|
||||||
|
|
@ -33,7 +44,13 @@ export function createServerReference<A: Iterable<any>, T>(
|
||||||
id: string,
|
id: string,
|
||||||
exportName: string,
|
exportName: string,
|
||||||
): (...A) => Promise<T> {
|
): (...A) => Promise<T> {
|
||||||
return createServerReferenceImpl(id + '#' + exportName, noServerCall);
|
return createServerReferenceImpl(
|
||||||
|
id + '#' + exportName,
|
||||||
|
noServerCall,
|
||||||
|
undefined,
|
||||||
|
findSourceMapURL,
|
||||||
|
exportName,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type EncodeFormActionCallback = <A>(
|
type EncodeFormActionCallback = <A>(
|
||||||
|
|
@ -60,7 +77,7 @@ export function createFromNodeStream<T>(
|
||||||
options ? options.encodeFormAction : undefined,
|
options ? options.encodeFormAction : undefined,
|
||||||
options && typeof options.nonce === 'string' ? options.nonce : undefined,
|
options && typeof options.nonce === 'string' ? options.nonce : undefined,
|
||||||
undefined, // TODO: If encodeReply is supported, this should support temporaryReferences
|
undefined, // TODO: If encodeReply is supported, this should support temporaryReferences
|
||||||
undefined, // TODO: findSourceMapUrl
|
__DEV__ ? findSourceMapURL : undefined,
|
||||||
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
__DEV__ && options ? options.replayConsoleLogs === true : false, // defaults to false
|
||||||
__DEV__ && options && options.environmentName
|
__DEV__ && options && options.environmentName
|
||||||
? options.environmentName
|
? options.environmentName
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ declare var parcelRequire: {
|
||||||
extendImportMap: (importMap: {[string]: string}) => void,
|
extendImportMap: (importMap: {[string]: string}) => void,
|
||||||
meta: {
|
meta: {
|
||||||
publicUrl: string,
|
publicUrl: string,
|
||||||
|
devServer: string | null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user