mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Supersedes https://github.com/vercel/next.js/pull/93475 ## Summary There's a few fixes bundled in here. We're fixing `Infinity` getting serialized to `null`, which caused cache handlers and build workers to misinterpret it as "don't cache" instead of "cache forever". We use the existing infinity sentinel for that. We're also making the checks a bit more specific from `typeof !== 'number'` to forbidding `NaN` and `-Infinity`. The checks are applied consistently for profiles, inline cache life, and arguments to `fetch` and `unstable_cache`. Now that configs are also validated, we need to make sure that we only do `revalidate > expire` validation when both values are specified. There's a regression test for that, which https://github.com/vercel/next.js/pull/93475 didn't do correctly. Also, per review below: - Now actually using returned value for `unstable_cache` - Added validation + infinity fix for `revalidateTag` profile - Added validation + infinity fix in `config.experimental.staleTimes` and `config.expireTime` - Added deep clone for `defaultConfig.cacheLife` ## Test Plan New e2e tests that fail before the fixes. --------- Co-authored-by: Zack Tanner <[email protected]>
96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
|
|
const validPage = `export default function Page() {
|
|
return <p>hello</p>
|
|
}`
|
|
|
|
describe('use-cache-invalid-cache-life', () => {
|
|
const { next, skipped } = nextTestSetup({
|
|
files: __dirname,
|
|
skipStart: true,
|
|
skipDeployment: true,
|
|
})
|
|
|
|
if (skipped) return
|
|
|
|
it('fails the build for a config profile with a non-finite value other than Infinity', async () => {
|
|
await next.patchFile(
|
|
'next.config.js',
|
|
`module.exports = {
|
|
cacheComponents: true,
|
|
cacheLife: {
|
|
invalid: { revalidate: -Infinity },
|
|
},
|
|
}`
|
|
)
|
|
await next.patchFile('app/page.tsx', validPage)
|
|
|
|
const { exitCode, cliOutput } = await next.build()
|
|
expect(exitCode).not.toBe(0)
|
|
expect(cliOutput).toContain(
|
|
'Invalid "cacheLife.invalid.revalidate" provided, expected a finite number of seconds or Infinity, received -Infinity'
|
|
)
|
|
})
|
|
|
|
it('fails the build for a config profile with a revalidate longer than its expire', async () => {
|
|
await next.patchFile(
|
|
'next.config.js',
|
|
`module.exports = {
|
|
cacheComponents: true,
|
|
cacheLife: {
|
|
inconsistent: { revalidate: 100, expire: 50 },
|
|
},
|
|
}`
|
|
)
|
|
await next.patchFile('app/page.tsx', validPage)
|
|
|
|
const { exitCode, cliOutput } = await next.build()
|
|
expect(exitCode).not.toBe(0)
|
|
expect(cliOutput).toContain(
|
|
'the expire option must be greater than the revalidate option'
|
|
)
|
|
})
|
|
|
|
it('does not fail the build for a partial default profile whose backfilled revalidate is longer than its expire', async () => {
|
|
await next.patchFile(
|
|
'next.config.js',
|
|
`module.exports = {
|
|
cacheComponents: true,
|
|
cacheLife: {
|
|
default: { expire: 0 },
|
|
},
|
|
}`
|
|
)
|
|
await next.patchFile('app/page.tsx', validPage)
|
|
|
|
const { exitCode } = await next.build()
|
|
expect(exitCode).toBe(0)
|
|
})
|
|
|
|
it('fails the build for an inline cacheLife() profile with a non-finite value other than Infinity', async () => {
|
|
await next.patchFile(
|
|
'next.config.js',
|
|
`module.exports = {
|
|
cacheComponents: true,
|
|
}`
|
|
)
|
|
await next.patchFile(
|
|
'app/page.tsx',
|
|
`import { cacheLife } from 'next/cache'
|
|
|
|
export default async function Page() {
|
|
'use cache'
|
|
cacheLife({ expire: NaN })
|
|
|
|
return <p>never rendered</p>
|
|
}`
|
|
)
|
|
|
|
const { exitCode, cliOutput } = await next.build()
|
|
expect(exitCode).not.toBe(0)
|
|
expect(cliOutput).toContain(
|
|
'Invalid `cacheLife()` option "expire" provided, expected a finite number of seconds or Infinity, received NaN.'
|
|
)
|
|
})
|
|
})
|