Skip to content

Commit

Permalink
trigger single-fetch types via future interface merging
Browse files Browse the repository at this point in the history
  • Loading branch information
pcattori committed Aug 20, 2024
1 parent c05c97c commit 6a43507
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 8 deletions.
1 change: 1 addition & 0 deletions packages/remix-cloudflare/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
1 change: 1 addition & 0 deletions packages/remix-deno/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HandleErrorFunction,
Expand Down
1 change: 1 addition & 0 deletions packages/remix-node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
1 change: 1 addition & 0 deletions packages/remix-server-runtime/future.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export interface Future {}
2 changes: 1 addition & 1 deletion packages/remix-server-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export {
data as unstable_data,
} from "./single-fetch";
export type {
Serialize as unstable_Serialize,
SingleFetchResult as UNSAFE_SingleFetchResult,
SingleFetchResults as UNSAFE_SingleFetchResults,
} from "./single-fetch";
Expand Down Expand Up @@ -60,6 +59,7 @@ export type {
EntryContext,
ErrorResponse,
FlashSessionData,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
2 changes: 2 additions & 0 deletions packages/remix-server-runtime/reexport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export type { AppLoadContext } from "./data";

export type { EntryContext } from "./entry";

export type { Future } from "./future";

export type {
HtmlLinkDescriptor,
LinkDescriptor,
Expand Down
12 changes: 12 additions & 0 deletions packages/remix-server-runtime/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import type {
} from "./routeModules";
import { expectType } from "./typecheck";
import { type Expect, type Equal } from "./typecheck";
import { type SerializeFrom as SingleFetch_SerializeFrom } from "./single-fetch";
import type { Future } from "./future";

type SingleFetchEnabled = Future extends {
singleFetch: infer T extends boolean;
}
? T
: false;

// prettier-ignore
/**
Expand All @@ -17,6 +25,10 @@ import { type Expect, type Equal } from "./typecheck";
* `type LoaderData = SerializeFrom<typeof loader>`
*/
export type SerializeFrom<T> =
SingleFetchEnabled extends true ?
T extends (...args: []) => unknown ? SingleFetch_SerializeFrom<T> :
never
:
T extends (...args: any[]) => infer Output ?
Parameters<T> extends [ClientLoaderFunctionArgs | ClientActionFunctionArgs] ?
// Client data functions may not serialize
Expand Down
43 changes: 36 additions & 7 deletions packages/remix-server-runtime/single-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import type { AppLoadContext } from "./data";
import { sanitizeError, sanitizeErrors } from "./errors";
import { getDocumentHeaders } from "./headers";
import { ServerMode } from "./mode";
import type { TypedDeferredData, TypedResponse } from "./responses";
import type { TypedResponse } from "./responses";
import { isRedirectStatusCode, isResponse } from "./responses";
import type { SerializeFrom } from "./serialize";
import type { Jsonify } from "./jsonify";

export const SingleFetchRedirectSymbol = Symbol("SingleFetchRedirect");

Expand Down Expand Up @@ -379,15 +379,44 @@ type Serializable =
| Set<Serializable>
| Promise<Serializable>;

// prettier-ignore
type Serialize<T> =
T extends void ? undefined :

// First, let type stay as-is if its already serializable...
T extends Serializable ? T :

// ...then don't allow functions to be serialized...
T extends (...args: any[]) => unknown ? undefined :

// ...lastly handle inner types for all container types allowed by `turbo-stream`

// Promise
T extends Promise<infer U> ? Promise<Serialize<U>> :

// Map & Set
T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> :
T extends Set<infer U> ? Set<Serialize<U>> :

// Array
T extends [] ? [] :
T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] :
T extends Array<infer U> ? Array<Serialize<U>> :
T extends readonly unknown[] ? readonly Serialize<T[number]>[] :

// Record
T extends Record<any, any> ? {[K in keyof T]: Serialize<T[K]>} :

undefined

type Fn = (...args: any[]) => 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 Fn> =
Awaited<ReturnType<T>> extends TypedDeferredData<infer D> ? D :
Awaited<ReturnType<T>> extends TypedResponse<Record<string, unknown>> ? SerializeFrom<T> :
Awaited<ReturnType<T>> extends DataWithResponseInit<infer D> ? D :
Awaited<ReturnType<T>>;
export type SerializeFrom<T extends Fn> =
Awaited<ReturnType<T>> extends TypedResponse<Record<string, unknown>> ? Jsonify<T> :
Awaited<ReturnType<T>> extends DataWithResponseInit<infer D> ? Serialize<D> :
Serialize<Awaited<ReturnType<T>>>;

0 comments on commit 6a43507

Please sign in to comment.