Fix RenderOpts in next-server
(#10776)
* Correctly pass preview data * remove todo * re-do change * fix types * Prevent regression
This commit is contained in:
parent
94009422c5
commit
8f01a4ae83
4 changed files with 90 additions and 11 deletions
|
@ -260,6 +260,7 @@ const COOKIE_NAME_PRERENDER_BYPASS = `__prerender_bypass`
|
|||
const COOKIE_NAME_PRERENDER_DATA = `__next_preview_data`
|
||||
|
||||
export const SYMBOL_PREVIEW_DATA = Symbol(COOKIE_NAME_PRERENDER_DATA)
|
||||
const SYMBOL_CLEARED_COOKIES = Symbol(COOKIE_NAME_PRERENDER_BYPASS)
|
||||
|
||||
export function tryGetPreviewData(
|
||||
req: IncomingMessage,
|
||||
|
@ -405,6 +406,10 @@ function setPreviewData<T>(
|
|||
}
|
||||
|
||||
function clearPreviewData<T>(res: NextApiResponse<T>): NextApiResponse<T> {
|
||||
if (SYMBOL_CLEARED_COOKIES in res) {
|
||||
return res
|
||||
}
|
||||
|
||||
const { serialize } = require('cookie') as typeof import('cookie')
|
||||
const previous = res.getHeader('Set-Cookie')
|
||||
res.setHeader(`Set-Cookie`, [
|
||||
|
@ -432,6 +437,11 @@ function clearPreviewData<T>(res: NextApiResponse<T>): NextApiResponse<T> {
|
|||
path: '/',
|
||||
}),
|
||||
])
|
||||
|
||||
Object.defineProperty(res, SYMBOL_CLEARED_COOKIES, {
|
||||
value: true,
|
||||
enumerable: false,
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import pathMatch from './lib/path-match'
|
|||
import { recursiveReadDirSync } from './lib/recursive-readdir-sync'
|
||||
import { loadComponents, LoadComponentsReturnType } from './load-components'
|
||||
import { normalizePagePath } from './normalize-page-path'
|
||||
import { renderToHTML } from './render'
|
||||
import { RenderOpts, RenderOptsPartial, renderToHTML } from './render'
|
||||
import { getPagePath } from './require'
|
||||
import Router, {
|
||||
DynamicRoutes,
|
||||
|
@ -115,6 +115,7 @@ export default class Server {
|
|||
documentMiddlewareEnabled: boolean
|
||||
hasCssMode: boolean
|
||||
dev?: boolean
|
||||
previewProps: __ApiPreviewProps
|
||||
}
|
||||
private compression?: Middleware
|
||||
private onErrorMiddleware?: ({ err }: { err: Error }) => Promise<void>
|
||||
|
@ -165,6 +166,7 @@ export default class Server {
|
|||
staticMarkup,
|
||||
buildId: this.buildId,
|
||||
generateEtags,
|
||||
previewProps: this.getPreviewProps(),
|
||||
}
|
||||
|
||||
// Only the `publicRuntimeConfig` key is exposed to the client side
|
||||
|
@ -668,13 +670,12 @@ export default class Server {
|
|||
}
|
||||
}
|
||||
|
||||
const previewProps = this.getPreviewProps()
|
||||
await apiResolver(
|
||||
req,
|
||||
res,
|
||||
query,
|
||||
pageModule,
|
||||
{ ...previewProps },
|
||||
this.renderOpts.previewProps,
|
||||
this.onErrorMiddleware
|
||||
)
|
||||
return true
|
||||
|
@ -869,7 +870,7 @@ export default class Server {
|
|||
res: ServerResponse,
|
||||
pathname: string,
|
||||
{ components, query }: FindComponentsResult,
|
||||
opts: any
|
||||
opts: RenderOptsPartial
|
||||
): Promise<string | false | null> {
|
||||
// we need to ensure the status code if /404 is visited directly
|
||||
if (pathname === '/404') {
|
||||
|
@ -890,7 +891,7 @@ export default class Server {
|
|||
const hasStaticPaths = !!components.getStaticPaths
|
||||
|
||||
// Toggle whether or not this is a Data request
|
||||
const isDataReq = query._nextDataReq
|
||||
const isDataReq = !!query._nextDataReq
|
||||
delete query._nextDataReq
|
||||
|
||||
// Serverless requests need its URL transformed back into the original
|
||||
|
@ -958,8 +959,11 @@ export default class Server {
|
|||
})
|
||||
}
|
||||
|
||||
const previewProps = this.getPreviewProps()
|
||||
const previewData = tryGetPreviewData(req, res, { ...previewProps })
|
||||
const previewData = tryGetPreviewData(
|
||||
req,
|
||||
res,
|
||||
this.renderOpts.previewProps
|
||||
)
|
||||
const isPreviewMode = previewData !== false
|
||||
|
||||
// Compute the SPR cache key
|
||||
|
@ -1017,15 +1021,16 @@ export default class Server {
|
|||
pageData = renderResult.renderOpts.pageData
|
||||
sprRevalidate = renderResult.renderOpts.revalidate
|
||||
} else {
|
||||
const renderOpts = {
|
||||
const renderOpts: RenderOpts = {
|
||||
...components,
|
||||
...opts,
|
||||
}
|
||||
renderResult = await renderToHTML(req, res, pathname, query, renderOpts)
|
||||
|
||||
html = renderResult
|
||||
pageData = renderOpts.pageData
|
||||
sprRevalidate = renderOpts.revalidate
|
||||
// TODO: change this to a different passing mechanism
|
||||
pageData = (renderOpts as any).pageData
|
||||
sprRevalidate = (renderOpts as any).revalidate
|
||||
}
|
||||
|
||||
return { html, pageData, sprRevalidate }
|
||||
|
|
|
@ -124,7 +124,7 @@ function render(
|
|||
return { html, head }
|
||||
}
|
||||
|
||||
type RenderOpts = LoadComponentsReturnType & {
|
||||
export type RenderOptsPartial = {
|
||||
staticMarkup: boolean
|
||||
buildId: string
|
||||
canonicalBase: string
|
||||
|
@ -147,6 +147,8 @@ type RenderOpts = LoadComponentsReturnType & {
|
|||
previewProps: __ApiPreviewProps
|
||||
}
|
||||
|
||||
export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
|
||||
|
||||
function renderDocument(
|
||||
Document: DocumentType,
|
||||
{
|
||||
|
@ -517,6 +519,7 @@ export async function renderToHTML(
|
|||
|
||||
props.pageProps = data.props
|
||||
// pass up revalidate and props for export
|
||||
// TODO: change this to a different passing mechanism
|
||||
;(renderOpts as any).revalidate = data.revalidate
|
||||
;(renderOpts as any).pageData = props
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
findPort,
|
||||
initNextServerScript,
|
||||
killApp,
|
||||
launchApp,
|
||||
nextBuild,
|
||||
nextStart,
|
||||
renderViaHTTP,
|
||||
|
@ -201,6 +202,66 @@ const startServerlessEmulator = async (dir, port) => {
|
|||
}
|
||||
|
||||
describe('Prerender Preview Mode', () => {
|
||||
describe('Development Mode', () => {
|
||||
beforeAll(async () => {
|
||||
await fs.remove(nextConfigPath)
|
||||
})
|
||||
|
||||
let appPort, app
|
||||
it('should start development application', async () => {
|
||||
appPort = await findPort()
|
||||
app = await launchApp(appDir, appPort)
|
||||
})
|
||||
|
||||
let previewCookieString
|
||||
it('should enable preview mode', async () => {
|
||||
const res = await fetchViaHTTP(appPort, '/api/preview', { lets: 'goooo' })
|
||||
expect(res.status).toBe(200)
|
||||
|
||||
const cookies = res.headers
|
||||
.get('set-cookie')
|
||||
.split(',')
|
||||
.map(cookie.parse)
|
||||
|
||||
expect(cookies.length).toBe(2)
|
||||
previewCookieString =
|
||||
cookie.serialize('__prerender_bypass', cookies[0].__prerender_bypass) +
|
||||
'; ' +
|
||||
cookie.serialize('__next_preview_data', cookies[1].__next_preview_data)
|
||||
})
|
||||
|
||||
it('should return cookies to be expired after dev server reboot', async () => {
|
||||
await killApp(app)
|
||||
app = await launchApp(appDir, appPort)
|
||||
|
||||
const res = await fetchViaHTTP(
|
||||
appPort,
|
||||
'/',
|
||||
{},
|
||||
{ headers: { Cookie: previewCookieString } }
|
||||
)
|
||||
expect(res.status).toBe(200)
|
||||
|
||||
const body = await res.text()
|
||||
// "err":{"name":"TypeError","message":"Cannot read property 'previewModeId' of undefined"
|
||||
expect(body).not.toContain('err')
|
||||
expect(body).not.toContain('TypeError')
|
||||
expect(body).not.toContain('previewModeId')
|
||||
|
||||
const cookies = res.headers
|
||||
.get('set-cookie')
|
||||
.replace(/(=\w{3}),/g, '$1')
|
||||
.split(',')
|
||||
.map(cookie.parse)
|
||||
|
||||
expect(cookies.length).toBe(2)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Server Mode', () => {
|
||||
beforeAll(async () => {
|
||||
await fs.remove(nextConfigPath)
|
||||
|
|
Loading…
Reference in a new issue