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 all 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
26 changes: 26 additions & 0 deletions .changeset/light-avocados-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@remix-run/server-runtime": patch
---

fix: Properly categorize internal framework-thrown error Responses as error boundary errors

Previously there was some ambiguity around _"thrown Responses go to the `CatchBoundary`"_.
The `CatchBoundary` exists to give the _user_ a place to handle non-happy path code flows
such that they can throw Response instances from _their own code_ and handle them in a
`CatchBoundary`. However, there are a handful of framework-internal errors that make
sense to have a non-500 status code, and the fact that these were being thrown as Responses
was causing them to go into the CatchBoundary, even though they were not user-thrown.

With this change, anything thrown by the framework itself (`Error` or `Response`) will
go to the `ErrorBoundary`, and any user-thrown `Response` instances will go to the
`CatchBoundary`. Thereis one exception to this rule, which is that framework-detected
404's will continue to go to the `CatchBoundary` since users should have one single
location to handle 404 displays.

The primary affected use cases are scenarios such as:

* HTTP `OPTIONS` requests (405 Unsupported Method )
* `GET` requests to routes without loaders (400 Bad Request)
* `POST` requests to routes without actions (405 Method Not Allowed)
* Missing route id in `_data` parameters (403 Forbidden)
* Non-matching route id included in `_data` parameters (403 Forbidden)
110 changes: 0 additions & 110 deletions integration/catch-boundary-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ test.describe("CatchBoundary", () => {

let HAS_BOUNDARY_LOADER = "/yes/loader";
let HAS_BOUNDARY_ACTION = "/yes/action";
let HAS_BOUNDARY_NO_LOADER_OR_ACTION = "/yes/no-loader-or-action";
let NO_BOUNDARY_ACTION = "/no/action";
let NO_BOUNDARY_LOADER = "/no/loader";
let NO_BOUNDARY_NO_LOADER_OR_ACTION = "/no/no-loader-or-action";

let NOT_FOUND_HREF = "/not/found";

Expand Down Expand Up @@ -64,8 +62,6 @@ test.describe("CatchBoundary", () => {
<Form method="post">
<button formAction="${HAS_BOUNDARY_ACTION}" type="submit" />
<button formAction="${NO_BOUNDARY_ACTION}" type="submit" />
<button formAction="${HAS_BOUNDARY_NO_LOADER_OR_ACTION}" type="submit" />
<button formAction="${NO_BOUNDARY_NO_LOADER_OR_ACTION}" type="submit" />
</Form>

<Link to="${HAS_BOUNDARY_LOADER}">
Expand All @@ -79,39 +75,6 @@ test.describe("CatchBoundary", () => {
}
`,

"app/routes/fetcher-boundary.jsx": js`
import { useFetcher } from "@remix-run/react";
export function CatchBoundary() {
return <p id="fetcher-boundary">${OWN_BOUNDARY_TEXT}</p>
}
export default function() {
let fetcher = useFetcher();

return (
<div>
<fetcher.Form method="post">
<button formAction="${NO_BOUNDARY_NO_LOADER_OR_ACTION}" type="submit" />
</fetcher.Form>
</div>
)
}
`,

"app/routes/fetcher-no-boundary.jsx": js`
import { useFetcher } from "@remix-run/react";
export default function() {
let fetcher = useFetcher();

return (
<div>
<fetcher.Form method="post">
<button formAction="${NO_BOUNDARY_NO_LOADER_OR_ACTION}" type="submit" />
</fetcher.Form>
</div>
)
}
`,

[`app/routes${HAS_BOUNDARY_ACTION}.jsx`]: js`
import { Form } from "@remix-run/react";
export async function action() {
Expand Down Expand Up @@ -147,21 +110,6 @@ test.describe("CatchBoundary", () => {
}
`,

[`app/routes${HAS_BOUNDARY_NO_LOADER_OR_ACTION}.jsx`]: js`
export function CatchBoundary() {
return <div id="boundary-no-loader-or-action">${OWN_BOUNDARY_TEXT}</div>
}
export default function Index() {
return <div/>
}
`,

[`app/routes${NO_BOUNDARY_NO_LOADER_OR_ACTION}.jsx`]: js`
export default function Index() {
return <div/>
}
`,

[`app/routes${HAS_BOUNDARY_LOADER}.jsx`]: js`
export function loader() {
throw new Response("", { status: 401 })
Expand Down Expand Up @@ -250,12 +198,6 @@ test.describe("CatchBoundary", () => {
await page.waitForSelector("#root-boundary");
});

test("invalid request methods", async () => {
let res = await fixture.requestDocument("/", { method: "OPTIONS" });
expect(res.status).toBe(405);
expect(await res.text()).toMatch(ROOT_BOUNDARY_TEXT);
});

test("own boundary, action, document request", async () => {
let params = new URLSearchParams();
let res = await fixture.postDocument(HAS_BOUNDARY_ACTION, params);
Expand Down Expand Up @@ -334,58 +276,6 @@ test.describe("CatchBoundary", () => {
await page.waitForSelector("#root-boundary");
});

test("renders root boundary in document POST without action requests", async () => {
let res = await fixture.requestDocument(NO_BOUNDARY_NO_LOADER_OR_ACTION, {
method: "post",
});
expect(res.status).toBe(405);
expect(await res.text()).toMatch(ROOT_BOUNDARY_TEXT);
});

test("renders root boundary in action script transitions without action from other routes", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/");
await app.clickSubmitButton(NO_BOUNDARY_NO_LOADER_OR_ACTION);
await page.waitForSelector("#root-boundary");
});

test("renders own boundary in document POST without action requests", async () => {
let res = await fixture.requestDocument(HAS_BOUNDARY_NO_LOADER_OR_ACTION, {
method: "post",
});
expect(res.status).toBe(405);
expect(await res.text()).toMatch(OWN_BOUNDARY_TEXT);
});

test("renders own boundary in action script transitions without action from other routes", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/");
await app.clickSubmitButton(HAS_BOUNDARY_NO_LOADER_OR_ACTION);
await page.waitForSelector("#boundary-no-loader-or-action");
});

test("renders own boundary in fetcher action submission without action from other routes", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/fetcher-boundary");
await app.clickSubmitButton(NO_BOUNDARY_NO_LOADER_OR_ACTION);
await page.waitForSelector("#fetcher-boundary");
});

test("renders root boundary in fetcher action submission without action from other routes", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/fetcher-no-boundary");
await app.clickSubmitButton(NO_BOUNDARY_NO_LOADER_OR_ACTION);
await page.waitForSelector("#root-boundary");
});

test("uses correct catch boundary on server action errors", async ({
page,
}) => {
Expand Down
13 changes: 13 additions & 0 deletions integration/compiler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,19 @@ test.describe("compiler", () => {
});

test.describe("serverBareModulesPlugin", () => {
let ogConsole: typeof global.console;
test.beforeEach(() => {
ogConsole = global.console;
// @ts-ignore
global.console = {
log() {},
warn() {},
error() {},
};
});
test.afterEach(() => {
global.console = ogConsole;
});
test("warns when a module isn't installed", async () => {
let buildOutput: string;
let buildStdio = new PassThrough();
Expand Down
Loading