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

Add initial support for unstable_getServerProps #10077

Merged
merged 41 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
440d855
Add support for unstable_getServerProps
ijjk Jan 13, 2020
8a2d304
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 14, 2020
1ebb040
Apply suggestions from review
ijjk Jan 14, 2020
2cc40cf
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 14, 2020
576572b
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 15, 2020
6c0ac34
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 15, 2020
def1507
Add no-cache header and update types
ijjk Jan 17, 2020
30ad22f
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 17, 2020
40b3e68
Revert sharing of load-components type
ijjk Jan 17, 2020
7397571
Add catchall test and update routes-manifest field
ijjk Jan 17, 2020
8fcab0b
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 17, 2020
25be5ef
Update header check
ijjk Jan 17, 2020
83c89a8
Update to pass query for getServerProps data requests
ijjk Jan 17, 2020
351d424
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 17, 2020
9a5bf52
Update to not cache getServerProps requests
ijjk Jan 17, 2020
2c77127
Merge remote-tracking branch 'upstream/canary' into add/getserverprops
ijjk Jan 19, 2020
931d86c
Rename server side props identifier
ijjk Jan 19, 2020
3c17711
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 20, 2020
65d3634
Merge branch 'canary' into add/getServerProps
ijjk Jan 21, 2020
e4ad30c
Merge branch 'canary' into add/getServerProps
ijjk Jan 21, 2020
d456df1
Update to nest props for getServerProps
ijjk Jan 22, 2020
9ed63e5
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 22, 2020
fea16ca
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 24, 2020
55e05fc
Merge branch 'canary' into add/getServerProps
ijjk Jan 24, 2020
1ad34d7
Add no-cache header in serverless-loader also
ijjk Jan 24, 2020
7a1cc60
Update to throw error for mixed SSG/serverProps earlier
ijjk Jan 24, 2020
ff69a10
Add comment explaining params chosing in serverless-loader
ijjk Jan 24, 2020
862a617
Update invalidKeysMsg to return a string and inline throwing
ijjk Jan 24, 2020
9a2a4de
Inline throwing mixed SSG/serverProps error
ijjk Jan 24, 2020
a3c2576
Update setting cache header in serverless-loader
ijjk Jan 24, 2020
bb290f1
Add separate getServerData method in router
ijjk Jan 24, 2020
43a88b4
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 24, 2020
aadac7e
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 24, 2020
300d354
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 25, 2020
95fd2d8
Merge remote-tracking branch 'upstream/canary' into add/getServerProps
ijjk Jan 27, 2020
15bf407
Update checkIsSSG -> isDataIdentifier
ijjk Jan 27, 2020
1d8752b
Refactor router getData back to ternary
ijjk Jan 27, 2020
27bbb2b
Apply suggestions to build/index.ts
ijjk Jan 27, 2020
d9ee71c
drop return
ijjk Jan 27, 2020
2e8ef84
De-dupe extra escape regex
ijjk Jan 27, 2020
4fe847a
Add param test
ijjk Jan 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions packages/next/build/babel/plugins/next-ssg-transform.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { NodePath, PluginObj } from '@babel/core'
import * as BabelTypes from '@babel/types'
import { SERVER_PROPS_SSG_CONFLICT } from '../../../lib/constants'

const pageComponentVar = '__NEXT_COMP'
const prerenderId = '__N_SSG'
const serverPropsId = '__N_SSP'

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 = {
refs: Set<NodePath<BabelTypes.Identifier>>
isPrerender: boolean
isServerProps: boolean
done: boolean
}

Expand Down Expand Up @@ -44,7 +49,7 @@ function decorateSsgExport(
'=',
t.memberExpression(
t.identifier(pageComponentVar),
t.identifier(prerenderId)
t.identifier(state.isPrerender ? prerenderId : serverPropsId)
),
t.booleanLiteral(true)
),
Expand All @@ -55,6 +60,24 @@ function decorateSsgExport(
})
}

const isDataIdentifier = (name: string, state: PluginState): boolean => {
if (ssgExports.has(name)) {
if (name === EXPORT_NAME_GET_SERVER_PROPS) {
if (state.isPrerender) {
throw new Error(SERVER_PROPS_SSG_CONFLICT)
}
state.isServerProps = true
ijjk marked this conversation as resolved.
Show resolved Hide resolved
} else {
if (state.isServerProps) {
throw new Error(SERVER_PROPS_SSG_CONFLICT)
}
state.isPrerender = true
}
return true
}
return false
}

export default function nextTransformSsg({
types: t,
}: {
Expand Down Expand Up @@ -134,10 +157,11 @@ export default function nextTransformSsg({
enter(_, state) {
state.refs = new Set<NodePath<BabelTypes.Identifier>>()
state.isPrerender = false
state.isServerProps = false
state.done = false
},
exit(path, state) {
if (!state.isPrerender) {
if (!state.isPrerender && !state.isServerProps) {
return
}

Expand Down Expand Up @@ -239,8 +263,7 @@ export default function nextTransformSsg({
const specifiers = path.get('specifiers')
if (specifiers.length) {
specifiers.forEach(s => {
if (ssgExports.has(s.node.exported.name)) {
state.isPrerender = true
if (isDataIdentifier(s.node.exported.name, state)) {
s.remove()
}
})
Expand All @@ -259,8 +282,7 @@ export default function nextTransformSsg({
switch (decl.node.type) {
case 'FunctionDeclaration': {
const name = decl.node.id!.name
if (ssgExports.has(name)) {
state.isPrerender = true
if (isDataIdentifier(name, state)) {
path.remove()
}
break
Expand All @@ -274,8 +296,7 @@ export default function nextTransformSsg({
return
}
const name = d.node.id.name
if (ssgExports.has(name)) {
state.isPrerender = true
if (isDataIdentifier(name, state)) {
d.remove()
}
})
Expand Down
70 changes: 55 additions & 15 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
} from './utils'
import getBaseWebpackConfig from './webpack-config'
import { writeBuildId } from './write-build-id'
import escapeStringRegexp from 'escape-string-regexp'

const fsAccess = promisify(fs.access)
const fsUnlink = promisify(fs.unlink)
Expand Down Expand Up @@ -258,22 +259,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 @@ -405,6 +407,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 additionalSsgPaths = new Map<string, Array<string>>()
const pageInfos = new Map<string, PageInfo>()
const pagesManifest = JSON.parse(await fsReadFile(manifestPath, 'utf8'))
Expand Down Expand Up @@ -502,6 +505,8 @@ export default async function build(dir: string, conf = null): Promise<void> {
additionalSsgPaths.set(page, result.prerenderRoutes)
ssgPageRoutes = result.prerenderRoutes
}
} else if (result.hasServerProps) {
serverPropsPages.add(page)
} else if (result.isStatic && customAppGetInitialProps === false) {
staticPages.add(page)
isStatic = true
Expand All @@ -525,6 +530,41 @@ 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) {
const dataRoute = path.posix.join(
'/_next/data',
buildId,
`${page === '/' ? '/index' : page}.json`
)

routesManifest.serverPropsRoutes[page] = {
page,
dataRouteRegex: isDynamicRoute(page)
? getRouteRegex(dataRoute.replace(/\.json$/, '')).re.source.replace(
/\(\?:\\\/\)\?\$$/,
'\\.json$'
)
: new RegExp(
`^${path.posix.join(
'/_next/data',
escapeStringRegexp(buildId),
`${page === '/' ? '/index' : page}.json`
)}$`
).source,
}
}

await fsWriteFile(
routesManifestPath,
JSON.stringify(routesManifest),
'utf8'
)
}
// Since custom _app.js can wrap the 404 page we have to opt-out of static optimization if it has getInitialProps
// Only export the static 404 when there is no /_error present
const useStatic404 =
Expand Down
19 changes: 17 additions & 2 deletions packages/next/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
Rewrite,
getRedirectStatus,
} from '../lib/check-custom-routes'
import { SSG_GET_INITIAL_PROPS_CONFLICT } from '../lib/constants'
import {
SSG_GET_INITIAL_PROPS_CONFLICT,
SERVER_PROPS_GET_INIT_PROPS_CONFLICT,
SERVER_PROPS_SSG_CONFLICT,
} from '../lib/constants'
import prettyBytes from '../lib/pretty-bytes'
import { recursiveReadDir } from '../lib/recursive-readdir'
import { getRouteMatcher, getRouteRegex } from '../next-server/lib/router/utils'
Expand Down Expand Up @@ -481,6 +485,7 @@ export async function isPageStatic(
): Promise<{
isStatic?: boolean
isHybridAmp?: boolean
hasServerProps?: boolean
hasStaticProps?: boolean
prerenderRoutes?: string[] | undefined
}> {
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(SSG_GET_INITIAL_PROPS_CONFLICT)
}

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

if (hasStaticProps && hasServerProps) {
throw new Error(SERVER_PROPS_SSG_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 @@ -593,10 +607,11 @@ export async function isPageStatic(

const config = mod.config || {}
return {
isStatic: !hasStaticProps && !hasGetInitialProps,
isStatic: !hasStaticProps && !hasGetInitialProps && !hasServerProps,
isHybridAmp: config.amp === 'hybrid',
prerenderRoutes: prerenderPaths,
hasStaticProps,
hasServerProps,
}
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') return {}
Expand Down
15 changes: 13 additions & 2 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,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 @@ -207,6 +208,7 @@ const nextServerlessLoader: loader.Loader = function() {
Document,
buildManifest,
unstable_getStaticProps,
unstable_getServerProps,
unstable_getStaticPaths,
reactLoadableManifest,
canonicalBase: "${canonicalBase}",
Expand Down Expand Up @@ -237,7 +239,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,15 +275,22 @@ const nextServerlessLoader: loader.Loader = function() {
`
: `const nowParams = null;`
}
// make sure to set renderOpts to the correct params e.g. _params
// if provided from worker or params if we're parsing them here
renderOpts.params = _params || params
ijjk marked this conversation as resolved.
Show resolved Hide resolved

let result = await renderToHTML(req, res, "${page}", Object.assign({}, unstable_getStaticProps ? {} : parsedUrl.query, nowParams ? nowParams : params, _params), renderOpts)

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\`
unstable_getServerProps
? \`no-cache, no-store, must-revalidate\`
: \`s-maxage=\${renderOpts.revalidate}, stale-while-revalidate\`
)
res.end(payload)
return null
Expand All @@ -295,6 +304,7 @@ const nextServerlessLoader: loader.Loader = function() {
const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, {
unstable_getStaticProps: undefined,
unstable_getStaticPaths: undefined,
unstable_getServerProps: undefined,
Component: Error
}))
return result
Expand All @@ -304,6 +314,7 @@ const nextServerlessLoader: loader.Loader = function() {
const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, {
unstable_getStaticProps: undefined,
unstable_getStaticPaths: undefined,
unstable_getServerProps: undefined,
Component: Error,
err
}))
Expand Down
2 changes: 1 addition & 1 deletion packages/next/export/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export default async function({
html = components.Component
queryWithAutoExportWarn()
} else {
curRenderOpts = { ...components, ...renderOpts, ampPath }
curRenderOpts = { ...components, ...renderOpts, ampPath, params }
html = await renderMethod(req, res, page, query, curRenderOpts)
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/next/lib/constants.ts
Original file line number Diff line number Diff line change
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 SSG_GET_INITIAL_PROPS_CONFLICT = `You can not use getInitialProps with unstable_getStaticProps. To use SSG, 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_SSG_CONFLICT = `You can not use unstable_getStaticProps with unstable_getServerProps. To use SSG, please remove your unstable_getServerProps`
Loading