102 lines
5.0 KiB
JavaScript
Executable File
102 lines
5.0 KiB
JavaScript
Executable File
import { PAGE_SEGMENT_KEY } from '../shared/lib/segment';
|
|
// TODO: We should only have to export `normalizeFlightData`, however because the initial flight data
|
|
// that gets passed to `createInitialRouterState` doesn't conform to the `FlightDataPath` type (it's missing the root segment)
|
|
// we're currently exporting it so we can use it directly. This should be fixed as part of the unification of
|
|
// the different ways we express `FlightSegmentPath`.
|
|
export function getFlightDataPartsFromPath(flightDataPath) {
|
|
// Pick the last 4 items from the `FlightDataPath` to get the [tree, seedData, viewport, isHeadPartial].
|
|
const flightDataPathLength = 4;
|
|
// tree, seedData, and head are *always* the last three items in the `FlightDataPath`.
|
|
const [tree, seedData, head, isHeadPartial] = flightDataPath.slice(-flightDataPathLength);
|
|
// The `FlightSegmentPath` is everything except the last three items. For a root render, it won't be present.
|
|
const segmentPath = flightDataPath.slice(0, -flightDataPathLength);
|
|
var _segmentPath_;
|
|
return {
|
|
// TODO: Unify these two segment path helpers. We are inconsistently pushing an empty segment ("")
|
|
// to the start of the segment path in some places which makes it hard to use solely the segment path.
|
|
// Look for "// TODO-APP: remove ''" in the codebase.
|
|
pathToSegment: segmentPath.slice(0, -1),
|
|
segmentPath,
|
|
// if the `FlightDataPath` corresponds with the root, there'll be no segment path,
|
|
// in which case we default to ''.
|
|
segment: (_segmentPath_ = segmentPath[segmentPath.length - 1]) != null ? _segmentPath_ : '',
|
|
tree,
|
|
seedData,
|
|
head,
|
|
isHeadPartial,
|
|
isRootRender: flightDataPath.length === flightDataPathLength
|
|
};
|
|
}
|
|
export function getNextFlightSegmentPath(flightSegmentPath) {
|
|
// Since `FlightSegmentPath` is a repeated tuple of `Segment` and `ParallelRouteKey`, we slice off two items
|
|
// to get the next segment path.
|
|
return flightSegmentPath.slice(2);
|
|
}
|
|
export function normalizeFlightData(flightData) {
|
|
// FlightData can be a string when the server didn't respond with a proper flight response,
|
|
// or when a redirect happens, to signal to the client that it needs to perform an MPA navigation.
|
|
if (typeof flightData === 'string') {
|
|
return flightData;
|
|
}
|
|
return flightData.map((flightDataPath)=>getFlightDataPartsFromPath(flightDataPath));
|
|
}
|
|
/**
|
|
* This function is used to prepare the flight router state for the request.
|
|
* It removes markers that are not needed by the server, and are purely used
|
|
* for stashing state on the client.
|
|
* @param flightRouterState - The flight router state to prepare.
|
|
* @param isHmrRefresh - Whether this is an HMR refresh request.
|
|
* @returns The prepared flight router state.
|
|
*/ export function prepareFlightRouterStateForRequest(flightRouterState, isHmrRefresh) {
|
|
// HMR requests need the complete, unmodified state for proper functionality
|
|
if (isHmrRefresh) {
|
|
return encodeURIComponent(JSON.stringify(flightRouterState));
|
|
}
|
|
return encodeURIComponent(JSON.stringify(stripClientOnlyDataFromFlightRouterState(flightRouterState)));
|
|
}
|
|
/**
|
|
* Recursively strips client-only data from FlightRouterState while preserving
|
|
* server-needed information for proper rendering decisions.
|
|
*/ function stripClientOnlyDataFromFlightRouterState(flightRouterState) {
|
|
const [segment, parallelRoutes, _url, refreshMarker, isRootLayout, hasLoadingBoundary] = flightRouterState;
|
|
// __PAGE__ segments are always fetched from the server, so there's
|
|
// no need to send them up
|
|
const cleanedSegment = stripSearchParamsFromPageSegment(segment);
|
|
// Recursively process parallel routes
|
|
const cleanedParallelRoutes = {};
|
|
for (const [key, childState] of Object.entries(parallelRoutes)){
|
|
cleanedParallelRoutes[key] = stripClientOnlyDataFromFlightRouterState(childState);
|
|
}
|
|
const result = [
|
|
cleanedSegment,
|
|
cleanedParallelRoutes,
|
|
null,
|
|
shouldPreserveRefreshMarker(refreshMarker) ? refreshMarker : null
|
|
];
|
|
// Append optional fields if present
|
|
if (isRootLayout !== undefined) {
|
|
result[4] = isRootLayout;
|
|
}
|
|
if (hasLoadingBoundary !== undefined) {
|
|
result[5] = hasLoadingBoundary;
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Strips search parameters from __PAGE__ segments to prevent sensitive
|
|
* client-side data from being sent to the server.
|
|
*/ function stripSearchParamsFromPageSegment(segment) {
|
|
if (typeof segment === 'string' && segment.startsWith(PAGE_SEGMENT_KEY + '?')) {
|
|
return PAGE_SEGMENT_KEY;
|
|
}
|
|
return segment;
|
|
}
|
|
/**
|
|
* Determines whether the refresh marker should be sent to the server
|
|
* Client-only markers like 'refresh' are stripped, while server-needed markers
|
|
* like 'refetch' and 'inside-shared-layout' are preserved.
|
|
*/ function shouldPreserveRefreshMarker(refreshMarker) {
|
|
return Boolean(refreshMarker && refreshMarker !== 'refresh');
|
|
}
|
|
|
|
//# sourceMappingURL=flight-data-helpers.js.map
|