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

cache: add unstable_noStore API #56930

Merged
merged 5 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/next/cache.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache'
export { revalidatePath } from 'next/dist/server/web/spec-extension/revalidate-path'
export { revalidateTag } from 'next/dist/server/web/spec-extension/revalidate-tag'
export { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store'
4 changes: 4 additions & 0 deletions packages/next/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const cacheExports = {
.revalidateTag,
revalidatePath: require('next/dist/server/web/spec-extension/revalidate-path')
.revalidatePath,
unstable_noStore:
require('next/dist/server/web/spec-extension/unstable-no-store')
.unstable_noStore,
}

// https://nodejs.org/api/esm.html#commonjs-namespaces
Expand All @@ -15,3 +18,4 @@ module.exports = cacheExports
exports.unstable_cache = cacheExports.unstable_cache
exports.revalidatePath = cacheExports.revalidatePath
exports.revalidateTag = cacheExports.revalidateTag
exports.unstable_noStore = cacheExports.unstable_noStore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface StaticGenerationStore {
readonly isOnDemandRevalidate?: boolean
readonly isPrerendering?: boolean
readonly isRevalidate?: boolean
readonly isUnstableCacheCallback?: boolean

forceDynamic?: boolean
fetchCache?:
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/web/exports/unstable-no-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file is for modularized imports for next/server to get fully-treeshaking.
export { unstable_noStore as default } from '../spec-extension/unstable-no-store'
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function unstable_cache<T extends Callback>(
fetchCache: 'only-no-store',
urlPathname: store?.urlPathname || '/',
isStaticGeneration: !!store?.isStaticGeneration,
isUnstableCacheCallback: true,
},
async () => {
const tags = validateTags(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { staticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external'
import { staticGenerationBailout } from '../../../client/components/static-generation-bailout'

export function unstable_noStore() {
const staticGenerationStore = staticGenerationAsyncStorage.getStore()

if (staticGenerationStore?.isUnstableCacheCallback) {
// if called within a next/cache call, we want to cache the result
// and defer to the next/cache call to handle how to cache the result.
return
}

staticGenerationBailout('unstable_noStore', {
link: 'https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering',
})
}
46 changes: 46 additions & 0 deletions test/e2e/app-dir/app-static/app-static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,12 @@ createNextDescribe(
'articles/[slug]/page_client-reference-manifest.js',
'articles/works.html',
'articles/works.rsc',
'no-store/dynamic/page.js',
'no-store/dynamic/page_client-reference-manifest.js',
'no-store/static.html',
'no-store/static.rsc',
'no-store/static/page.js',
'no-store/static/page_client-reference-manifest.js',
].sort()
)
})
Expand Down Expand Up @@ -1018,6 +1024,22 @@ createNextDescribe(
"initialRevalidateSeconds": false,
"srcRoute": "/hooks/use-search-params/with-suspense",
},
"/no-store/static": Object {
"dataRoute": "/no-store/static.rsc",
"experimentalBypassFor": Array [
Object {
"key": "Next-Action",
"type": "header",
},
Object {
"key": "content-type",
"type": "header",
"value": "multipart/form-data",
},
],
"initialRevalidateSeconds": false,
"srcRoute": "/no-store/static",
},
"/partial-gen-params-no-additional-lang/en/RAND": Object {
"dataRoute": "/partial-gen-params-no-additional-lang/en/RAND.rsc",
"experimentalBypassFor": Array [
Expand Down Expand Up @@ -2921,6 +2943,30 @@ createNextDescribe(
})
})

describe('unstable_noStore', () => {
it('should opt-out of static optimization', async () => {
const res = await next.fetch('/no-store/dynamic')
const html = await res.text()
const data = cheerio.load(html)('#uncached-data').text()
const res2 = await next.fetch('/no-store/dynamic')
const html2 = await res2.text()
const data2 = cheerio.load(html2)('#uncached-data').text()

expect(data).not.toEqual(data2)
})

it('should not opt-out of static optimization when used in next/cache', async () => {
const res = await next.fetch('/no-store/static')
const html = await res.text()
const data = cheerio.load(html)('#data').text()
const res2 = await next.fetch('/no-store/static')
const html2 = await res2.text()
const data2 = cheerio.load(html2)('#data').text()

expect(data).toEqual(data2)
})
})

it('should keep querystring on static page', async () => {
const browser = await next.browser('/blog/tim?message=hello-world')
const checkUrl = async () =>
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/app-dir/app-static/app/no-store/dynamic/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getUncachedRandomData } from '../no-store-fn'

export default async function Page() {
const uncachedData = await getUncachedRandomData()

return (
<div>
<p>random: {Math.random()}</p>
<p id="uncached-data">uncachedData: {uncachedData.random}</p>
</div>
)
}
8 changes: 8 additions & 0 deletions test/e2e/app-dir/app-static/app/no-store/no-store-fn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { unstable_noStore } from 'next/cache'

export function getUncachedRandomData() {
unstable_noStore()
return {
random: Math.random(),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use client'

export function RevalidateButton({ onClick }) {
return (
<form action={onClick}>
<button type="submit">revalidate</button>
</form>
)
}
28 changes: 28 additions & 0 deletions test/e2e/app-dir/app-static/app/no-store/static/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { revalidateTag, unstable_cache } from 'next/cache'
import { getUncachedRandomData } from '../no-store-fn'
import { RevalidateButton } from '../revalidate-button'

export default async function Page() {
async function revalidate() {
'use server'
await revalidateTag('no-store-fn')
}

const cachedData = await unstable_cache(
async () => {
return getUncachedRandomData()
},
['random'],
{
tags: ['no-store-fn'],
}
)()

return (
<div>
<p>random: {Math.random()}</p>
<p id="data">cachedData: {cachedData.random}</p>
<RevalidateButton onClick={revalidate} />
</div>
)
}
1 change: 1 addition & 0 deletions test/e2e/app-dir/app-static/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
logging: {
level: 'verbose',
},
serverActions: true,
ztanner marked this conversation as resolved.
Show resolved Hide resolved
incrementalCacheHandlerPath: process.env.CUSTOM_CACHE_HANDLER,
},

Expand Down