From 59f329f34d686c222ec7a8f6f8b5d8a9abc4ce50 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Mon, 14 Oct 2024 15:03:55 +0200 Subject: [PATCH] Add support for `'use cache'` in route handlers using the Edge runtime Same as #70897, but for the Edge runtime. The changes are based on what we're already doing for app pages using the Edge runtime. --- .../next-core/src/next_app/app_route_entry.rs | 7 ++++--- .../next/src/build/templates/edge-app-route.ts | 18 ++++++++++++++++++ .../next-edge-app-route-loader/index.ts | 3 ++- packages/next/src/build/webpack/utils.ts | 6 +++++- test/e2e/app-dir/app-static/app-static.test.ts | 4 ++++ .../dynamic-io/dynamic-io.routes.test.ts | 3 --- .../app/edge/route.ts | 3 +++ .../app/{ => node}/route.ts | 2 -- .../use-cache-route-handler-only.test.ts | 11 +++++++++-- 9 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 test/e2e/app-dir/use-cache-route-handler-only/app/edge/route.ts rename test/e2e/app-dir/use-cache-route-handler-only/app/{ => node}/route.ts (75%) diff --git a/crates/next-core/src/next_app/app_route_entry.rs b/crates/next-core/src/next_app/app_route_entry.rs index 0c0a8dd31d1d0..b37e0f62808ee 100644 --- a/crates/next-core/src/next_app/app_route_entry.rs +++ b/crates/next-core/src/next_app/app_route_entry.rs @@ -113,7 +113,7 @@ pub async fn get_app_route_entry( Vc::upcast(module_asset_context), project_root, rsc_entry, - pathname.clone(), + page, ); } @@ -131,7 +131,7 @@ async fn wrap_edge_route( asset_context: Vc>, project_root: Vc, entry: Vc>, - pathname: RcStr, + page: AppPage, ) -> Result>> { const INNER: &str = "INNER_ROUTE_ENTRY"; @@ -140,6 +140,7 @@ async fn wrap_edge_route( project_root, indexmap! { "VAR_USERLAND" => INNER.into(), + "VAR_PAGE" => page.to_string().into(), }, indexmap! {}, indexmap! {}, @@ -161,6 +162,6 @@ async fn wrap_edge_route( asset_context, project_root, wrapped, - pathname, + AppPath::from(page).to_string().into(), )) } diff --git a/packages/next/src/build/templates/edge-app-route.ts b/packages/next/src/build/templates/edge-app-route.ts index 226252d069080..de572403ee38b 100644 --- a/packages/next/src/build/templates/edge-app-route.ts +++ b/packages/next/src/build/templates/edge-app-route.ts @@ -1,8 +1,26 @@ +import { createServerModuleMap } from '../../server/app-render/action-utils' +import { setReferenceManifestsSingleton } from '../../server/app-render/encryption-utils' import { EdgeRouteModuleWrapper } from '../../server/web/edge-route-module-wrapper' // Import the userland code. import * as module from 'VAR_USERLAND' +const maybeJSONParse = (str?: string) => (str ? JSON.parse(str) : undefined) + +const rscManifest = self.__RSC_MANIFEST?.['VAR_PAGE'] +const rscServerManifest = maybeJSONParse(self.__RSC_SERVER_MANIFEST) + +if (rscManifest && rscServerManifest) { + setReferenceManifestsSingleton({ + clientReferenceManifest: rscManifest, + serverActionsManifest: rscServerManifest, + serverModuleMap: createServerModuleMap({ + serverActionsManifest: rscServerManifest, + pageName: 'VAR_PAGE', + }), + }) +} + export const ComponentMod = module export default EdgeRouteModuleWrapper.wrap(module.routeModule) diff --git a/packages/next/src/build/webpack/loaders/next-edge-app-route-loader/index.ts b/packages/next/src/build/webpack/loaders/next-edge-app-route-loader/index.ts index 8b9b8d244a81e..83779f7fac9fa 100644 --- a/packages/next/src/build/webpack/loaders/next-edge-app-route-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-edge-app-route-loader/index.ts @@ -36,7 +36,7 @@ const EdgeAppRouteLoader: webpack.LoaderDefinitionFunction { const modRequest: string | undefined = (dependency as any).request if (modRequest?.includes('next-app-loader')) { diff --git a/test/e2e/app-dir/app-static/app-static.test.ts b/test/e2e/app-dir/app-static/app-static.test.ts index 2f3e884c72a05..7b0f5ff6e529a 100644 --- a/test/e2e/app-dir/app-static/app-static.test.ts +++ b/test/e2e/app-dir/app-static/app-static.test.ts @@ -775,9 +775,11 @@ describe('app-dir static/dynamic handling', () => { "api/large-data/route.js", "api/large-data/route_client-reference-manifest.js", "api/revalidate-path-edge/route.js", + "api/revalidate-path-edge/route_client-reference-manifest.js", "api/revalidate-path-node/route.js", "api/revalidate-path-node/route_client-reference-manifest.js", "api/revalidate-tag-edge/route.js", + "api/revalidate-tag-edge/route_client-reference-manifest.js", "api/revalidate-tag-node/route.js", "api/revalidate-tag-node/route_client-reference-manifest.js", "articles/[slug]/page.js", @@ -935,6 +937,7 @@ describe('app-dir static/dynamic handling', () => { "response-url/page.js", "response-url/page_client-reference-manifest.js", "route-handler-edge/revalidate-360/route.js", + "route-handler-edge/revalidate-360/route_client-reference-manifest.js", "route-handler/no-store-force-static/route.js", "route-handler/no-store-force-static/route_client-reference-manifest.js", "route-handler/no-store/route.js", @@ -968,6 +971,7 @@ describe('app-dir static/dynamic handling', () => { "stale-cache-serving-edge/app-page/page.js", "stale-cache-serving-edge/app-page/page_client-reference-manifest.js", "stale-cache-serving-edge/route-handler/route.js", + "stale-cache-serving-edge/route-handler/route_client-reference-manifest.js", "stale-cache-serving/app-page/page.js", "stale-cache-serving/app-page/page_client-reference-manifest.js", "stale-cache-serving/route-handler/route.js", diff --git a/test/e2e/app-dir/dynamic-io/dynamic-io.routes.test.ts b/test/e2e/app-dir/dynamic-io/dynamic-io.routes.test.ts index 932059247c832..68fa1d10df3ec 100644 --- a/test/e2e/app-dir/dynamic-io/dynamic-io.routes.test.ts +++ b/test/e2e/app-dir/dynamic-io/dynamic-io.routes.test.ts @@ -241,8 +241,6 @@ describe('dynamic-io', () => { expect(message2).toEqual(json.message2) } - // TODO: Edge is missing Server Manifest for routes. - /* str = await next.render('/routes/-edge/use_cache-cached', {}) json = JSON.parse(str) @@ -259,7 +257,6 @@ describe('dynamic-io', () => { expect(json.value).toEqual('at runtime') expect(message1).toEqual(json.message1) expect(message2).toEqual(json.message2) - */ } ) diff --git a/test/e2e/app-dir/use-cache-route-handler-only/app/edge/route.ts b/test/e2e/app-dir/use-cache-route-handler-only/app/edge/route.ts new file mode 100644 index 0000000000000..fcdab7a38b9c2 --- /dev/null +++ b/test/e2e/app-dir/use-cache-route-handler-only/app/edge/route.ts @@ -0,0 +1,3 @@ +export const runtime = 'edge' + +export { GET } from '../node/route' diff --git a/test/e2e/app-dir/use-cache-route-handler-only/app/route.ts b/test/e2e/app-dir/use-cache-route-handler-only/app/node/route.ts similarity index 75% rename from test/e2e/app-dir/use-cache-route-handler-only/app/route.ts rename to test/e2e/app-dir/use-cache-route-handler-only/app/node/route.ts index d52bf97fa2c8d..a995c872c214a 100644 --- a/test/e2e/app-dir/use-cache-route-handler-only/app/route.ts +++ b/test/e2e/app-dir/use-cache-route-handler-only/app/node/route.ts @@ -5,8 +5,6 @@ async function getCachedRandom() { export async function GET() { const rand1 = await getCachedRandom() - // TODO: Remove this extra micro task when bug in use cache wrapper is fixed. - await Promise.resolve() const rand2 = await getCachedRandom() const response = JSON.stringify({ rand1, rand2 }) diff --git a/test/e2e/app-dir/use-cache-route-handler-only/use-cache-route-handler-only.test.ts b/test/e2e/app-dir/use-cache-route-handler-only/use-cache-route-handler-only.test.ts index 6b44fc35d1007..37a9d3086ebb3 100644 --- a/test/e2e/app-dir/use-cache-route-handler-only/use-cache-route-handler-only.test.ts +++ b/test/e2e/app-dir/use-cache-route-handler-only/use-cache-route-handler-only.test.ts @@ -10,8 +10,15 @@ describe('use-cache-route-handler-only', () => { const itSkipTurbopack = isTurbopack ? it.skip : it - itSkipTurbopack('should cache results in route handlers', async () => { - const response = await next.fetch('/') + itSkipTurbopack('should cache results in node route handlers', async () => { + const response = await next.fetch('/node') + const { rand1, rand2 } = await response.json() + + expect(rand1).toEqual(rand2) + }) + + itSkipTurbopack('should cache results in edge route handlers', async () => { + const response = await next.fetch('/edge') const { rand1, rand2 } = await response.json() expect(rand1).toEqual(rand2)