Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single-fetch typesafety #9893

Merged
merged 17 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions integration/single-fetch-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,19 @@ const files = {
};
}

class MyClass {}

export function loader({ request }) {
if (new URL(request.url).searchParams.has("error")) {
throw new Error("Loader Error");
}
return {
message: "DATA",
date: new Date("${ISO_DATE}"),
unserializable: {
function: () => {},
class: new MyClass(),
},
};
}

Expand Down Expand Up @@ -113,13 +119,19 @@ const files = {
}, { status: 201, headers: { 'X-Action': 'yes' }});
}

class MyClass {}

export function loader({ request }) {
if (new URL(request.url).searchParams.has("error")) {
throw new Error("Loader Error");
}
return data({
message: "DATA",
date: new Date("${ISO_DATE}"),
unserializable: {
function: () => {},
class: new MyClass(),
},
}, { status: 206, headers: { 'X-Loader': 'yes' }});
}

Expand Down Expand Up @@ -175,7 +187,7 @@ test.describe("single-fetch", () => {
expect(res.headers.get("Content-Type")).toBe("text/x-script");

res = await fixture.requestSingleFetchData("/data.data");
expect(res.data).toEqual({
expect(res.data).toStrictEqual({
root: {
data: {
message: "ROOT",
Expand All @@ -185,6 +197,10 @@ test.describe("single-fetch", () => {
data: {
message: "DATA",
date: new Date(ISO_DATE),
unserializable: {
function: undefined,
class: undefined,
},
},
},
});
Expand Down Expand Up @@ -255,7 +271,7 @@ test.describe("single-fetch", () => {
let res = await fixture.requestSingleFetchData("/data-with-response.data");
expect(res.status).toEqual(206);
expect(res.headers.get("X-Loader")).toEqual("yes");
expect(res.data).toEqual({
expect(res.data).toStrictEqual({
root: {
data: {
message: "ROOT",
Expand All @@ -265,6 +281,10 @@ test.describe("single-fetch", () => {
data: {
message: "DATA",
date: new Date(ISO_DATE),
unserializable: {
function: undefined,
class: undefined,
},
},
},
});
Expand Down
3 changes: 1 addition & 2 deletions packages/remix-cloudflare/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ export {
createRequestHandler,
createSession,
unstable_data,
unstable_defineLoader,
unstable_defineAction,
defer,
broadcastDevReady,
logDevReady,
Expand Down Expand Up @@ -42,6 +40,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
3 changes: 1 addition & 2 deletions packages/remix-deno/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ export {
unstable_composeUploadHandlers,
unstable_createMemoryUploadHandler,
unstable_data,
unstable_defineAction,
unstable_defineLoader,
unstable_parseMultipartFormData,
} from "@remix-run/server-runtime";

Expand All @@ -45,6 +43,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HandleErrorFunction,
Expand Down
3 changes: 1 addition & 2 deletions packages/remix-node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ export {
createRequestHandler,
createSession,
unstable_data,
unstable_defineLoader,
unstable_defineAction,
defer,
broadcastDevReady,
logDevReady,
Expand Down Expand Up @@ -54,6 +52,7 @@ export type {
DataFunctionArgs,
EntryContext,
ErrorResponse,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
73 changes: 0 additions & 73 deletions packages/remix-react/future/single-fetch.d.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/remix-react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,6 @@ export { ScrollRestoration } from "./scroll-restoration";

export type { RemixServerProps } from "./server";
export { RemixServer } from "./server";
export type {
ClientAction as unstable_ClientAction,
ClientLoader as unstable_ClientLoader,
} from "./single-fetch";
export {
defineClientAction as unstable_defineClientAction,
defineClientLoader as unstable_defineClientLoader,
} from "./single-fetch";

export type {
FutureConfig as UNSAFE_FutureConfig,
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@remix-run/server-runtime": "workspace:*",
"react-router": "0.0.0-experimental-7d87ffb8c",
"react-router-dom": "0.0.0-experimental-7d87ffb8c",
"turbo-stream": "2.3.0"
"turbo-stream": "2.4.0"
},
"devDependencies": {
"@remix-run/node": "workspace:*",
Expand Down
1 change: 0 additions & 1 deletion packages/remix-react/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ module.exports = function rollup() {
{ src: "LICENSE.md", dest: [outputDir, sourceDir] },
{ src: `${sourceDir}/package.json`, dest: outputDir },
{ src: `${sourceDir}/README.md`, dest: outputDir },
{ src: `${sourceDir}/future`, dest: outputDir },
],
}),
copyToPlaygrounds(),
Expand Down
30 changes: 8 additions & 22 deletions packages/remix-react/single-fetch.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as React from "react";
import type {
ActionFunctionArgs as RRActionArgs,
LoaderFunctionArgs as RRLoaderArgs,
unstable_DataStrategyFunction as DataStrategyFunction,
unstable_HandlerResult as HandlerResult,
} from "@remix-run/router";
Expand All @@ -14,9 +12,6 @@ import {
import type {
UNSAFE_SingleFetchResult as SingleFetchResult,
UNSAFE_SingleFetchResults as SingleFetchResults,
unstable_Action,
unstable_Loader,
unstable_Serialize,
} from "@remix-run/server-runtime";
import { UNSAFE_SingleFetchRedirectSymbol as SingleFetchRedirectSymbol } from "@remix-run/server-runtime";
import type {
Expand All @@ -31,23 +26,6 @@ import { escapeHtml } from "./markup";
import type { RouteModules } from "./routeModules";
import invariant from "./invariant";

// clientLoader
type ClientLoaderArgs = RRLoaderArgs<undefined> & {
serverLoader: <T extends unstable_Loader>() => Promise<unstable_Serialize<T>>;
};
export type ClientLoader = (args: ClientLoaderArgs) => unknown;
export let defineClientLoader = <T extends ClientLoader>(
clientLoader: T
): T & { hydrate?: boolean } => clientLoader;

// clientAction
type ClientActionArgs = RRActionArgs<undefined> & {
serverAction: <T extends unstable_Action>() => Promise<unstable_Serialize<T>>;
};
export type ClientAction = (args: ClientActionArgs) => unknown;
export let defineClientAction = <T extends ClientAction>(clientAction: T): T =>
clientAction;

interface StreamTransferProps {
context: EntryContext;
identifier: number;
Expand Down Expand Up @@ -380,6 +358,14 @@ export function decodeViaTurboStream(
return { value: { [SingleFetchRedirectSymbol]: rest[0] } };
}
},
(type, value) => {
if (type === "SingleFetchFallback") {
return { value: undefined };
}
if (type === "SingleFetchClassInstance") {
return { value };
}
},
],
});
}
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 {}
pcattori marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 1 addition & 5 deletions packages/remix-server-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ export { defer, json, redirect, redirectDocument, replace } from "./responses";
export {
SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol,
data as unstable_data,
defineLoader as unstable_defineLoader,
defineAction as unstable_defineAction,
} from "./single-fetch";
export type {
Loader as unstable_Loader,
Action as unstable_Action,
Serialize as unstable_Serialize,
SingleFetchResult as UNSAFE_SingleFetchResult,
SingleFetchResults as UNSAFE_SingleFetchResults,
} from "./single-fetch";
Expand Down Expand Up @@ -64,6 +59,7 @@ export type {
EntryContext,
ErrorResponse,
FlashSessionData,
Future,
HandleDataRequestFunction,
HandleDocumentRequestFunction,
HeadersArgs,
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-server-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"cookie": "^0.6.0",
"set-cookie-parser": "^2.4.8",
"source-map": "^0.7.3",
"turbo-stream": "2.3.0"
"turbo-stream": "2.4.0"
},
"devDependencies": {
"@types/set-cookie-parser": "^2.4.1",
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: 11 additions & 1 deletion packages/remix-server-runtime/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ 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";

// prettier-ignore
type SingleFetchEnabled =
Future extends { singleFetch: infer T extends boolean } ? T : false

// prettier-ignore
/**
Expand All @@ -17,6 +23,11 @@ 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 All @@ -27,7 +38,6 @@ export type SerializeFrom<T> =
:
// Back compat: manually defined data type, not inferred from loader nor action
Jsonify<Awaited<T>>
;

// note: cannot be inlined as logic requires union distribution
// prettier-ignore
Expand Down
Loading
Loading