diff --git a/packages/remix-cloudflare/index.ts b/packages/remix-cloudflare/index.ts index a21c55b7516..d1463dbcaf7 100644 --- a/packages/remix-cloudflare/index.ts +++ b/packages/remix-cloudflare/index.ts @@ -13,7 +13,9 @@ export { createRequestHandler, createSession, unstable_defineLoader, + unstable_defineClientLoader, unstable_defineAction, + unstable_defineClientAction, defer, broadcastDevReady, logDevReady, diff --git a/packages/remix-deno/index.ts b/packages/remix-deno/index.ts index bd91abef915..160c21d9d50 100644 --- a/packages/remix-deno/index.ts +++ b/packages/remix-deno/index.ts @@ -17,7 +17,9 @@ export { broadcastDevReady, createSession, unstable_defineLoader, + unstable_defineClientLoader, unstable_defineAction, + unstable_defineClientAction, defer, isCookie, isSession, diff --git a/packages/remix-node/index.ts b/packages/remix-node/index.ts index eaf64599f3c..e1587d6dcbc 100644 --- a/packages/remix-node/index.ts +++ b/packages/remix-node/index.ts @@ -25,7 +25,9 @@ export { createRequestHandler, createSession, unstable_defineLoader, + unstable_defineClientLoader, unstable_defineAction, + unstable_defineClientAction, defer, broadcastDevReady, logDevReady, diff --git a/packages/remix-react/future/single-fetch.d.ts b/packages/remix-react/future/single-fetch.d.ts index d5be4e5e110..fa37a8a338b 100644 --- a/packages/remix-react/future/single-fetch.d.ts +++ b/packages/remix-react/future/single-fetch.d.ts @@ -1,26 +1,14 @@ import type { MetaArgs, UIMatch, UNSAFE_MetaMatch } from "@remix-run/react"; import type { - Loader, - Action, - SerializeFrom, - TypedDeferredData, - TypedResponse, + unstable_Loader as Loader, + unstable_Action as Action, + unstable_Serialize as Serialize, } from "@remix-run/server-runtime"; import type { useFetcher as useFetcherRR, FetcherWithComponents, } from "react-router-dom"; -// Backwards-compatible type for Remix v2 where json/defer still use the old types, -// and only non-json/defer returns use the new types. This allows for incremental -// migration of loaders to return naked objects. In the next major version, -// json/defer will be removed so everything will use the new simplified typings. -// prettier-ignore -type Serialize<T extends Loader | Action> = - Awaited<ReturnType<T>> extends TypedDeferredData<infer D> ? D : - Awaited<ReturnType<T>> extends TypedResponse<Record<string, unknown>> ? SerializeFrom<T> : - Awaited<ReturnType<T>>; - declare module "@remix-run/react" { export function useLoaderData<T extends Loader>(): Serialize<T>; diff --git a/packages/remix-server-runtime/index.ts b/packages/remix-server-runtime/index.ts index ac614c95313..2840bdc6a33 100644 --- a/packages/remix-server-runtime/index.ts +++ b/packages/remix-server-runtime/index.ts @@ -5,13 +5,22 @@ export { parseMultipartFormData as unstable_parseMultipartFormData, } from "./formData"; export { defer, json, redirect, redirectDocument } from "./responses"; + +export { + SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, + defineLoader as unstable_defineLoader, + defineClientLoader as unstable_defineClientLoader, + defineAction as unstable_defineAction, + defineClientAction as unstable_defineClientAction, +} from "./single-fetch"; export type { - Loader, - Action, + Loader as unstable_Loader, + Action as unstable_Action, + Serialize as unstable_Serialize, SingleFetchResult as UNSAFE_SingleFetchResult, SingleFetchResults as UNSAFE_SingleFetchResults, } from "./single-fetch"; -export { SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol } from "./single-fetch"; + export { createRequestHandler } from "./server"; export { createSession, @@ -87,7 +96,3 @@ export type { UploadHandler, UploadHandlerPart, } from "./reexport"; -export { - defineLoader as unstable_defineLoader, - defineAction as unstable_defineAction, -} from "./reexport"; diff --git a/packages/remix-server-runtime/reexport.ts b/packages/remix-server-runtime/reexport.ts index 2a0316600b5..cc7fc46f507 100644 --- a/packages/remix-server-runtime/reexport.ts +++ b/packages/remix-server-runtime/reexport.ts @@ -61,5 +61,3 @@ export type { SessionStorage, FlashSessionData, } from "./sessions"; - -export { defineLoader, defineAction } from "./single-fetch"; diff --git a/packages/remix-server-runtime/single-fetch.ts b/packages/remix-server-runtime/single-fetch.ts index 3104eb62b4f..1ba3e2bb771 100644 --- a/packages/remix-server-runtime/single-fetch.ts +++ b/packages/remix-server-runtime/single-fetch.ts @@ -23,6 +23,7 @@ import type { import { ResponseStubOperationsSymbol } from "./routeModules"; import type { TypedDeferredData, TypedResponse } from "./responses"; import { isDeferredData, isRedirectStatusCode, isResponse } from "./responses"; +import type { SerializeFrom } from "./serialize"; export const SingleFetchRedirectSymbol = Symbol("SingleFetchRedirect"); const ResponseStubActionSymbol = Symbol("ResponseStubAction"); @@ -533,25 +534,50 @@ type DataFunctionReturnValue = | TypedDeferredData<Record<string, unknown>> | TypedResponse<Record<string, unknown>>; +// Backwards-compatible type for Remix v2 where json/defer still use the old types, +// and only non-json/defer returns use the new types. This allows for incremental +// migration of loaders to return naked objects. In the next major version, +// json/defer will be removed so everything will use the new simplified typings. +// prettier-ignore +export type Serialize<T extends Loader | ClientLoader | Action | ClientAction> = + Awaited<ReturnType<T>> extends TypedDeferredData<infer D> ? D : + Awaited<ReturnType<T>> extends TypedResponse<Record<string, unknown>> ? SerializeFrom<T> : + Awaited<ReturnType<T>>; + +// loader type LoaderArgs = RRLoaderArgs<AppLoadContext> & { // Context is always provided in Remix, and typed for module augmentation support. context: AppLoadContext; response: ResponseStub; }; - export type Loader = ( args: LoaderArgs ) => MaybePromise<DataFunctionReturnValue>; +export let defineLoader = <T extends Loader>(loader: T): T => loader; +// clientLoader +type ClientLoaderArgs = RRLoaderArgs<undefined> & { + serverLoader: <T extends Loader>() => Promise<Serialize<T>>; +}; +type ClientLoader = (args: ClientLoaderArgs) => MaybePromise<Serializable>; +export let defineClientLoader = <T extends ClientLoader>(clientLoader: T): T => + clientLoader; + +// action type ActionArgs = RRActionArgs<AppLoadContext> & { // Context is always provided in Remix, and typed for module augmentation support. context: AppLoadContext; response: ResponseStub; }; - export type Action = ( args: ActionArgs ) => MaybePromise<DataFunctionReturnValue>; - -export let defineLoader = <T extends Loader>(loader: T): T => loader; export let defineAction = <T extends Action>(action: T): T => action; + +// clientAction +type ClientActionArgs = RRActionArgs<undefined> & { + serverAction: <T extends Action>() => Promise<Serialize<T>>; +}; +type ClientAction = (args: ClientActionArgs) => MaybePromise<Serializable>; +export let defineClientAction = <T extends ClientAction>(clientAction: T): T => + clientAction;