diff --git a/packages/next/next-server/server/spr-cache.ts b/packages/next/next-server/server/spr-cache.ts index 9d3d6eb8d30ae..c2e383e9edfd8 100644 --- a/packages/next/next-server/server/spr-cache.ts +++ b/packages/next/next-server/server/spr-cache.ts @@ -5,7 +5,9 @@ import { promisify } from 'util' import { PrerenderManifest } from '../../build' import { PRERENDER_MANIFEST } from '../lib/constants' import { normalizePagePath } from './normalize-page-path' +import mkdirpOrig from 'mkdirp' +const mkdirp = promisify(mkdirpOrig) const readFile = promisify(fs.readFile) const writeFile = promisify(fs.writeFile) @@ -166,7 +168,9 @@ export async function setSprCache( // `next build` output's manifest. if (sprOptions.flushToDisk) { try { - await writeFile(getSeedPath(pathname, 'html'), data.html, 'utf8') + const seedPath = getSeedPath(pathname, 'html') + await mkdirp(path.dirname(seedPath)) + await writeFile(seedPath, data.html, 'utf8') await writeFile( getSeedPath(pathname, 'json'), JSON.stringify(data.pageData), diff --git a/test/integration/prerender/pages/user/[user]/profile.js b/test/integration/prerender/pages/user/[user]/profile.js new file mode 100644 index 0000000000000..cd40d92f5f216 --- /dev/null +++ b/test/integration/prerender/pages/user/[user]/profile.js @@ -0,0 +1,30 @@ +import React from 'react' +import Link from 'next/link' + +// eslint-disable-next-line camelcase +export async function unstable_getStaticParams () { + return [] +} + +// eslint-disable-next-line camelcase +export async function unstable_getStaticProps ({ params }) { + return { + props: { + user: params.user, + time: (await import('perf_hooks')).performance.now() + }, + revalidate: 10 + } +} + +export default ({ user, time }) => { + return ( + <> +

User: {user}

+ time: {time} + + to home + + + ) +} diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 4f2bd8fb1721f..824b93a272dfe 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -25,6 +25,7 @@ let appPort let buildId let distPagesDir let exportDir +let stderr const startServer = async (optEnv = {}) => { const scriptPath = join(appDir, 'server.js') @@ -264,6 +265,11 @@ const runTests = (dev = false) => { dataRoute: `/_next/data/${buildId}/blog/[post]/[comment].json`, dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$`, routeRegex: '^\\/blog\\/([^\\/]+?)\\/([^\\/]+?)(?:\\/)?$' + }, + '/user/[user]/profile': { + dataRoute: `/_next/data/${buildId}/user/[user]/profile.json`, + dataRouteRegex: `^\\/_next\\/data\\/${escapedBuildId}\\/user\\/([^\\/]+?)\\/profile\\.json$`, + routeRegex: `^\\/user\\/([^\\/]+?)\\/profile(?:\\/)?$` } }) }) @@ -351,6 +357,12 @@ const runTests = (dev = false) => { const val = await browser.eval('window.thisShouldStay') expect(val).toBe(true) }) + + it('should not error when flushing cache files', async () => { + await fetchViaHTTP(appPort, '/user/user-1/profile') + await waitFor(500) + expect(stderr).not.toMatch(/Failed to update prerender files for/) + }) } } @@ -374,8 +386,13 @@ describe('SPR Prerender', () => { 'utf8' ) await nextBuild(appDir) + stderr = '' appPort = await findPort() - app = nextStart(appDir, appPort) + app = nextStart(appDir, appPort, { + onStderr: msg => { + stderr += msg + } + }) distPagesDir = join(appDir, '.next/serverless/pages') buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8') }) @@ -404,8 +421,13 @@ describe('SPR Prerender', () => { await fs.unlink(nextConfig) } catch (_) {} await nextBuild(appDir) + stderr = '' appPort = await findPort() - app = await nextStart(appDir, appPort) + app = await nextStart(appDir, appPort, { + onStderr: msg => { + stderr += msg + } + }) buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8') distPagesDir = join(appDir, '.next/server/static', buildId, 'pages') })