forked from calcom/cal.com
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: migrate trpc, ssgInit, ssrInit
- Loading branch information
1 parent
6c4b115
commit c4565da
Showing
48 changed files
with
402 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
// originally from in the "experimental playground for tRPC + next.js 13" repo owned by trpc team | ||
// file link: https://github.com/trpc/next-13/blob/main/%40trpc/next-layout/createTRPCNextLayout.ts | ||
// repo link: https://github.com/trpc/next-13 | ||
// code is / will continue to be adapted for our usage | ||
import { dehydrate, QueryClient } from "@tanstack/query-core"; | ||
import type { DehydratedState, QueryKey } from "@tanstack/react-query"; | ||
|
||
import type { Maybe, TRPCClientError, TRPCClientErrorLike } from "@calcom/trpc"; | ||
import { | ||
callProcedure, | ||
type AnyProcedure, | ||
type AnyQueryProcedure, | ||
type AnyRouter, | ||
type DataTransformer, | ||
type inferProcedureInput, | ||
type inferProcedureOutput, | ||
type inferRouterContext, | ||
type MaybePromise, | ||
type ProcedureRouterRecord, | ||
} from "@calcom/trpc/server"; | ||
|
||
import { createRecursiveProxy, createFlatProxy } from "@trpc/server/shared"; | ||
|
||
export function getArrayQueryKey( | ||
queryKey: string | [string] | [string, ...unknown[]] | unknown[], | ||
type: string | ||
): QueryKey { | ||
const queryKeyArrayed = Array.isArray(queryKey) ? queryKey : [queryKey]; | ||
const [arrayPath, input] = queryKeyArrayed; | ||
|
||
if (!input && (!type || type === "any")) { | ||
return arrayPath.length ? [arrayPath] : ([] as unknown as QueryKey); | ||
} | ||
|
||
return [ | ||
arrayPath, | ||
{ | ||
...(typeof input !== "undefined" && { input: input }), | ||
...(type && type !== "any" && { type: type }), | ||
}, | ||
]; | ||
} | ||
|
||
// copy starts | ||
// copied from trpc/trpc repo | ||
// ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L37-#L58 | ||
function transformQueryOrMutationCacheErrors< | ||
TState extends DehydratedState["queries"][0] | DehydratedState["mutations"][0] | ||
>(result: TState): TState { | ||
const error = result.state.error as Maybe<TRPCClientError<any>>; | ||
if (error instanceof Error && error.name === "TRPCClientError") { | ||
const newError: TRPCClientErrorLike<any> = { | ||
message: error.message, | ||
data: error.data, | ||
shape: error.shape, | ||
}; | ||
return { | ||
...result, | ||
state: { | ||
...result.state, | ||
error: newError, | ||
}, | ||
}; | ||
} | ||
return result; | ||
} | ||
// copy ends | ||
|
||
interface CreateTRPCNextLayoutOptions<TRouter extends AnyRouter> { | ||
router: TRouter; | ||
createContext: () => MaybePromise<inferRouterContext<TRouter>>; | ||
transformer?: DataTransformer; | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export type DecorateProcedure<TProcedure extends AnyProcedure> = TProcedure extends AnyQueryProcedure | ||
? { | ||
fetch(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>; | ||
fetchInfinite(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>; | ||
prefetch(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>; | ||
prefetchInfinite(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>; | ||
} | ||
: never; | ||
|
||
type OmitNever<TType> = Pick< | ||
TType, | ||
{ | ||
[K in keyof TType]: TType[K] extends never ? never : K; | ||
}[keyof TType] | ||
>; | ||
/** | ||
* @internal | ||
*/ | ||
export type DecoratedProcedureRecord< | ||
TProcedures extends ProcedureRouterRecord, | ||
TPath extends string = "" | ||
> = OmitNever<{ | ||
[TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter | ||
? DecoratedProcedureRecord<TProcedures[TKey]["_def"]["record"], `${TPath}${TKey & string}.`> | ||
: TProcedures[TKey] extends AnyQueryProcedure | ||
? DecorateProcedure<TProcedures[TKey]> | ||
: never; | ||
}>; | ||
|
||
type CreateTRPCNextLayout<TRouter extends AnyRouter> = DecoratedProcedureRecord<TRouter["_def"]["record"]> & { | ||
dehydrate(): Promise<DehydratedState>; | ||
queryClient: QueryClient; | ||
}; | ||
|
||
const getStateContainer = <TRouter extends AnyRouter>(opts: CreateTRPCNextLayoutOptions<TRouter>) => { | ||
let _trpc: { | ||
queryClient: QueryClient; | ||
context: inferRouterContext<TRouter>; | ||
} | null = null; | ||
|
||
return () => { | ||
if (_trpc === null) { | ||
_trpc = { | ||
context: opts.createContext(), | ||
queryClient: new QueryClient(), | ||
}; | ||
} | ||
|
||
return _trpc; | ||
}; | ||
}; | ||
|
||
export function createTRPCNextLayout<TRouter extends AnyRouter>( | ||
opts: CreateTRPCNextLayoutOptions<TRouter> | ||
): CreateTRPCNextLayout<TRouter> { | ||
const getState = getStateContainer(opts); | ||
|
||
const transformer = opts.transformer ?? { | ||
serialize: (v) => v, | ||
deserialize: (v) => v, | ||
}; | ||
|
||
return createFlatProxy((key) => { | ||
const state = getState(); | ||
const { queryClient } = state; | ||
if (key === "queryClient") { | ||
return queryClient; | ||
} | ||
|
||
if (key === "dehydrate") { | ||
// copy starts | ||
// copied from trpc/trpc repo | ||
// ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L214-#L229 | ||
const dehydratedCache = dehydrate(queryClient, { | ||
shouldDehydrateQuery() { | ||
// makes sure errors are also dehydrated | ||
return true; | ||
}, | ||
}); | ||
|
||
// since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects | ||
const dehydratedCacheWithErrors = { | ||
...dehydratedCache, | ||
queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors), | ||
mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors), | ||
}; | ||
|
||
return () => transformer.serialize(dehydratedCacheWithErrors); | ||
} | ||
// copy ends | ||
|
||
return createRecursiveProxy(async (callOpts) => { | ||
const path = [key, ...callOpts.path]; | ||
const utilName = path.pop(); | ||
const ctx = await state.context; | ||
|
||
const caller = opts.router.createCaller(ctx); | ||
|
||
const pathStr = path.join("."); | ||
const input = callOpts.args[0]; | ||
|
||
if (utilName === "fetchInfinite") { | ||
return queryClient.fetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () => | ||
caller.query(pathStr, input) | ||
); | ||
} | ||
|
||
if (utilName === "prefetch") { | ||
return queryClient.prefetchQuery({ | ||
queryKey: getArrayQueryKey([path, input], "query"), | ||
queryFn: async () => { | ||
const res = await callProcedure({ | ||
procedures: opts.router._def.procedures, | ||
path: pathStr, | ||
rawInput: input, | ||
ctx, | ||
type: "query", | ||
}); | ||
return res; | ||
}, | ||
}); | ||
} | ||
|
||
if (utilName === "prefetchInfinite") { | ||
return queryClient.prefetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () => | ||
caller.query(pathStr, input) | ||
); | ||
} | ||
|
||
return queryClient.fetchQuery(getArrayQueryKey([path, input], "query"), () => | ||
caller.query(pathStr, input) | ||
); | ||
}) as CreateTRPCNextLayout<TRouter>; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { appRouter } from "@calcom/trpc/server/routers/_app"; | ||
|
||
export const serverClient = appRouter.createCaller({}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { serverSideTranslations } from "next-i18next/serverSideTranslations"; | ||
import { headers } from "next/headers"; | ||
import superjson from "superjson"; | ||
|
||
import { CALCOM_VERSION } from "@calcom/lib/constants"; | ||
import prisma from "@calcom/prisma"; | ||
import { appRouter } from "@calcom/trpc/server/routers/_app"; | ||
|
||
import { createTRPCNextLayout } from "./createTRPCNextLayout"; | ||
|
||
export async function ssgInit() { | ||
const locale = headers().get("x-locale") ?? "en"; | ||
|
||
const i18n = (await serverSideTranslations(locale, ["common"])) || "en"; | ||
|
||
const ssg = createTRPCNextLayout({ | ||
router: appRouter, | ||
transformer: superjson, | ||
createContext() { | ||
return { prisma, session: null, locale, i18n }; | ||
}, | ||
}); | ||
|
||
// i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch | ||
// we can set query data directly to the queryClient | ||
const queryKey = [ | ||
["viewer", "public", "i18n"], | ||
{ input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" }, | ||
]; | ||
|
||
ssg.queryClient.setQueryData(queryKey, { i18n }); | ||
|
||
return ssg; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { type GetServerSidePropsContext } from "next"; | ||
import { serverSideTranslations } from "next-i18next/serverSideTranslations"; | ||
import { headers, cookies } from "next/headers"; | ||
import superjson from "superjson"; | ||
|
||
import { getLocale } from "@calcom/features/auth/lib/getLocale"; | ||
import { CALCOM_VERSION } from "@calcom/lib/constants"; | ||
import prisma from "@calcom/prisma"; | ||
import { appRouter } from "@calcom/trpc/server/routers/_app"; | ||
|
||
import { createTRPCNextLayout } from "./createTRPCNextLayout"; | ||
|
||
export async function ssrInit(options?: { noI18nPreload: boolean }) { | ||
const req = { | ||
headers: headers(), | ||
cookies: cookies(), | ||
}; | ||
|
||
const locale = await getLocale(req); | ||
|
||
const i18n = (await serverSideTranslations(locale, ["common", "vital"])) || "en"; | ||
|
||
const ssr = createTRPCNextLayout({ | ||
router: appRouter, | ||
transformer: superjson, | ||
createContext() { | ||
return { prisma, session: null, locale, i18n, req: req as unknown as GetServerSidePropsContext["req"] }; | ||
}, | ||
}); | ||
|
||
// i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch | ||
// we can set query data directly to the queryClient | ||
const queryKey = [ | ||
["viewer", "public", "i18n"], | ||
{ input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" }, | ||
]; | ||
if (!options?.noI18nPreload) { | ||
ssr.queryClient.setQueryData(queryKey, { i18n }); | ||
} | ||
|
||
await Promise.allSettled([ | ||
// So feature flags are available on first render | ||
ssr.viewer.features.map.prefetch(), | ||
// Provides a better UX to the users who have already upgraded. | ||
ssr.viewer.teams.hasTeamPlan.prefetch(), | ||
ssr.viewer.public.session.prefetch(), | ||
]); | ||
|
||
return ssr; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ | |
```ts | ||
import { renderEmail } from "@calcom/emails"; | ||
|
||
renderEmail("TeamInviteEmail", */{ | ||
await renderEmail("TeamInviteEmail", */{ | ||
language: t, | ||
from: "[email protected]", | ||
to: "[email protected]", | ||
|
Oops, something went wrong.