Skip to content

Commit dabc72a

Browse files
committed
patch for the fetch cache with ISR
1 parent ea7f2b5 commit dabc72a

File tree

3 files changed

+214
-0
lines changed

3 files changed

+214
-0
lines changed

packages/open-next/src/build/createServerBundle.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
1717
import * as buildHelper from "./helper.js";
1818
import { installDependencies } from "./installDeps.js";
1919
import { type CodePatcher, applyCodePatches } from "./patch/codePatcher.js";
20+
import {
21+
patchFetchCacheForISR,
22+
patchUnstableCacheForISR,
23+
} from "./patch/patchFetchCacheISR.js";
2024

2125
interface CodeCustomization {
2226
// These patches are meant to apply on user and next generated code
@@ -179,6 +183,8 @@ async function generateBundle(
179183
const additionalCodePatches = codeCustomization?.additionalCodePatches ?? [];
180184

181185
await applyCodePatches(options, Array.from(tracedFiles), manifests, [
186+
patchFetchCacheForISR,
187+
patchUnstableCacheForISR,
182188
...additionalCodePatches,
183189
]);
184190

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { getCrossPlatformPathRegex } from "utils/regex.js";
2+
import { createPatchCode } from "./astCodePatcher.js";
3+
import type { CodePatcher } from "./codePatcher";
4+
5+
export const fetchRule = `
6+
rule:
7+
kind: member_expression
8+
pattern: $WORK_STORE.isOnDemandRevalidate
9+
inside:
10+
kind: ternary_expression
11+
all:
12+
- has: {kind: 'null'}
13+
- has:
14+
kind: await_expression
15+
has:
16+
kind: call_expression
17+
all:
18+
- has:
19+
kind: member_expression
20+
has:
21+
kind: property_identifier
22+
field: property
23+
regex: get
24+
- has:
25+
kind: arguments
26+
has:
27+
kind: object
28+
has:
29+
kind: pair
30+
all:
31+
- has:
32+
kind: property_identifier
33+
field: key
34+
regex: softTags
35+
inside:
36+
kind: variable_declarator
37+
38+
fix:
39+
($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)
40+
`;
41+
42+
export const unstable_cacheRule = `
43+
rule:
44+
kind: member_expression
45+
pattern: $STORE_OR_CACHE.isOnDemandRevalidate
46+
fix:
47+
($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)
48+
`;
49+
50+
export const patchFetchCacheForISR: CodePatcher = {
51+
name: "patch-fetch-cache-for-isr",
52+
patches: [
53+
{
54+
after: "14.0.0",
55+
field: {
56+
pathFilter: getCrossPlatformPathRegex(
57+
String.raw`(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`,
58+
{ escape: false },
59+
),
60+
contentFilter: /\.isOnDemandRevalidate/,
61+
patchCode: createPatchCode(fetchRule),
62+
},
63+
},
64+
],
65+
};
66+
67+
export const patchUnstableCacheForISR: CodePatcher = {
68+
name: "patch-unstable-cache-for-isr",
69+
patches: [
70+
{
71+
after: "14.0.0",
72+
field: {
73+
pathFilter: getCrossPlatformPathRegex(
74+
String.raw`(spec-extension/unstable-cache\.js)$`,
75+
{ escape: false },
76+
),
77+
contentFilter: /\.isOnDemandRevalidate/,
78+
patchCode: createPatchCode(unstable_cacheRule),
79+
},
80+
},
81+
],
82+
};
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
2+
import {
3+
unstable_cacheRule,
4+
fetchRule,
5+
} from "@opennextjs/aws/build/patch/patchFetchCacheISR.js";
6+
import { describe } from "vitest";
7+
8+
const unstable_cacheCode = `
9+
if (// when we are nested inside of other unstable_cache's
10+
// we should bypass cache similar to fetches
11+
!isNestedUnstableCache && workStore.fetchCache !== 'force-no-store' && !workStore.isOnDemandRevalidate && !incrementalCache.isOnDemandRevalidate && !workStore.isDraftMode) {
12+
// We attempt to get the current cache entry from the incremental cache.
13+
const cacheEntry = await incrementalCache.get(cacheKey, {
14+
kind: _responsecache.IncrementalCacheKind.FETCH,
15+
revalidate: options.revalidate,
16+
tags,
17+
softTags: implicitTags,
18+
fetchIdx,
19+
fetchUrl
20+
});
21+
}
22+
else {
23+
noStoreFetchIdx += 1;
24+
// We are in Pages Router or were called outside of a render. We don't have a store
25+
// so we just call the callback directly when it needs to run.
26+
// If the entry is fresh we return it. If the entry is stale we return it but revalidate the entry in
27+
// the background. If the entry is missing or invalid we generate a new entry and return it.
28+
if (!incrementalCache.isOnDemandRevalidate) {
29+
// We aren't doing an on demand revalidation so we check use the cache if valid
30+
const implicitTags = !workUnitStore || workUnitStore.type === 'unstable-cache' ? [] : workUnitStore.implicitTags;
31+
const cacheEntry = await incrementalCache.get(cacheKey, {
32+
kind: _responsecache.IncrementalCacheKind.FETCH,
33+
revalidate: options.revalidate,
34+
tags,
35+
fetchIdx,
36+
fetchUrl,
37+
softTags: implicitTags
38+
});
39+
}
40+
`;
41+
42+
const patchFetchCacheCodeunMinified = `
43+
const entry = workStore.isOnDemandRevalidate ? null : await incrementalCache.get(cacheKey, {
44+
kind: _responsecache.IncrementalCacheKind.FETCH,
45+
revalidate: finalRevalidate,
46+
fetchUrl,
47+
fetchIdx,
48+
tags,
49+
softTags: implicitTags
50+
});
51+
`;
52+
53+
const patchFetchCacheCodeMinifiedNext15 = `
54+
let t=P.isOnDemandRevalidate?null:await V.get(n,{kind:l.IncrementalCacheKind.FETCH,revalidate:_,fetchUrl:y,fetchIdx:X,tags:N,softTags:C});
55+
`;
56+
57+
describe("patchUnstableCacheForISR", () => {
58+
test("on unminified code", async () => {
59+
expect(
60+
patchCode(unstable_cacheCode, unstable_cacheRule),
61+
).toMatchInlineSnapshot(`
62+
"if (// when we are nested inside of other unstable_cache's
63+
// we should bypass cache similar to fetches
64+
!isNestedUnstableCache && workStore.fetchCache !== 'force-no-store' && !(workStore.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation) && !(incrementalCache.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation) && !workStore.isDraftMode) {
65+
// We attempt to get the current cache entry from the incremental cache.
66+
const cacheEntry = await incrementalCache.get(cacheKey, {
67+
kind: _responsecache.IncrementalCacheKind.FETCH,
68+
revalidate: options.revalidate,
69+
tags,
70+
softTags: implicitTags,
71+
fetchIdx,
72+
fetchUrl
73+
});
74+
}
75+
else {
76+
noStoreFetchIdx += 1;
77+
// We are in Pages Router or were called outside of a render. We don't have a store
78+
// so we just call the callback directly when it needs to run.
79+
// If the entry is fresh we return it. If the entry is stale we return it but revalidate the entry in
80+
// the background. If the entry is missing or invalid we generate a new entry and return it.
81+
if (!(incrementalCache.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)) {
82+
// We aren't doing an on demand revalidation so we check use the cache if valid
83+
const implicitTags = !workUnitStore || workUnitStore.type === 'unstable-cache' ? [] : workUnitStore.implicitTags;
84+
const cacheEntry = await incrementalCache.get(cacheKey, {
85+
kind: _responsecache.IncrementalCacheKind.FETCH,
86+
revalidate: options.revalidate,
87+
tags,
88+
fetchIdx,
89+
fetchUrl,
90+
softTags: implicitTags
91+
});
92+
}
93+
"
94+
`);
95+
});
96+
});
97+
98+
describe("patchFetchCacheISR", () => {
99+
describe("Next 15", () => {
100+
test("on unminified code", async () => {
101+
expect(
102+
patchCode(patchFetchCacheCodeunMinified, fetchRule),
103+
).toMatchInlineSnapshot(`
104+
"const entry = (workStore.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation) ? null : await incrementalCache.get(cacheKey, {
105+
kind: _responsecache.IncrementalCacheKind.FETCH,
106+
revalidate: finalRevalidate,
107+
fetchUrl,
108+
fetchIdx,
109+
tags,
110+
softTags: implicitTags
111+
});
112+
"
113+
`);
114+
});
115+
116+
test("on minified code", async () => {
117+
expect(
118+
patchCode(patchFetchCacheCodeMinifiedNext15, fetchRule),
119+
).toMatchInlineSnapshot(`
120+
"let t=(P.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)?null:await V.get(n,{kind:l.IncrementalCacheKind.FETCH,revalidate:_,fetchUrl:y,fetchIdx:X,tags:N,softTags:C});
121+
"
122+
`);
123+
});
124+
});
125+
//TODO: Add test for Next 14.2.24
126+
});

0 commit comments

Comments
 (0)