Skip to content

Commit

Permalink
Add support for unstable_getServerProps
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Jan 13, 2020
1 parent 9f6fdb9 commit 440d855
Show file tree
Hide file tree
Showing 21 changed files with 732 additions and 64 deletions.
2 changes: 2 additions & 0 deletions packages/next/build/babel/plugins/next-ssg-transform.ts
Expand Up @@ -6,10 +6,12 @@ const prerenderId = '__NEXT_SPR'

export const EXPORT_NAME_GET_STATIC_PROPS = 'unstable_getStaticProps'
export const EXPORT_NAME_GET_STATIC_PATHS = 'unstable_getStaticPaths'
export const EXPORT_NAME_GET_SERVER_PROPS = 'unstable_getServerProps'

const ssgExports = new Set([
EXPORT_NAME_GET_STATIC_PROPS,
EXPORT_NAME_GET_STATIC_PATHS,
EXPORT_NAME_GET_SERVER_PROPS,
])

type PluginState = {
Expand Down
57 changes: 42 additions & 15 deletions packages/next/build/index.ts
Expand Up @@ -254,22 +254,23 @@ export default async function build(dir: string, conf = null): Promise<void> {
}
}

const routesManifestPath = path.join(distDir, ROUTES_MANIFEST)
const routesManifest: any = {
version: 1,
basePath: config.experimental.basePath,
redirects: redirects.map(r => buildCustomRoute(r, 'redirect')),
rewrites: rewrites.map(r => buildCustomRoute(r, 'rewrite')),
headers: headers.map(r => buildCustomRoute(r, 'header')),
dynamicRoutes: getSortedRoutes(dynamicRoutes).map(page => ({
page,
regex: getRouteRegex(page).re.source,
})),
}

await mkdirp(distDir)
await fsWriteFile(
path.join(distDir, ROUTES_MANIFEST),
JSON.stringify({
version: 1,
basePath: config.experimental.basePath,
redirects: redirects.map(r => buildCustomRoute(r, 'redirect')),
rewrites: rewrites.map(r => buildCustomRoute(r, 'rewrite')),
headers: headers.map(r => buildCustomRoute(r, 'header')),
dynamicRoutes: getSortedRoutes(dynamicRoutes).map(page => ({
page,
regex: getRouteRegex(page).re.source,
})),
}),
'utf8'
)
// We need to write the manifest with rewrites before build
// so serverless can import the manifest
await fsWriteFile(routesManifestPath, JSON.stringify(routesManifest), 'utf8')

const configs = await Promise.all([
getBaseWebpackConfig(dir, {
Expand Down Expand Up @@ -401,6 +402,7 @@ export default async function build(dir: string, conf = null): Promise<void> {
const staticPages = new Set<string>()
const invalidPages = new Set<string>()
const hybridAmpPages = new Set<string>()
const serverPropsPages = new Set<string>()
const additionalSprPaths = new Map<string, Array<string>>()
const pageInfos = new Map<string, PageInfo>()
const pagesManifest = JSON.parse(await fsReadFile(manifestPath, 'utf8'))
Expand Down Expand Up @@ -500,6 +502,8 @@ export default async function build(dir: string, conf = null): Promise<void> {
} else if (result.static && customAppGetInitialProps === false) {
staticPages.add(page)
isStatic = true
} else if (result.serverProps) {
serverPropsPages.add(page)
}
} catch (err) {
if (err.message !== 'INVALID_DEFAULT_EXPORT') throw err
Expand All @@ -520,6 +524,29 @@ export default async function build(dir: string, conf = null): Promise<void> {
)
staticCheckWorkers.end()

if (serverPropsPages.size > 0) {
// We update the routes manifest after the build with the
// serverProps routes since we can't determine this until after build
routesManifest.serverPropsRoutes = {}

for (const page of serverPropsPages) {
routesManifest.serverPropsRoutes[page] = {
page,
dataRoute: path.posix.join(
'/_next/data',
buildId,
`${page === '/' ? '/index' : page}.json`
),
}
}

await fsWriteFile(
routesManifestPath,
JSON.stringify(routesManifest),
'utf8'
)
}

if (invalidPages.size > 0) {
throw new Error(
`Build optimization failed: found page${
Expand Down
19 changes: 17 additions & 2 deletions packages/next/build/utils.ts
Expand Up @@ -5,7 +5,11 @@ import path from 'path'
import { isValidElementType } from 'react-is'
import stripAnsi from 'strip-ansi'
import { Redirect, Rewrite } from '../lib/check-custom-routes'
import { SPR_GET_INITIAL_PROPS_CONFLICT } from '../lib/constants'
import {
SPR_GET_INITIAL_PROPS_CONFLICT,
SERVER_PROPS_GET_INIT_PROPS_CONFLICT,
SERVER_PROPS_SPR_CONFLICT,
} from '../lib/constants'
import prettyBytes from '../lib/pretty-bytes'
import { recursiveReadDir } from '../lib/recursive-readdir'
import { DEFAULT_REDIRECT_STATUS } from '../next-server/lib/constants'
Expand Down Expand Up @@ -482,6 +486,7 @@ export async function isPageStatic(
static?: boolean
prerender?: boolean
isHybridAmp?: boolean
serverProps?: boolean
prerenderRoutes?: string[] | undefined
}> {
try {
Expand All @@ -496,6 +501,7 @@ export async function isPageStatic(
const hasGetInitialProps = !!(Comp as any).getInitialProps
const hasStaticProps = !!mod.unstable_getStaticProps
const hasStaticPaths = !!mod.unstable_getStaticPaths
const hasServerProps = !!mod.unstable_getServerProps
const hasLegacyStaticParams = !!mod.unstable_getStaticParams

if (hasLegacyStaticParams) {
Expand All @@ -510,6 +516,14 @@ export async function isPageStatic(
throw new Error(SPR_GET_INITIAL_PROPS_CONFLICT)
}

if (hasGetInitialProps && hasServerProps) {
throw new Error(SERVER_PROPS_GET_INIT_PROPS_CONFLICT)
}

if (hasStaticProps && hasServerProps) {
throw new Error(SERVER_PROPS_SPR_CONFLICT)
}

// A page cannot have static parameters if it is not a dynamic page.
if (hasStaticProps && hasStaticPaths && !isDynamicRoute(page)) {
throw new Error(
Expand Down Expand Up @@ -579,9 +593,10 @@ export async function isPageStatic(

const config = mod.config || {}
return {
static: !hasStaticProps && !hasGetInitialProps,
static: !hasStaticProps && !hasGetInitialProps && !hasServerProps,
isHybridAmp: config.amp === 'hybrid',
prerenderRoutes: prerenderPaths,
serverProps: hasServerProps,
prerender: hasStaticProps,
}
} catch (err) {
Expand Down
23 changes: 14 additions & 9 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Expand Up @@ -185,6 +185,7 @@ const nextServerlessLoader: loader.Loader = function() {
export const unstable_getStaticProps = ComponentInfo['unstable_getStaticProp' + 's']
export const unstable_getStaticParams = ComponentInfo['unstable_getStaticParam' + 's']
export const unstable_getStaticPaths = ComponentInfo['unstable_getStaticPath' + 's']
export const unstable_getServerProps = ComponentInfo['unstable_getServerProp' + 's']
${dynamicRouteMatcher}
${handleRewrites}
Expand All @@ -206,17 +207,18 @@ const nextServerlessLoader: loader.Loader = function() {
Document,
buildManifest,
unstable_getStaticProps,
unstable_getServerProps,
unstable_getStaticPaths,
reactLoadableManifest,
canonicalBase: "${canonicalBase}",
buildId: "${buildId}",
assetPrefix: "${assetPrefix}",
..._renderOpts
}
let sprData = false
let _nextData = false
if (req.url.match(/_next\\/data/)) {
sprData = true
_nextData = true
req.url = req.url
.replace(new RegExp('/_next/data/${escapedBuildId}/'), '/')
.replace(/\\.json$/, '')
Expand All @@ -235,7 +237,7 @@ const nextServerlessLoader: loader.Loader = function() {
${page === '/_error' ? `res.statusCode = 404` : ''}
${
pageIsDynamicRoute
? `const params = fromExport && !unstable_getStaticProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};`
? `const params = fromExport && !unstable_getStaticProps && !unstable_getServerProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};`
: `const params = {};`
}
${
Expand Down Expand Up @@ -273,14 +275,17 @@ const nextServerlessLoader: loader.Loader = function() {
}
let result = await renderToHTML(req, res, "${page}", Object.assign({}, unstable_getStaticProps ? {} : parsedUrl.query, nowParams ? nowParams : params, _params), renderOpts)
if (sprData && !fromExport) {
const payload = JSON.stringify(renderOpts.sprData)
if (_nextData && !fromExport) {
const payload = JSON.stringify(renderOpts.pageData)
res.setHeader('Content-Type', 'application/json')
res.setHeader('Content-Length', Buffer.byteLength(payload))
res.setHeader(
'Cache-Control',
\`s-maxage=\${renderOpts.revalidate}, stale-while-revalidate\`
)
if (renderOpts.revalidate) {
res.setHeader(
'Cache-Control',
\`s-maxage=\${renderOpts.revalidate}, stale-while-revalidate\`
)
}
res.end(payload)
return null
}
Expand Down
6 changes: 3 additions & 3 deletions packages/next/export/index.ts
Expand Up @@ -276,7 +276,7 @@ export default async function(
}

const progress = !options.silent && createProgress(filteredPaths.length)
const sprDataDir = options.buildExport
const pageDataDir = options.buildExport
? outDir
: join(outDir, '_next/data', buildId)

Expand Down Expand Up @@ -318,7 +318,7 @@ export default async function(
distDir,
buildId,
outDir,
sprDataDir,
pageDataDir,
renderOpts,
serverRuntimeConfig,
subFolders,
Expand Down Expand Up @@ -360,7 +360,7 @@ export default async function(
subFolders && route !== '/index' ? `${sep}index` : ''
}.html`
)
const jsonDest = join(sprDataDir, `${route}.json`)
const jsonDest = join(pageDataDir, `${route}.json`)

await mkdirp(dirname(htmlDest))
await mkdirp(dirname(jsonDest))
Expand Down
8 changes: 4 additions & 4 deletions packages/next/export/worker.js
Expand Up @@ -25,7 +25,7 @@ export default async function({
distDir,
buildId,
outDir,
sprDataDir,
pageDataDir,
renderOpts,
buildExport,
serverRuntimeConfig,
Expand Down Expand Up @@ -234,14 +234,14 @@ export default async function({
}
}

if (curRenderOpts.sprData) {
if (curRenderOpts.pageData) {
const dataFile = join(
sprDataDir,
pageDataDir,
htmlFilename.replace(/\.html$/, '.json')
)

await mkdirp(dirname(dataFile))
await writeFileP(dataFile, JSON.stringify(curRenderOpts.sprData), 'utf8')
await writeFileP(dataFile, JSON.stringify(curRenderOpts.pageData), 'utf8')
}
results.fromBuildExportRevalidate = curRenderOpts.revalidate

Expand Down
4 changes: 4 additions & 0 deletions packages/next/lib/constants.ts
Expand Up @@ -25,3 +25,7 @@ export const DOT_NEXT_ALIAS = 'private-dot-next'
export const PUBLIC_DIR_MIDDLEWARE_CONFLICT = `You can not have a '_next' folder inside of your public folder. This conflicts with the internal '/_next' route. https://err.sh/zeit/next.js/public-next-folder-conflict`

export const SPR_GET_INITIAL_PROPS_CONFLICT = `You can not use getInitialProps with unstable_getStaticProps. To use SPR, please remove your getInitialProps`

export const SERVER_PROPS_GET_INIT_PROPS_CONFLICT = `You can not use getInitialProps with unstable_getServerProps. Please remove one or the other`

export const SERVER_PROPS_SPR_CONFLICT = `You can not use unstable_getStaticProps with unstable_getServerProps. To use SPR, please remove your unstable_getServerProps`
8 changes: 8 additions & 0 deletions packages/next/next-server/server/load-components.ts
@@ -1,3 +1,5 @@
import { IncomingMessage, ServerResponse } from 'http'
import { ParsedUrlQuery } from 'querystring'
import {
BUILD_MANIFEST,
CLIENT_STATIC_FILES_PATH,
Expand All @@ -22,6 +24,11 @@ export type LoadComponentsReturnType = {
revalidate?: number | boolean
}
unstable_getStaticPaths?: () => void
unstable_getServerProps?: (context: {
req: IncomingMessage
res: ServerResponse
query: ParsedUrlQuery
}) => Promise<{ [key: string]: any }>
buildManifest?: any
reactLoadableManifest?: any
Document?: any
Expand Down Expand Up @@ -90,6 +97,7 @@ export async function loadComponents(
DocumentMiddleware,
reactLoadableManifest,
pageConfig: ComponentMod.config || {},
unstable_getServerProps: ComponentMod.unstable_getServerProps,
unstable_getStaticProps: ComponentMod.unstable_getStaticProps,
unstable_getStaticPaths: ComponentMod.unstable_getStaticPaths,
}
Expand Down

0 comments on commit 440d855

Please sign in to comment.