Skip to content

Commit d447125

Browse files
Support composable cache in Next 16 (#1053)
Co-authored-by: Dario Piotrowicz <dario@cloudflare.com>
1 parent 07b3818 commit d447125

File tree

12 files changed

+89
-156
lines changed

12 files changed

+89
-156
lines changed

.changeset/calm-parents-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
Support composable cache in Next 16

examples/e2e/experimental/next.config.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@ const nextConfig: NextConfig = {
44
/* config options here */
55
cleanDistDir: true,
66
output: "standalone",
7-
eslint: {
8-
ignoreDuringBuilds: true,
9-
},
7+
cacheComponents: true,
108
typescript: {
119
// Ignore type errors during build for now, we'll need to figure this out later
1210
ignoreBuildErrors: true,
1311
},
14-
experimental: {
15-
cacheComponents: true,
16-
},
1712
};
1813

1914
export default nextConfig;

examples/e2e/experimental/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"scripts": {
66
"dev": "next dev --turbopack --port 3004",
7-
"build": "next build",
7+
"build": "next build --webpack",
88
"start": "next start --port 3004",
99
"lint": "next lint",
1010
"clean": "rm -rf .turbo node_modules .next .open-next",
@@ -15,7 +15,7 @@
1515
},
1616
"dependencies": {
1717
"@opennextjs/cloudflare": "workspace:*",
18-
"next": "15.4.2-canary.29",
18+
"next": "catalog:e2e",
1919
"react": "catalog:e2e",
2020
"react-dom": "catalog:e2e"
2121
},
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { revalidateTag } from "next/cache";
22

33
export function GET() {
4-
revalidateTag("fullyTagged");
4+
revalidateTag("fullyTagged", "max");
55
return new Response("DONE");
66
}

examples/e2e/experimental/src/app/ppr/page.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { DynamicComponent } from "@/components/dynamic";
22
import { StaticComponent } from "@/components/static";
33
import { Suspense } from "react";
44

5-
export const experimental_ppr = true;
6-
75
export default function PPRPage() {
86
return (
97
<div>

examples/e2e/experimental/src/app/use-cache/ssr/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ export default async function Page() {
99
<div>
1010
<h1>Cache</h1>
1111
<p>{_headers.get("accept") ?? "No accept headers"}</p>
12+
<h2>cached:</h2>
1213
<Suspense fallback={<p>Loading...</p>}>
1314
<FullyCachedComponent />
1415
</Suspense>
16+
<h2>cached (with tag):</h2>
1517
<Suspense fallback={<p>Loading...</p>}>
1618
<FullyCachedComponentWithTag />
1719
</Suspense>
20+
<h2>isr:</h2>
1821
<Suspense fallback={<p>Loading...</p>}>
1922
<ISRComponent />
2023
</Suspense>

examples/e2e/experimental/src/components/cached.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { unstable_cacheLife, unstable_cacheTag } from "next/cache";
1+
import { cacheLife, cacheTag } from "next/cache";
22

33
export async function FullyCachedComponent() {
44
"use cache";
@@ -11,7 +11,7 @@ export async function FullyCachedComponent() {
1111

1212
export async function FullyCachedComponentWithTag() {
1313
"use cache";
14-
unstable_cacheTag("fullyTagged");
14+
cacheTag("fullyTagged");
1515
return (
1616
<div>
1717
<p data-testid="fully-cached-with-tag">{Date.now()}</p>
@@ -21,7 +21,7 @@ export async function FullyCachedComponentWithTag() {
2121

2222
export async function ISRComponent() {
2323
"use cache";
24-
unstable_cacheLife({
24+
cacheLife({
2525
stale: 1,
2626
revalidate: 5,
2727
});

examples/e2e/experimental/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"moduleResolution": "bundler",
1212
"resolveJsonModule": true,
1313
"isolatedModules": true,
14-
"jsx": "preserve",
14+
"jsx": "react-jsx",
1515
"incremental": true,
1616
"plugins": [
1717
{
@@ -22,6 +22,6 @@
2222
"@/*": ["./src/*"]
2323
}
2424
},
25-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
2626
"exclude": ["node_modules"]
2727
}

packages/cloudflare/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"dependencies": {
5555
"@ast-grep/napi": "0.40.0",
5656
"@dotenvx/dotenvx": "catalog:",
57-
"@opennextjs/aws": "3.9.6",
57+
"@opennextjs/aws": "3.9.7",
5858
"cloudflare": "^4.4.1",
5959
"enquirer": "^2.4.1",
6060
"glob": "catalog:",

packages/cloudflare/src/cli/build/patches/plugins/next-server.spec.ts

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "./next-server.js";
1111

1212
describe("Next Server", () => {
13-
const nextServerCode = `
13+
const next15ServerCode = `
1414
class NextNodeServer extends _baseserver.default {
1515
constructor(options){
1616
// Initialize super class
@@ -168,8 +168,27 @@ class NextNodeServer extends _baseserver.default {
168168
// ...
169169
}`;
170170

171+
const next16ServerCode = `
172+
class NextNodeServer extends _baseserver.default {
173+
// ...
174+
175+
async loadCustomCacheHandlers() {
176+
const { cacheMaxMemorySize, cacheHandlers } = this.nextConfig;
177+
if (!cacheHandlers) return;
178+
// If we've already initialized the cache handlers interface, don't do it
179+
// again.
180+
if (!(0, _handlers.initializeCacheHandlers)(cacheMaxMemorySize)) return;
181+
for (const [kind, handler] of Object.entries(cacheHandlers)){
182+
if (!handler) continue;
183+
(0, _handlers.setCacheHandler)(kind, (0, _interopdefault.interopDefault)(await dynamicImportEsmDefault((0, _formatdynamicimportpath.formatDynamicImportPath)(this.distDir, handler))));
184+
}
185+
}
186+
// ...
187+
}
188+
`;
189+
171190
test("build ID", () => {
172-
expect(computePatchDiff("next-server.js", nextServerCode, buildIdRule)).toMatchInlineSnapshot(`
191+
expect(computePatchDiff("next-server.js", next15ServerCode, buildIdRule)).toMatchInlineSnapshot(`
173192
"Index: next-server.js
174193
===================================================================
175194
--- next-server.js
@@ -206,7 +225,7 @@ class NextNodeServer extends _baseserver.default {
206225
});
207226

208227
test("cache handler", () => {
209-
expect(computePatchDiff("next-server.js", nextServerCode, createCacheHandlerRule("manifest")))
228+
expect(computePatchDiff("next-server.js", next15ServerCode, createCacheHandlerRule("manifest")))
210229
.toMatchInlineSnapshot(`
211230
"Index: next-server.js
212231
===================================================================
@@ -234,9 +253,10 @@ class NextNodeServer extends _baseserver.default {
234253
`);
235254
});
236255

237-
test("composable cache handler", () => {
238-
expect(computePatchDiff("next-server.js", nextServerCode, createComposableCacheHandlersRule("manifest")))
239-
.toMatchInlineSnapshot(`
256+
test("composable cache handler (Next 15)", () => {
257+
expect(
258+
computePatchDiff("next-server.js", next15ServerCode, createComposableCacheHandlersRule("manifest"))
259+
).toMatchInlineSnapshot(`
240260
"Index: next-server.js
241261
===================================================================
242262
--- next-server.js
@@ -268,8 +288,38 @@ class NextNodeServer extends _baseserver.default {
268288
`);
269289
});
270290

291+
test("composable cache handler (Next 16)", () => {
292+
expect(
293+
computePatchDiff("next-server.js", next16ServerCode, createComposableCacheHandlersRule("manifest"))
294+
).toMatchInlineSnapshot(`
295+
"Index: next-server.js
296+
===================================================================
297+
--- next-server.js
298+
+++ next-server.js
299+
@@ -1,10 +1,15 @@
300+
-
301+
class NextNodeServer extends _baseserver.default {
302+
// ...
303+
304+
async loadCustomCacheHandlers() {
305+
- const { cacheMaxMemorySize, cacheHandlers } = this.nextConfig;
306+
+ const cacheHandlers = null;
307+
+const handlersSymbol = Symbol.for('@next/cache-handlers');
308+
+const handlersMapSymbol = Symbol.for('@next/cache-handlers-map');
309+
+const handlersSetSymbol = Symbol.for('@next/cache-handlers-set');
310+
+globalThis[handlersMapSymbol] = new Map();
311+
+globalThis[handlersMapSymbol].set("default", require('manifest').default);
312+
+globalThis[handlersSetSymbol] = new Set(globalThis[handlersMapSymbol].values());
313+
if (!cacheHandlers) return;
314+
// If we've already initialized the cache handlers interface, don't do it
315+
// again.
316+
if (!(0, _handlers.initializeCacheHandlers)(cacheMaxMemorySize)) return;
317+
"
318+
`);
319+
});
320+
271321
test("disable node middleware", () => {
272-
expect(computePatchDiff("next-server.js", nextServerCode, disableNodeMiddlewareRule))
322+
expect(computePatchDiff("next-server.js", next15ServerCode, disableNodeMiddlewareRule))
273323
.toMatchInlineSnapshot(`
274324
"Index: next-server.js
275325
===================================================================
@@ -312,7 +362,8 @@ class NextNodeServer extends _baseserver.default {
312362
});
313363

314364
test("attachRequestMeta", () => {
315-
expect(computePatchDiff("next-server.js", nextServerCode, attachRequestMetaRule)).toMatchInlineSnapshot(`
365+
expect(computePatchDiff("next-server.js", next15ServerCode, attachRequestMetaRule))
366+
.toMatchInlineSnapshot(`
316367
"Index: next-server.js
317368
===================================================================
318369
--- next-server.js

0 commit comments

Comments
 (0)