diff --git a/packages/remix-react/future/single-fetch.d.ts b/packages/remix-react/future/single-fetch.d.ts index ddef3d7098c..3436e077de1 100644 --- a/packages/remix-react/future/single-fetch.d.ts +++ b/packages/remix-react/future/single-fetch.d.ts @@ -1,7 +1,7 @@ import type { MetaArgs, UIMatch, UNSAFE_MetaMatch } from "@remix-run/react"; import type { - LoaderFunctionArgs, - ActionFunctionArgs, + Loader, + Action, SerializeFrom, TypedDeferredData, TypedResponse, @@ -11,90 +11,52 @@ import type { FetcherWithComponents, } from "react-router-dom"; -type Serializable = - | undefined - | null - | boolean - | string - | symbol - | number - | Array - | { [key: PropertyKey]: Serializable } - | bigint - | Date - | URL - | RegExp - | Error - | Map - | Set - | Promise; - -type DataFunctionReturnValue = - | Serializable - | TypedDeferredData> - | TypedResponse>; - -type LoaderFunction_SingleFetch = ( - args: LoaderFunctionArgs -) => Promise | DataFunctionReturnValue; -type ActionFunction_SingleFetch = ( - args: ActionFunctionArgs -) => Promise | DataFunctionReturnValue; - // 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 SingleFetchSerialize_V2 = +type Serialize = Awaited> extends TypedDeferredData ? D : Awaited> extends TypedResponse> ? SerializeFrom : Awaited>; declare module "@remix-run/react" { - export function useLoaderData(): T extends LoaderFunction_SingleFetch - ? SingleFetchSerialize_V2 - : never; + export function useLoaderData(): T extends Loader ? Serialize : T; - export function useActionData(): T extends ActionFunction_SingleFetch - ? SingleFetchSerialize_V2 | undefined - : never; + export function useActionData(): T extends Action + ? Serialize | undefined + : T; export function useRouteLoaderData( routeId: string - ): T extends LoaderFunction_SingleFetch ? SingleFetchSerialize_V2 : never; + ): T extends Loader ? Serialize : never; export function useFetcher( opts?: Parameters[0] ): FetcherWithComponents< - TData extends LoaderFunction_SingleFetch | ActionFunction_SingleFetch - ? SingleFetchSerialize_V2 - : never + TData extends Loader | Action ? Serialize : TData >; export type UIMatch_SingleFetch = Omit< UIMatch, "data" > & { - data: D extends LoaderFunction_SingleFetch - ? SingleFetchSerialize_V2 - : never; + data: D extends Loader ? Serialize : never; }; interface MetaMatch_SingleFetch< RouteId extends string = string, - Loader extends LoaderFunction_SingleFetch | unknown = unknown - > extends Omit, "data"> { - data: Loader extends LoaderFunction_SingleFetch - ? SingleFetchSerialize_V2 - : unknown; + L extends Loader | unknown = unknown + > extends Omit, "data"> { + data: L extends Loader ? Serialize : unknown; } type MetaMatches_SingleFetch< - MatchLoaders extends Record< + MatchLoaders extends Record = Record< string, - LoaderFunction_SingleFetch | unknown - > = Record + unknown + > > = Array< { [K in keyof MatchLoaders]: MetaMatch_SingleFetch< @@ -105,17 +67,13 @@ declare module "@remix-run/react" { >; export interface MetaArgs_SingleFetch< - Loader extends LoaderFunction_SingleFetch | unknown = unknown, - MatchLoaders extends Record< + L extends Loader | unknown = unknown, + MatchLoaders extends Record = Record< string, - LoaderFunction_SingleFetch | unknown - > = Record - > extends Omit, "data" | "matches"> { - data: - | (Loader extends LoaderFunction_SingleFetch - ? SingleFetchSerialize_V2 - : unknown) - | undefined; + unknown + > + > extends Omit, "data" | "matches"> { + data: (L extends Loader ? Serialize : unknown) | undefined; matches: MetaMatches_SingleFetch; } } diff --git a/packages/remix-server-runtime/index.ts b/packages/remix-server-runtime/index.ts index 7744129cdb7..5ce73bb3d57 100644 --- a/packages/remix-server-runtime/index.ts +++ b/packages/remix-server-runtime/index.ts @@ -6,6 +6,8 @@ export { } from "./formData"; export { defer, json, redirect, redirectDocument } from "./responses"; export type { + Loader, + Action, SingleFetchResult as UNSAFE_SingleFetchResult, SingleFetchResults as UNSAFE_SingleFetchResults, } from "./single-fetch"; diff --git a/packages/remix-server-runtime/single-fetch.ts b/packages/remix-server-runtime/single-fetch.ts index bdfe745f7fc..fb37dccffcb 100644 --- a/packages/remix-server-runtime/single-fetch.ts +++ b/packages/remix-server-runtime/single-fetch.ts @@ -1,4 +1,6 @@ import type { + ActionFunctionArgs as RRActionArgs, + LoaderFunctionArgs as RRLoaderArgs, StaticHandler, unstable_DataStrategyFunctionArgs as DataStrategyFunctionArgs, unstable_DataStrategyFunction as DataStrategyFunction, @@ -19,6 +21,7 @@ import type { ResponseStubOperation, } from "./routeModules"; import { ResponseStubOperationsSymbol } from "./routeModules"; +import type { TypedDeferredData, TypedResponse } from "./responses"; import { isDeferredData, isRedirectStatusCode, isResponse } from "./responses"; export const SingleFetchRedirectSymbol = Symbol("SingleFetchRedirect"); @@ -504,3 +507,51 @@ export function encodeViaTurboStream( ], }); } + +type MaybePromise = T | Promise; + +type Serializable = + | undefined + | null + | boolean + | string + | symbol + | number + | Array + | { [key: PropertyKey]: Serializable } + | bigint + | Date + | URL + | RegExp + | Error + | Map + | Set + | Promise; + +type DataFunctionReturnValue = + | Serializable + | TypedDeferredData> + | TypedResponse>; + +type LoaderArgs = RRLoaderArgs & { + // Context is always provided in Remix, and typed for module augmentation support. + context: AppLoadContext; + response: ResponseStub; +}; + +export type Loader = ( + args: LoaderArgs +) => MaybePromise; + +type ActionArgs = RRActionArgs & { + // Context is always provided in Remix, and typed for module augmentation support. + context: AppLoadContext; + response: ResponseStub; +}; + +export type Action = ( + args: ActionArgs +) => MaybePromise; + +export let defineLoader = (loader: T): T => loader; +export let defineAction = (action: T): T => action;