未验证 提交 8f01a4ae 编写于 作者: J Joe Haddad 提交者: GitHub

Fix RenderOpts in `next-server` (#10776)

* Correctly pass preview data

* remove todo

* re-do change

* fix types

* Prevent regression
上级 94009422
......@@ -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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册