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

feat(remix-server-runtime): RRR 1.3 / 1.4 - handleDocumentRequest #4385

Merged
merged 32 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9d0d6e3
initial work for document requests
jacob-ebey Oct 18, 2022
fccc933
Keep thrown responses on the throw path
brophdawg11 Oct 19, 2022
138222f
get headers integration tests passing
brophdawg11 Oct 19, 2022
ebcc937
temp work on error/catch boundary distinction
brophdawg11 Oct 19, 2022
0e84d2f
chore: update test to be more reliable
jacob-ebey Oct 19, 2022
52a6fc3
be more protective when accessing route module
jacob-ebey Oct 19, 2022
2b5140f
be more protective when accessing route module
jacob-ebey Oct 19, 2022
7a6045f
revert
jacob-ebey Oct 19, 2022
4a8997e
Get catch boundary integration tests passing
brophdawg11 Oct 20, 2022
af9d450
Get error boundary tests passing
brophdawg11 Oct 20, 2022
ad09bef
test: mdx test ignores non-existing asset request
jacob-ebey Oct 21, 2022
2f44df4
fix: handle no root catch boundary
jacob-ebey Oct 21, 2022
8c12f0a
remove ".only"
jacob-ebey Oct 21, 2022
8af6805
Updates
brophdawg11 Oct 21, 2022
b25d64f
Copy over latest @remix-run/router files
brophdawg11 Oct 21, 2022
32af5b6
Bring over latest router files
brophdawg11 Oct 26, 2022
01e2277
Add data request error tests
brophdawg11 Oct 26, 2022
077e9b5
Updates for catch.error boundary data responses
brophdawg11 Oct 27, 2022
194ea13
fix: ensure route modules are loaded regardless of error state
jacob-ebey Oct 27, 2022
8d7c86a
ALL GREEN BABY ✅
brophdawg11 Oct 28, 2022
b93e0c7
Fix lint issues and one broken unit test
brophdawg11 Oct 31, 2022
c1ae7a9
Fix tsc build error
brophdawg11 Oct 31, 2022
5d1c6d1
make fetcher tests more reliable
brophdawg11 Nov 1, 2022
63cf680
Merge branch 'dev' into rrr-document-requests-1
brophdawg11 Nov 1, 2022
e6e7f9d
🧹 code
brophdawg11 Nov 1, 2022
3829f50
updated to cover aborted request case
jacob-ebey Nov 2, 2022
b944b7c
chore: remove assert flow (#4554)
jacob-ebey Nov 9, 2022
5db567e
Change missing loader from 405 -> 400 error
brophdawg11 Nov 15, 2022
8a14ade
Bring over external redirect logic from react router
brophdawg11 Nov 15, 2022
3ce0ac3
add changeset
brophdawg11 Nov 15, 2022
e3c6225
Merge branch 'dev' into rrr-document-requests-1
brophdawg11 Nov 15, 2022
ccae87f
fix tsc error
brophdawg11 Nov 15, 2022
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
3 changes: 3 additions & 0 deletions integration/transition-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ test.describe("rendering", () => {
await app.goto("/");
let responses = app.collectDataResponses();
await app.clickLink(`/${PAGE}`);
await page.waitForLoadState("networkidle");

expect(
responses.map((res) => new URL(res.url()).searchParams.get("_data"))
Expand All @@ -227,6 +228,7 @@ test.describe("rendering", () => {
await app.goto(`/${PAGE}`);
let responses = app.collectDataResponses();
await app.clickLink(`/${PAGE}/${CHILD}`);
await page.waitForLoadState("networkidle");

expect(
responses.map((res) => new URL(res.url()).searchParams.get("_data"))
Expand All @@ -244,6 +246,7 @@ test.describe("rendering", () => {

await app.clickLink(`/${REDIRECT}`);
await page.waitForURL(/\/page/);
await page.waitForLoadState("networkidle");

expect(
responses
Expand Down
179 changes: 110 additions & 69 deletions packages/remix-server-runtime/__tests__/server-test.ts

Large diffs are not rendered by default.

19 changes: 18 additions & 1 deletion packages/remix-server-runtime/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@ export async function callRouteAction({
action,
params,
request,
isRemixRouterRequest,
}: {
loadContext: AppLoadContext;
routeId: string;
action?: ActionFunction;
params: DataFunctionArgs["params"];
request: Request;
isRemixRouterRequest?: boolean;
}) {
if (!action) {
let response = new Response(null, { status: 405 });
let response = new Response(null, {
status: 405,
statusText: "Method Not Allowed",
brophdawg11 marked this conversation as resolved.
Show resolved Hide resolved
});
response.headers.set("X-Remix-Catch", "yes");
return response;
}
Expand All @@ -51,6 +56,11 @@ export async function callRouteAction({

if (!isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
// This needs to be thrown so @remix-run/router knows to handle it as an error
// and not a successful returned response
if (isRemixRouterRequest) {
throw error;
}
}
result = error;
}
Expand All @@ -71,12 +81,14 @@ export async function callRouteLoader({
loader,
params,
request,
isRemixRouterRequest,
}: {
request: Request;
routeId: string;
loader?: LoaderFunction;
params: DataFunctionArgs["params"];
loadContext: AppLoadContext;
isRemixRouterRequest?: boolean;
}) {
if (!loader) {
throw new Error(
Expand All @@ -100,6 +112,11 @@ export async function callRouteLoader({

if (!isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
// This needs to be thrown so @remix-run/router knows to handle it as an error
// and not a successful returned response
if (isRemixRouterRequest) {
throw error;
}
}
result = error;
}
Expand Down
29 changes: 29 additions & 0 deletions packages/remix-server-runtime/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { splitCookiesString } from "set-cookie-parser";
import type { ServerBuild } from "./build";
import type { ServerRoute } from "./routes";
import type { RouteMatch } from "./routeMatching";
import type { StaticHandlerContext } from "./router";

export function getDocumentHeaders(
build: ServerBuild,
Expand Down Expand Up @@ -35,6 +36,34 @@ export function getDocumentHeaders(
}, new Headers());
}

export function getDocumentHeadersRR(
build: ServerBuild,
context: StaticHandlerContext,
matches: RouteMatch<ServerRoute>[]
) {
return matches.reduce((parentHeaders, match, index) => {
let { id } = match.route;
let routeModule = build.routes[id].module;
let loaderHeaders = context.loaderHeaders?.[id] || new Headers();
let actionHeaders = context.actionHeaders?.[id] || new Headers();
let headers = new Headers(
routeModule.headers
? typeof routeModule.headers === "function"
? routeModule.headers({ loaderHeaders, parentHeaders, actionHeaders })
: routeModule.headers
: undefined
);

// Automatically preserve Set-Cookie headers that were set either by the
// loader or by a parent route.
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
prependCookies(parentHeaders, headers);

return headers;
}, new Headers());
}

function prependCookies(parentHeaders: Headers, childHeaders: Headers): void {
let parentSetCookieString = parentHeaders.get("Set-Cookie");

Expand Down
68 changes: 48 additions & 20 deletions packages/remix-server-runtime/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,9 @@ export function createRouter(init: RouterInit): Router {
//#region createStaticHandler
////////////////////////////////////////////////////////////////////////////////

const validActionMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);

export function unstable_createStaticHandler(
routes: AgnosticRouteObject[]
): StaticHandler {
Expand Down Expand Up @@ -1803,7 +1806,25 @@ export function unstable_createStaticHandler(
let location = createLocation("", createPath(url), null, "default");
let matches = matchRoutes(dataRoutes, location);

if (!matches) {
if (!validRequestMethods.has(request.method)) {
brophdawg11 marked this conversation as resolved.
Show resolved Hide resolved
let {
matches: methodNotAllowedMatches,
route,
error,
} = getMethodNotAllowedMatches(dataRoutes);
return {
location,
matches: methodNotAllowedMatches,
loaderData: {},
actionData: null,
errors: {
[route.id]: error,
},
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {},
};
} else if (!matches) {
let {
matches: notFoundMatches,
route,
Expand All @@ -1817,7 +1838,7 @@ export function unstable_createStaticHandler(
errors: {
[route.id]: error,
},
statusCode: 404,
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {},
};
Expand Down Expand Up @@ -1856,7 +1877,12 @@ export function unstable_createStaticHandler(
let location = createLocation("", createPath(url), null, "default");
let matches = matchRoutes(dataRoutes, location);

if (!matches) {
if (!validRequestMethods.has(request.method)) {
throw createRouterErrorResponse(null, {
status: 405,
statusText: "Method Not Allowed",
});
brophdawg11 marked this conversation as resolved.
Show resolved Hide resolved
} else if (!matches) {
throw createRouterErrorResponse(null, {
status: 404,
statusText: "Not Found",
Expand Down Expand Up @@ -1899,17 +1925,13 @@ export function unstable_createStaticHandler(
matches: AgnosticDataRouteMatch[],
isRouteRequest: boolean
): Promise<Omit<StaticHandlerContext, "location"> | Response> {
invariant(
request.method !== "HEAD",
"query()/queryRoute() do not support HEAD requests"
);
invariant(
request.signal,
"query()/queryRoute() requests must contain an AbortController signal"
);

try {
if (request.method !== "GET") {
if (validActionMethods.has(request.method)) {
let result = await submit(
request,
matches,
Expand Down Expand Up @@ -1956,7 +1978,7 @@ export function unstable_createStaticHandler(
if (!actionMatch.route.action) {
let href = createHref(new URL(request.url));
if (isRouteRequest) {
throw createRouterErrorResponse(`No action found for [${href}]`, {
throw createRouterErrorResponse(null, {
brophdawg11 marked this conversation as resolved.
Show resolved Hide resolved
status: 405,
statusText: "Method Not Allowed",
});
Expand Down Expand Up @@ -2728,16 +2750,18 @@ function findNearestBoundary(
);
}

function getNotFoundMatches(routes: AgnosticDataRouteObject[]): {
function getShortCircuitMatches(
routes: AgnosticDataRouteObject[],
status: number,
statusText: string
): {
matches: AgnosticDataRouteMatch[];
route: AgnosticDataRouteObject;
error: ErrorResponse;
} {
// Prefer a root layout route if present, otherwise shim in a route object
let route = routes.find(
(r) => r.index || r.path === "" || r.path === "/"
) || {
id: "__shim-404-route__",
let route = routes.find((r) => r.index || !r.path || r.path === "/") || {
id: `__shim-${status}-route__`,
brophdawg11 marked this conversation as resolved.
Show resolved Hide resolved
};

return {
Expand All @@ -2750,10 +2774,18 @@ function getNotFoundMatches(routes: AgnosticDataRouteObject[]): {
},
],
route,
error: new ErrorResponse(404, "Not Found", null),
error: new ErrorResponse(status, statusText, null),
};
}

function getNotFoundMatches(routes: AgnosticDataRouteObject[]) {
return getShortCircuitMatches(routes, 404, "Not Found");
}

function getMethodNotAllowedMatches(routes: AgnosticDataRouteObject[]) {
return getShortCircuitMatches(routes, 405, "Method Not Allowed");
}

function getMethodNotAllowedResult(path: Location | string): ErrorResult {
let href = typeof path === "string" ? path : createHref(path);
console.warn(
Expand All @@ -2763,11 +2795,7 @@ function getMethodNotAllowedResult(path: Location | string): ErrorResult {
);
return {
type: ResultType.error,
error: new ErrorResponse(
405,
"Method Not Allowed",
`No action found for [${href}]`
),
error: new ErrorResponse(405, "Method Not Allowed", ""),
};
}

Expand Down
2 changes: 2 additions & 0 deletions packages/remix-server-runtime/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function createStaticHandlerDataRoutes(
routeId: route.id,
loader: route.module.loader,
loadContext,
isRemixRouterRequest: true,
})
: undefined,
action: route.module.action
Expand All @@ -84,6 +85,7 @@ export function createStaticHandlerDataRoutes(
routeId: route.id,
action: route.module.action,
loadContext,
isRemixRouterRequest: true,
})
: undefined,
handle: route.module.handle,
Expand Down
Loading