From 0b837b4908f6f108065f34750e638a58fc18d16c Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 19 Mar 2024 14:55:19 -0400 Subject: [PATCH 1/3] Scope single fetch headers calls to only loaded routes --- integration/single-fetch-test.ts | 90 +++++++++++++++++++++++- packages/remix-server-runtime/headers.ts | 10 ++- packages/remix-server-runtime/server.ts | 6 +- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/integration/single-fetch-test.ts b/integration/single-fetch-test.ts index 8646069c245..2eb39b0b862 100644 --- a/integration/single-fetch-test.ts +++ b/integration/single-fetch-test.ts @@ -416,7 +416,7 @@ test.describe("single-fetch", () => { expect(urls).toEqual([]); }); - test("returns loader headers through the headers function", async () => { + test("handles headers correctly for loader and action calls", async () => { let fixture = await createFixture({ config: { future: { @@ -476,6 +476,94 @@ test.describe("single-fetch", () => { expect(res.headers.get("x-headers-function")).toEqual(null); }); + test("scopes loader headers to the _routes param if present", async () => { + let fixture = await createFixture({ + config: { + future: { + unstable_singleFetch: true, + }, + }, + files: { + ...files, + "app/routes/a.tsx": js` + export function headers({ loaderHeaders }) { + let headers = new Headers(loaderHeaders); + headers.set('x-a-headers', 'true') + return headers; + } + + export function loader({ request }) { + return new Response(null, { headers: { "x-a-loader": "true" } }); + } + + export default function Comp() { + return null; + } + `, + "app/routes/a.b.tsx": js` + export function headers({ loaderHeaders, parentHeaders }) { + let headers = new Headers(parentHeaders); + loaderHeaders.forEach((value, name) => headers.set(name, value)); + headers.set('x-b-headers', 'true') + return headers; + } + + export function loader({ request }) { + return new Response(null, { headers: { "x-b-loader": "true" } }); + } + + export default function Comp() { + return null; + } + `, + "app/routes/a.b.c.tsx": js` + export function headers({ loaderHeaders, parentHeaders }) { + let headers = new Headers(parentHeaders); + loaderHeaders.forEach((value, name) => headers.set(name, value)); + headers.set('x-c-headers', 'true') + return headers; + } + + export function loader({ request }) { + return new Response(null, { headers: { "x-c-loader": "true" } }); + } + + export default function Comp() { + return null; + } + `, + }, + }); + + let res = await fixture.requestSingleFetchData("/a/b/c.data"); + expect(res.headers.get("x-a-loader")).toEqual("true"); + expect(res.headers.get("x-a-headers")).toEqual("true"); + expect(res.headers.get("x-b-loader")).toEqual("true"); + expect(res.headers.get("x-b-headers")).toEqual("true"); + expect(res.headers.get("x-c-loader")).toEqual("true"); + expect(res.headers.get("x-c-headers")).toEqual("true"); + + res = await fixture.requestSingleFetchData( + "/a/b/c.data?_routes=routes%2Fa,routes%2Fa.b" + ); + expect(res.headers.get("x-a-loader")).toEqual("true"); + expect(res.headers.get("x-a-headers")).toEqual("true"); + expect(res.headers.get("x-b-loader")).toEqual("true"); + expect(res.headers.get("x-b-headers")).toEqual("true"); + expect(res.headers.get("x-c-loader")).toBeNull(); + expect(res.headers.get("x-c-headers")).toBeNull(); + + res = await fixture.requestSingleFetchData( + "/a/b/c.data?_routes=routes%2Fa" + ); + expect(res.headers.get("x-a-loader")).toEqual("true"); + expect(res.headers.get("x-a-headers")).toEqual("true"); + expect(res.headers.get("x-b-loader")).toBeNull(); + expect(res.headers.get("x-b-headers")).toBeNull(); + expect(res.headers.get("x-c-loader")).toBeNull(); + expect(res.headers.get("x-c-headers")).toBeNull(); + }); + test.describe("client loaders", () => { test("when no routes have client loaders", async ({ page }) => { let fixture = await createFixture( diff --git a/packages/remix-server-runtime/headers.ts b/packages/remix-server-runtime/headers.ts index d65644a0551..9fb577bd47c 100644 --- a/packages/remix-server-runtime/headers.ts +++ b/packages/remix-server-runtime/headers.ts @@ -3,10 +3,12 @@ import { splitCookiesString } from "set-cookie-parser"; import type { ServerBuild } from "./build"; -export function getDocumentHeadersRR( +export function getDocumentHeaders( build: ServerBuild, - context: StaticHandlerContext + context: StaticHandlerContext, + loadRouteIds?: string[] ): Headers { + debugger; let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors![m.route.id]) : -1; @@ -15,6 +17,10 @@ export function getDocumentHeadersRR( ? context.matches.slice(0, boundaryIdx + 1) : context.matches; + if (loadRouteIds) { + matches = matches.filter((m) => loadRouteIds.includes(m.route.id)); + } + let errorHeaders: Headers | undefined; if (boundaryIdx >= 0) { diff --git a/packages/remix-server-runtime/server.ts b/packages/remix-server-runtime/server.ts index e8199f1ffd5..b4f03e55640 100644 --- a/packages/remix-server-runtime/server.ts +++ b/packages/remix-server-runtime/server.ts @@ -24,7 +24,7 @@ import { serializeError, serializeErrors, } from "./errors"; -import { getDocumentHeadersRR as getDocumentHeaders } from "./headers"; +import { getDocumentHeaders } from "./headers"; import invariant from "./invariant"; import { ServerMode, isServerMode } from "./mode"; import type { RouteMatch } from "./routeMatching"; @@ -353,7 +353,6 @@ async function handleSingleFetchRequest( request, handlerUrl, staticHandler, - matches, loadContext, handleError, serverMode, @@ -447,7 +446,6 @@ async function singleFetchLoaders( request: Request, handlerUrl: URL, staticHandler: StaticHandler, - matches: RouteMatch[] | null, loadContext: AppLoadContext, handleError: (err: unknown) => void, serverMode: ServerMode, @@ -509,7 +507,7 @@ async function singleFetchLoaders( return { result: results, - headers: getDocumentHeaders(build, context), + headers: getDocumentHeaders(build, context, loadRouteIds), status: context.statusCode, }; } catch (error: unknown) { From 7c1998852476bec8b6fd87dcb210be5438a683f2 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 19 Mar 2024 14:57:50 -0400 Subject: [PATCH 2/3] More tests --- integration/single-fetch-test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/integration/single-fetch-test.ts b/integration/single-fetch-test.ts index 2eb39b0b862..3c0736d8dfe 100644 --- a/integration/single-fetch-test.ts +++ b/integration/single-fetch-test.ts @@ -562,6 +562,26 @@ test.describe("single-fetch", () => { expect(res.headers.get("x-b-headers")).toBeNull(); expect(res.headers.get("x-c-loader")).toBeNull(); expect(res.headers.get("x-c-headers")).toBeNull(); + + res = await fixture.requestSingleFetchData( + "/a/b/c.data?_routes=routes%2Fa.b.c" + ); + expect(res.headers.get("x-a-loader")).toBeNull(); + expect(res.headers.get("x-a-headers")).toBeNull(); + expect(res.headers.get("x-b-loader")).toBeNull(); + expect(res.headers.get("x-b-headers")).toBeNull(); + expect(res.headers.get("x-c-loader")).toEqual("true"); + expect(res.headers.get("x-c-headers")).toEqual("true"); + + res = await fixture.requestSingleFetchData( + "/a/b/c.data?_routes=routes%2Fa,routes%2Fa.b.c" + ); + expect(res.headers.get("x-a-loader")).toEqual("true"); + expect(res.headers.get("x-a-loader")).toEqual("true"); + expect(res.headers.get("x-b-headers")).toBeNull(); + expect(res.headers.get("x-b-headers")).toBeNull(); + expect(res.headers.get("x-c-loader")).toEqual("true"); + expect(res.headers.get("x-c-headers")).toEqual("true"); }); test.describe("client loaders", () => { From 09ae241e0665fff392267c6806d9a3054ccbbac3 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 19 Mar 2024 15:00:09 -0400 Subject: [PATCH 3/3] Remove debugger --- packages/remix-server-runtime/headers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/remix-server-runtime/headers.ts b/packages/remix-server-runtime/headers.ts index 9fb577bd47c..698b6af1542 100644 --- a/packages/remix-server-runtime/headers.ts +++ b/packages/remix-server-runtime/headers.ts @@ -8,7 +8,6 @@ export function getDocumentHeaders( context: StaticHandlerContext, loadRouteIds?: string[] ): Headers { - debugger; let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors![m.route.id]) : -1;