All files / apps/web/src/utils ssr.tsx

73.46% Statements 36/49
64.28% Branches 9/14
78.57% Functions 11/14
71.73% Lines 33/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127                              332x   332x       604x 604x     604x             332x     604x         332x       39x 1x   38x                           332x         204x 24x   180x 180x 566x   20x       332x     8051x   7872x 23484x         8051x   23484x   8051x 8051x 8051x 8051x 1313x 857x                     1313x 2x                             7706x    
import type React from "react";
 
import type { QueryKey } from "@tanstack/react-query";
import { useQueryClient } from "@tanstack/react-query";
import { useRouterState } from "@tanstack/react-router";
import { TRPCClientError } from "@trpc/client";
import type { ResolverDef, TRPCQueryOptions } from "@trpc/tanstack-react-query";
import { isNonNullish } from "remeda";
 
import { useBooleanState } from "~app/hooks/use-boolean-state";
import { useMountEffect } from "~app/hooks/use-mount-effect";
import { transformer } from "~utils/transformer";
import type { RouterContext } from "~web/pages/__root";
 
// see https://github.com/TanStack/router/issues/4084
const ERROR_TAG = "__error__";
 
const getPrefetchPromise = async (
	ctx: PrefetchContext,
	options: ReturnType<TRPCQueryOptions<ResolverDef>>,
) => {
	try {
		const query = (await ctx.context.queryClient.fetchQuery(
			options,
		)) as unknown;
		return transformer.serialize(query);
	} catch (error) {
		return { [ERROR_TAG]: true, message: String(error) };
	}
};
 
type PrefetchContext = { context: RouterContext };
const prefetchQuery = (
	ctx: PrefetchContext,
	options: ReturnType<TRPCQueryOptions<ResolverDef>>,
) => ({
	queryKey: options.queryKey,
	promise: getPrefetchPromise(ctx, options),
});
 
export const prefetchQueries = (
	ctx: PrefetchContext,
	...optionsSet: ReturnType<TRPCQueryOptions<ResolverDef>>[]
) => {
	Eif (!import.meta.env.SSR) {
		return [];
	}
	return optionsSet.map((options) => prefetchQuery(ctx, options));
};
 
class SerializedTRPCError extends Error {
	public queryKey: QueryKey;
	public errorObject: Error;
	public constructor(queryKey: QueryKey, errorObject: Error) {
		super(errorObject.message);
		this.name = "SerializedTRPCError";
		this.queryKey = queryKey;
		this.errorObject = errorObject;
	}
}
 
export const prefetchQueriesWith = async <T,>(
	ctx: PrefetchContext,
	getPromise: () => Promise<T>,
	getOptions: (result: T) => ReturnType<TRPCQueryOptions<ResolverDef>>[],
) => {
	Eif (!import.meta.env.SSR) {
		return [];
	}
	try {
		const result = await getPromise();
		return getOptions(result).map((options) => prefetchQuery(ctx, options));
	} catch {
		return [];
	}
};
 
export const HydrationBoundary: React.FC<React.PropsWithChildren> = ({
	children,
}) => {
	const prefetchedQueries = useRouterState({
		select: (state) =>
			state.matches.flatMap((match) =>
				typeof match.loaderData === "object" && "prefetched" in match.loaderData
					? match.loaderData.prefetched
					: [],
			),
	});
	const matchErrors = useRouterState({
		select: (state) =>
			state.matches.map((match) => match.error).filter(isNonNullish),
	});
	const localQueryClient = useQueryClient();
	const [isMounted, { setTrue: setMounted }] = useBooleanState();
	useMountEffect(setMounted);
	if (!isMounted) {
		for (const query of prefetchedQueries) {
			void localQueryClient.prefetchQuery({
				queryKey: query.queryKey,
				queryFn: async () => {
					const result = await query.promise;
					if (ERROR_TAG in result) {
						throw new TRPCClientError(result.message);
					}
					return transformer.deserialize(result);
				},
			});
		}
		for (const error of matchErrors) {
			Iif ("errorObject" in error) {
				const castedError = error as SerializedTRPCError;
				const serializedError = new SerializedTRPCError(
					castedError.queryKey,
					castedError.errorObject,
				);
				void localQueryClient.prefetchQuery({
					queryKey: serializedError.queryKey,
					queryFn: () => {
						throw serializedError.errorObject;
					},
				});
			}
		}
	}
	return <>{children}</>;
};