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

Fix RenderOpts in next-server #10776

Merged
merged 8 commits into from Mar 2, 2020
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
10 changes: 10 additions & 0 deletions packages/next/next-server/server/api-utils.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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`, [
Expand Down Expand Up @@ -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
}

Expand Down
25 changes: 15 additions & 10 deletions packages/next/next-server/server/next-server.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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') {
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }
Expand Down
5 changes: 4 additions & 1 deletion packages/next/next-server/server/render.tsx
Expand Up @@ -124,7 +124,7 @@ function render(
return { html, head }
}

type RenderOpts = LoadComponentsReturnType & {
export type RenderOptsPartial = {
staticMarkup: boolean
buildId: string
canonicalBase: string
Expand All @@ -147,6 +147,8 @@ type RenderOpts = LoadComponentsReturnType & {
previewProps: __ApiPreviewProps
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial

function renderDocument(
Document: DocumentType,
{
Expand Down Expand Up @@ -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
}
Expand Down
61 changes: 61 additions & 0 deletions test/integration/prerender-preview/test/index.test.js
Expand Up @@ -8,6 +8,7 @@ import {
findPort,
initNextServerScript,
killApp,
launchApp,
nextBuild,
nextStart,
renderViaHTTP,
Expand Down Expand Up @@ -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)
Expand Down