mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[Flight ESM] Wire up Source Maps in the flight-esm fixture (#30758)
Same as #29708 but for the flight-esm fixture.
This commit is contained in:
parent
7a3fcc9898
commit
e483df4658
|
|
@ -131,6 +131,45 @@ app.use(
|
|||
express.static('node_modules/react-server-dom-esm/esm')
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
app.get('/source-maps', async function (req, res, next) {
|
||||
// Proxy the request to the regional server.
|
||||
const proxiedHeaders = {
|
||||
'X-Forwarded-Host': req.hostname,
|
||||
'X-Forwarded-For': req.ips,
|
||||
'X-Forwarded-Port': 3000,
|
||||
'X-Forwarded-Proto': req.protocol,
|
||||
};
|
||||
|
||||
const promiseForData = request(
|
||||
{
|
||||
host: '127.0.0.1',
|
||||
port: 3001,
|
||||
method: req.method,
|
||||
path: req.originalUrl,
|
||||
headers: proxiedHeaders,
|
||||
},
|
||||
req
|
||||
);
|
||||
|
||||
try {
|
||||
const rscResponse = await promiseForData;
|
||||
res.set('Content-type', 'application/json');
|
||||
rscResponse.on('data', data => {
|
||||
res.write(data);
|
||||
res.flush();
|
||||
});
|
||||
rscResponse.on('end', data => {
|
||||
res.end();
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Failed to proxy request: ${e.stack}`);
|
||||
res.statusCode = 500;
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Global Fizz/Webpack Server listening on port 3000...');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ const app = express();
|
|||
const compress = require('compression');
|
||||
const {Readable} = require('node:stream');
|
||||
|
||||
const nodeModule = require('node:module');
|
||||
|
||||
app.use(compress());
|
||||
|
||||
// Application
|
||||
|
|
@ -116,6 +118,88 @@ app.get('/todos', function (req, res) {
|
|||
]);
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const rootDir = path.resolve(__dirname, '../');
|
||||
|
||||
app.get('/source-maps', async function (req, res, next) {
|
||||
try {
|
||||
res.set('Content-type', 'application/json');
|
||||
let requestedFilePath = req.query.name;
|
||||
|
||||
let isCompiledOutput = false;
|
||||
if (requestedFilePath.startsWith('file://')) {
|
||||
// We assume that if it was prefixed with file:// it's referring to the compiled output
|
||||
// and if it's a direct file path we assume it's source mapped back to original format.
|
||||
isCompiledOutput = true;
|
||||
requestedFilePath = url.fileURLToPath(requestedFilePath);
|
||||
}
|
||||
|
||||
const relativePath = path.relative(rootDir, requestedFilePath);
|
||||
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
||||
// This is outside the root directory of the app. Forbid it to be served.
|
||||
res.status = 403;
|
||||
res.write('{}');
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceMap = nodeModule.findSourceMap(requestedFilePath);
|
||||
let map;
|
||||
if (requestedFilePath.startsWith('node:')) {
|
||||
// This is a node internal. We don't include any source code for this but we still
|
||||
// generate a source map for it so that we can add it to an ignoreList automatically.
|
||||
map = {
|
||||
version: 3,
|
||||
// We use the node:// protocol convention to teach Chrome DevTools that this is
|
||||
// on a different protocol and not part of the current page.
|
||||
sources: ['node:///' + requestedFilePath.slice(5)],
|
||||
sourcesContent: ['// Node Internals'],
|
||||
mappings: 'AAAA',
|
||||
ignoreList: [0],
|
||||
sourceRoot: '',
|
||||
};
|
||||
} else if (!sourceMap || !isCompiledOutput) {
|
||||
// If a file doesn't have a source map, such as this file, then we generate a blank
|
||||
// source map that just contains the original content and segments pointing to the
|
||||
// original lines. If a line number points to uncompiled output, like if source mapping
|
||||
// was already applied we also use this path.
|
||||
const sourceContent = await readFile(requestedFilePath, 'utf8');
|
||||
const lines = sourceContent.split('\n').length;
|
||||
// We ensure to absolute
|
||||
const sourceURL = url.pathToFileURL(requestedFilePath);
|
||||
map = {
|
||||
version: 3,
|
||||
sources: [sourceURL],
|
||||
sourcesContent: [sourceContent],
|
||||
// Note: This approach to mapping each line only lets you jump to each line
|
||||
// not jump to a column within a line. To do that, you need a proper source map
|
||||
// generated for each parsed segment or add a segment for each column.
|
||||
mappings: 'AAAA' + ';AACA'.repeat(lines - 1),
|
||||
sourceRoot: '',
|
||||
// Add any node_modules to the ignore list automatically.
|
||||
ignoreList: requestedFilePath.includes('node_modules')
|
||||
? [0]
|
||||
: undefined,
|
||||
};
|
||||
} else {
|
||||
// We always set prepareStackTrace before reading the stack so that we get the stack
|
||||
// without source maps applied. Therefore we have to use the original source map.
|
||||
// If something read .stack before we did, we might observe the line/column after
|
||||
// source mapping back to the original file. We use the isCompiledOutput check above
|
||||
// in that case.
|
||||
map = sourceMap.payload;
|
||||
}
|
||||
res.write(JSON.stringify(map));
|
||||
res.end();
|
||||
} catch (x) {
|
||||
res.status = 500;
|
||||
res.write('{}');
|
||||
res.end();
|
||||
console.error(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
app.listen(3001, () => {
|
||||
console.log('Regional Flight Server listening on port 3001...');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@ import ReactDOM from 'react-dom/client';
|
|||
import {createFromFetch, encodeReply} from 'react-server-dom-esm/client';
|
||||
|
||||
const moduleBaseURL = '/src/';
|
||||
|
||||
function findSourceMapURL(fileName) {
|
||||
return (
|
||||
document.location.origin +
|
||||
'/source-maps?name=' +
|
||||
encodeURIComponent(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
let updateRoot;
|
||||
async function callServer(id, args) {
|
||||
const response = fetch('/', {
|
||||
|
|
@ -17,6 +26,7 @@ async function callServer(id, args) {
|
|||
const {returnValue, root} = await createFromFetch(response, {
|
||||
callServer,
|
||||
moduleBaseURL,
|
||||
findSourceMapURL,
|
||||
});
|
||||
// Refresh the tree with the new RSC payload.
|
||||
startTransition(() => {
|
||||
|
|
@ -34,6 +44,7 @@ let data = createFromFetch(
|
|||
{
|
||||
callServer,
|
||||
moduleBaseURL,
|
||||
findSourceMapURL,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ import {createFromFetch, encodeReply} from 'react-server-dom-webpack/client';
|
|||
// TODO: This should be a dependency of the App but we haven't implemented CSS in Node yet.
|
||||
import './style.css';
|
||||
|
||||
function findSourceMapURL(fileName) {
|
||||
return (
|
||||
document.location.origin +
|
||||
'/source-maps?name=' +
|
||||
encodeURIComponent(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
let updateRoot;
|
||||
async function callServer(id, args) {
|
||||
const response = fetch('/', {
|
||||
|
|
@ -16,7 +24,10 @@ async function callServer(id, args) {
|
|||
},
|
||||
body: await encodeReply(args),
|
||||
});
|
||||
const {returnValue, root} = await createFromFetch(response, {callServer});
|
||||
const {returnValue, root} = await createFromFetch(response, {
|
||||
callServer,
|
||||
findSourceMapURL,
|
||||
});
|
||||
// Refresh the tree with the new RSC payload.
|
||||
startTransition(() => {
|
||||
updateRoot(root);
|
||||
|
|
@ -39,13 +50,7 @@ async function hydrateApp() {
|
|||
}),
|
||||
{
|
||||
callServer,
|
||||
findSourceMapURL(fileName) {
|
||||
return (
|
||||
document.location.origin +
|
||||
'/source-maps?name=' +
|
||||
encodeURIComponent(fileName)
|
||||
);
|
||||
},
|
||||
findSourceMapURL,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user