From 364b4fe7972ae82df6f594a8e7ecb60b45d2c025 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 26 Feb 2020 12:14:34 -0600 Subject: [PATCH 01/29] Make sure rewrites are handled in serverless mode correctly (#10697) Co-authored-by: Joe Haddad --- .../webpack/loaders/next-serverless-loader.ts | 20 ++--- test/integration/custom-routes/next.config.js | 6 +- .../custom-routes/pages/api/dynamic/[slug].js | 1 + test/integration/custom-routes/server.js | 36 ++++++++ .../custom-routes/test/index.test.js | 85 ++++++++++++++++++- 5 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 test/integration/custom-routes/pages/api/dynamic/[slug].js create mode 100644 test/integration/custom-routes/server.js diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 6d92d5092494..47ab3582dcf4 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -93,6 +93,7 @@ const nextServerlessLoader: loader.Loader = function() { const handleRewrites = ` const getCustomRouteMatcher = pathMatch(true) + const {prepareDestination} = require('next/dist/next-server/server/router') function handleRewrites(parsedUrl) { for (const rewrite of rewrites) { @@ -100,19 +101,14 @@ const nextServerlessLoader: loader.Loader = function() { const params = matcher(parsedUrl.pathname) if (params) { - parsedUrl.query = { - ...parsedUrl.query, - ...params - } - const parsedDest = parse(rewrite.destination) - const destCompiler = pathToRegexp.compile( - \`\${parsedDest.pathname}\${parsedDest.hash || ''}\` + const { parsedDestination } = prepareDestination( + rewrite.destination, + params ) - const newUrl = destCompiler(params) - const parsedNewUrl = parse(newUrl) + Object.assign(parsedUrl.query, parsedDestination.query, params) + delete parsedDestination.query - parsedUrl.pathname = parsedNewUrl.pathname - parsedUrl.hash = parsedNewUrl.hash + Object.assign(parsedUrl, parsedDestination) if (parsedUrl.pathname === '${page}'){ break @@ -170,7 +166,7 @@ const nextServerlessLoader: loader.Loader = function() { ` : '' } - const parsedUrl = parse(req.url, true) + const parsedUrl = handleRewrites(parse(req.url, true)) const params = ${ pageIsDynamicRoute diff --git a/test/integration/custom-routes/next.config.js b/test/integration/custom-routes/next.config.js index 27155c470423..3ff2dca5755d 100644 --- a/test/integration/custom-routes/next.config.js +++ b/test/integration/custom-routes/next.config.js @@ -73,7 +73,11 @@ module.exports = { }, { source: '/api-hello-param/:name', - destination: '/api/hello?name=:name', + destination: '/api/hello?hello=:name', + }, + { + source: '/api-dynamic-param/:name', + destination: '/api/dynamic/:name?hello=:name', }, { source: '/:path/post-321', diff --git a/test/integration/custom-routes/pages/api/dynamic/[slug].js b/test/integration/custom-routes/pages/api/dynamic/[slug].js new file mode 100644 index 000000000000..fd03963a3a95 --- /dev/null +++ b/test/integration/custom-routes/pages/api/dynamic/[slug].js @@ -0,0 +1 @@ +export default async (req, res) => res.json({ query: req.query }) diff --git a/test/integration/custom-routes/server.js b/test/integration/custom-routes/server.js new file mode 100644 index 000000000000..e07441ae678c --- /dev/null +++ b/test/integration/custom-routes/server.js @@ -0,0 +1,36 @@ +const path = require('path') +const http = require('http') + +const server = http.createServer((req, res) => { + const pagePath = page => path.join('.next/serverless/pages/', page) + const render = page => { + require(`./${pagePath(page)}`).render(req, res) + } + const apiCall = page => { + require(`./${pagePath(page)}`).default(req, res) + } + + switch (req.url) { + case '/blog/post-1': { + return render('/blog/[post]') + } + case '/query-rewrite/first/second': { + return render('/with-params') + } + case '/api-hello-param/first': { + return apiCall('/api/hello') + } + case '/api-dynamic-param/first': { + return apiCall('/api/dynamic/[slug]') + } + default: { + res.statusCode(404) + return res.end('404') + } + } +}) + +const port = process.env.PORT || 3000 +server.listen(port, () => { + console.log('ready on', port) +}) diff --git a/test/integration/custom-routes/test/index.test.js b/test/integration/custom-routes/test/index.test.js index e1769fddb9c9..a3436c75ec52 100644 --- a/test/integration/custom-routes/test/index.test.js +++ b/test/integration/custom-routes/test/index.test.js @@ -18,6 +18,7 @@ import { getBrowserBodyText, waitFor, normalizeRegEx, + initNextServerScript, } from 'next-test-utils' jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 @@ -308,7 +309,9 @@ const runTests = (isDev = false) => { it('should handle api rewrite with param successfully', async () => { const data = await renderViaHTTP(appPort, '/api-hello-param/hello') - expect(JSON.parse(data)).toEqual({ query: { name: 'hello' } }) + expect(JSON.parse(data)).toEqual({ + query: { name: 'hello', hello: 'hello' }, + }) }) it('should handle encoded value in the pathname correctly', async () => { @@ -593,10 +596,15 @@ const runTests = (isDev = false) => { source: '/api-hello-regex/(.*)', }, { - destination: '/api/hello?name=:name', + destination: '/api/hello?hello=:name', regex: normalizeRegEx('^\\/api-hello-param(?:\\/([^\\/]+?))$'), source: '/api-hello-param/:name', }, + { + destination: '/api/dynamic/:name?hello=:name', + regex: normalizeRegEx('^\\/api-dynamic-param(?:\\/([^\\/]+?))$'), + source: '/api-dynamic-param/:name', + }, { destination: '/with-params', regex: normalizeRegEx('^(?:\\/([^\\/]+?))\\/post-321$'), @@ -608,6 +616,10 @@ const runTests = (isDev = false) => { page: '/another/[id]', regex: normalizeRegEx('^\\/another\\/([^\\/]+?)(?:\\/)?$'), }, + { + page: '/api/dynamic/[slug]', + regex: normalizeRegEx('^\\/api\\/dynamic\\/([^\\/]+?)(?:\\/)?$'), + }, { page: '/blog/[post]', regex: normalizeRegEx('^\\/blog\\/([^\\/]+?)(?:\\/)?$'), @@ -723,10 +735,77 @@ describe('Custom routes', () => { buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8') }) afterAll(async () => { - await killApp(app) await fs.writeFile(nextConfigPath, nextConfigContent, 'utf8') + await killApp(app) }) runTests() }) + + describe('raw serverless mode', () => { + beforeAll(async () => { + nextConfigContent = await fs.readFile(nextConfigPath, 'utf8') + await fs.writeFile( + nextConfigPath, + nextConfigContent.replace(/\/\/ target/, 'target'), + 'utf8' + ) + await nextBuild(appDir) + + appPort = await findPort() + app = await initNextServerScript(join(appDir, 'server.js'), /ready on/, { + ...process.env, + PORT: appPort, + }) + }) + afterAll(async () => { + await fs.writeFile(nextConfigPath, nextConfigContent, 'utf8') + await killApp(app) + }) + + it('should apply rewrites in lambda correctly for page route', async () => { + const html = await renderViaHTTP(appPort, '/query-rewrite/first/second') + const data = JSON.parse( + cheerio + .load(html)('p') + .text() + ) + expect(data).toEqual({ + first: 'first', + second: 'second', + section: 'first', + name: 'second', + }) + }) + + it('should apply rewrites in lambda correctly for dynamic route', async () => { + const html = await renderViaHTTP(appPort, '/blog/post-1') + expect(html).toContain('post-2') + }) + + it('should apply rewrites in lambda correctly for API route', async () => { + const data = JSON.parse( + await renderViaHTTP(appPort, '/api-hello-param/first') + ) + expect(data).toEqual({ + query: { + name: 'first', + hello: 'first', + }, + }) + }) + + it('should apply rewrites in lambda correctly for dynamic API route', async () => { + const data = JSON.parse( + await renderViaHTTP(appPort, '/api-dynamic-param/first') + ) + expect(data).toEqual({ + query: { + slug: 'first', + name: 'first', + hello: 'first', + }, + }) + }) + }) }) From 395714a475ce454208c1d6e4649d53a26d82dbe4 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 26 Feb 2020 12:26:55 -0600 Subject: [PATCH 02/29] Update url prop handling for pages with new data methods (#10653) * Make sure to show error when url prop is returned for a page * Update test and handle undefined pageProps * Handle empty props * Apply suggestions from code review Co-Authored-By: Tim Neutkens * Update tests * Update to not add url prop for SSG/SSP pages * Update errsh for reserved prop * Update errsh wording some more * Update tests and to warn instead of error * Update reserved prop warning * Include page in url prop warning Co-authored-by: Tim Neutkens Co-authored-by: Joe Haddad --- errors/reserved-page-prop.md | 9 ++++ .../build/babel/plugins/next-ssg-transform.ts | 10 ++-- packages/next/next-server/lib/constants.ts | 2 + .../next-server/server/load-components.ts | 51 +++++++++++++++++-- packages/next/next-server/server/render.tsx | 13 +++++ packages/next/pages/_app.tsx | 15 +++++- .../getserverprops/test/index.test.js | 40 ++++++++++++++- test/integration/prerender/test/index.test.js | 25 +++++++++ 8 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 errors/reserved-page-prop.md diff --git a/errors/reserved-page-prop.md b/errors/reserved-page-prop.md new file mode 100644 index 000000000000..ae83aaababfe --- /dev/null +++ b/errors/reserved-page-prop.md @@ -0,0 +1,9 @@ +# Reserved Page Prop + +#### Why This Error Occurred + +In a page's `getInitialProps` a reserved prop was returned. Currently the only reserved page prop is `url` for legacy reasons. + +#### Possible Ways to Fix It + +Change the name of the prop returned from `getInitialProps` to any other name. diff --git a/packages/next/build/babel/plugins/next-ssg-transform.ts b/packages/next/build/babel/plugins/next-ssg-transform.ts index b4039bbe7616..2a7cea08e6b7 100644 --- a/packages/next/build/babel/plugins/next-ssg-transform.ts +++ b/packages/next/build/babel/plugins/next-ssg-transform.ts @@ -1,10 +1,12 @@ import { NodePath, PluginObj } from '@babel/core' import * as BabelTypes from '@babel/types' import { SERVER_PROPS_SSG_CONFLICT } from '../../../lib/constants' +import { + STATIC_PROPS_ID, + SERVER_PROPS_ID, +} from '../../../next-server/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' @@ -53,7 +55,7 @@ function decorateSsgExport( '=', t.memberExpression( t.identifier(pageComponentVar), - t.identifier(state.isPrerender ? prerenderId : serverPropsId) + t.identifier(state.isPrerender ? STATIC_PROPS_ID : SERVER_PROPS_ID) ), t.booleanLiteral(true) ), @@ -80,7 +82,7 @@ function decorateSsgExport( '=', t.memberExpression( t.identifier((defaultSpecifier as any).local.name), - t.identifier(state.isPrerender ? prerenderId : serverPropsId) + t.identifier(state.isPrerender ? STATIC_PROPS_ID : SERVER_PROPS_ID) ), t.booleanLiteral(true) ), diff --git a/packages/next/next-server/lib/constants.ts b/packages/next/next-server/lib/constants.ts index d52a40da605a..40f97db6f7b4 100644 --- a/packages/next/next-server/lib/constants.ts +++ b/packages/next/next-server/lib/constants.ts @@ -34,3 +34,5 @@ export const ROUTE_NAME_REGEX = /^static[/\\][^/\\]+[/\\]pages[/\\](.*)\.js$/ export const SERVERLESS_ROUTE_NAME_REGEX = /^pages[/\\](.*)\.js$/ export const TEMPORARY_REDIRECT_STATUS = 307 export const PERMANENT_REDIRECT_STATUS = 308 +export const STATIC_PROPS_ID = '__N_SSG' +export const SERVER_PROPS_ID = '__N_SSP' diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index e4852ab52676..b088480e485d 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -5,6 +5,8 @@ import { CLIENT_STATIC_FILES_PATH, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, + STATIC_PROPS_ID, + SERVER_PROPS_ID, } from '../lib/constants' import { join } from 'path' import { requirePage } from './require' @@ -16,6 +18,20 @@ export function interopDefault(mod: any) { return mod.default || mod } +function addComponentPropsId( + Component: any, + getStaticProps: any, + getServerProps: any +) { + // Mark the component with the SSG or SSP id here since we don't run + // the SSG babel transform for server mode + if (getStaticProps) { + Component[STATIC_PROPS_ID] = true + } else if (getServerProps) { + Component[SERVER_PROPS_ID] = true + } +} + export type ManifestItem = { id: number | string name: string @@ -66,11 +82,24 @@ export async function loadComponents( ): Promise { if (serverless) { const Component = await requirePage(pathname, distDir, serverless) + const { + unstable_getStaticProps, + unstable_getStaticPaths, + unstable_getServerProps, + } = Component + + addComponentPropsId( + Component, + unstable_getStaticProps, + unstable_getServerProps + ) + return { Component, pageConfig: Component.config || {}, - unstable_getStaticProps: Component.unstable_getStaticProps, - unstable_getStaticPaths: Component.unstable_getStaticPaths, + unstable_getStaticProps, + unstable_getStaticPaths, + unstable_getServerProps, } as LoadComponentsReturnType } const documentPath = join( @@ -111,6 +140,18 @@ export async function loadComponents( interopDefault(AppMod), ]) + const { + unstable_getServerProps, + unstable_getStaticProps, + unstable_getStaticPaths, + } = ComponentMod + + addComponentPropsId( + Component, + unstable_getStaticProps, + unstable_getServerProps + ) + return { App, Document, @@ -119,8 +160,8 @@ export async function loadComponents( DocumentMiddleware, reactLoadableManifest, pageConfig: ComponentMod.config || {}, - unstable_getServerProps: ComponentMod.unstable_getServerProps, - unstable_getStaticProps: ComponentMod.unstable_getStaticProps, - unstable_getStaticPaths: ComponentMod.unstable_getStaticPaths, + unstable_getServerProps, + unstable_getStaticProps, + unstable_getStaticPaths, } } diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index ae4d7ceefd57..8e848519c1c8 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -546,6 +546,19 @@ export async function renderToHTML( props.pageProps = data.props ;(renderOpts as any).pageData = props } + + if ( + !isSpr && // we only show this warning for legacy pages + !unstable_getServerProps && + process.env.NODE_ENV !== 'production' && + Object.keys(props?.pageProps || {}).includes('url') + ) { + console.warn( + `The prop \`url\` is a reserved prop in Next.js for legacy reasons and will be overridden on page ${pathname}\n` + + `See more info here: https://err.sh/zeit/next.js/reserved-page-prop` + ) + } + // We only need to do this if we want to support calling // _app's getInitialProps for getServerProps if not this can be removed if (isDataReq) return props diff --git a/packages/next/pages/_app.tsx b/packages/next/pages/_app.tsx index 3971441a2433..0132f5ab4825 100644 --- a/packages/next/pages/_app.tsx +++ b/packages/next/pages/_app.tsx @@ -42,8 +42,19 @@ export default class App

extends React.Component< render() { const { router, Component, pageProps } = this.props as AppProps - const url = createUrl(router) - return + + return ( + + ) } } diff --git a/test/integration/getserverprops/test/index.test.js b/test/integration/getserverprops/test/index.test.js index 1c932da181e2..68a7215ce68b 100644 --- a/test/integration/getserverprops/test/index.test.js +++ b/test/integration/getserverprops/test/index.test.js @@ -23,6 +23,7 @@ const nextConfig = join(appDir, 'next.config.js') let app let appPort let buildId +let stderr const expectedManifestRoutes = () => ({ '/something': { @@ -322,6 +323,31 @@ const runTests = (dev = false) => { }) if (dev) { + it('should not show warning from url prop being returned', async () => { + const urlPropPage = join(appDir, 'pages/url-prop.js') + await fs.writeFile( + urlPropPage, + ` + export async function unstable_getServerProps() { + return { + props: { + url: 'something' + } + } + } + + export default ({ url }) =>

url: {url}

+ ` + ) + + const html = await renderViaHTTP(appPort, '/url-prop') + await fs.remove(urlPropPage) + expect(stderr).not.toMatch( + /The prop `url` is a reserved prop in Next.js for legacy reasons and will be overridden on page \/url-prop/ + ) + expect(html).toMatch(/url:.*?something/) + }) + it('should show error for extra keys returned from getServerProps', async () => { const html = await renderViaHTTP(appPort, '/invalid-keys') expect(html).toContain( @@ -365,8 +391,13 @@ const runTests = (dev = false) => { describe('unstable_getServerProps', () => { describe('dev mode', () => { beforeAll(async () => { + stderr = '' appPort = await findPort() - app = await launchApp(appDir, appPort) + app = await launchApp(appDir, appPort, { + onStderr(msg) { + stderr += msg + }, + }) buildId = 'development' }) afterAll(() => killApp(app)) @@ -382,8 +413,13 @@ describe('unstable_getServerProps', () => { 'utf8' ) await nextBuild(appDir) + stderr = '' appPort = await findPort() - app = await nextStart(appDir, appPort) + app = await nextStart(appDir, appPort, { + onStderr(msg) { + stderr += msg + }, + }) buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8') }) afterAll(() => killApp(app)) diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index f5431a853cd4..309f06690be3 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -428,6 +428,31 @@ const runTests = (dev = false, looseMode = false) => { // ) // }) + it('should not show warning from url prop being returned', async () => { + const urlPropPage = join(appDir, 'pages/url-prop.js') + await fs.writeFile( + urlPropPage, + ` + export async function unstable_getStaticProps() { + return { + props: { + url: 'something' + } + } + } + + export default ({ url }) =>

url: {url}

+ ` + ) + + const html = await renderViaHTTP(appPort, '/url-prop') + await fs.remove(urlPropPage) + expect(stderr).not.toMatch( + /The prop `url` is a reserved prop in Next.js for legacy reasons and will be overridden on page \/url-prop/ + ) + expect(html).toMatch(/url:.*?something/) + }) + it('should always show fallback for page not in getStaticPaths', async () => { const html = await renderViaHTTP(appPort, '/blog/post-321') const $ = cheerio.load(html) From 6dea45bdc930dd2d9d602a1c3430b5b3422a42f1 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 26 Feb 2020 13:35:02 -0600 Subject: [PATCH 03/29] Add dataRoutes field to routes-manifest for SSG and serverProps routes (#10622) * Add dataRoutes field to routes-manifest for SSG and serverProps routes * Update routes-manifest test --- packages/next/build/index.ts | 15 +-- .../dynamic-routing/test/index.test.js | 31 ++++++ .../getserverprops/test/index.test.js | 98 ++++++++++--------- test/integration/prerender/test/index.test.js | 77 +++++++++++++++ 4 files changed, 169 insertions(+), 52 deletions(-) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 841f5472178b..849dff8b950b 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -569,12 +569,13 @@ export default async function build(dir: string, conf = null): Promise { ) staticCheckWorkers.end() - if (serverPropsPages.size > 0) { + if (serverPropsPages.size > 0 || ssgPages.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) { + // data routes since we can't determine these until after build + routesManifest.dataRoutes = getSortedRoutes([ + ...serverPropsPages, + ...ssgPages, + ]).map(page => { const pagePath = normalizePagePath(page) const dataRoute = path.posix.join( '/_next/data', @@ -582,7 +583,7 @@ export default async function build(dir: string, conf = null): Promise { `${pagePath}.json` ) - routesManifest.serverPropsRoutes[page] = { + return { page, dataRouteRegex: isDynamicRoute(page) ? getRouteRegex(dataRoute.replace(/\.json$/, '')).re.source.replace( @@ -597,7 +598,7 @@ export default async function build(dir: string, conf = null): Promise { )}$` ).source, } - } + }) await fsWriteFile( routesManifestPath, diff --git a/test/integration/dynamic-routing/test/index.test.js b/test/integration/dynamic-routing/test/index.test.js index 77978c4e95cb..4c73f4430f55 100644 --- a/test/integration/dynamic-routing/test/index.test.js +++ b/test/integration/dynamic-routing/test/index.test.js @@ -15,6 +15,7 @@ import { normalizeRegEx, } from 'next-test-utils' import cheerio from 'cheerio' +import escapeRegex from 'escape-string-regexp' jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 @@ -485,6 +486,10 @@ function runTests(dev) { route.regex = normalizeRegEx(route.regex) } + for (const route of manifest.dataRoutes) { + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) + } + expect(manifest).toEqual({ version: 1, pages404: true, @@ -492,6 +497,32 @@ function runTests(dev) { headers: [], rewrites: [], redirects: [], + dataRoutes: [ + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/all\\-ssg\\/(.+?)\\.json$` + ), + page: '/p1/p2/all-ssg/[...rest]', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/nested\\-all\\-ssg\\/(.+?)\\.json$` + ), + page: '/p1/p2/nested-all-ssg/[...rest]', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/p1\\/p2\\/predefined\\-ssg\\/(.+?)\\.json$` + ), + page: '/p1/p2/predefined-ssg/[...rest]', + }, + ], dynamicRoutes: [ { page: '/blog/[name]/comment/[id]', diff --git a/test/integration/getserverprops/test/index.test.js b/test/integration/getserverprops/test/index.test.js index 68a7215ce68b..84707e9eccf5 100644 --- a/test/integration/getserverprops/test/index.test.js +++ b/test/integration/getserverprops/test/index.test.js @@ -25,72 +25,72 @@ let appPort let buildId let stderr -const expectedManifestRoutes = () => ({ - '/something': { - page: '/something', +const expectedManifestRoutes = () => [ + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/something.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/index.json$` ), + page: '/', }, - '/blog/[post]': { - page: '/blog/[post]', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog\\/([^/]+?)\\.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/another.json$` ), + page: '/another', }, - '/': { - page: '/', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/index.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog.json$` ), + page: '/blog', }, - '/default-revalidate': { - page: '/default-revalidate', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/default-revalidate.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog\\/([^\\/]+?)\\.json$` ), + page: '/blog/[post]', }, - '/catchall/[...path]': { - page: '/catchall/[...path]', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/catchall\\/(.+?)\\.json$` + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$` ), + page: '/blog/[post]/[comment]', }, - '/blog': { - page: '/blog', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/catchall\\/(.+?)\\.json$` ), + page: '/catchall/[...path]', }, - '/blog/[post]/[comment]': { - page: '/blog/[post]/[comment]', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/blog\\/([^/]+?)\\/([^/]+?)\\.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/default-revalidate.json$` ), + page: '/default-revalidate', }, - '/user/[user]/profile': { - page: '/user/[user]/profile', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex( - buildId - )}\\/user\\/([^/]+?)\\/profile\\.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/invalid-keys.json$` ), + page: '/invalid-keys', }, - '/another': { - page: '/another', + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/another.json$` + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/something.json$` ), + page: '/something', }, - '/invalid-keys': { + { dataRouteRegex: normalizeRegEx( - `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/invalid-keys.json$` + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/user\\/([^\\/]+?)\\/profile\\.json$` ), - page: '/invalid-keys', + page: '/user/[user]/profile', }, -}) +] const navigateTest = (dev = false) => { it('should navigate between pages successfully', async () => { @@ -209,7 +209,10 @@ const runTests = (dev = false) => { expect(JSON.parse(query)).toEqual({ path: ['first'] }) const data = JSON.parse( - await renderViaHTTP(appPort, `/_next/data/${buildId}/catchall/first.json`) + await renderViaHTTP( + appPort, + `/_next/data/${escapeRegex(buildId)}/catchall/first.json` + ) ) expect(data.pageProps.params).toEqual({ path: ['first'] }) @@ -217,7 +220,10 @@ const runTests = (dev = false) => { it('should return data correctly', async () => { const data = JSON.parse( - await renderViaHTTP(appPort, `/_next/data/${buildId}/something.json`) + await renderViaHTTP( + appPort, + `/_next/data/${escapeRegex(buildId)}/something.json` + ) ) expect(data.pageProps.world).toBe('world') }) @@ -226,7 +232,7 @@ const runTests = (dev = false) => { const data = JSON.parse( await renderViaHTTP( appPort, - `/_next/data/${buildId}/something.json?another=thing` + `/_next/data/${escapeRegex(buildId)}/something.json?another=thing` ) ) expect(data.pageProps.query.another).toBe('thing') @@ -234,7 +240,10 @@ const runTests = (dev = false) => { it('should return data correctly for dynamic page', async () => { const data = JSON.parse( - await renderViaHTTP(appPort, `/_next/data/${buildId}/blog/post-1.json`) + await renderViaHTTP( + appPort, + `/_next/data/${escapeRegex(buildId)}/blog/post-1.json` + ) ) expect(data.pageProps.post).toBe('post-1') }) @@ -367,15 +376,14 @@ const runTests = (dev = false) => { }) it('should output routes-manifest correctly', async () => { - const { serverPropsRoutes } = await fs.readJSON( + const { dataRoutes } = await fs.readJSON( join(appDir, '.next/routes-manifest.json') ) - for (const key of Object.keys(serverPropsRoutes)) { - const val = serverPropsRoutes[key].dataRouteRegex - serverPropsRoutes[key].dataRouteRegex = normalizeRegEx(val) + for (const route of dataRoutes) { + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) } - expect(serverPropsRoutes).toEqual(expectedManifestRoutes()) + expect(dataRoutes).toEqual(expectedManifestRoutes()) }) it('should set no-cache, no-store, must-revalidate header', async () => { diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 309f06690be3..7d52eaed288e 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -589,6 +589,83 @@ const runTests = (dev = false, looseMode = false) => { }) } + it('outputs dataRoutes in routes-manifest correctly', async () => { + const { dataRoutes } = JSON.parse( + await fs.readFile(join(appDir, '.next/routes-manifest.json'), 'utf8') + ) + + for (const route of dataRoutes) { + route.dataRouteRegex = normalizeRegEx(route.dataRouteRegex) + } + + expect(dataRoutes).toEqual([ + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/index.json$` + ), + page: '/', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/another.json$` + ), + page: '/another', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/blog.json$` + ), + page: '/blog', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/blog\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/blog\\/([^\\/]+?)\\/([^\\/]+?)\\.json$` + ), + page: '/blog/[post]/[comment]', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/catchall\\/(.+?)\\.json$` + ), + page: '/catchall/[...slug]', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/default-revalidate.json$` + ), + page: '/default-revalidate', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex(buildId)}\\/something.json$` + ), + page: '/something', + }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/user\\/([^\\/]+?)\\/profile\\.json$` + ), + page: '/user/[user]/profile', + }, + ]) + }) + it('outputs a prerender-manifest correctly', async () => { const manifest = JSON.parse( await fs.readFile(join(appDir, '.next/prerender-manifest.json'), 'utf8') From b3ffdabbadbfa8f3ebde55df419ae19e27217ece Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 26 Feb 2020 13:49:08 -0600 Subject: [PATCH 04/29] v9.2.3-canary.13 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index 16f7b566a72b..d7ce38857e23 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.12" + "version": "9.2.3-canary.13" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 964c2db771de..6036cbedc0d7 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 1fbd0f861d5d..9dc52445a1f5 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 7e738971e6e1..63b50800e02c 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index be4a9963fb26..27c703f307cb 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index e6c68f5361de..64968e3e1954 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 8856bcab3352..4f01cec1aede 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 84df2a082b10..5c15bfa50511 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 3f8906b4f757..2ddb5038971f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.12", + "version": "9.2.3-canary.13", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.12", + "@next/polyfill-nomodule": "9.2.3-canary.13", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From 47ff1eb95af57724aa178cbd18a781a5a58c6738 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Thu, 27 Feb 2020 07:23:28 -0500 Subject: [PATCH 05/29] Ability to Disable SSG Fallback (#10701) * Ability to Disable SSG Fallback * Throw error when value is missing * Fix existing tests * Adjust error message * Do not render fallback at build time for `fallback: false` page * Fix existing fallback behavior * fix build * fix version * fix some tests * Fix last test * Add docs for get static paths * Add explicit mode tests * test for fallback error message --- errors/invalid-getstaticpaths-value.md | 25 +++- packages/next/build/index.ts | 41 +++++-- packages/next/build/utils.ts | 27 +++-- packages/next/export/worker.js | 2 +- .../next-server/server/load-components.ts | 1 + .../next/next-server/server/next-server.ts | 92 +++++++++----- packages/next/next-server/server/spr-cache.ts | 2 +- .../pages/[slug].js | 2 +- .../pages/p1/p2/all-ssg/[...rest].js | 1 + .../p1/p2/nested-all-ssg/[...rest]/index.js | 1 + .../pages/p1/p2/predefined-ssg/[...rest].js | 1 + .../pages/index.js.alt | 2 +- .../pages/[...slug].js | 2 +- .../pages/[foo]/[post].js | 2 +- .../prerender/pages/blog/[post]/[comment].js | 1 + .../prerender/pages/blog/[post]/index.js | 1 + .../pages/catchall-explicit/[...slug].js | 30 +++++ .../prerender/pages/catchall/[...slug].js | 1 + .../prerender/pages/user/[user]/profile.js | 2 +- test/integration/prerender/test/index.test.js | 113 +++++++++++++++++- 20 files changed, 292 insertions(+), 57 deletions(-) create mode 100644 test/integration/prerender/pages/catchall-explicit/[...slug].js diff --git a/errors/invalid-getstaticpaths-value.md b/errors/invalid-getstaticpaths-value.md index 49ba0064159b..47dfe60a7bba 100644 --- a/errors/invalid-getstaticpaths-value.md +++ b/errors/invalid-getstaticpaths-value.md @@ -11,7 +11,30 @@ Make sure to return the following shape from `unstable_getStaticPaths`: ```js export async function unstable_getStaticPaths() { return { - paths: Array + paths: Array, + fallback: boolean } } ``` + +There are two required properties: + +1. `paths`: this property is an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of URLs ("paths") that should be statically generated at build-time. The returned paths must match the dynamic route shape. + - You may return a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) or an [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) that explicitly defines all URL `params`. + ```js + // pages/blog/[slug].js + export async function unstable_getStaticPaths() { + return { + paths: [ + // String variant: + '/blog/first-post', + // Object variant: + { params: { slug: 'second-post' } }, + ], + fallback: true, + } + } + ``` +1. `fallback`: this property is a [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), specifying whether or not a fallback version of this page should be generated. + - Enabling `fallback` (via `true`) allows you to return a subset of all the possible paths that should be statically generated. At runtime, Next.js will statically generate the remaining paths the **first time they are requested**. Consecutive calls to the path will be served as-if it was statically generated at build-time. This reduces build times when dealing with thousands or millions of pages. + - Disabling `fallback` (via `false`) requires you return the full collection of paths you would like to statically generate at build-time. At runtime, any path that was not generated at build-time **will 404**. diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 849dff8b950b..1ce20330f19f 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -90,13 +90,13 @@ export type SsgRoute = { export type DynamicSsgRoute = { routeRegex: string - fallback: string + fallback: string | false dataRoute: string dataRouteRegex: string } export type PrerenderManifest = { - version: number + version: 2 routes: { [route: string]: SsgRoute } dynamicRoutes: { [route: string]: DynamicSsgRoute } preview: __ApiPreviewProps @@ -432,6 +432,7 @@ export default async function build(dir: string, conf = null): Promise { const buildManifestPath = path.join(distDir, BUILD_MANIFEST) const ssgPages = new Set() + const ssgFallbackPages = new Set() const staticPages = new Set() const invalidPages = new Set() const hybridAmpPages = new Set() @@ -478,6 +479,7 @@ export default async function build(dir: string, conf = null): Promise { let isStatic = false let isHybridAmp = false let ssgPageRoutes: string[] | null = null + let hasSsgFallback: boolean = false pagesManifest[page] = bundleRelative.replace(/\\/g, '/') @@ -533,6 +535,10 @@ export default async function build(dir: string, conf = null): Promise { additionalSsgPaths.set(page, result.prerenderRoutes) ssgPageRoutes = result.prerenderRoutes } + if (result.prerenderFallback) { + hasSsgFallback = true + ssgFallbackPages.add(page) + } } else if (result.hasServerProps) { serverPropsPages.add(page) } else if (result.isStatic && customAppGetInitialProps === false) { @@ -564,6 +570,7 @@ export default async function build(dir: string, conf = null): Promise { isSsg, isHybridAmp, ssgPageRoutes, + hasSsgFallback, }) }) ) @@ -669,9 +676,15 @@ export default async function build(dir: string, conf = null): Promise { if (isDynamicRoute(page)) { tbdPrerenderRoutes.push(page) - // Override the rendering for the dynamic page to be treated as a - // fallback render. - defaultMap[page] = { page, query: { __nextFallback: true } } + if (ssgFallbackPages.has(page)) { + // Override the rendering for the dynamic page to be treated as a + // fallback render. + defaultMap[page] = { page, query: { __nextFallback: true } } + } else { + // Remove dynamically routed pages from the default path map when + // fallback behavior is disabled. + delete defaultMap[page] + } } }) // Append the "well-known" routes we should prerender for, e.g. blog @@ -736,12 +749,16 @@ export default async function build(dir: string, conf = null): Promise { for (const page of combinedPages) { const isSsg = ssgPages.has(page) + const isSsgFallback = ssgFallbackPages.has(page) const isDynamic = isDynamicRoute(page) const hasAmp = hybridAmpPages.has(page) let file = normalizePagePath(page) - // We should always have an HTML file to move for each page - await moveExportedPage(page, file, isSsg, 'html') + // The dynamic version of SSG pages are only prerendered if the fallback + // is enabled. Below, we handle the specific prerenders of these. + if (!(isSsg && isDynamic && !isSsgFallback)) { + await moveExportedPage(page, file, isSsg, 'html') + } if (hasAmp) { await moveExportedPage(`${page}.amp`, `${file}.amp`, isSsg, 'html') @@ -760,7 +777,7 @@ export default async function build(dir: string, conf = null): Promise { } } else { // For a dynamic SSG page, we did not copy its data exports and only - // copy the fallback HTML file. + // copy the fallback HTML file (if present). // We must also copy specific versions of this page as defined by // `unstable_getStaticPaths` (additionalSsgPaths). const extraRoutes = additionalSsgPaths.get(page) || [] @@ -814,14 +831,16 @@ export default async function build(dir: string, conf = null): Promise { finalDynamicRoutes[tbdRoute] = { routeRegex: getRouteRegex(tbdRoute).re.source, dataRoute, - fallback: `${normalizedRoute}.html`, + fallback: ssgFallbackPages.has(tbdRoute) + ? `${normalizedRoute}.html` + : false, dataRouteRegex: getRouteRegex( dataRoute.replace(/\.json$/, '') ).re.source.replace(/\(\?:\\\/\)\?\$$/, '\\.json$'), } }) const prerenderManifest: PrerenderManifest = { - version: 1, + version: 2, routes: finalPrerenderRoutes, dynamicRoutes: finalDynamicRoutes, preview: previewProps, @@ -834,7 +853,7 @@ export default async function build(dir: string, conf = null): Promise { ) } else { const prerenderManifest: PrerenderManifest = { - version: 1, + version: 2, routes: {}, dynamicRoutes: {}, preview: previewProps, diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 8b9d19cb6978..65e563c2ae5d 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -42,6 +42,7 @@ export interface PageInfo { static: boolean isSsg: boolean ssgPageRoutes: string[] | null + hasSsgFallback: boolean serverBundle: string } @@ -500,7 +501,7 @@ export async function getPageSizeInKb( export async function buildStaticPaths( page: string, unstable_getStaticPaths: Unstable_getStaticPaths -): Promise> { +): Promise<{ paths: string[]; fallback: boolean }> { const prerenderPaths = new Set() const _routeRegex = getRouteRegex(page) const _routeMatcher = getRouteMatcher(_routeRegex) @@ -511,7 +512,7 @@ export async function buildStaticPaths( const staticPathsResult = await unstable_getStaticPaths() const expectedReturnVal = - `Expected: { paths: [] }\n` + + `Expected: { paths: [], fallback: boolean }\n` + `See here for more info: https://err.sh/zeit/next.js/invalid-getstaticpaths-value` if ( @@ -525,7 +526,7 @@ export async function buildStaticPaths( } const invalidStaticPathKeys = Object.keys(staticPathsResult).filter( - key => key !== 'paths' + key => !(key === 'paths' || key === 'fallback') ) if (invalidStaticPathKeys.length > 0) { @@ -536,6 +537,13 @@ export async function buildStaticPaths( ) } + if (typeof staticPathsResult.fallback !== 'boolean') { + throw new Error( + `The \`fallback\` key must be returned from unstable_getStaticProps in ${page}.\n` + + expectedReturnVal + ) + } + const toPrerender = staticPathsResult.paths if (!Array.isArray(toPrerender)) { @@ -601,7 +609,7 @@ export async function buildStaticPaths( } }) - return [...prerenderPaths] + return { paths: [...prerenderPaths], fallback: staticPathsResult.fallback } } export async function isPageStatic( @@ -614,6 +622,7 @@ export async function isPageStatic( hasServerProps?: boolean hasStaticProps?: boolean prerenderRoutes?: string[] | undefined + prerenderFallback?: boolean | undefined }> { try { require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig) @@ -667,11 +676,12 @@ export async function isPageStatic( } let prerenderRoutes: Array | undefined + let prerenderFallback: boolean | undefined if (hasStaticProps && hasStaticPaths) { - prerenderRoutes = await buildStaticPaths( - page, - mod.unstable_getStaticPaths - ) + ;({ + paths: prerenderRoutes, + fallback: prerenderFallback, + } = await buildStaticPaths(page, mod.unstable_getStaticPaths)) } const config = mod.config || {} @@ -679,6 +689,7 @@ export async function isPageStatic( isStatic: !hasStaticProps && !hasGetInitialProps && !hasServerProps, isHybridAmp: config.amp === 'hybrid', prerenderRoutes, + prerenderFallback, hasStaticProps, hasServerProps, } diff --git a/packages/next/export/worker.js b/packages/next/export/worker.js index 3997d954e3cf..ae5b4bd0b0d7 100644 --- a/packages/next/export/worker.js +++ b/packages/next/export/worker.js @@ -264,7 +264,7 @@ export default async function({ return results } catch (error) { console.error( - `\nError occurred prerendering page "${path}" https://err.sh/next.js/prerender-error:\n` + + `\nError occurred prerendering page "${path}". Read more: https://err.sh/next.js/prerender-error:\n` + error ) return { ...results, error: true } diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index b088480e485d..e4052770e8ae 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -52,6 +52,7 @@ type Unstable_getStaticProps = (ctx: { export type Unstable_getStaticPaths = () => Promise<{ paths: Array + fallback: boolean }> type Unstable_getServerProps = (context: { diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index e4fbcc676cbf..c02696bcf707 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -6,6 +6,7 @@ import nanoid from 'next/dist/compiled/nanoid/index.js' import { join, resolve, sep } from 'path' import { parse as parseQs, ParsedUrlQuery } from 'querystring' import { format as formatUrl, parse as parseUrl, UrlWithParsedQuery } from 'url' +import { PrerenderManifest } from '../../build' import { getRedirectStatus, Header, @@ -292,15 +293,17 @@ export default class Server { return require(join(this.distDir, ROUTES_MANIFEST)) } - private _cachedPreviewProps: __ApiPreviewProps | undefined - protected getPreviewProps(): __ApiPreviewProps { - if (this._cachedPreviewProps) { - return this._cachedPreviewProps + private _cachedPreviewManifest: PrerenderManifest | undefined + protected getPrerenderManifest(): PrerenderManifest { + if (this._cachedPreviewManifest) { + return this._cachedPreviewManifest } - return (this._cachedPreviewProps = require(join( - this.distDir, - PRERENDER_MANIFEST - )).preview) + const manifest = require(join(this.distDir, PRERENDER_MANIFEST)) + return (this._cachedPreviewManifest = manifest) + } + + protected getPreviewProps(): __ApiPreviewProps { + return this.getPrerenderManifest().preview } protected generateRoutes(): { @@ -870,7 +873,7 @@ export default class Server { pathname: string, { components, query }: FindComponentsResult, opts: any - ): Promise { + ): Promise { // we need to ensure the status code if /404 is visited directly if (pathname === '/404') { res.statusCode = 404 @@ -1029,24 +1032,35 @@ export default class Server { // we lazy load the staticPaths to prevent the user // from waiting on them for the page to load in dev mode let staticPaths: string[] | undefined + let hasStaticFallback = false - if (!isProduction && hasStaticPaths) { - const __getStaticPaths = async () => { - const paths = await this.staticPathsWorker!.loadStaticPaths( - this.distDir, - this.buildId, - pathname, - !this.renderOpts.dev && this._isLikeServerless - ) - return paths + if (hasStaticPaths) { + if (isProduction) { + // `staticPaths` is intentionally set to `undefined` as it should've + // been caught above when checking disk data. + staticPaths = undefined + + // Read whether or not fallback should exist from the manifest. + hasStaticFallback = + typeof this.getPrerenderManifest().dynamicRoutes[pathname] + .fallback === 'string' + } else { + const __getStaticPaths = async () => { + const paths = await this.staticPathsWorker!.loadStaticPaths( + this.distDir, + this.buildId, + pathname, + !this.renderOpts.dev && this._isLikeServerless + ) + return paths + } + ;({ paths: staticPaths, fallback: hasStaticFallback } = ( + await withCoalescedInvoke(__getStaticPaths)( + `staticPaths-${pathname}`, + [] + ) + ).value) } - - staticPaths = ( - await withCoalescedInvoke(__getStaticPaths)( - `staticPaths-${pathname}`, - [] - ) - ).value } // const isForcedBlocking = @@ -1074,6 +1088,16 @@ export default class Server { // `getStaticPaths` (isProduction || !staticPaths || !staticPaths.includes(urlPathname)) ) { + if ( + // In development, fall through to render to handle missing + // getStaticPaths. + (isProduction || staticPaths) && + // When fallback isn't present, abort this render so we 404 + !hasStaticFallback + ) { + return false + } + let html: string // Production already emitted the fallback as static HTML. @@ -1137,13 +1161,16 @@ export default class Server { try { const result = await this.findPageComponents(pathname, query) if (result) { - return await this.renderToHTMLWithComponents( + const result2 = await this.renderToHTMLWithComponents( req, res, pathname, result, { ...this.renderOpts, amphtml, hasAmp } ) + if (result2 !== false) { + return result2 + } } if (this.dynamicRoutes) { @@ -1159,7 +1186,7 @@ export default class Server { params ) if (result) { - return await this.renderToHTMLWithComponents( + const result2 = await this.renderToHTMLWithComponents( req, res, dynamicRoute.page, @@ -1171,6 +1198,9 @@ export default class Server { hasAmp, } ) + if (result2 !== false) { + return result2 + } } } } @@ -1224,9 +1254,9 @@ export default class Server { result = await this.findPageComponents('/_error', query) } - let html + let html: string | null try { - html = await this.renderToHTMLWithComponents( + const result2 = await this.renderToHTMLWithComponents( req, res, using404Page ? '/404' : '/_error', @@ -1236,6 +1266,10 @@ export default class Server { err, } ) + if (result2 === false) { + throw new Error('invariant: failed to render error page') + } + html = result2 } catch (err) { console.error(err) res.statusCode = 500 diff --git a/packages/next/next-server/server/spr-cache.ts b/packages/next/next-server/server/spr-cache.ts index 072496d9c036..c449fcd331db 100644 --- a/packages/next/next-server/server/spr-cache.ts +++ b/packages/next/next-server/server/spr-cache.ts @@ -74,7 +74,7 @@ export function initializeSprCache({ if (dev) { prerenderManifest = { - version: -1, + version: -1 as any, // letting us know this doesn't conform to spec routes: {}, dynamicRoutes: {}, preview: null as any, // `preview` is special case read in next-dev-server diff --git a/test/integration/catches-missing-getStaticProps/pages/[slug].js b/test/integration/catches-missing-getStaticProps/pages/[slug].js index 9c4668013529..7a87525aacc9 100644 --- a/test/integration/catches-missing-getStaticProps/pages/[slug].js +++ b/test/integration/catches-missing-getStaticProps/pages/[slug].js @@ -1,5 +1,5 @@ export async function unstable_getStaticPaths() { - return { paths: ['/hello', '/world'] } + return { paths: ['/hello', '/world'], fallback: true } } export default () =>

something is missing 🤔

diff --git a/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js b/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js index 0d054a972ace..85ace5dad12e 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js +++ b/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js @@ -9,6 +9,7 @@ export function unstable_getStaticProps({ params }) { export function unstable_getStaticPaths() { return { paths: [], + fallback: true, } } diff --git a/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js b/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js index cb57d820b359..03418a2de5c7 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js +++ b/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js @@ -15,6 +15,7 @@ export function unstable_getStaticProps({ params }) { export function unstable_getStaticPaths() { return { paths: [], + fallback: true, } } diff --git a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js index 72abcefb0852..55cf9c8a29e8 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js +++ b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js @@ -12,6 +12,7 @@ export function unstable_getStaticPaths() { `/p1/p2/predefined-ssg/one-level`, `/p1/p2/predefined-ssg/1st-level/2nd-level`, ], + fallback: true, } } diff --git a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt index 910eef220630..4756984cc3a9 100644 --- a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt +++ b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt @@ -1,6 +1,6 @@ export const unstable_getStaticPaths = async () => { return { - props: { world: 'world' } + props: { world: 'world' }, fallback: true } } diff --git a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js index 8ef33a193719..f8413c5dc5ff 100644 --- a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js +++ b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js @@ -2,7 +2,7 @@ import React from 'react' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return { paths: [{ params: { slug: 'hello' } }] } + return { paths: [{ params: { slug: 'hello' } }], fallback: true } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js index 4e0933f554fc..ef80ed41e876 100644 --- a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js +++ b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js @@ -2,7 +2,7 @@ import React from 'react' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return { paths: [{ foo: 'bad', baz: 'herro' }] } + return { paths: [{ foo: 'bad', baz: 'herro' }], fallback: true } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender/pages/blog/[post]/[comment].js b/test/integration/prerender/pages/blog/[post]/[comment].js index a64b89b540f2..0c1174ffbb77 100644 --- a/test/integration/prerender/pages/blog/[post]/[comment].js +++ b/test/integration/prerender/pages/blog/[post]/[comment].js @@ -8,6 +8,7 @@ export async function unstable_getStaticPaths() { '/blog/post-1/comment-1', { params: { post: 'post-2', comment: 'comment-2' } }, ], + fallback: true, } } diff --git a/test/integration/prerender/pages/blog/[post]/index.js b/test/integration/prerender/pages/blog/[post]/index.js index e96ceaf1a276..2b63657aae2d 100644 --- a/test/integration/prerender/pages/blog/[post]/index.js +++ b/test/integration/prerender/pages/blog/[post]/index.js @@ -13,6 +13,7 @@ export async function unstable_getStaticPaths() { '/blog/post.1', '/blog/post.1', // handle duplicates ], + fallback: true, } } diff --git a/test/integration/prerender/pages/catchall-explicit/[...slug].js b/test/integration/prerender/pages/catchall-explicit/[...slug].js new file mode 100644 index 000000000000..e992e8c79bbd --- /dev/null +++ b/test/integration/prerender/pages/catchall-explicit/[...slug].js @@ -0,0 +1,30 @@ +export async function unstable_getStaticProps({ params: { slug } }) { + if (slug[0] === 'delayby3s') { + await new Promise(resolve => setTimeout(resolve, 3000)) + } + + return { + props: { + slug, + }, + revalidate: 1, + } +} + +export async function unstable_getStaticPaths() { + return { + paths: [ + { params: { slug: ['first'] } }, + '/catchall-explicit/second', + { params: { slug: ['another', 'value'] } }, + '/catchall-explicit/hello/another', + ], + fallback: false, + } +} + +export default ({ slug }) => { + // Important to not check for `slug` existence (testing that build does not + // render fallback version and error) + return

Hi {slug.join(' ')}

+} diff --git a/test/integration/prerender/pages/catchall/[...slug].js b/test/integration/prerender/pages/catchall/[...slug].js index b0010c612446..5a4834499e3e 100644 --- a/test/integration/prerender/pages/catchall/[...slug].js +++ b/test/integration/prerender/pages/catchall/[...slug].js @@ -21,6 +21,7 @@ export async function unstable_getStaticPaths() { { params: { slug: ['another', 'value'] } }, '/catchall/hello/another', ], + fallback: true, } } diff --git a/test/integration/prerender/pages/user/[user]/profile.js b/test/integration/prerender/pages/user/[user]/profile.js index 47f3bcd3cd62..a80e175b6d20 100644 --- a/test/integration/prerender/pages/user/[user]/profile.js +++ b/test/integration/prerender/pages/user/[user]/profile.js @@ -3,7 +3,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return { paths: [] } + return { paths: [], fallback: true } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 7d52eaed288e..823437388dc2 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -92,6 +92,26 @@ const expectedManifestRoutes = () => ({ initialRevalidateSeconds: 10, srcRoute: '/blog/[post]', }, + '/catchall-explicit/another/value': { + dataRoute: `/_next/data/${buildId}/catchall-explicit/another/value.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/first': { + dataRoute: `/_next/data/${buildId}/catchall-explicit/first.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/hello/another': { + dataRoute: `/_next/data/${buildId}/catchall-explicit/hello/another.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, + '/catchall-explicit/second': { + dataRoute: `/_next/data/${buildId}/catchall-explicit/second.json`, + initialRevalidateSeconds: 1, + srcRoute: '/catchall-explicit/[...slug]', + }, '/another': { dataRoute: `/_next/data/${buildId}/another.json`, initialRevalidateSeconds: 1, @@ -418,6 +438,49 @@ const runTests = (dev = false, looseMode = false) => { ) }) + it('should support prerendered catchall-explicit route (nested)', async () => { + const html = await renderViaHTTP( + appPort, + '/catchall-explicit/another/value' + ) + const $ = cheerio.load(html) + + expect( + JSON.parse( + cheerio + .load(html)('#__NEXT_DATA__') + .text() + ).isFallback + ).toBe(false) + expect($('#catchall').text()).toMatch(/Hi.*?another value/) + }) + + it('should support prerendered catchall-explicit route (single)', async () => { + const html = await renderViaHTTP(appPort, '/catchall-explicit/second') + const $ = cheerio.load(html) + + expect( + JSON.parse( + cheerio + .load(html)('#__NEXT_DATA__') + .text() + ).isFallback + ).toBe(false) + expect($('#catchall').text()).toMatch(/Hi.*?second/) + }) + + if (!looseMode) { + it('should 404 for a missing catchall explicit route', async () => { + const res = await fetchViaHTTP( + appPort, + '/catchall-explicit/notreturnedinpaths' + ) + expect(res.status).toBe(404) + const html = await res.text() + expect(html).toMatch(/This page could not be found/) + }) + } + if (dev) { // TODO: re-enable when this is supported in dev // it('should show error when rewriting to dynamic SSG page', async () => { @@ -561,6 +624,36 @@ const runTests = (dev = false, looseMode = false) => { } }) + it('should error on dynamic page without getStaticPaths returning fallback property', async () => { + const curPage = join(__dirname, '../pages/temp2/[slug].js') + await fs.mkdirp(dirname(curPage)) + await fs.writeFile( + curPage, + ` + export async function unstable_getStaticPaths() { + return { + paths: [] + } + } + export async function unstable_getStaticProps() { + return { + props: { + hello: 'world' + } + } + } + export default () => 'oops' + ` + ) + await waitFor(1000) + try { + const html = await renderViaHTTP(appPort, '/temp2/hello') + expect(html).toMatch(/`fallback` key must be returned from/) + } finally { + await fs.remove(curPage) + } + }) + it('should not re-call getStaticProps when updating query', async () => { const browser = await webdriver(appPort, '/something?hello=world') await waitFor(2000) @@ -641,6 +734,14 @@ const runTests = (dev = false, looseMode = false) => { ), page: '/catchall/[...slug]', }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + buildId + )}\\/catchall\\-explicit\\/(.+?)\\.json$` + ), + page: '/catchall-explicit/[...slug]', + }, { dataRouteRegex: normalizeRegEx( `^\\/_next\\/data\\/${escapeRegex( @@ -683,7 +784,7 @@ const runTests = (dev = false, looseMode = false) => { } }) - expect(manifest.version).toBe(1) + expect(manifest.version).toBe(2) expect(manifest.routes).toEqual(expectedManifestRoutes()) expect(manifest.dynamicRoutes).toEqual({ '/blog/[post]': { @@ -722,6 +823,16 @@ const runTests = (dev = false, looseMode = false) => { `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\/(.+?)\\.json$` ), }, + '/catchall-explicit/[...slug]': { + dataRoute: `/_next/data/${buildId}/catchall-explicit/[...slug].json`, + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapedBuildId}\\/catchall\\-explicit\\/(.+?)\\.json$` + ), + fallback: false, + routeRegex: normalizeRegEx( + '^\\/catchall\\-explicit\\/(.+?)(?:\\/)?$' + ), + }, }) }) From 5cba994016982cc81b70cbeac37ccb338544dc55 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 27 Feb 2020 14:10:49 +0100 Subject: [PATCH 06/29] v9.2.3-canary.14 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index d7ce38857e23..77694e6c808e 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.13" + "version": "9.2.3-canary.14" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 6036cbedc0d7..1653a4446a0a 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 9dc52445a1f5..d048ee5c84b2 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 63b50800e02c..3c2c31560101 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index 27c703f307cb..e8b2589d395d 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index 64968e3e1954..9198f32f6699 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 4f01cec1aede..25480c918bb8 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 5c15bfa50511..df542a659586 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 2ddb5038971f..767f4e607a25 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.13", + "version": "9.2.3-canary.14", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.13", + "@next/polyfill-nomodule": "9.2.3-canary.14", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From 663f5c4b6e385ebe168e00110ca5217f56af6161 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Thu, 27 Feb 2020 09:25:17 -0500 Subject: [PATCH 07/29] Fix Error Message --- packages/next/build/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 65e563c2ae5d..c7ed129ecfad 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -539,7 +539,7 @@ export async function buildStaticPaths( if (typeof staticPathsResult.fallback !== 'boolean') { throw new Error( - `The \`fallback\` key must be returned from unstable_getStaticProps in ${page}.\n` + + `The \`fallback\` key must be returned from unstable_getStaticPaths in ${page}.\n` + expectedReturnVal ) } From 0b88d17aebd6da21ff20398e1106e5dcd46d46e9 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 27 Feb 2020 15:34:27 +0100 Subject: [PATCH 08/29] v9.2.3-canary.15 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index 77694e6c808e..4dbbb5cba872 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.14" + "version": "9.2.3-canary.15" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 1653a4446a0a..48df67b53c36 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index d048ee5c84b2..ff8145203498 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 3c2c31560101..00e400102174 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index e8b2589d395d..134652a810da 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index 9198f32f6699..d53f4105b8df 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 25480c918bb8..c861a7beb1af 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index df542a659586..a2941e710cc3 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 767f4e607a25..cb9528143513 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.14", + "version": "9.2.3-canary.15", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.14", + "@next/polyfill-nomodule": "9.2.3-canary.15", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From 75559f14311dbbd2029fd43eb3721d21aaa63c0d Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Thu, 27 Feb 2020 10:32:33 -0500 Subject: [PATCH 09/29] Add --example= to create-next-app (#10226) * Initial support for URLs * Added folder support * Added --example-path * Bug fix * Also install deps * Updated error message * Bug fix and replace the file path * Added tests Co-authored-by: Shu Uesugi --- packages/create-next-app/create-app.ts | 98 ++++++++++++++++--- packages/create-next-app/helpers/examples.ts | 61 ++++++++++-- packages/create-next-app/index.ts | 20 +++- .../integration/create-next-app/index.test.js | 73 ++++++++++++++ 4 files changed, 228 insertions(+), 24 deletions(-) diff --git a/packages/create-next-app/create-app.ts b/packages/create-next-app/create-app.ts index 2f6c558f63f5..9ef35850afe5 100644 --- a/packages/create-next-app/create-app.ts +++ b/packages/create-next-app/create-app.ts @@ -5,7 +5,14 @@ import makeDir from 'make-dir' import os from 'os' import path from 'path' -import { downloadAndExtractExample, hasExample } from './helpers/examples' +import { + hasExample, + hasRepo, + getRepoInfo, + downloadAndExtractExample, + downloadAndExtractRepo, + RepoInfo, +} from './helpers/examples' import { tryGitInit } from './helpers/git' import { install } from './helpers/install' import { isFolderEmpty } from './helpers/is-folder-empty' @@ -16,20 +23,69 @@ export async function createApp({ appPath, useNpm, example, + examplePath, }: { appPath: string useNpm: boolean example?: string + examplePath?: string }) { + let repoInfo: RepoInfo | undefined + if (example) { - const found = await hasExample(example) - if (!found) { - console.error( - `Could not locate an example named ${chalk.red( - `"${example}"` - )}. Please check your spelling and try again.` - ) - process.exit(1) + let repoUrl: URL | undefined + + try { + repoUrl = new URL(example) + } catch (error) { + if (error.code !== 'ERR_INVALID_URL') { + console.error(error) + process.exit(1) + } + } + + if (repoUrl) { + if (repoUrl.origin !== 'https://github.com') { + console.error( + `Invalid URL: ${chalk.red( + `"${example}"` + )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.` + ) + process.exit(1) + } + + repoInfo = getRepoInfo(repoUrl, examplePath) + + if (!repoInfo) { + console.error( + `Found invalid GitHub URL: ${chalk.red( + `"${example}"` + )}. Please fix the URL and try again.` + ) + process.exit(1) + } + + const found = await hasRepo(repoInfo) + + if (!found) { + console.error( + `Could not locate the repository for ${chalk.red( + `"${example}"` + )}. Please check that the repository exists and try again.` + ) + process.exit(1) + } + } else { + const found = await hasExample(example) + + if (!found) { + console.error( + `Could not locate an example named ${chalk.red( + `"${example}"` + )}. Please check your spelling and try again.` + ) + process.exit(1) + } } } @@ -53,13 +109,23 @@ export async function createApp({ process.chdir(root) if (example) { - console.log( - `Downloading files for example ${chalk.cyan( - example - )}. This might take a moment.` - ) - console.log() - await downloadAndExtractExample(root, example) + if (repoInfo) { + console.log( + `Downloading files from repo ${chalk.cyan( + example + )}. This might take a moment.` + ) + console.log() + await downloadAndExtractRepo(root, repoInfo) + } else { + console.log( + `Downloading files for example ${chalk.cyan( + example + )}. This might take a moment.` + ) + console.log() + await downloadAndExtractExample(root, example) + } // Copy our default `.gitignore` if the application did not provide one const ignorePath = path.join(root, '.gitignore') diff --git a/packages/create-next-app/helpers/examples.ts b/packages/create-next-app/helpers/examples.ts index 746f5cd3a860..2b0dad8d5c33 100644 --- a/packages/create-next-app/helpers/examples.ts +++ b/packages/create-next-app/helpers/examples.ts @@ -2,20 +2,69 @@ import got from 'got' import promisePipe from 'promisepipe' import tar from 'tar' -export async function hasExample(name: string): Promise { - const res = await got( +export type RepoInfo = { + username: string + name: string + branch: string + filePath: string +} + +export async function isUrlOk(url: string) { + const res = await got(url).catch(e => e) + return res.statusCode === 200 +} + +export function getRepoInfo( + url: URL, + examplePath?: string +): RepoInfo | undefined { + const [, username, name, t, _branch, ...file] = url.pathname.split('/') + const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/') + // If examplePath is available, the branch name takes the entire path + const branch = examplePath + ? `${_branch}/${file.join('/')}`.replace(new RegExp(`/${filePath}|/$`), '') + : _branch + + if (username && name && branch && t === 'tree') { + return { username, name, branch, filePath } + } +} + +export function hasRepo({ username, name, branch, filePath }: RepoInfo) { + const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents` + const packagePath = `${filePath ? `/${filePath}` : ''}/package.json` + + return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`) +} + +export function hasExample(name: string): Promise { + return isUrlOk( `https://api.github.com/repos/zeit/next.js/contents/examples/${encodeURIComponent( name )}/package.json` - ).catch(e => e) - return res.statusCode === 200 + ) +} + +export function downloadAndExtractRepo( + root: string, + { username, name, branch, filePath }: RepoInfo +): Promise { + return promisePipe( + got.stream( + `https://codeload.github.com/${username}/${name}/tar.gz/${branch}` + ), + tar.extract( + { cwd: root, strip: filePath ? filePath.split('/').length + 1 : 1 }, + [`${name}-${branch}${filePath ? `/${filePath}` : ''}`] + ) + ) } -export async function downloadAndExtractExample( +export function downloadAndExtractExample( root: string, name: string ): Promise { - return await promisePipe( + return promisePipe( got.stream('https://codeload.github.com/zeit/next.js/tar.gz/canary'), tar.extract({ cwd: root, strip: 3 }, [`next.js-canary/examples/${name}`]) ) diff --git a/packages/create-next-app/index.ts b/packages/create-next-app/index.ts index 99377ebcd6fc..326a0698a3c6 100644 --- a/packages/create-next-app/index.ts +++ b/packages/create-next-app/index.ts @@ -21,8 +21,23 @@ const program = new Commander.Command(packageJson.name) }) .option('--use-npm') .option( - '-e, --example ', - 'an example to bootstrap the app with' + '-e, --example |', + ` + + An example to bootstrap the app with. You can use an example name + from the official Next.js repo or a GitHub URL. The URL can use + any branch and/or subdirectory +` + ) + .option( + '--example-path ', + ` + + In a rare case, your GitHub URL might contain a branch name with + a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). + In this case, you must specify the path to the example separately: + --example-path foo/bar +` ) .allowUnknownOption() .parse(process.argv) @@ -89,6 +104,7 @@ async function run() { example: (typeof program.example === 'string' && program.example.trim()) || undefined, + examplePath: program.examplePath, }) } diff --git a/test/integration/create-next-app/index.test.js b/test/integration/create-next-app/index.test.js index 5b1eef06ba0f..c60777c3e85d 100644 --- a/test/integration/create-next-app/index.test.js +++ b/test/integration/create-next-app/index.test.js @@ -81,4 +81,77 @@ describe('create next app', () => { fs.existsSync(path.join(cwd, projectName, '.gitignore')) ).toBeTruthy() }) + + it('should allow example with GitHub URL', async () => { + const projectName = 'github-app' + const res = await run( + projectName, + '--example', + 'https://github.com/zeit/next-learn-demo/tree/master/1-navigate-between-pages' + ) + + expect(res.exitCode).toBe(0) + expect( + fs.existsSync(path.join(cwd, projectName, 'package.json')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/index.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/about.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, '.gitignore')) + ).toBeTruthy() + }) + + it('should allow example with GitHub URL and example-path', async () => { + const projectName = 'github-example-path' + const res = await run( + projectName, + '--example', + 'https://github.com/zeit/next-learn-demo/tree/master', + '--example-path', + '1-navigate-between-pages' + ) + + expect(res.exitCode).toBe(0) + expect( + fs.existsSync(path.join(cwd, projectName, 'package.json')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/index.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/about.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, '.gitignore')) + ).toBeTruthy() + }) + + it('should use --example-path over the file path in the GitHub URL', async () => { + const projectName = 'github-example-path-2' + const res = await run( + projectName, + '--example', + 'https://github.com/zeit/next-learn-demo/tree/master/1-navigate-between-pages', + '--example-path', + '1-navigate-between-pages' + ) + + expect(res.exitCode).toBe(0) + expect( + fs.existsSync(path.join(cwd, projectName, 'package.json')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/index.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, 'pages/about.js')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(cwd, projectName, '.gitignore')) + ).toBeTruthy() + }) }) From 2789e7e0c25c72fbf6be3ef070e530186af14430 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 27 Feb 2020 11:04:30 -0600 Subject: [PATCH 10/29] Rename getServerProps to getServerSideProps (#10722) --- docs/concepts/_data-fetching.md | 4 ++-- errors/404-get-initial-props.md | 2 +- examples/with-apollo/lib/apollo.js | 2 +- .../build/babel/plugins/next-ssg-transform.ts | 2 +- packages/next/build/utils.ts | 4 ++-- .../webpack/loaders/next-serverless-loader.ts | 12 +++++------ packages/next/lib/constants.ts | 6 +++--- .../next-server/server/load-components.ts | 20 +++++++++---------- .../next/next-server/server/next-server.ts | 2 +- packages/next/next-server/server/render.tsx | 20 +++++++++---------- packages/next/pages/_app.tsx | 2 +- test/integration/404-page/test/index.test.js | 4 ++-- .../pages/another/index.js | 2 +- .../pages/blog/[post]/[comment].js | 2 +- .../pages/blog/[post]/index.js | 2 +- .../pages/blog/index.js | 2 +- .../pages/catchall/[...path].js | 2 +- .../pages/default-revalidate.js | 2 +- .../pages/index.js | 2 +- .../pages/invalid-keys.js | 2 +- .../pages/normal.js | 0 .../pages/something.js | 2 +- .../pages/user/[user]/profile.js | 2 +- .../test/index.test.js | 14 ++++++------- .../world.txt | 0 .../pages/index.js | 2 +- .../pages/index.js.alt | 2 +- .../test/index.test.js | 6 +++--- 28 files changed, 62 insertions(+), 62 deletions(-) rename test/integration/{getserverprops => getserversideprops}/pages/another/index.js (93%) rename test/integration/{getserverprops => getserversideprops}/pages/blog/[post]/[comment].js (87%) rename test/integration/{getserverprops => getserversideprops}/pages/blog/[post]/index.js (92%) rename test/integration/{getserverprops => getserversideprops}/pages/blog/index.js (89%) rename test/integration/{getserverprops => getserversideprops}/pages/catchall/[...path].js (92%) rename test/integration/{getserverprops => getserversideprops}/pages/default-revalidate.js (88%) rename test/integration/{getserverprops => getserversideprops}/pages/index.js (95%) rename test/integration/{getserverprops => getserversideprops}/pages/invalid-keys.js (91%) rename test/integration/{getserverprops => getserversideprops}/pages/normal.js (100%) rename test/integration/{getserverprops => getserversideprops}/pages/something.js (92%) rename test/integration/{getserverprops => getserversideprops}/pages/user/[user]/profile.js (86%) rename test/integration/{getserverprops => getserversideprops}/test/index.test.js (96%) rename test/integration/{getserverprops => getserversideprops}/world.txt (100%) diff --git a/docs/concepts/_data-fetching.md b/docs/concepts/_data-fetching.md index 0e8508530184..b5496b255f37 100644 --- a/docs/concepts/_data-fetching.md +++ b/docs/concepts/_data-fetching.md @@ -46,7 +46,7 @@ export default HomePage [Read more about how Server-Side Rendering works](). -To opt-in to Server-Side Rendering, making every request render HTML on-demand you use the `getServerProps` method. +To opt-in to Server-Side Rendering, making every request render HTML on-demand you use the `getServerSideProps` method. It allows you to fetch data before rendering starts when a request comes in. @@ -56,7 +56,7 @@ Taking the same example as Static Generation, but opted into rendering the page import fetch from 'isomorphic-unfetch' // Called when a request comes in. -export async function getServerProps() { +export async function getServerSideProps() { const res = await fetch('https://api.github.com/repos/zeit/next.js') const json = await res.json() diff --git a/errors/404-get-initial-props.md b/errors/404-get-initial-props.md index dc0e1ecb3510..af7fde5a3b48 100644 --- a/errors/404-get-initial-props.md +++ b/errors/404-get-initial-props.md @@ -2,7 +2,7 @@ #### Why This Error Occurred -In your `404.js` page you added `getInitialProps` or `getServerProps` which is not allowed as this prevents the page from being static and `404.js` is meant to provide more flexibility for a static 404 page. Having a static 404 page is the most ideal as this page can be served very often and if not static puts extra strain on your server and more invocations for serverless functions which increase the cost of hosting your site unnecessarily. +In your `404.js` page you added `getInitialProps` or `getServerSideProps` which is not allowed as this prevents the page from being static and `404.js` is meant to provide more flexibility for a static 404 page. Having a static 404 page is the most ideal as this page can be served very often and if not static puts extra strain on your server and more invocations for serverless functions which increase the cost of hosting your site unnecessarily. #### Possible Ways to Fix It diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index 17a40a6d309c..bff375c2cf92 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -11,7 +11,7 @@ let globalApolloClient = null /** * Installs the Apollo Client on NextPageContext * or NextAppContext. Useful if you want to use apolloClient - * inside getStaticProps, getStaticPaths or getServerProps + * inside getStaticProps, getStaticPaths or getServerSideProps * @param {NextPageContext | NextAppContext} ctx */ export const initOnContext = ctx => { diff --git a/packages/next/build/babel/plugins/next-ssg-transform.ts b/packages/next/build/babel/plugins/next-ssg-transform.ts index 2a7cea08e6b7..97a27c7f8ff1 100644 --- a/packages/next/build/babel/plugins/next-ssg-transform.ts +++ b/packages/next/build/babel/plugins/next-ssg-transform.ts @@ -10,7 +10,7 @@ const pageComponentVar = '__NEXT_COMP' 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' +export const EXPORT_NAME_GET_SERVER_PROPS = 'unstable_getServerSideProps' const ssgExports = new Set([ EXPORT_NAME_GET_STATIC_PROPS, diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index c7ed129ecfad..8269949a55dd 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -202,7 +202,7 @@ export async function printTreeView( serverless ? '(Lambda)' : '(Server)', `server-side renders at runtime (uses ${chalk.cyan( 'getInitialProps' - )} or ${chalk.cyan('getServerProps')})`, + )} or ${chalk.cyan('getServerSideProps')})`, ], [ 'â—‹', @@ -636,7 +636,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 hasServerProps = !!mod.unstable_getServerSideProps const hasLegacyStaticParams = !!mod.unstable_getStaticParams if (hasLegacyStaticParams) { diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 47ab3582dcf4..0b6c770fc1a3 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -220,7 +220,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'] + export const unstable_getServerSideProps = ComponentInfo['unstable_getServerSideProp' + 's'] ${dynamicRouteMatcher} ${handleRewrites} @@ -242,7 +242,7 @@ const nextServerlessLoader: loader.Loader = function() { Document, buildManifest, unstable_getStaticProps, - unstable_getServerProps, + unstable_getServerSideProps, unstable_getStaticPaths, reactLoadableManifest, canonicalBase: "${canonicalBase}", @@ -275,7 +275,7 @@ const nextServerlessLoader: loader.Loader = function() { ${page === '/_error' ? `res.statusCode = 404` : ''} ${ pageIsDynamicRoute - ? `const params = fromExport && !unstable_getStaticProps && !unstable_getServerProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};` + ? `const params = fromExport && !unstable_getStaticProps && !unstable_getServerSideProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};` : `const params = {};` } ${ @@ -331,7 +331,7 @@ const nextServerlessLoader: loader.Loader = function() { 'Cache-Control', isPreviewMode ? \`private, no-cache, no-store, max-age=0, must-revalidate\` - : unstable_getServerProps + : unstable_getServerSideProps ? \`no-cache, no-store, must-revalidate\` : \`s-maxage=\${renderOpts.revalidate}, stale-while-revalidate\` ) @@ -352,7 +352,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, + unstable_getServerSideProps: undefined, Component: Error })) return result @@ -362,7 +362,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, + unstable_getServerSideProps: undefined, Component: Error, err })) diff --git a/packages/next/lib/constants.ts b/packages/next/lib/constants.ts index 4391cbc755c0..1070627fad7a 100644 --- a/packages/next/lib/constants.ts +++ b/packages/next/lib/constants.ts @@ -26,8 +26,8 @@ export const PUBLIC_DIR_MIDDLEWARE_CONFLICT = `You can not have a '_next' folder 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_GET_INIT_PROPS_CONFLICT = `You can not use getInitialProps with unstable_getServerSideProps. 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` +export const SERVER_PROPS_SSG_CONFLICT = `You can not use unstable_getStaticProps with unstable_getServerSideProps. To use SSG, please remove your unstable_getServerSideProps` -export const PAGES_404_GET_INITIAL_PROPS_ERROR = `\`pages/404\` can not have getInitialProps/getServerProps, https://err.sh/zeit/next.js/404-get-initial-props` +export const PAGES_404_GET_INITIAL_PROPS_ERROR = `\`pages/404\` can not have getInitialProps/getServerSideProps, https://err.sh/zeit/next.js/404-get-initial-props` diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index e4052770e8ae..856ea05a6efd 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -21,13 +21,13 @@ export function interopDefault(mod: any) { function addComponentPropsId( Component: any, getStaticProps: any, - getServerProps: any + getServerSideProps: any ) { // Mark the component with the SSG or SSP id here since we don't run // the SSG babel transform for server mode if (getStaticProps) { Component[STATIC_PROPS_ID] = true - } else if (getServerProps) { + } else if (getServerSideProps) { Component[SERVER_PROPS_ID] = true } } @@ -55,7 +55,7 @@ export type Unstable_getStaticPaths = () => Promise<{ fallback: boolean }> -type Unstable_getServerProps = (context: { +type Unstable_getServerSideProps = (context: { params: ParsedUrlQuery | undefined req: IncomingMessage res: ServerResponse @@ -72,7 +72,7 @@ export type LoadComponentsReturnType = { App: AppType unstable_getStaticProps?: Unstable_getStaticProps unstable_getStaticPaths?: Unstable_getStaticPaths - unstable_getServerProps?: Unstable_getServerProps + unstable_getServerSideProps?: Unstable_getServerSideProps } export async function loadComponents( @@ -86,13 +86,13 @@ export async function loadComponents( const { unstable_getStaticProps, unstable_getStaticPaths, - unstable_getServerProps, + unstable_getServerSideProps, } = Component addComponentPropsId( Component, unstable_getStaticProps, - unstable_getServerProps + unstable_getServerSideProps ) return { @@ -100,7 +100,7 @@ export async function loadComponents( pageConfig: Component.config || {}, unstable_getStaticProps, unstable_getStaticPaths, - unstable_getServerProps, + unstable_getServerSideProps, } as LoadComponentsReturnType } const documentPath = join( @@ -142,7 +142,7 @@ export async function loadComponents( ]) const { - unstable_getServerProps, + unstable_getServerSideProps, unstable_getStaticProps, unstable_getStaticPaths, } = ComponentMod @@ -150,7 +150,7 @@ export async function loadComponents( addComponentPropsId( Component, unstable_getStaticProps, - unstable_getServerProps + unstable_getServerSideProps ) return { @@ -161,7 +161,7 @@ export async function loadComponents( DocumentMiddleware, reactLoadableManifest, pageConfig: ComponentMod.config || {}, - unstable_getServerProps, + unstable_getServerSideProps, unstable_getStaticProps, unstable_getStaticPaths, } diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index c02696bcf707..4e1ec838ac3e 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -889,7 +889,7 @@ export default class Server { typeof components.Component === 'object' && typeof (components.Component as any).renderReqToHTML === 'function' const isSSG = !!components.unstable_getStaticProps - const isServerProps = !!components.unstable_getServerProps + const isServerProps = !!components.unstable_getServerSideProps const hasStaticPaths = !!components.unstable_getStaticPaths // Toggle whether or not this is a Data request diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 8e848519c1c8..287dd361ed7d 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -275,7 +275,7 @@ export async function renderToHTML( ErrorDebug, unstable_getStaticProps, unstable_getStaticPaths, - unstable_getServerProps, + unstable_getServerSideProps, isDataReq, params, previewProps, @@ -323,7 +323,7 @@ export async function renderToHTML( !hasPageGetInitialProps && defaultAppGetInitialProps && !isSpr && - !unstable_getServerProps + !unstable_getServerSideProps if ( process.env.NODE_ENV !== 'production' && @@ -349,11 +349,11 @@ export async function renderToHTML( throw new Error(SSG_GET_INITIAL_PROPS_CONFLICT + ` ${pathname}`) } - if (hasPageGetInitialProps && unstable_getServerProps) { + if (hasPageGetInitialProps && unstable_getServerSideProps) { throw new Error(SERVER_PROPS_GET_INIT_PROPS_CONFLICT + ` ${pathname}`) } - if (unstable_getServerProps && isSpr) { + if (unstable_getServerSideProps && isSpr) { throw new Error(SERVER_PROPS_SSG_CONFLICT + ` ${pathname}`) } @@ -529,8 +529,8 @@ export async function renderToHTML( console.error(err) } - if (unstable_getServerProps && !isFallback) { - const data = await unstable_getServerProps({ + if (unstable_getServerSideProps && !isFallback) { + const data = await unstable_getServerSideProps({ params, query, req, @@ -540,7 +540,7 @@ export async function renderToHTML( const invalidKeys = Object.keys(data).filter(key => key !== 'props') if (invalidKeys.length) { - throw new Error(invalidKeysMsg('getServerProps', invalidKeys)) + throw new Error(invalidKeysMsg('getServerSideProps', invalidKeys)) } props.pageProps = data.props @@ -549,7 +549,7 @@ export async function renderToHTML( if ( !isSpr && // we only show this warning for legacy pages - !unstable_getServerProps && + !unstable_getServerSideProps && process.env.NODE_ENV !== 'production' && Object.keys(props?.pageProps || {}).includes('url') ) { @@ -560,10 +560,10 @@ export async function renderToHTML( } // We only need to do this if we want to support calling - // _app's getInitialProps for getServerProps if not this can be removed + // _app's getInitialProps for getServerSideProps if not this can be removed if (isDataReq) return props - // We don't call getStaticProps or getServerProps while generating + // We don't call getStaticProps or getServerSideProps while generating // the fallback so make sure to set pageProps to an empty object if (isFallback) { props.pageProps = {} diff --git a/packages/next/pages/_app.tsx b/packages/next/pages/_app.tsx index 0132f5ab4825..32727d7f2ea9 100644 --- a/packages/next/pages/_app.tsx +++ b/packages/next/pages/_app.tsx @@ -48,7 +48,7 @@ export default class App

extends React.Component< {...pageProps} { // we don't add the legacy URL prop if it's using non-legacy - // methods like getStaticProps and getServerProps + // methods like getStaticProps and getServerSideProps ...(!((Component as any).__N_SSG || (Component as any).__N_SSP) ? { url: createUrl(router) } : {}) diff --git a/test/integration/404-page/test/index.test.js b/test/integration/404-page/test/index.test.js index 0656fe67c1c3..9029af04c2f9 100644 --- a/test/integration/404-page/test/index.test.js +++ b/test/integration/404-page/test/index.test.js @@ -149,7 +149,7 @@ describe('404 Page Support', () => { await fs.move(`${pages404}.bak`, pages404) expect(stderr).toContain( - `\`pages/404\` can not have getInitialProps/getServerProps, https://err.sh/zeit/next.js/404-get-initial-props` + `\`pages/404\` can not have getInitialProps/getServerSideProps, https://err.sh/zeit/next.js/404-get-initial-props` ) expect(code).toBe(1) }) @@ -180,7 +180,7 @@ describe('404 Page Support', () => { await fs.remove(pages404) await fs.move(`${pages404}.bak`, pages404) - const error = `\`pages/404\` can not have getInitialProps/getServerProps, https://err.sh/zeit/next.js/404-get-initial-props` + const error = `\`pages/404\` can not have getInitialProps/getServerSideProps, https://err.sh/zeit/next.js/404-get-initial-props` expect(stderr).toContain(error) }) diff --git a/test/integration/getserverprops/pages/another/index.js b/test/integration/getserversideprops/pages/another/index.js similarity index 93% rename from test/integration/getserverprops/pages/another/index.js rename to test/integration/getserversideprops/pages/another/index.js index a5c3afb6343c..de46cda8bd03 100644 --- a/test/integration/getserverprops/pages/another/index.js +++ b/test/integration/getserversideprops/pages/another/index.js @@ -3,7 +3,7 @@ import fs from 'fs' import findUp from 'find-up' // eslint-disable-next-line camelcase -export async function unstable_getServerProps() { +export async function unstable_getServerSideProps() { const text = fs .readFileSync( findUp.sync('world.txt', { diff --git a/test/integration/getserverprops/pages/blog/[post]/[comment].js b/test/integration/getserversideprops/pages/blog/[post]/[comment].js similarity index 87% rename from test/integration/getserverprops/pages/blog/[post]/[comment].js rename to test/integration/getserversideprops/pages/blog/[post]/[comment].js index 292c98024942..a5a42247ecb1 100644 --- a/test/integration/getserverprops/pages/blog/[post]/[comment].js +++ b/test/integration/getserversideprops/pages/blog/[post]/[comment].js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ query }) { +export async function unstable_getServerSideProps({ query }) { return { props: { post: query.post, diff --git a/test/integration/getserverprops/pages/blog/[post]/index.js b/test/integration/getserversideprops/pages/blog/[post]/index.js similarity index 92% rename from test/integration/getserverprops/pages/blog/[post]/index.js rename to test/integration/getserversideprops/pages/blog/[post]/index.js index aa4e0c5cc2bd..ba9c5978ea88 100644 --- a/test/integration/getserverprops/pages/blog/[post]/index.js +++ b/test/integration/getserversideprops/pages/blog/[post]/index.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ params }) { +export async function unstable_getServerSideProps({ params }) { if (params.post === 'post-10') { await new Promise(resolve => { setTimeout(() => resolve(), 1000) diff --git a/test/integration/getserverprops/pages/blog/index.js b/test/integration/getserversideprops/pages/blog/index.js similarity index 89% rename from test/integration/getserverprops/pages/blog/index.js rename to test/integration/getserversideprops/pages/blog/index.js index 056a1860a3e3..be51f782e42d 100644 --- a/test/integration/getserverprops/pages/blog/index.js +++ b/test/integration/getserversideprops/pages/blog/index.js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerProps() { +export async function unstable_getServerSideProps() { return { props: { slugs: ['post-1', 'post-2'], diff --git a/test/integration/getserverprops/pages/catchall/[...path].js b/test/integration/getserversideprops/pages/catchall/[...path].js similarity index 92% rename from test/integration/getserverprops/pages/catchall/[...path].js rename to test/integration/getserversideprops/pages/catchall/[...path].js index 5e1bd3543f63..45efb3e6c85f 100644 --- a/test/integration/getserverprops/pages/catchall/[...path].js +++ b/test/integration/getserversideprops/pages/catchall/[...path].js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ params }) { +export async function unstable_getServerSideProps({ params }) { return { props: { world: 'world', diff --git a/test/integration/getserverprops/pages/default-revalidate.js b/test/integration/getserversideprops/pages/default-revalidate.js similarity index 88% rename from test/integration/getserverprops/pages/default-revalidate.js rename to test/integration/getserversideprops/pages/default-revalidate.js index 15bb81ba617e..582ef524ec1a 100644 --- a/test/integration/getserverprops/pages/default-revalidate.js +++ b/test/integration/getserversideprops/pages/default-revalidate.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerProps() { +export async function unstable_getServerSideProps() { return { props: { world: 'world', diff --git a/test/integration/getserverprops/pages/index.js b/test/integration/getserversideprops/pages/index.js similarity index 95% rename from test/integration/getserverprops/pages/index.js rename to test/integration/getserversideprops/pages/index.js index f906758f846b..fc84e9848d8d 100644 --- a/test/integration/getserverprops/pages/index.js +++ b/test/integration/getserversideprops/pages/index.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerProps() { +export async function unstable_getServerSideProps() { return { props: { world: 'world', diff --git a/test/integration/getserverprops/pages/invalid-keys.js b/test/integration/getserversideprops/pages/invalid-keys.js similarity index 91% rename from test/integration/getserverprops/pages/invalid-keys.js rename to test/integration/getserversideprops/pages/invalid-keys.js index 6eeba67fb6a7..4184eb8bcfab 100644 --- a/test/integration/getserverprops/pages/invalid-keys.js +++ b/test/integration/getserversideprops/pages/invalid-keys.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ params, query }) { +export async function unstable_getServerSideProps({ params, query }) { return { world: 'world', query: query || {}, diff --git a/test/integration/getserverprops/pages/normal.js b/test/integration/getserversideprops/pages/normal.js similarity index 100% rename from test/integration/getserverprops/pages/normal.js rename to test/integration/getserversideprops/pages/normal.js diff --git a/test/integration/getserverprops/pages/something.js b/test/integration/getserversideprops/pages/something.js similarity index 92% rename from test/integration/getserverprops/pages/something.js rename to test/integration/getserversideprops/pages/something.js index f3d5ef1ef4de..361bbb44b509 100644 --- a/test/integration/getserverprops/pages/something.js +++ b/test/integration/getserversideprops/pages/something.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ params, query }) { +export async function unstable_getServerSideProps({ params, query }) { return { props: { world: 'world', diff --git a/test/integration/getserverprops/pages/user/[user]/profile.js b/test/integration/getserversideprops/pages/user/[user]/profile.js similarity index 86% rename from test/integration/getserverprops/pages/user/[user]/profile.js rename to test/integration/getserversideprops/pages/user/[user]/profile.js index a4fb1e13403b..c14992d56d4d 100644 --- a/test/integration/getserverprops/pages/user/[user]/profile.js +++ b/test/integration/getserversideprops/pages/user/[user]/profile.js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerProps({ query }) { +export async function unstable_getServerSideProps({ query }) { return { props: { user: query.user, diff --git a/test/integration/getserverprops/test/index.test.js b/test/integration/getserversideprops/test/index.test.js similarity index 96% rename from test/integration/getserverprops/test/index.test.js rename to test/integration/getserversideprops/test/index.test.js index 84707e9eccf5..963215c85850 100644 --- a/test/integration/getserverprops/test/index.test.js +++ b/test/integration/getserversideprops/test/index.test.js @@ -186,7 +186,7 @@ const runTests = (dev = false) => { expect(html).toMatch(/hello.*?world/) }) - it('should SSR getServerProps page correctly', async () => { + it('should SSR getServerSideProps page correctly', async () => { const html = await renderViaHTTP(appPort, '/blog/post-1') expect(html).toMatch(/Post:.*?post-1/) }) @@ -298,7 +298,7 @@ const runTests = (dev = false) => { expect(await browser.eval('window.beforeClick')).not.toBe('true') }) - it('should always call getServerProps without caching', async () => { + it('should always call getServerSideProps without caching', async () => { const initialRes = await fetchViaHTTP(appPort, '/something') const initialHtml = await initialRes.text() expect(initialHtml).toMatch(/hello.*?world/) @@ -314,7 +314,7 @@ const runTests = (dev = false) => { expect(newHtml !== newerHtml).toBe(true) }) - it('should not re-call getServerProps when updating query', async () => { + it('should not re-call getServerSideProps when updating query', async () => { const browser = await webdriver(appPort, '/something?hello=world') await waitFor(2000) @@ -337,7 +337,7 @@ const runTests = (dev = false) => { await fs.writeFile( urlPropPage, ` - export async function unstable_getServerProps() { + export async function unstable_getServerSideProps() { return { props: { url: 'something' @@ -357,10 +357,10 @@ const runTests = (dev = false) => { expect(html).toMatch(/url:.*?something/) }) - it('should show error for extra keys returned from getServerProps', async () => { + it('should show error for extra keys returned from getServerSideProps', async () => { const html = await renderViaHTTP(appPort, '/invalid-keys') expect(html).toContain( - `Additional keys were returned from \`getServerProps\`. Properties intended for your component must be nested under the \`props\` key, e.g.:` + `Additional keys were returned from \`getServerSideProps\`. Properties intended for your component must be nested under the \`props\` key, e.g.:` ) expect(html).toContain( `Keys that need to be moved: world, query, params, time, random` @@ -396,7 +396,7 @@ const runTests = (dev = false) => { } } -describe('unstable_getServerProps', () => { +describe('unstable_getServerSideProps', () => { describe('dev mode', () => { beforeAll(async () => { stderr = '' diff --git a/test/integration/getserverprops/world.txt b/test/integration/getserversideprops/world.txt similarity index 100% rename from test/integration/getserverprops/world.txt rename to test/integration/getserversideprops/world.txt diff --git a/test/integration/mixed-ssg-serverprops-error/pages/index.js b/test/integration/mixed-ssg-serverprops-error/pages/index.js index dedf3d00be14..7bf4d452e293 100644 --- a/test/integration/mixed-ssg-serverprops-error/pages/index.js +++ b/test/integration/mixed-ssg-serverprops-error/pages/index.js @@ -4,7 +4,7 @@ export const unstable_getStaticProps = async () => { } } -export const unstable_getServerProps = async () => { +export const unstable_getServerSideProps = async () => { return { props: { world: 'world' }, } diff --git a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt index 4756984cc3a9..9d6334f42ef0 100644 --- a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt +++ b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt @@ -4,7 +4,7 @@ export const unstable_getStaticPaths = async () => { } } -export const unstable_getServerProps = async () => { +export const unstable_getServerSideProps = async () => { return { props: { world: 'world' } } diff --git a/test/integration/mixed-ssg-serverprops-error/test/index.test.js b/test/integration/mixed-ssg-serverprops-error/test/index.test.js index 794cf77cae22..27fdee631a1a 100644 --- a/test/integration/mixed-ssg-serverprops-error/test/index.test.js +++ b/test/integration/mixed-ssg-serverprops-error/test/index.test.js @@ -11,13 +11,13 @@ const indexPage = join(appDir, 'pages/index.js') const indexPageAlt = `${indexPage}.alt` const indexPageBak = `${indexPage}.bak` -describe('Mixed getStaticProps and getServerProps error', () => { - it('should error when exporting both getStaticProps and getServerProps', async () => { +describe('Mixed getStaticProps and getServerSideProps error', () => { + it('should error when exporting both getStaticProps and getServerSideProps', async () => { const { stderr } = await nextBuild(appDir, [], { stderr: true }) expect(stderr).toContain(SERVER_PROPS_SSG_CONFLICT) }) - it('should error when exporting both getStaticPaths and getServerProps', async () => { + it('should error when exporting both getStaticPaths and getServerSideProps', async () => { await fs.move(indexPage, indexPageBak) await fs.move(indexPageAlt, indexPage) From c9d9f1131cdb95983f8d816e9fac3a0855284ca2 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 27 Feb 2020 11:57:39 -0600 Subject: [PATCH 11/29] Remove `unstable_` prefix from new methods (#10723) * Rename getServerProps to getServerSideProps * Remove unstable_ prefix from new methods * Add error when legacy methods are detected * Add legacy methods for babel transform * Add unstable_getServerSideProps also * Apply suggestions from code review Co-Authored-By: Joe Haddad * Update types import Co-authored-by: Joe Haddad --- errors/invalid-getstaticpaths-value.md | 10 +-- errors/invalid-getstaticprops-value.md | 8 +- .../z-experimental-next-news/pages/ask.js | 2 +- .../z-experimental-next-news/pages/index.js | 2 +- .../pages/item/[id].js | 2 +- .../z-experimental-next-news/pages/jobs.js | 2 +- .../z-experimental-next-news/pages/newest.js | 2 +- .../pages/news/[id].js | 2 +- .../z-experimental-next-news/pages/show.js | 2 +- .../build/babel/plugins/next-ssg-transform.ts | 13 ++- packages/next/build/index.ts | 2 +- packages/next/build/utils.ts | 53 ++++++++---- .../webpack/loaders/next-serverless-loader.ts | 33 ++++---- packages/next/export/worker.js | 15 ++-- packages/next/lib/constants.ts | 6 +- .../next-server/server/load-components.ts | 73 +++++------------ .../next/next-server/server/next-server.ts | 8 +- packages/next/next-server/server/render.tsx | 30 +++---- packages/next/server/static-paths-worker.ts | 6 +- packages/next/types/index.d.ts | 23 ++++++ .../pages/[slug].js | 2 +- .../test/index.test.js | 2 +- .../pages/p1/p2/all-ssg/[...rest].js | 4 +- .../p1/p2/nested-all-ssg/[...rest]/index.js | 4 +- .../pages/p1/p2/predefined-ssg/[...rest].js | 4 +- .../getserversideprops/pages/another/index.js | 2 +- .../pages/blog/[post]/[comment].js | 2 +- .../pages/blog/[post]/index.js | 2 +- .../getserversideprops/pages/blog/index.js | 2 +- .../pages/catchall/[...path].js | 2 +- .../pages/default-revalidate.js | 2 +- .../getserversideprops/pages/index.js | 2 +- .../getserversideprops/pages/invalid-keys.js | 2 +- .../getserversideprops/pages/something.js | 2 +- .../pages/user/[user]/profile.js | 2 +- .../getserversideprops/test/index.test.js | 4 +- .../legacy-ssg-methods-error/pages/index.js | 7 ++ .../test/index.test.js | 81 +++++++++++++++++++ .../pages/index.js | 4 +- .../pages/index.js.alt | 4 +- .../pages/[...slug].js | 4 +- .../test/index.test.js | 2 +- .../pages/[foo]/[post].js | 4 +- .../prerender-legacy/pages/blog/[post].js | 2 +- .../prerender-legacy/test/index.test.js | 4 +- .../prerender-preview/pages/index.js | 2 +- .../prerender/pages/another/index.js | 2 +- .../prerender/pages/blog/[post]/[comment].js | 4 +- .../prerender/pages/blog/[post]/index.js | 4 +- .../integration/prerender/pages/blog/index.js | 2 +- .../pages/catchall-explicit/[...slug].js | 4 +- .../prerender/pages/catchall/[...slug].js | 4 +- .../prerender/pages/default-revalidate.js | 2 +- test/integration/prerender/pages/index.js | 2 +- test/integration/prerender/pages/something.js | 2 +- .../prerender/pages/user/[user]/profile.js | 4 +- test/integration/prerender/test/index.test.js | 14 ++-- .../babel-plugin-next-ssg-transform.test.js | 62 +++++++------- test/unit/next-babel-loader.test.js | 10 +-- 59 files changed, 336 insertions(+), 228 deletions(-) create mode 100644 test/integration/legacy-ssg-methods-error/pages/index.js create mode 100644 test/integration/legacy-ssg-methods-error/test/index.test.js diff --git a/errors/invalid-getstaticpaths-value.md b/errors/invalid-getstaticpaths-value.md index 47dfe60a7bba..f1e8543d9503 100644 --- a/errors/invalid-getstaticpaths-value.md +++ b/errors/invalid-getstaticpaths-value.md @@ -1,15 +1,15 @@ -# Invalid unstable_getStaticPaths Return Value +# Invalid getStaticPaths Return Value #### Why This Error Occurred -In one of the page's `unstable_getStaticPaths` the return value had the incorrect shape. +In one of the page's `getStaticPaths` the return value had the incorrect shape. #### Possible Ways to Fix It -Make sure to return the following shape from `unstable_getStaticPaths`: +Make sure to return the following shape from `getStaticPaths`: ```js -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: Array, fallback: boolean @@ -23,7 +23,7 @@ There are two required properties: - You may return a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) or an [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) that explicitly defines all URL `params`. ```js // pages/blog/[slug].js - export async function unstable_getStaticPaths() { + export async function getStaticPaths() { return { paths: [ // String variant: diff --git a/errors/invalid-getstaticprops-value.md b/errors/invalid-getstaticprops-value.md index 5368ba600194..ace1cdceb52b 100644 --- a/errors/invalid-getstaticprops-value.md +++ b/errors/invalid-getstaticprops-value.md @@ -1,15 +1,15 @@ -# Invalid unstable_getStaticProps Return Value +# Invalid getStaticProps Return Value #### Why This Error Occurred -In one of the page's `unstable_getStaticProps` the return value had the incorrect shape. +In one of the page's `getStaticProps` the return value had the incorrect shape. #### Possible Ways to Fix It -Make sure to return the following shape from `unstable_getStaticProps`: +Make sure to return the following shape from `getStaticProps`: ```js -export async function unstable_getStaticProps() { +export async function getStaticProps() { return { props: { [key: string]: any } } diff --git a/examples/z-experimental-next-news/pages/ask.js b/examples/z-experimental-next-news/pages/ask.js index 4757d7d97623..93e84e5f4bb7 100644 --- a/examples/z-experimental-next-news/pages/ask.js +++ b/examples/z-experimental-next-news/pages/ask.js @@ -4,7 +4,7 @@ import Stories from '../components/stories' import getStories from '../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const page = 1 const stories = await getStories('askstories', { page }) return { props: { page, stories } } diff --git a/examples/z-experimental-next-news/pages/index.js b/examples/z-experimental-next-news/pages/index.js index 326cfc2e8fdf..78d5ff36a8f9 100644 --- a/examples/z-experimental-next-news/pages/index.js +++ b/examples/z-experimental-next-news/pages/index.js @@ -4,7 +4,7 @@ import Stories from '../components/stories' import getStories from '../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const page = 1 const stories = await getStories('topstories', { page }) return { props: { page, stories } } diff --git a/examples/z-experimental-next-news/pages/item/[id].js b/examples/z-experimental-next-news/pages/item/[id].js index c44e60a6dfef..12957562d0b6 100644 --- a/examples/z-experimental-next-news/pages/item/[id].js +++ b/examples/z-experimental-next-news/pages/item/[id].js @@ -4,7 +4,7 @@ import Item from '../../components/item' import getItem from '../../lib/get-item' import getComments from '../../lib/get-comments' -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { const story = await getItem(params.id) return { props: { story } } } diff --git a/examples/z-experimental-next-news/pages/jobs.js b/examples/z-experimental-next-news/pages/jobs.js index 5fd4bf202e33..2d7e6607d9fc 100644 --- a/examples/z-experimental-next-news/pages/jobs.js +++ b/examples/z-experimental-next-news/pages/jobs.js @@ -4,7 +4,7 @@ import Stories from '../components/stories' import getStories from '../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const page = 1 const stories = await getStories('jobstories', { page }) return { props: { page, stories } } diff --git a/examples/z-experimental-next-news/pages/newest.js b/examples/z-experimental-next-news/pages/newest.js index 60c39074d15a..ed276101b8e9 100644 --- a/examples/z-experimental-next-news/pages/newest.js +++ b/examples/z-experimental-next-news/pages/newest.js @@ -4,7 +4,7 @@ import Stories from '../components/stories' import getStories from '../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const page = 1 const stories = await getStories('newstories', { page }) return { props: { page, stories } } diff --git a/examples/z-experimental-next-news/pages/news/[id].js b/examples/z-experimental-next-news/pages/news/[id].js index a5251da89db3..4a77671d64eb 100644 --- a/examples/z-experimental-next-news/pages/news/[id].js +++ b/examples/z-experimental-next-news/pages/news/[id].js @@ -4,7 +4,7 @@ import Stories from '../../components/stories' import getStories from '../../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { const page = Number(params.id) const stories = await getStories('topstories', { page }) return { props: { page, stories } } diff --git a/examples/z-experimental-next-news/pages/show.js b/examples/z-experimental-next-news/pages/show.js index ba99ecb278b1..61be9121f09c 100644 --- a/examples/z-experimental-next-news/pages/show.js +++ b/examples/z-experimental-next-news/pages/show.js @@ -4,7 +4,7 @@ import Stories from '../components/stories' import getStories from '../lib/get-stories' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const page = 1 const stories = await getStories('showstories', { page }) return { props: { page, stories } } diff --git a/packages/next/build/babel/plugins/next-ssg-transform.ts b/packages/next/build/babel/plugins/next-ssg-transform.ts index 97a27c7f8ff1..e8d5ad60cb2e 100644 --- a/packages/next/build/babel/plugins/next-ssg-transform.ts +++ b/packages/next/build/babel/plugins/next-ssg-transform.ts @@ -8,14 +8,21 @@ import { const pageComponentVar = '__NEXT_COMP' -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_getServerSideProps' +export const EXPORT_NAME_GET_STATIC_PROPS = 'getStaticProps' +export const EXPORT_NAME_GET_STATIC_PATHS = 'getStaticPaths' +export const EXPORT_NAME_GET_SERVER_PROPS = 'getServerSideProps' const ssgExports = new Set([ EXPORT_NAME_GET_STATIC_PROPS, EXPORT_NAME_GET_STATIC_PATHS, EXPORT_NAME_GET_SERVER_PROPS, + + // legacy methods added so build doesn't fail from importing + // server-side only methods + `unstable_getStaticProps`, + `unstable_getStaticPaths`, + `unstable_getServerProps`, + `unstable_getServerSideProps`, ]) type PluginState = { diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 1ce20330f19f..6533ac91cfa1 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -779,7 +779,7 @@ export default async function build(dir: string, conf = null): Promise { // For a dynamic SSG page, we did not copy its data exports and only // copy the fallback HTML file (if present). // We must also copy specific versions of this page as defined by - // `unstable_getStaticPaths` (additionalSsgPaths). + // `getStaticPaths` (additionalSsgPaths). const extraRoutes = additionalSsgPaths.get(page) || [] for (const route of extraRoutes) { await moveExportedPage(route, route, true, 'html') diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 8269949a55dd..e0e9ae3d605f 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -15,7 +15,7 @@ import { recursiveReadDir } from '../lib/recursive-readdir' import { getRouteMatcher, getRouteRegex } from '../next-server/lib/router/utils' import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic' import { findPageFile } from '../server/lib/find-page-file' -import { Unstable_getStaticPaths } from '../next-server/server/load-components' +import { GetStaticPaths } from 'next/types' const fileGzipStats: { [k: string]: Promise } = {} const fsStatGzip = (file: string) => { @@ -500,7 +500,7 @@ export async function getPageSizeInKb( export async function buildStaticPaths( page: string, - unstable_getStaticPaths: Unstable_getStaticPaths + getStaticPaths: GetStaticPaths ): Promise<{ paths: string[]; fallback: boolean }> { const prerenderPaths = new Set() const _routeRegex = getRouteRegex(page) @@ -509,7 +509,7 @@ export async function buildStaticPaths( // Get the default list of allowed params. const _validParamKeys = Object.keys(_routeMatcher(page)) - const staticPathsResult = await unstable_getStaticPaths() + const staticPathsResult = await getStaticPaths() const expectedReturnVal = `Expected: { paths: [], fallback: boolean }\n` + @@ -521,7 +521,7 @@ export async function buildStaticPaths( Array.isArray(staticPathsResult) ) { throw new Error( - `Invalid value returned from unstable_getStaticPaths in ${page}. Received ${typeof staticPathsResult} ${expectedReturnVal}` + `Invalid value returned from getStaticPaths in ${page}. Received ${typeof staticPathsResult} ${expectedReturnVal}` ) } @@ -531,7 +531,7 @@ export async function buildStaticPaths( if (invalidStaticPathKeys.length > 0) { throw new Error( - `Extra keys returned from unstable_getStaticPaths in ${page} (${invalidStaticPathKeys.join( + `Extra keys returned from getStaticPaths in ${page} (${invalidStaticPathKeys.join( ', ' )}) ${expectedReturnVal}` ) @@ -539,7 +539,7 @@ export async function buildStaticPaths( if (typeof staticPathsResult.fallback !== 'boolean') { throw new Error( - `The \`fallback\` key must be returned from unstable_getStaticPaths in ${page}.\n` + + `The \`fallback\` key must be returned from getStaticPaths in ${page}.\n` + expectedReturnVal ) } @@ -548,7 +548,7 @@ export async function buildStaticPaths( if (!Array.isArray(toPrerender)) { throw new Error( - `Invalid \`paths\` value returned from unstable_getStaticProps in ${page}.\n` + + `Invalid \`paths\` value returned from getStaticProps in ${page}.\n` + `\`paths\` must be an array of strings or objects of shape { params: [key: string]: string }` ) } @@ -572,7 +572,7 @@ export async function buildStaticPaths( const invalidKeys = Object.keys(entry).filter(key => key !== 'params') if (invalidKeys.length) { throw new Error( - `Additional keys were returned from \`unstable_getStaticPaths\` in page "${page}". ` + + `Additional keys were returned from \`getStaticPaths\` in page "${page}". ` + `URL Parameters intended for this dynamic route must be nested under the \`params\` key, i.e.:` + `\n\n\treturn { params: { ${_validParamKeys .map(k => `${k}: ...`) @@ -593,7 +593,7 @@ export async function buildStaticPaths( throw new Error( `A required parameter (${validParamKey}) was not provided as ${ repeat ? 'an array' : 'a string' - } in unstable_getStaticPaths for ${page}` + } in getStaticPaths for ${page}` ) } @@ -634,14 +634,35 @@ export async function isPageStatic( } const hasGetInitialProps = !!(Comp as any).getInitialProps - const hasStaticProps = !!mod.unstable_getStaticProps - const hasStaticPaths = !!mod.unstable_getStaticPaths - const hasServerProps = !!mod.unstable_getServerSideProps + const hasStaticProps = !!mod.getStaticProps + const hasStaticPaths = !!mod.getStaticPaths + const hasServerProps = !!mod.getServerSideProps + const hasLegacyServerProps = !!mod.unstable_getServerProps + const hasLegacyStaticProps = !!mod.unstable_getStaticProps + const hasLegacyStaticPaths = !!mod.unstable_getStaticPaths const hasLegacyStaticParams = !!mod.unstable_getStaticParams if (hasLegacyStaticParams) { throw new Error( - `unstable_getStaticParams was replaced with unstable_getStaticPaths. Please update your code.` + `unstable_getStaticParams was replaced with getStaticPaths. Please update your code.` + ) + } + + if (hasLegacyStaticPaths) { + throw new Error( + `unstable_getStaticPaths was replaced with getStaticPaths. Please update your code.` + ) + } + + if (hasLegacyStaticProps) { + throw new Error( + `unstable_getStaticProps was replaced with getStaticProps. Please update your code.` + ) + } + + if (hasLegacyServerProps) { + throw new Error( + `unstable_getServerProps was replaced with getServerSideProps. Please update your code.` ) } @@ -663,14 +684,14 @@ export async function isPageStatic( // A page cannot have static parameters if it is not a dynamic page. if (hasStaticProps && hasStaticPaths && !pageIsDynamic) { throw new Error( - `unstable_getStaticPaths can only be used with dynamic pages, not '${page}'.` + + `getStaticPaths can only be used with dynamic pages, not '${page}'.` + `\nLearn more: https://nextjs.org/docs#dynamic-routing` ) } if (hasStaticProps && pageIsDynamic && !hasStaticPaths) { throw new Error( - `unstable_getStaticPaths is required for dynamic SSG pages and is missing for '${page}'.` + + `getStaticPaths is required for dynamic SSG pages and is missing for '${page}'.` + `\nRead more: https://err.sh/next.js/invalid-getstaticpaths-value` ) } @@ -681,7 +702,7 @@ export async function isPageStatic( ;({ paths: prerenderRoutes, fallback: prerenderFallback, - } = await buildStaticPaths(page, mod.unstable_getStaticPaths)) + } = await buildStaticPaths(page, mod.getStaticPaths)) } const config = mod.config || {} diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 0b6c770fc1a3..e8c8176b513d 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -217,10 +217,15 @@ const nextServerlessLoader: loader.Loader = function() { const Component = ComponentInfo.default export default Component - export const unstable_getStaticProps = ComponentInfo['unstable_getStaticProp' + 's'] export const unstable_getStaticParams = ComponentInfo['unstable_getStaticParam' + 's'] + export const getStaticProps = ComponentInfo['getStaticProp' + 's'] + export const getStaticPaths = ComponentInfo['getStaticPath' + 's'] + export const getServerSideProps = ComponentInfo['getServerSideProp' + 's'] + + // kept for detecting legacy exports + export const unstable_getStaticProps = ComponentInfo['unstable_getStaticProp' + 's'] export const unstable_getStaticPaths = ComponentInfo['unstable_getStaticPath' + 's'] - export const unstable_getServerSideProps = ComponentInfo['unstable_getServerSideProp' + 's'] + export const unstable_getServerProps = ComponentInfo['unstable_getServerProp' + 's'] ${dynamicRouteMatcher} ${handleRewrites} @@ -241,9 +246,9 @@ const nextServerlessLoader: loader.Loader = function() { App, Document, buildManifest, - unstable_getStaticProps, - unstable_getServerSideProps, - unstable_getStaticPaths, + getStaticProps, + getServerSideProps, + getStaticPaths, reactLoadableManifest, canonicalBase: "${canonicalBase}", buildId: "${buildId}", @@ -275,7 +280,7 @@ const nextServerlessLoader: loader.Loader = function() { ${page === '/_error' ? `res.statusCode = 404` : ''} ${ pageIsDynamicRoute - ? `const params = fromExport && !unstable_getStaticProps && !unstable_getServerSideProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};` + ? `const params = fromExport && !getStaticProps && !getServerSideProps ? {} : dynamicRouteMatcher(parsedUrl.pathname) || {};` : `const params = {};` } ${ @@ -320,7 +325,7 @@ const nextServerlessLoader: loader.Loader = function() { const previewData = tryGetPreviewData(req, res, options.previewProps) const isPreviewMode = previewData !== false - let result = await renderToHTML(req, res, "${page}", Object.assign({}, unstable_getStaticProps ? {} : parsedUrl.query, nowParams ? nowParams : params, _params, isFallback ? { __nextFallback: 'true' } : {}), renderOpts) + let result = await renderToHTML(req, res, "${page}", Object.assign({}, getStaticProps ? {} : parsedUrl.query, nowParams ? nowParams : params, _params, isFallback ? { __nextFallback: 'true' } : {}), renderOpts) if (_nextData && !fromExport) { const payload = JSON.stringify(renderOpts.pageData) @@ -331,7 +336,7 @@ const nextServerlessLoader: loader.Loader = function() { 'Cache-Control', isPreviewMode ? \`private, no-cache, no-store, max-age=0, must-revalidate\` - : unstable_getServerSideProps + : getServerSideProps ? \`no-cache, no-store, must-revalidate\` : \`s-maxage=\${renderOpts.revalidate}, stale-while-revalidate\` ) @@ -350,9 +355,9 @@ const nextServerlessLoader: loader.Loader = function() { if (err.code === 'ENOENT') { res.statusCode = 404 const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, { - unstable_getStaticProps: undefined, - unstable_getStaticPaths: undefined, - unstable_getServerSideProps: undefined, + getStaticProps: undefined, + getStaticPaths: undefined, + getServerSideProps: undefined, Component: Error })) return result @@ -360,9 +365,9 @@ const nextServerlessLoader: loader.Loader = function() { console.error(err) res.statusCode = 500 const result = await renderToHTML(req, res, "/_error", parsedUrl.query, Object.assign({}, options, { - unstable_getStaticProps: undefined, - unstable_getStaticPaths: undefined, - unstable_getServerSideProps: undefined, + getStaticProps: undefined, + getStaticPaths: undefined, + getServerSideProps: undefined, Component: Error, err })) diff --git a/packages/next/export/worker.js b/packages/next/export/worker.js index ae5b4bd0b0d7..86e820cd4327 100644 --- a/packages/next/export/worker.js +++ b/packages/next/export/worker.js @@ -119,9 +119,9 @@ export default async function({ let renderMethod = renderToHTML // eslint-disable-next-line camelcase - const renderedDuringBuild = unstable_getStaticProps => { + const renderedDuringBuild = getStaticProps => { // eslint-disable-next-line camelcase - return !buildExport && unstable_getStaticProps && !isDynamicRoute(path) + return !buildExport && getStaticProps && !isDynamicRoute(path) } if (serverless) { @@ -147,9 +147,9 @@ export default async function({ } else { // for non-dynamic SSG pages we should have already // prerendered the file - if (renderedDuringBuild(mod.unstable_getStaticProps)) return results + if (renderedDuringBuild(mod.getStaticProps)) return results - if (mod.unstable_getStaticProps && !htmlFilepath.endsWith('.html')) { + if (mod.getStaticProps && !htmlFilepath.endsWith('.html')) { // make sure it ends with .html if the name contains a dot htmlFilename += '.html' htmlFilepath += '.html' @@ -174,15 +174,12 @@ export default async function({ // for non-dynamic SSG pages we should have already // prerendered the file - if (renderedDuringBuild(components.unstable_getStaticProps)) { + if (renderedDuringBuild(components.getStaticProps)) { return results } // TODO: de-dupe the logic here between serverless and server mode - if ( - components.unstable_getStaticProps && - !htmlFilepath.endsWith('.html') - ) { + if (components.getStaticProps && !htmlFilepath.endsWith('.html')) { // make sure it ends with .html if the name contains a dot htmlFilepath += '.html' htmlFilename += '.html' diff --git a/packages/next/lib/constants.ts b/packages/next/lib/constants.ts index 1070627fad7a..e8025b25e9ec 100644 --- a/packages/next/lib/constants.ts +++ b/packages/next/lib/constants.ts @@ -24,10 +24,10 @@ 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 SSG_GET_INITIAL_PROPS_CONFLICT = `You can not use getInitialProps with getStaticProps. To use SSG, please remove your getInitialProps` -export const SERVER_PROPS_GET_INIT_PROPS_CONFLICT = `You can not use getInitialProps with unstable_getServerSideProps. Please remove one or the other` +export const SERVER_PROPS_GET_INIT_PROPS_CONFLICT = `You can not use getInitialProps with getServerSideProps. Please remove getInitialProps.` -export const SERVER_PROPS_SSG_CONFLICT = `You can not use unstable_getStaticProps with unstable_getServerSideProps. To use SSG, please remove your unstable_getServerSideProps` +export const SERVER_PROPS_SSG_CONFLICT = `You can not use getStaticProps with getServerSideProps. To use SSG, please remove getServerSideProps` export const PAGES_404_GET_INITIAL_PROPS_ERROR = `\`pages/404\` can not have getInitialProps/getServerSideProps, https://err.sh/zeit/next.js/404-get-initial-props` diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index 856ea05a6efd..3b8fa7accd0e 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -1,5 +1,3 @@ -import { IncomingMessage, ServerResponse } from 'http' -import { ParsedUrlQuery } from 'querystring' import { BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, @@ -12,7 +10,13 @@ import { join } from 'path' import { requirePage } from './require' import { BuildManifest } from './get-page-files' import { AppType, DocumentType } from '../lib/utils' -import { PageConfig, NextPageContext } from 'next/types' +import { + PageConfig, + NextPageContext, + GetStaticPaths, + GetServerSideProps, + GetStaticProps, +} from 'next/types' export function interopDefault(mod: any) { return mod.default || mod @@ -41,27 +45,6 @@ export type ManifestItem = { type ReactLoadableManifest = { [moduleId: string]: ManifestItem[] } -type Unstable_getStaticProps = (ctx: { - params: ParsedUrlQuery | undefined - preview?: boolean - previewData?: any -}) => Promise<{ - props: { [key: string]: any } - revalidate?: number | boolean -}> - -export type Unstable_getStaticPaths = () => Promise<{ - paths: Array - fallback: boolean -}> - -type Unstable_getServerSideProps = (context: { - params: ParsedUrlQuery | undefined - req: IncomingMessage - res: ServerResponse - query: ParsedUrlQuery -}) => Promise<{ [key: string]: any }> - export type LoadComponentsReturnType = { Component: React.ComponentType pageConfig?: PageConfig @@ -70,9 +53,9 @@ export type LoadComponentsReturnType = { Document: DocumentType DocumentMiddleware?: (ctx: NextPageContext) => void App: AppType - unstable_getStaticProps?: Unstable_getStaticProps - unstable_getStaticPaths?: Unstable_getStaticPaths - unstable_getServerSideProps?: Unstable_getServerSideProps + getStaticProps?: GetStaticProps + getStaticPaths?: GetStaticPaths + getServerSideProps?: GetServerSideProps } export async function loadComponents( @@ -83,24 +66,16 @@ export async function loadComponents( ): Promise { if (serverless) { const Component = await requirePage(pathname, distDir, serverless) - const { - unstable_getStaticProps, - unstable_getStaticPaths, - unstable_getServerSideProps, - } = Component + const { getStaticProps, getStaticPaths, getServerSideProps } = Component - addComponentPropsId( - Component, - unstable_getStaticProps, - unstable_getServerSideProps - ) + addComponentPropsId(Component, getStaticProps, getServerSideProps) return { Component, pageConfig: Component.config || {}, - unstable_getStaticProps, - unstable_getStaticPaths, - unstable_getServerSideProps, + getStaticProps, + getStaticPaths, + getServerSideProps, } as LoadComponentsReturnType } const documentPath = join( @@ -141,17 +116,9 @@ export async function loadComponents( interopDefault(AppMod), ]) - const { - unstable_getServerSideProps, - unstable_getStaticProps, - unstable_getStaticPaths, - } = ComponentMod + const { getServerSideProps, getStaticProps, getStaticPaths } = ComponentMod - addComponentPropsId( - Component, - unstable_getStaticProps, - unstable_getServerSideProps - ) + addComponentPropsId(Component, getStaticProps, getServerSideProps) return { App, @@ -161,8 +128,8 @@ export async function loadComponents( DocumentMiddleware, reactLoadableManifest, pageConfig: ComponentMod.config || {}, - unstable_getServerSideProps, - unstable_getStaticProps, - unstable_getStaticPaths, + getServerSideProps, + getStaticProps, + getStaticPaths, } } diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 4e1ec838ac3e..1c41aaf6e200 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -810,7 +810,7 @@ export default class Server { return { components, query: { - ...(components.unstable_getStaticProps + ...(components.getStaticProps ? { _nextDataReq: query._nextDataReq } : query), ...(params || {}), @@ -888,9 +888,9 @@ export default class Server { const isLikeServerless = typeof components.Component === 'object' && typeof (components.Component as any).renderReqToHTML === 'function' - const isSSG = !!components.unstable_getStaticProps - const isServerProps = !!components.unstable_getServerSideProps - const hasStaticPaths = !!components.unstable_getStaticPaths + const isSSG = !!components.getStaticProps + const isServerProps = !!components.getServerSideProps + const hasStaticPaths = !!components.getStaticPaths // Toggle whether or not this is a Data request const isDataReq = query._nextDataReq diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 287dd361ed7d..1b063dacdeb0 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -273,9 +273,9 @@ export async function renderToHTML( buildManifest, reactLoadableManifest, ErrorDebug, - unstable_getStaticProps, - unstable_getStaticPaths, - unstable_getServerSideProps, + getStaticProps, + getStaticPaths, + getServerSideProps, isDataReq, params, previewProps, @@ -311,7 +311,7 @@ export async function renderToHTML( const isFallback = !!query.__nextFallback delete query.__nextFallback - const isSpr = !!unstable_getStaticProps + const isSpr = !!getStaticProps const defaultAppGetInitialProps = App.getInitialProps === (App as any).origGetInitialProps @@ -323,7 +323,7 @@ export async function renderToHTML( !hasPageGetInitialProps && defaultAppGetInitialProps && !isSpr && - !unstable_getServerSideProps + !getServerSideProps if ( process.env.NODE_ENV !== 'production' && @@ -349,23 +349,23 @@ export async function renderToHTML( throw new Error(SSG_GET_INITIAL_PROPS_CONFLICT + ` ${pathname}`) } - if (hasPageGetInitialProps && unstable_getServerSideProps) { + if (hasPageGetInitialProps && getServerSideProps) { throw new Error(SERVER_PROPS_GET_INIT_PROPS_CONFLICT + ` ${pathname}`) } - if (unstable_getServerSideProps && isSpr) { + if (getServerSideProps && isSpr) { throw new Error(SERVER_PROPS_SSG_CONFLICT + ` ${pathname}`) } - if (!!unstable_getStaticPaths && !isSpr) { + if (!!getStaticPaths && !isSpr) { throw new Error( - `unstable_getStaticPaths was added without a unstable_getStaticProps in ${pathname}. Without unstable_getStaticProps, unstable_getStaticPaths does nothing` + `getStaticPaths was added without a getStaticProps in ${pathname}. Without getStaticProps, getStaticPaths does nothing` ) } - if (isSpr && pageIsDynamic && !unstable_getStaticPaths) { + if (isSpr && pageIsDynamic && !getStaticPaths) { throw new Error( - `unstable_getStaticPaths is required for dynamic SSG pages and is missing for '${pathname}'.` + + `getStaticPaths is required for dynamic SSG pages and is missing for '${pathname}'.` + `\nRead more: https://err.sh/next.js/invalid-getstaticpaths-value` ) } @@ -467,7 +467,7 @@ export async function renderToHTML( // instantly. There's no need to pass this data down from a previous // invoke, where we'd have to consider server & serverless. const previewData = tryGetPreviewData(req, res, previewProps) - const data = await unstable_getStaticProps!({ + const data = await getStaticProps!({ ...(pageIsDynamic ? { params: query as ParsedUrlQuery, @@ -529,8 +529,8 @@ export async function renderToHTML( console.error(err) } - if (unstable_getServerSideProps && !isFallback) { - const data = await unstable_getServerSideProps({ + if (getServerSideProps && !isFallback) { + const data = await getServerSideProps({ params, query, req, @@ -549,7 +549,7 @@ export async function renderToHTML( if ( !isSpr && // we only show this warning for legacy pages - !unstable_getServerSideProps && + !getServerSideProps && process.env.NODE_ENV !== 'production' && Object.keys(props?.pageProps || {}).includes('url') ) { diff --git a/packages/next/server/static-paths-worker.ts b/packages/next/server/static-paths-worker.ts index 51d0047a5aad..ba09801f0d6e 100644 --- a/packages/next/server/static-paths-worker.ts +++ b/packages/next/server/static-paths-worker.ts @@ -28,13 +28,13 @@ export async function loadStaticPaths( serverless ) - if (!components.unstable_getStaticPaths) { + if (!components.getStaticPaths) { // we shouldn't get to this point since the worker should // only be called for SSG pages with getStaticPaths throw new Error( - `Invariant: failed to load page with unstable_getStaticPaths for ${pathname}` + `Invariant: failed to load page with getStaticPaths for ${pathname}` ) } - return buildStaticPaths(pathname, components.unstable_getStaticPaths) + return buildStaticPaths(pathname, components.getStaticPaths) } diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index f0a1bfc0897a..986e551fec9e 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -3,6 +3,8 @@ /// import React from 'react' +import { ParsedUrlQuery } from 'querystring' +import { IncomingMessage, ServerResponse } from 'http' import { NextPageContext, @@ -62,4 +64,25 @@ export { NextApiHandler, } +export type GetStaticProps = (ctx: { + params: ParsedUrlQuery | undefined + preview?: boolean + previewData?: any +}) => Promise<{ + props: { [key: string]: any } + revalidate?: number | boolean +}> + +export type GetStaticPaths = () => Promise<{ + paths: Array + fallback: boolean +}> + +export type GetServerSideProps = (context: { + params: ParsedUrlQuery | undefined + req: IncomingMessage + res: ServerResponse + query: ParsedUrlQuery +}) => Promise<{ [key: string]: any }> + export default next diff --git a/test/integration/catches-missing-getStaticProps/pages/[slug].js b/test/integration/catches-missing-getStaticProps/pages/[slug].js index 7a87525aacc9..df6775cabe94 100644 --- a/test/integration/catches-missing-getStaticProps/pages/[slug].js +++ b/test/integration/catches-missing-getStaticProps/pages/[slug].js @@ -1,4 +1,4 @@ -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: ['/hello', '/world'], fallback: true } } diff --git a/test/integration/catches-missing-getStaticProps/test/index.test.js b/test/integration/catches-missing-getStaticProps/test/index.test.js index af8319252891..fec687871b6a 100644 --- a/test/integration/catches-missing-getStaticProps/test/index.test.js +++ b/test/integration/catches-missing-getStaticProps/test/index.test.js @@ -14,7 +14,7 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 const appDir = join(__dirname, '../') const nextConfig = join(appDir, 'next.config.js') -const errorRegex = /unstable_getStaticPaths was added without a unstable_getStaticProps in/ +const errorRegex = /getStaticPaths was added without a getStaticProps in/ describe('Catches Missing getStaticProps', () => { afterAll(() => fs.remove(nextConfig)) diff --git a/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js b/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js index 85ace5dad12e..2feba203a2bf 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js +++ b/test/integration/dynamic-routing/pages/p1/p2/all-ssg/[...rest].js @@ -2,11 +2,11 @@ function All({ params }) { return

{JSON.stringify(params)}
} -export function unstable_getStaticProps({ params }) { +export function getStaticProps({ params }) { return { props: { params } } } -export function unstable_getStaticPaths() { +export function getStaticPaths() { return { paths: [], fallback: true, diff --git a/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js b/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js index 03418a2de5c7..39b9da58c20a 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js +++ b/test/integration/dynamic-routing/pages/p1/p2/nested-all-ssg/[...rest]/index.js @@ -8,11 +8,11 @@ function All({ params }) { ) } -export function unstable_getStaticProps({ params }) { +export function getStaticProps({ params }) { return { props: { params } } } -export function unstable_getStaticPaths() { +export function getStaticPaths() { return { paths: [], fallback: true, diff --git a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js index 55cf9c8a29e8..0d1824ccaaf0 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js +++ b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js @@ -2,11 +2,11 @@ function All({ params }) { return
{JSON.stringify(params)}
} -export function unstable_getStaticProps({ params }) { +export function getStaticProps({ params }) { return { props: { params } } } -export function unstable_getStaticPaths() { +export function getStaticPaths() { return { paths: [ `/p1/p2/predefined-ssg/one-level`, diff --git a/test/integration/getserversideprops/pages/another/index.js b/test/integration/getserversideprops/pages/another/index.js index de46cda8bd03..5270b73f050c 100644 --- a/test/integration/getserversideprops/pages/another/index.js +++ b/test/integration/getserversideprops/pages/another/index.js @@ -3,7 +3,7 @@ import fs from 'fs' import findUp from 'find-up' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps() { +export async function getServerSideProps() { const text = fs .readFileSync( findUp.sync('world.txt', { diff --git a/test/integration/getserversideprops/pages/blog/[post]/[comment].js b/test/integration/getserversideprops/pages/blog/[post]/[comment].js index a5a42247ecb1..f1449358ae4f 100644 --- a/test/integration/getserversideprops/pages/blog/[post]/[comment].js +++ b/test/integration/getserversideprops/pages/blog/[post]/[comment].js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ query }) { +export async function getServerSideProps({ query }) { return { props: { post: query.post, diff --git a/test/integration/getserversideprops/pages/blog/[post]/index.js b/test/integration/getserversideprops/pages/blog/[post]/index.js index ba9c5978ea88..c507475cf752 100644 --- a/test/integration/getserversideprops/pages/blog/[post]/index.js +++ b/test/integration/getserversideprops/pages/blog/[post]/index.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ params }) { +export async function getServerSideProps({ params }) { if (params.post === 'post-10') { await new Promise(resolve => { setTimeout(() => resolve(), 1000) diff --git a/test/integration/getserversideprops/pages/blog/index.js b/test/integration/getserversideprops/pages/blog/index.js index be51f782e42d..627f651550ed 100644 --- a/test/integration/getserversideprops/pages/blog/index.js +++ b/test/integration/getserversideprops/pages/blog/index.js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps() { +export async function getServerSideProps() { return { props: { slugs: ['post-1', 'post-2'], diff --git a/test/integration/getserversideprops/pages/catchall/[...path].js b/test/integration/getserversideprops/pages/catchall/[...path].js index 45efb3e6c85f..de807caa3cdf 100644 --- a/test/integration/getserversideprops/pages/catchall/[...path].js +++ b/test/integration/getserversideprops/pages/catchall/[...path].js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ params }) { +export async function getServerSideProps({ params }) { return { props: { world: 'world', diff --git a/test/integration/getserversideprops/pages/default-revalidate.js b/test/integration/getserversideprops/pages/default-revalidate.js index 582ef524ec1a..57e55a20508b 100644 --- a/test/integration/getserversideprops/pages/default-revalidate.js +++ b/test/integration/getserversideprops/pages/default-revalidate.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps() { +export async function getServerSideProps() { return { props: { world: 'world', diff --git a/test/integration/getserversideprops/pages/index.js b/test/integration/getserversideprops/pages/index.js index fc84e9848d8d..becc0df1c5c0 100644 --- a/test/integration/getserversideprops/pages/index.js +++ b/test/integration/getserversideprops/pages/index.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps() { +export async function getServerSideProps() { return { props: { world: 'world', diff --git a/test/integration/getserversideprops/pages/invalid-keys.js b/test/integration/getserversideprops/pages/invalid-keys.js index 4184eb8bcfab..3cf83a33bde5 100644 --- a/test/integration/getserversideprops/pages/invalid-keys.js +++ b/test/integration/getserversideprops/pages/invalid-keys.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ params, query }) { +export async function getServerSideProps({ params, query }) { return { world: 'world', query: query || {}, diff --git a/test/integration/getserversideprops/pages/something.js b/test/integration/getserversideprops/pages/something.js index 361bbb44b509..5b32921c5bbf 100644 --- a/test/integration/getserversideprops/pages/something.js +++ b/test/integration/getserversideprops/pages/something.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ params, query }) { +export async function getServerSideProps({ params, query }) { return { props: { world: 'world', diff --git a/test/integration/getserversideprops/pages/user/[user]/profile.js b/test/integration/getserversideprops/pages/user/[user]/profile.js index c14992d56d4d..9a6b30f37b48 100644 --- a/test/integration/getserversideprops/pages/user/[user]/profile.js +++ b/test/integration/getserversideprops/pages/user/[user]/profile.js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getServerSideProps({ query }) { +export async function getServerSideProps({ query }) { return { props: { user: query.user, diff --git a/test/integration/getserversideprops/test/index.test.js b/test/integration/getserversideprops/test/index.test.js index 963215c85850..f2387061eae7 100644 --- a/test/integration/getserversideprops/test/index.test.js +++ b/test/integration/getserversideprops/test/index.test.js @@ -337,7 +337,7 @@ const runTests = (dev = false) => { await fs.writeFile( urlPropPage, ` - export async function unstable_getServerSideProps() { + export async function getServerSideProps() { return { props: { url: 'something' @@ -396,7 +396,7 @@ const runTests = (dev = false) => { } } -describe('unstable_getServerSideProps', () => { +describe('getServerSideProps', () => { describe('dev mode', () => { beforeAll(async () => { stderr = '' diff --git a/test/integration/legacy-ssg-methods-error/pages/index.js b/test/integration/legacy-ssg-methods-error/pages/index.js new file mode 100644 index 000000000000..8bf35c1ea717 --- /dev/null +++ b/test/integration/legacy-ssg-methods-error/pages/index.js @@ -0,0 +1,7 @@ +export async function unstable_getStaticProps() { + return { + props: {}, + } +} + +export default () => 'hi' diff --git a/test/integration/legacy-ssg-methods-error/test/index.test.js b/test/integration/legacy-ssg-methods-error/test/index.test.js new file mode 100644 index 000000000000..23f85e10ffd2 --- /dev/null +++ b/test/integration/legacy-ssg-methods-error/test/index.test.js @@ -0,0 +1,81 @@ +/* eslint-env jest */ +/* global jasmine */ +import fs from 'fs-extra' +import { join } from 'path' +import { nextBuild } from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 + +const appDir = join(__dirname, '..') +const indexPage = join(appDir, 'pages/index.js') +let origIndexPage = '' + +const runTests = (serverless = false) => { + if (serverless) { + const nextConfig = join(appDir, 'next.config.js') + + beforeEach(() => + fs.writeFile( + nextConfig, + ` + module.exports = { + target: 'experimental-serverless-trace' + } + ` + ) + ) + + afterAll(() => fs.remove(nextConfig)) + } + + it('should error when legacy unstable_getStaticProps', async () => { + const { stderr, code } = await nextBuild(appDir, [], { stderr: true }) + expect(code).toBe(1) + expect(stderr).toContain( + `unstable_getStaticProps was replaced with getStaticProps. Please update your code.` + ) + }) + + it('should error when legacy unstable_getServerProps', async () => { + await fs.writeFile( + indexPage, + origIndexPage.replace('getStaticProps', 'getServerProps') + ) + + const { stderr, code } = await nextBuild(appDir, [], { stderr: true }) + + expect(code).toBe(1) + expect(stderr).toContain( + `unstable_getServerProps was replaced with getServerSideProps. Please update your code.` + ) + }) + + it('should error when legacy unstable_getServerProps', async () => { + await fs.writeFile( + indexPage, + origIndexPage.replace('getStaticProps', 'getStaticPaths') + ) + + const { stderr, code } = await nextBuild(appDir, [], { stderr: true }) + + expect(code).toBe(1) + expect(stderr).toContain( + `unstable_getStaticPaths was replaced with getStaticPaths. Please update your code.` + ) + }) +} + +describe('Mixed getStaticProps and getServerSideProps error', () => { + beforeAll(async () => { + origIndexPage = await fs.readFile(indexPage, 'utf8') + }) + afterEach(() => fs.writeFile(indexPage, origIndexPage)) + + describe('server mode', () => { + runTests(false) + }) + + describe('serverless mode', () => { + runTests(true) + }) +}) diff --git a/test/integration/mixed-ssg-serverprops-error/pages/index.js b/test/integration/mixed-ssg-serverprops-error/pages/index.js index 7bf4d452e293..46967c38fc4d 100644 --- a/test/integration/mixed-ssg-serverprops-error/pages/index.js +++ b/test/integration/mixed-ssg-serverprops-error/pages/index.js @@ -1,10 +1,10 @@ -export const unstable_getStaticProps = async () => { +export const getStaticProps = async () => { return { props: { world: 'world' }, } } -export const unstable_getServerSideProps = async () => { +export const getServerSideProps = async () => { return { props: { world: 'world' }, } diff --git a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt index 9d6334f42ef0..1e3ee159ba57 100644 --- a/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt +++ b/test/integration/mixed-ssg-serverprops-error/pages/index.js.alt @@ -1,10 +1,10 @@ -export const unstable_getStaticPaths = async () => { +export const getStaticPaths = async () => { return { props: { world: 'world' }, fallback: true } } -export const unstable_getServerSideProps = async () => { +export const getServerSideProps = async () => { return { props: { world: 'world' } } diff --git a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js index f8413c5dc5ff..f0eef4d55972 100644 --- a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js +++ b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js @@ -1,12 +1,12 @@ import React from 'react' // eslint-disable-next-line camelcase -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [{ params: { slug: 'hello' } }], fallback: true } } // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { post: params.post, diff --git a/test/integration/prerender-invalid-catchall-params/test/index.test.js b/test/integration/prerender-invalid-catchall-params/test/index.test.js index d2b47c0c8b09..ac1f966335a3 100644 --- a/test/integration/prerender-invalid-catchall-params/test/index.test.js +++ b/test/integration/prerender-invalid-catchall-params/test/index.test.js @@ -11,7 +11,7 @@ describe('Invalid Prerender Catchall Params', () => { const out = await nextBuild(appDir, [], { stderr: true }) expect(out.stderr).toMatch(`Build error occurred`) expect(out.stderr).toMatch( - 'A required parameter (slug) was not provided as an array in unstable_getStaticPaths for /[...slug]' + 'A required parameter (slug) was not provided as an array in getStaticPaths for /[...slug]' ) }) }) diff --git a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js index ef80ed41e876..4dc885727859 100644 --- a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js +++ b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js @@ -1,12 +1,12 @@ import React from 'react' // eslint-disable-next-line camelcase -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [{ foo: 'bad', baz: 'herro' }], fallback: true } } // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { post: params.post, diff --git a/test/integration/prerender-legacy/pages/blog/[post].js b/test/integration/prerender-legacy/pages/blog/[post].js index aeaa7c4a371c..c7aa49e37b24 100644 --- a/test/integration/prerender-legacy/pages/blog/[post].js +++ b/test/integration/prerender-legacy/pages/blog/[post].js @@ -6,7 +6,7 @@ export async function unstable_getStaticParams() { } // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { post: params.post, diff --git a/test/integration/prerender-legacy/test/index.test.js b/test/integration/prerender-legacy/test/index.test.js index 1cec1d583466..2f122c5b365f 100644 --- a/test/integration/prerender-legacy/test/index.test.js +++ b/test/integration/prerender-legacy/test/index.test.js @@ -14,7 +14,7 @@ describe('Legacy Prerender', () => { const out = await nextBuild(appDir, [], { stderr: true }) expect(out.stderr).toMatch(`Build error occurred`) expect(out.stderr).toMatch( - 'unstable_getStaticParams was replaced with unstable_getStaticPaths. Please update your code.' + 'unstable_getStaticParams was replaced with getStaticPaths. Please update your code.' ) }) @@ -27,7 +27,7 @@ describe('Legacy Prerender', () => { await fs.remove(nextConfig) expect(out.stderr).toMatch(`Build error occurred`) expect(out.stderr).toMatch( - 'unstable_getStaticParams was replaced with unstable_getStaticPaths. Please update your code.' + 'unstable_getStaticParams was replaced with getStaticPaths. Please update your code.' ) }) }) diff --git a/test/integration/prerender-preview/pages/index.js b/test/integration/prerender-preview/pages/index.js index a9da9aa29357..8fa4d03b6b51 100644 --- a/test/integration/prerender-preview/pages/index.js +++ b/test/integration/prerender-preview/pages/index.js @@ -1,4 +1,4 @@ -export function unstable_getStaticProps({ preview, previewData }) { +export function getStaticProps({ preview, previewData }) { return { props: { hasProps: true, preview, previewData } } } diff --git a/test/integration/prerender/pages/another/index.js b/test/integration/prerender/pages/another/index.js index 19a7709d3dbe..f8eb26a2e003 100644 --- a/test/integration/prerender/pages/another/index.js +++ b/test/integration/prerender/pages/another/index.js @@ -3,7 +3,7 @@ import fs from 'fs' import findUp from 'find-up' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { const text = fs .readFileSync( findUp.sync('world.txt', { diff --git a/test/integration/prerender/pages/blog/[post]/[comment].js b/test/integration/prerender/pages/blog/[post]/[comment].js index 0c1174ffbb77..b2d89f3cba27 100644 --- a/test/integration/prerender/pages/blog/[post]/[comment].js +++ b/test/integration/prerender/pages/blog/[post]/[comment].js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [ '/blog/post-1/comment-1', @@ -13,7 +13,7 @@ export async function unstable_getStaticPaths() { } // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { post: params.post, diff --git a/test/integration/prerender/pages/blog/[post]/index.js b/test/integration/prerender/pages/blog/[post]/index.js index 2b63657aae2d..1f33676647a8 100644 --- a/test/integration/prerender/pages/blog/[post]/index.js +++ b/test/integration/prerender/pages/blog/[post]/index.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [ '/blog/post-1', @@ -20,7 +20,7 @@ export async function unstable_getStaticPaths() { let counter = 0 // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { if (params.post === 'post-10') { await new Promise(resolve => { setTimeout(() => resolve(), 1000) diff --git a/test/integration/prerender/pages/blog/index.js b/test/integration/prerender/pages/blog/index.js index 6ec9f06b567f..c4a0b940819b 100644 --- a/test/integration/prerender/pages/blog/index.js +++ b/test/integration/prerender/pages/blog/index.js @@ -2,7 +2,7 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { return { props: { slugs: ['post-1', 'post-2'], diff --git a/test/integration/prerender/pages/catchall-explicit/[...slug].js b/test/integration/prerender/pages/catchall-explicit/[...slug].js index e992e8c79bbd..6b3bfbccacff 100644 --- a/test/integration/prerender/pages/catchall-explicit/[...slug].js +++ b/test/integration/prerender/pages/catchall-explicit/[...slug].js @@ -1,4 +1,4 @@ -export async function unstable_getStaticProps({ params: { slug } }) { +export async function getStaticProps({ params: { slug } }) { if (slug[0] === 'delayby3s') { await new Promise(resolve => setTimeout(resolve, 3000)) } @@ -11,7 +11,7 @@ export async function unstable_getStaticProps({ params: { slug } }) { } } -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [ { params: { slug: ['first'] } }, diff --git a/test/integration/prerender/pages/catchall/[...slug].js b/test/integration/prerender/pages/catchall/[...slug].js index 5a4834499e3e..0ce61b73d3a5 100644 --- a/test/integration/prerender/pages/catchall/[...slug].js +++ b/test/integration/prerender/pages/catchall/[...slug].js @@ -1,6 +1,6 @@ import { useRouter } from 'next/router' -export async function unstable_getStaticProps({ params: { slug } }) { +export async function getStaticProps({ params: { slug } }) { if (slug[0] === 'delayby3s') { await new Promise(resolve => setTimeout(resolve, 3000)) } @@ -13,7 +13,7 @@ export async function unstable_getStaticProps({ params: { slug } }) { } } -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [ { params: { slug: ['first'] } }, diff --git a/test/integration/prerender/pages/default-revalidate.js b/test/integration/prerender/pages/default-revalidate.js index 582a0c5e7c85..e8125f3ca32a 100644 --- a/test/integration/prerender/pages/default-revalidate.js +++ b/test/integration/prerender/pages/default-revalidate.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { return { props: { world: 'world', diff --git a/test/integration/prerender/pages/index.js b/test/integration/prerender/pages/index.js index 1c9bf5259b50..44960c35d3e7 100644 --- a/test/integration/prerender/pages/index.js +++ b/test/integration/prerender/pages/index.js @@ -1,7 +1,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps() { +export async function getStaticProps() { // throw new Error('oops from getStaticProps') return { props: { world: 'world', time: new Date().getTime() }, diff --git a/test/integration/prerender/pages/something.js b/test/integration/prerender/pages/something.js index fde8a1730767..d9e341e75570 100644 --- a/test/integration/prerender/pages/something.js +++ b/test/integration/prerender/pages/something.js @@ -3,7 +3,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { world: 'world', diff --git a/test/integration/prerender/pages/user/[user]/profile.js b/test/integration/prerender/pages/user/[user]/profile.js index a80e175b6d20..3c95d23cd97b 100644 --- a/test/integration/prerender/pages/user/[user]/profile.js +++ b/test/integration/prerender/pages/user/[user]/profile.js @@ -2,12 +2,12 @@ import React from 'react' import Link from 'next/link' // eslint-disable-next-line camelcase -export async function unstable_getStaticPaths() { +export async function getStaticPaths() { return { paths: [], fallback: true } } // eslint-disable-next-line camelcase -export async function unstable_getStaticProps({ params }) { +export async function getStaticProps({ params }) { return { props: { user: params.user, diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 823437388dc2..a71b87e0773a 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -496,7 +496,7 @@ const runTests = (dev = false, looseMode = false) => { await fs.writeFile( urlPropPage, ` - export async function unstable_getStaticProps() { + export async function getStaticProps() { return { props: { url: 'something' @@ -603,7 +603,7 @@ const runTests = (dev = false, looseMode = false) => { await fs.writeFile( curPage, ` - export async function unstable_getStaticProps() { + export async function getStaticProps() { return { props: { hello: 'world' @@ -617,7 +617,7 @@ const runTests = (dev = false, looseMode = false) => { try { const html = await renderViaHTTP(appPort, '/temp/hello') expect(html).toMatch( - /unstable_getStaticPaths is required for dynamic SSG pages and is missing for/ + /getStaticPaths is required for dynamic SSG pages and is missing for/ ) } finally { await fs.remove(curPage) @@ -630,12 +630,12 @@ const runTests = (dev = false, looseMode = false) => { await fs.writeFile( curPage, ` - export async function unstable_getStaticPaths() { + export async function getStaticPaths() { return { paths: [] } } - export async function unstable_getStaticProps() { + export async function getStaticProps() { return { props: { hello: 'world' @@ -1013,7 +1013,7 @@ describe('SSG Prerender', () => { await fs.writeFile( brokenPage, ` - export async function unstable_getStaticProps() { + export async function getStaticProps() { return { hello: 'world' } @@ -1028,7 +1028,7 @@ describe('SSG Prerender', () => { 'Additional keys were returned from `getStaticProps`' ) expect(stderr).not.toContain( - 'You can not use getInitialProps with unstable_getStaticProps' + 'You can not use getInitialProps with getStaticProps' ) }) }) diff --git a/test/unit/babel-plugin-next-ssg-transform.test.js b/test/unit/babel-plugin-next-ssg-transform.test.js index 7844ed417eca..bb374923c6f2 100644 --- a/test/unit/babel-plugin-next-ssg-transform.test.js +++ b/test/unit/babel-plugin-next-ssg-transform.test.js @@ -32,8 +32,8 @@ describe('babel plugin (next-ssg-transform)', () => { describe('getStaticProps support', () => { it('should remove separate named export specifiers', () => { const output = babel(trim` - export { unstable_getStaticPaths } from '.' - export { a as unstable_getStaticProps } from '.' + export { getStaticPaths } from '.' + export { a as getStaticProps } from '.' export default function Test() { return
@@ -46,7 +46,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove combined named export specifiers', () => { const output = babel(trim` - export { unstable_getStaticPaths, a as unstable_getStaticProps } from '.' + export { getStaticPaths, a as getStaticProps } from '.' export default function Test() { return
@@ -59,7 +59,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should retain extra named export specifiers', () => { const output = babel(trim` - export { unstable_getStaticPaths, a as unstable_getStaticProps, foo, bar as baz } from '.' + export { getStaticPaths, a as getStaticProps, foo, bar as baz } from '.' export default function Test() { return
@@ -72,11 +72,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove named export function declarations', () => { const output = babel(trim` - export function unstable_getStaticPaths() { + export function getStaticPaths() { return [] } - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -92,11 +92,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove named export function declarations (async)', () => { const output = babel(trim` - export async function unstable_getStaticPaths() { + export async function getStaticPaths() { return [] } - export async function unstable_getStaticProps() { + export async function getStaticProps() { return { props: {} } } @@ -112,7 +112,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should not remove extra named export function declarations', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -130,11 +130,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove named export variable declarations', () => { const output = babel(trim` - export const unstable_getStaticPaths = () => { + export const getStaticPaths = () => { return [] } - export const unstable_getStaticProps = function() { + export const getStaticProps = function() { return { props: {} } } @@ -150,11 +150,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove named export variable declarations (async)', () => { const output = babel(trim` - export const unstable_getStaticPaths = async () => { + export const getStaticPaths = async () => { return [] } - export const unstable_getStaticProps = async function() { + export const getStaticProps = async function() { return { props: {} } } @@ -170,11 +170,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should not remove extra named export variable declarations', () => { const output = babel(trim` - export const unstable_getStaticPaths = () => { + export const getStaticPaths = () => { return [] }, foo = 2 - export const unstable_getStaticProps = function() { + export const getStaticProps = function() { return { props: {} } } @@ -190,11 +190,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove re-exported variable declarations', () => { const output = babel(trim` - const unstable_getStaticPaths = () => { + const getStaticPaths = () => { return [] } - export { unstable_getStaticPaths } + export { getStaticPaths } export default function Test() { return
@@ -208,11 +208,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove re-exported variable declarations (safe)', () => { const output = babel(trim` - const unstable_getStaticPaths = () => { + const getStaticPaths = () => { return [] }, a = 2 - export { unstable_getStaticPaths } + export { getStaticPaths } export default function Test() { return
@@ -226,11 +226,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should remove re-exported function declarations', () => { const output = babel(trim` - function unstable_getStaticPaths() { + function getStaticPaths() { return [] } - export { unstable_getStaticPaths } + export { getStaticPaths } export default function Test() { return
@@ -244,11 +244,11 @@ describe('babel plugin (next-ssg-transform)', () => { it('should not crash for class declarations', () => { const output = babel(trim` - function unstable_getStaticPaths() { + function getStaticPaths() { return [] } - export { unstable_getStaticPaths } + export { getStaticPaths } export class MyClass {} @@ -288,7 +288,7 @@ describe('babel plugin (next-ssg-transform)', () => { const b2 = function apples() {}; const bla = () => {inception1}; - function unstable_getStaticProps() { + function getStaticProps() { abc(); drop_me; b; @@ -297,7 +297,7 @@ describe('babel plugin (next-ssg-transform)', () => { return { props: {var1} } } - export { unstable_getStaticProps } + export { getStaticProps } export default function Test() { return
@@ -324,7 +324,7 @@ describe('babel plugin (next-ssg-transform)', () => { return { bug }; } - export { unstable_getStaticProps } from 'a' + export { getStaticProps } from 'a' `) expect(output).toMatchInlineSnapshot( @@ -334,7 +334,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should support class exports', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -352,7 +352,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should support class exports 2', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -372,7 +372,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should support export { _ as default }', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -390,7 +390,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should support export { _ as default } with other specifiers', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } @@ -410,7 +410,7 @@ describe('babel plugin (next-ssg-transform)', () => { it('should support export { _ as default } with a class', () => { const output = babel(trim` - export function unstable_getStaticProps() { + export function getStaticProps() { return { props: {} } } diff --git a/test/unit/next-babel-loader.test.js b/test/unit/next-babel-loader.test.js index 92220a4fab04..5bf447095083 100644 --- a/test/unit/next-babel-loader.test.js +++ b/test/unit/next-babel-loader.test.js @@ -255,7 +255,7 @@ describe('next-babel-loader', () => { `import{c,d}from"e";` + `import{e as ee,f as ff}from"f";` + `` + - `export function unstable_getStaticProps() {foo;bar;baz;cats;baz2;ff; return { props: {} } }`, + `export function getStaticProps() {foo;bar;baz;cats;baz2;ff; return { props: {} } }`, { resourcePath: pageFile } ) expect(code).toMatchInlineSnapshot( @@ -276,12 +276,12 @@ describe('next-babel-loader', () => { `import{c,d}from"e";` + `import{e as ee,f as ff}from"f";` + `` + - `export function unstable_getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` + + `export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` + `export default function () { return bar(); }`, { resourcePath: pageFile, isServer: true } ) expect(code).toMatchInlineSnapshot( - `"import\\"core-js\\";import{foo,bar}from\\"a\\";import baz from\\"b\\";import ooo from\\"ooo\\";import*as React from\\"react\\";import baz2,{yeet}from\\"c\\";import baz3,{cats}from\\"d\\";import{c,d}from\\"e\\";import{e as ee,f as ff}from\\"f\\";export function unstable_getStaticProps(){foo();baz2();ff();ooo();return{props:{}};}export default function(){return bar();}"` + `"import\\"core-js\\";import{foo,bar}from\\"a\\";import baz from\\"b\\";import ooo from\\"ooo\\";import*as React from\\"react\\";import baz2,{yeet}from\\"c\\";import baz3,{cats}from\\"d\\";import{c,d}from\\"e\\";import{e as ee,f as ff}from\\"f\\";export function getStaticProps(){foo();baz2();ff();ooo();return{props:{}};}export default function(){return bar();}"` ) }) @@ -298,7 +298,7 @@ describe('next-babel-loader', () => { `import{c,d}from"e";` + `import{e as ee,f as ff}from"f";` + `` + - `export function unstable_getStaticProps() {foo();baz2();ff();ooo();cats; return { props: {} }}` + + `export function getStaticProps() {foo();baz2();ff();ooo();cats; return { props: {} }}` + `export default function () { return cats + bar(); }`, { resourcePath: pageFile, isServer: false } ) @@ -320,7 +320,7 @@ describe('next-babel-loader', () => { `import{c,d}from"e";` + `import{e as ee,f as ff}from"f";` + `` + - `export function unstable_getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` + + `export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` + `export default function () { return
{cats + bar()}
}`, { resourcePath: pageFile, isServer: false } ) From 0ff41daa484eaf23b8197e39107df464750f5fc9 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 27 Feb 2020 12:05:45 -0600 Subject: [PATCH 12/29] v9.2.3-canary.16 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index 4dbbb5cba872..e185dfca36d2 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.15" + "version": "9.2.3-canary.16" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 48df67b53c36..5907d5b92d7d 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index ff8145203498..f65ba4aa799e 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 00e400102174..4a878f1c0b6e 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index 134652a810da..664b7b9d5d56 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index d53f4105b8df..7bd7e7d23500 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index c861a7beb1af..213715269603 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index a2941e710cc3..a6622237da11 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index cb9528143513..fec959e57b76 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.15", + "version": "9.2.3-canary.16", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.15", + "@next/polyfill-nomodule": "9.2.3-canary.16", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From 5b5d72ba56eff2aad989248c643a3f39e3fcc393 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 27 Feb 2020 20:43:49 -0600 Subject: [PATCH 13/29] Fix buildId being escaped breaking test with certain build ids (#10728) --- .../getserversideprops/test/index.test.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/test/integration/getserversideprops/test/index.test.js b/test/integration/getserversideprops/test/index.test.js index f2387061eae7..98ce879a5e41 100644 --- a/test/integration/getserversideprops/test/index.test.js +++ b/test/integration/getserversideprops/test/index.test.js @@ -209,10 +209,7 @@ const runTests = (dev = false) => { expect(JSON.parse(query)).toEqual({ path: ['first'] }) const data = JSON.parse( - await renderViaHTTP( - appPort, - `/_next/data/${escapeRegex(buildId)}/catchall/first.json` - ) + await renderViaHTTP(appPort, `/_next/data/${buildId}/catchall/first.json`) ) expect(data.pageProps.params).toEqual({ path: ['first'] }) @@ -220,10 +217,7 @@ const runTests = (dev = false) => { it('should return data correctly', async () => { const data = JSON.parse( - await renderViaHTTP( - appPort, - `/_next/data/${escapeRegex(buildId)}/something.json` - ) + await renderViaHTTP(appPort, `/_next/data/${buildId}/something.json`) ) expect(data.pageProps.world).toBe('world') }) @@ -232,7 +226,7 @@ const runTests = (dev = false) => { const data = JSON.parse( await renderViaHTTP( appPort, - `/_next/data/${escapeRegex(buildId)}/something.json?another=thing` + `/_next/data/${buildId}/something.json?another=thing` ) ) expect(data.pageProps.query.another).toBe('thing') @@ -240,10 +234,7 @@ const runTests = (dev = false) => { it('should return data correctly for dynamic page', async () => { const data = JSON.parse( - await renderViaHTTP( - appPort, - `/_next/data/${escapeRegex(buildId)}/blog/post-1.json` - ) + await renderViaHTTP(appPort, `/_next/data/${buildId}/blog/post-1.json`) ) expect(data.pageProps.post).toBe('post-1') }) @@ -389,7 +380,7 @@ const runTests = (dev = false) => { it('should set no-cache, no-store, must-revalidate header', async () => { const res = await fetchViaHTTP( appPort, - `/_next/data/${escapeRegex(buildId)}/something.json` + `/_next/data/${buildId}/something.json` ) expect(res.headers.get('cache-control')).toContain('no-cache') }) From 7848615bd65dd45429383c06914cec6de9ca0e97 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph Date: Thu, 27 Feb 2020 19:08:58 -0800 Subject: [PATCH 14/29] Fix url-polyfill dep and re-enable native-url (#10726) * Adding native-url package * Bumping native-url version * Upgrading native-url * Logging stats object for debugging * Logging stats object for debugging * Adding try catch to the error lines * Experimenting with regex * Experimenting with regex * Experimenting with regex * Testing regex changes * Fixing defer-script test case to not include polyfill.js * Meging changes with existing polyfill work * Bumping version * adjust webpack config * Reduce size in size test * Remove 1kb from legacy * Bumping native-url version, includes fix for IE11 * Update lock file * Updating native-url, fixes issue on IE11 * Fix sourcemap being added in document * Adding Router as an app level dep. Fixes Router not being added as a dep to pages without Link when granularChunks is enabled * Fix typescript error * Fix modern + granularChunks hydration failing * Fix TS error * Update native-url version * Adding native-url with safari fix * Update url-polyfill in polyfill-nomodule package * Remove url-polyfill from next package.json Co-authored-by: Tim Neutkens Co-authored-by: JJ Kasper Co-authored-by: Joe Haddad --- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/build/webpack-config.ts | 5 ++--- packages/next/package.json | 2 +- test/integration/size-limit/test/index.test.js | 4 ++-- yarn.lock | 15 +++++++++++---- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index a6622237da11..bdeb9f370e94 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -13,7 +13,7 @@ "microbundle": "0.11.0", "object-assign": "4.1.1", "promise-polyfill": "8.1.3", - "url-polyfill": "1.1.7", + "url-polyfill": "1.1.8", "whatwg-fetch": "3.0.0" } } diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index a31af2318adf..f438213049d7 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -88,9 +88,8 @@ function getOptimizedAliases(isServer: boolean): { [pkg: string]: string } { 'object.assign/polyfill': path.join(shimAssign, 'polyfill.js'), 'object.assign/shim': path.join(shimAssign, 'shim.js'), - // TODO: re-enable when `native-url` supports Safari 10 - // // Replace: full URL polyfill with platform-based polyfill - // url: require.resolve('native-url'), + // Replace: full URL polyfill with platform-based polyfill + url: require.resolve('native-url'), } ) } diff --git a/packages/next/package.json b/packages/next/package.json index fec959e57b76..ecb1da1243ae 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -115,6 +115,7 @@ "lru-cache": "5.1.1", "mini-css-extract-plugin": "0.8.0", "mkdirp": "0.5.1", + "native-url": "0.2.6", "node-fetch": "2.6.0", "ora": "3.4.0", "path-to-regexp": "6.1.0", @@ -140,7 +141,6 @@ "thread-loader": "2.1.3", "unfetch": "4.1.0", "url": "0.11.0", - "url-polyfill": "1.1.7", "use-subscription": "1.1.1", "watchpack": "2.0.0-beta.13", "webpack": "4.41.2", diff --git a/test/integration/size-limit/test/index.test.js b/test/integration/size-limit/test/index.test.js index 1700dafa5efe..08a1262ded5a 100644 --- a/test/integration/size-limit/test/index.test.js +++ b/test/integration/size-limit/test/index.test.js @@ -80,7 +80,7 @@ describe('Production response size', () => { ) // These numbers are without gzip compression! - const delta = responseSizesBytes - 237 * 1024 + const delta = responseSizesBytes - 230 * 1024 expect(delta).toBeLessThanOrEqual(1024) // don't increase size more than 1kb expect(delta).toBeGreaterThanOrEqual(-1024) // don't decrease size more than 1kb without updating target }) @@ -100,7 +100,7 @@ describe('Production response size', () => { ) // These numbers are without gzip compression! - const delta = responseSizesBytes - 171 * 1024 + const delta = responseSizesBytes - 163 * 1024 expect(delta).toBeLessThanOrEqual(1024) // don't increase size more than 1kb expect(delta).toBeGreaterThanOrEqual(-1024) // don't decrease size more than 1kb without updating target }) diff --git a/yarn.lock b/yarn.lock index 4b158e74baf6..290c37a28f99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10953,6 +10953,13 @@ native-or-bluebird@^1.2.0: resolved "https://registry.yarnpkg.com/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz#39c47bfd7825d1fb9ffad32210ae25daadf101c9" integrity sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck= +native-url@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.2.6.tgz#ca1258f5ace169c716ff44eccbddb674e10399ae" + integrity sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA== + dependencies: + querystring "^0.2.0" + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -16552,10 +16559,10 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-polyfill@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.7.tgz#402ee84360eb549bbeb585f4c7971e79a31de9e3" - integrity sha512-ZrAxYWCREjmMtL8gSbSiKKLZZticgihCvVBtrFbUVpyoETt8GQJeG2okMWA8XryDAaHMjJfhnc+rnhXRbI4DXA== +url-polyfill@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.8.tgz#21eb58ad61192f52b77dcac8ab5293ae7bc67060" + integrity sha512-Ey61F4FEqhcu1vHSOMmjl0Vd/RPRLEjMj402qszD/dhMBrVfoUsnIj8KSZo2yj+eIlxJGKFdnm6ES+7UzMgZ3Q== url-template@^2.0.8: version "2.0.8" From 586fdf4eba8bd1d5348ce3d179731589bf09d9b7 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Fri, 28 Feb 2020 09:44:58 -0500 Subject: [PATCH 15/29] v9.2.3-canary.17 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index e185dfca36d2..2d7932159f6a 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.16" + "version": "9.2.3-canary.17" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 5907d5b92d7d..57935323fa29 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index f65ba4aa799e..eecce93a4713 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 4a878f1c0b6e..bdcd79d3f929 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index 664b7b9d5d56..11dcfe74ebf5 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index 7bd7e7d23500..d124dadf406e 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 213715269603..548b58f3dd5d 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index bdeb9f370e94..833c6cd0f09c 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index ecb1da1243ae..4c164d59c7e6 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.16", + "version": "9.2.3-canary.17", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.16", + "@next/polyfill-nomodule": "9.2.3-canary.17", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From d4fa6afa24fc261167f363ead1228c1fa51440b5 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Fri, 28 Feb 2020 08:36:19 -0800 Subject: [PATCH 16/29] Extract sendPayload and prepareServerlessUrl (#10732) --- .../next/next-server/server/next-server.ts | 133 ++++++++++-------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 1c41aaf6e200..bea3a8c54bc9 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -662,7 +662,7 @@ export default class Server { if (!this.renderOpts.dev && this._isLikeServerless) { if (typeof pageModule.default === 'function') { - this.prepareServerlessUrl(req, query) + prepareServerlessUrl(req, query) await pageModule.default(req, res) return true } @@ -823,50 +823,6 @@ export default class Server { return null } - private __sendPayload( - res: ServerResponse, - payload: any, - type: string, - options?: { revalidate: number | false; private: boolean } - ) { - // TODO: ETag? Cache-Control headers? Next-specific headers? - res.setHeader('Content-Type', type) - res.setHeader('Content-Length', Buffer.byteLength(payload)) - if (!this.renderOpts.dev) { - if (options?.private) { - res.setHeader( - 'Cache-Control', - `private, no-cache, no-store, max-age=0, must-revalidate` - ) - } else if (options?.revalidate) { - res.setHeader( - 'Cache-Control', - options.revalidate < 0 - ? `no-cache, no-store, must-revalidate` - : `s-maxage=${options.revalidate}, stale-while-revalidate` - ) - } else if (options?.revalidate === false) { - res.setHeader( - 'Cache-Control', - `s-maxage=31536000, stale-while-revalidate` - ) - } - } - res.end(payload) - } - - private prepareServerlessUrl(req: IncomingMessage, query: ParsedUrlQuery) { - const curUrl = parseUrl(req.url!, true) - req.url = formatUrl({ - ...curUrl, - search: undefined, - query: { - ...curUrl.query, - ...query, - }, - }) - } - private async renderToHTMLWithComponents( req: IncomingMessage, res: ServerResponse, @@ -918,18 +874,20 @@ export default class Server { true ) - this.__sendPayload( + sendPayload( res, JSON.stringify(renderResult?.renderOpts?.pageData), 'application/json', - { - revalidate: -1, - private: false, // Leave to user-land caching - } + !this.renderOpts.dev + ? { + revalidate: -1, + private: false, // Leave to user-land caching + } + : undefined ) return null } - this.prepareServerlessUrl(req, query) + prepareServerlessUrl(req, query) return (components.Component as any).renderReqToHTML(req, res) } @@ -939,10 +897,17 @@ export default class Server { ...opts, isDataReq, }) - this.__sendPayload(res, JSON.stringify(props), 'application/json', { - revalidate: -1, - private: false, // Leave to user-land caching - }) + sendPayload( + res, + JSON.stringify(props), + 'application/json', + !this.renderOpts.dev + ? { + revalidate: -1, + private: false, // Leave to user-land caching + } + : undefined + ) return null } @@ -972,11 +937,11 @@ export default class Server { ? JSON.stringify(cachedData.pageData) : cachedData.html - this.__sendPayload( + sendPayload( res, data, isDataReq ? 'application/json' : 'text/html; charset=utf-8', - cachedData.curRevalidate !== undefined + cachedData.curRevalidate !== undefined && !this.renderOpts.dev ? { revalidate: cachedData.curRevalidate, private: isPreviewMode } : undefined ) @@ -1108,7 +1073,7 @@ export default class Server { else { query.__nextFallback = 'true' if (isLikeServerless) { - this.prepareServerlessUrl(req, query) + prepareServerlessUrl(req, query) html = await (components.Component as any).renderReqToHTML(req, res) } else { html = (await renderToHTML(req, res, pathname, query, { @@ -1118,7 +1083,7 @@ export default class Server { } } - this.__sendPayload(res, html, 'text/html; charset=utf-8') + sendPayload(res, html, 'text/html; charset=utf-8') } const { @@ -1126,11 +1091,13 @@ export default class Server { value: { html, pageData, sprRevalidate }, } = await doRender(ssgCacheKey, []) if (!isResSent(res)) { - this.__sendPayload( + sendPayload( res, isDataReq ? JSON.stringify(pageData) : html, isDataReq ? 'application/json' : 'text/html; charset=utf-8', - { revalidate: sprRevalidate, private: isPreviewMode } + !this.renderOpts.dev + ? { revalidate: sprRevalidate, private: isPreviewMode } + : undefined ) } @@ -1352,3 +1319,47 @@ export default class Server { return isTargetLikeServerless(this.nextConfig.target) } } + +function sendPayload( + res: ServerResponse, + payload: any, + type: string, + options?: { revalidate: number | false; private: boolean } +) { + // TODO: ETag? Cache-Control headers? Next-specific headers? + res.setHeader('Content-Type', type) + res.setHeader('Content-Length', Buffer.byteLength(payload)) + if (options != null) { + if (options?.private) { + res.setHeader( + 'Cache-Control', + `private, no-cache, no-store, max-age=0, must-revalidate` + ) + } else if (options?.revalidate) { + res.setHeader( + 'Cache-Control', + options.revalidate < 0 + ? `no-cache, no-store, must-revalidate` + : `s-maxage=${options.revalidate}, stale-while-revalidate` + ) + } else if (options?.revalidate === false) { + res.setHeader( + 'Cache-Control', + `s-maxage=31536000, stale-while-revalidate` + ) + } + } + res.end(payload) +} + +function prepareServerlessUrl(req: IncomingMessage, query: ParsedUrlQuery) { + const curUrl = parseUrl(req.url!, true) + req.url = formatUrl({ + ...curUrl, + search: undefined, + query: { + ...curUrl.query, + ...query, + }, + }) +} From 3bfb6501f1a4589222add8396a426a6f03ec1ef1 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Fri, 28 Feb 2020 08:56:50 -0800 Subject: [PATCH 17/29] Extract getStaticPaths helper (#10731) * Extract getStaticPaths helper * Extract sendPayload and prepareServerlessUrl * Remove erroneously included file --- .../next/next-server/server/next-server.ts | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index bea3a8c54bc9..2717cf5218e8 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -823,6 +823,47 @@ export default class Server { return null } + private async getStaticPaths( + pathname: string + ): Promise<{ + staticPaths: string[] | undefined + hasStaticFallback: boolean + }> { + // we lazy load the staticPaths to prevent the user + // from waiting on them for the page to load in dev mode + let staticPaths: string[] | undefined + let hasStaticFallback = false + + if (!this.renderOpts.dev) { + // `staticPaths` is intentionally set to `undefined` as it should've + // been caught when checking disk data. + staticPaths = undefined + + // Read whether or not fallback should exist from the manifest. + hasStaticFallback = + typeof this.getPrerenderManifest().dynamicRoutes[pathname].fallback === + 'string' + } else { + const __getStaticPaths = async () => { + const paths = await this.staticPathsWorker!.loadStaticPaths( + this.distDir, + this.buildId, + pathname, + !this.renderOpts.dev && this._isLikeServerless + ) + return paths + } + ;({ paths: staticPaths, fallback: hasStaticFallback } = ( + await withCoalescedInvoke(__getStaticPaths)( + `staticPaths-${pathname}`, + [] + ) + ).value) + } + + return { staticPaths, hasStaticFallback } + } + private async renderToHTMLWithComponents( req: IncomingMessage, res: ServerResponse, @@ -994,39 +1035,9 @@ export default class Server { const isDynamicPathname = isDynamicRoute(pathname) const didRespond = isResSent(res) - // we lazy load the staticPaths to prevent the user - // from waiting on them for the page to load in dev mode - let staticPaths: string[] | undefined - let hasStaticFallback = false - - if (hasStaticPaths) { - if (isProduction) { - // `staticPaths` is intentionally set to `undefined` as it should've - // been caught above when checking disk data. - staticPaths = undefined - - // Read whether or not fallback should exist from the manifest. - hasStaticFallback = - typeof this.getPrerenderManifest().dynamicRoutes[pathname] - .fallback === 'string' - } else { - const __getStaticPaths = async () => { - const paths = await this.staticPathsWorker!.loadStaticPaths( - this.distDir, - this.buildId, - pathname, - !this.renderOpts.dev && this._isLikeServerless - ) - return paths - } - ;({ paths: staticPaths, fallback: hasStaticFallback } = ( - await withCoalescedInvoke(__getStaticPaths)( - `staticPaths-${pathname}`, - [] - ) - ).value) - } - } + const { staticPaths, hasStaticFallback } = hasStaticPaths + ? await this.getStaticPaths(pathname) + : { staticPaths: undefined, hasStaticFallback: false } // const isForcedBlocking = // req.headers['X-Prerender-Bypass-Mode'] !== 'Blocking' From b2624012ea52438dee783425e0d6b87d14c0fc9e Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 28 Feb 2020 11:40:27 -0600 Subject: [PATCH 18/29] Remove old eslint-ignores from unstable_ prefix (#10740) --- packages/next/export/worker.js | 2 -- test/integration/client-navigation/pages/nav/url-prop-change.js | 1 - test/integration/getserversideprops/pages/another/index.js | 1 - .../getserversideprops/pages/blog/[post]/[comment].js | 1 - test/integration/getserversideprops/pages/blog/[post]/index.js | 1 - test/integration/getserversideprops/pages/blog/index.js | 1 - test/integration/getserversideprops/pages/catchall/[...path].js | 1 - test/integration/getserversideprops/pages/default-revalidate.js | 1 - test/integration/getserversideprops/pages/index.js | 1 - test/integration/getserversideprops/pages/invalid-keys.js | 1 - test/integration/getserversideprops/pages/something.js | 1 - .../integration/getserversideprops/pages/user/[user]/profile.js | 1 - .../prerender-invalid-catchall-params/pages/[...slug].js | 2 -- test/integration/prerender-invalid-paths/pages/[foo]/[post].js | 2 -- test/integration/prerender-legacy/pages/blog/[post].js | 2 -- test/integration/prerender/pages/another/index.js | 1 - test/integration/prerender/pages/blog/[post]/[comment].js | 2 -- test/integration/prerender/pages/blog/[post]/index.js | 2 -- test/integration/prerender/pages/blog/index.js | 1 - test/integration/prerender/pages/default-revalidate.js | 1 - test/integration/prerender/pages/index.js | 1 - test/integration/prerender/pages/something.js | 1 - test/integration/prerender/pages/user/[user]/profile.js | 2 -- 23 files changed, 30 deletions(-) diff --git a/packages/next/export/worker.js b/packages/next/export/worker.js index 86e820cd4327..a5169bf8d62e 100644 --- a/packages/next/export/worker.js +++ b/packages/next/export/worker.js @@ -118,9 +118,7 @@ export default async function({ let curRenderOpts = {} let renderMethod = renderToHTML - // eslint-disable-next-line camelcase const renderedDuringBuild = getStaticProps => { - // eslint-disable-next-line camelcase return !buildExport && getStaticProps && !isDynamicRoute(path) } diff --git a/test/integration/client-navigation/pages/nav/url-prop-change.js b/test/integration/client-navigation/pages/nav/url-prop-change.js index fc80d8e5d415..0b2a1095f70c 100644 --- a/test/integration/client-navigation/pages/nav/url-prop-change.js +++ b/test/integration/client-navigation/pages/nav/url-prop-change.js @@ -10,7 +10,6 @@ export default class UrlPropChange extends React.Component { } } - // eslint-disable-next-line camelcase componentDidUpdate(prevProps) { if (prevProps.url !== this.props.url) { this.setState(() => { diff --git a/test/integration/getserversideprops/pages/another/index.js b/test/integration/getserversideprops/pages/another/index.js index 5270b73f050c..4fcaa6ebbea5 100644 --- a/test/integration/getserversideprops/pages/another/index.js +++ b/test/integration/getserversideprops/pages/another/index.js @@ -2,7 +2,6 @@ import Link from 'next/link' import fs from 'fs' import findUp from 'find-up' -// eslint-disable-next-line camelcase export async function getServerSideProps() { const text = fs .readFileSync( diff --git a/test/integration/getserversideprops/pages/blog/[post]/[comment].js b/test/integration/getserversideprops/pages/blog/[post]/[comment].js index f1449358ae4f..d901887a86d0 100644 --- a/test/integration/getserversideprops/pages/blog/[post]/[comment].js +++ b/test/integration/getserversideprops/pages/blog/[post]/[comment].js @@ -1,7 +1,6 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getServerSideProps({ query }) { return { props: { diff --git a/test/integration/getserversideprops/pages/blog/[post]/index.js b/test/integration/getserversideprops/pages/blog/[post]/index.js index c507475cf752..282d84cc1e16 100644 --- a/test/integration/getserversideprops/pages/blog/[post]/index.js +++ b/test/integration/getserversideprops/pages/blog/[post]/index.js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getServerSideProps({ params }) { if (params.post === 'post-10') { await new Promise(resolve => { diff --git a/test/integration/getserversideprops/pages/blog/index.js b/test/integration/getserversideprops/pages/blog/index.js index 627f651550ed..38739ee1f04f 100644 --- a/test/integration/getserversideprops/pages/blog/index.js +++ b/test/integration/getserversideprops/pages/blog/index.js @@ -1,7 +1,6 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getServerSideProps() { return { props: { diff --git a/test/integration/getserversideprops/pages/catchall/[...path].js b/test/integration/getserversideprops/pages/catchall/[...path].js index de807caa3cdf..cfbd2e1c3019 100644 --- a/test/integration/getserversideprops/pages/catchall/[...path].js +++ b/test/integration/getserversideprops/pages/catchall/[...path].js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getServerSideProps({ params }) { return { props: { diff --git a/test/integration/getserversideprops/pages/default-revalidate.js b/test/integration/getserversideprops/pages/default-revalidate.js index 57e55a20508b..605ca305df9e 100644 --- a/test/integration/getserversideprops/pages/default-revalidate.js +++ b/test/integration/getserversideprops/pages/default-revalidate.js @@ -1,6 +1,5 @@ import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getServerSideProps() { return { props: { diff --git a/test/integration/getserversideprops/pages/index.js b/test/integration/getserversideprops/pages/index.js index becc0df1c5c0..80ce2375b131 100644 --- a/test/integration/getserversideprops/pages/index.js +++ b/test/integration/getserversideprops/pages/index.js @@ -1,6 +1,5 @@ import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getServerSideProps() { return { props: { diff --git a/test/integration/getserversideprops/pages/invalid-keys.js b/test/integration/getserversideprops/pages/invalid-keys.js index 3cf83a33bde5..6ad6efbd9f29 100644 --- a/test/integration/getserversideprops/pages/invalid-keys.js +++ b/test/integration/getserversideprops/pages/invalid-keys.js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getServerSideProps({ params, query }) { return { world: 'world', diff --git a/test/integration/getserversideprops/pages/something.js b/test/integration/getserversideprops/pages/something.js index 5b32921c5bbf..902cdcdc8c3e 100644 --- a/test/integration/getserversideprops/pages/something.js +++ b/test/integration/getserversideprops/pages/something.js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getServerSideProps({ params, query }) { return { props: { diff --git a/test/integration/getserversideprops/pages/user/[user]/profile.js b/test/integration/getserversideprops/pages/user/[user]/profile.js index 9a6b30f37b48..32d081cecedc 100644 --- a/test/integration/getserversideprops/pages/user/[user]/profile.js +++ b/test/integration/getserversideprops/pages/user/[user]/profile.js @@ -1,7 +1,6 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getServerSideProps({ query }) { return { props: { diff --git a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js index f0eef4d55972..70efe79cbd54 100644 --- a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js +++ b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js @@ -1,11 +1,9 @@ import React from 'react' -// eslint-disable-next-line camelcase export async function getStaticPaths() { return { paths: [{ params: { slug: 'hello' } }], fallback: true } } -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { diff --git a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js index 4dc885727859..48c2b6a15dc1 100644 --- a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js +++ b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js @@ -1,11 +1,9 @@ import React from 'react' -// eslint-disable-next-line camelcase export async function getStaticPaths() { return { paths: [{ foo: 'bad', baz: 'herro' }], fallback: true } } -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { diff --git a/test/integration/prerender-legacy/pages/blog/[post].js b/test/integration/prerender-legacy/pages/blog/[post].js index c7aa49e37b24..b698ecb9a519 100644 --- a/test/integration/prerender-legacy/pages/blog/[post].js +++ b/test/integration/prerender-legacy/pages/blog/[post].js @@ -1,11 +1,9 @@ import React from 'react' -// eslint-disable-next-line camelcase export async function unstable_getStaticParams() { return ['/blog/post-1'] } -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { diff --git a/test/integration/prerender/pages/another/index.js b/test/integration/prerender/pages/another/index.js index f8eb26a2e003..1e2d91783b76 100644 --- a/test/integration/prerender/pages/another/index.js +++ b/test/integration/prerender/pages/another/index.js @@ -2,7 +2,6 @@ import Link from 'next/link' import fs from 'fs' import findUp from 'find-up' -// eslint-disable-next-line camelcase export async function getStaticProps() { const text = fs .readFileSync( diff --git a/test/integration/prerender/pages/blog/[post]/[comment].js b/test/integration/prerender/pages/blog/[post]/[comment].js index b2d89f3cba27..39ee2d9afce5 100644 --- a/test/integration/prerender/pages/blog/[post]/[comment].js +++ b/test/integration/prerender/pages/blog/[post]/[comment].js @@ -1,7 +1,6 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getStaticPaths() { return { paths: [ @@ -12,7 +11,6 @@ export async function getStaticPaths() { } } -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { diff --git a/test/integration/prerender/pages/blog/[post]/index.js b/test/integration/prerender/pages/blog/[post]/index.js index 1f33676647a8..8cae293f7a70 100644 --- a/test/integration/prerender/pages/blog/[post]/index.js +++ b/test/integration/prerender/pages/blog/[post]/index.js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getStaticPaths() { return { paths: [ @@ -19,7 +18,6 @@ export async function getStaticPaths() { let counter = 0 -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { if (params.post === 'post-10') { await new Promise(resolve => { diff --git a/test/integration/prerender/pages/blog/index.js b/test/integration/prerender/pages/blog/index.js index c4a0b940819b..71ba9e9b1cca 100644 --- a/test/integration/prerender/pages/blog/index.js +++ b/test/integration/prerender/pages/blog/index.js @@ -1,7 +1,6 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getStaticProps() { return { props: { diff --git a/test/integration/prerender/pages/default-revalidate.js b/test/integration/prerender/pages/default-revalidate.js index e8125f3ca32a..68570ca0ba72 100644 --- a/test/integration/prerender/pages/default-revalidate.js +++ b/test/integration/prerender/pages/default-revalidate.js @@ -1,6 +1,5 @@ import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getStaticProps() { return { props: { diff --git a/test/integration/prerender/pages/index.js b/test/integration/prerender/pages/index.js index 44960c35d3e7..d94150aa8f22 100644 --- a/test/integration/prerender/pages/index.js +++ b/test/integration/prerender/pages/index.js @@ -1,6 +1,5 @@ import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getStaticProps() { // throw new Error('oops from getStaticProps') return { diff --git a/test/integration/prerender/pages/something.js b/test/integration/prerender/pages/something.js index d9e341e75570..180818998c8d 100644 --- a/test/integration/prerender/pages/something.js +++ b/test/integration/prerender/pages/something.js @@ -2,7 +2,6 @@ import React from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { diff --git a/test/integration/prerender/pages/user/[user]/profile.js b/test/integration/prerender/pages/user/[user]/profile.js index 3c95d23cd97b..95bf6e7ad7b0 100644 --- a/test/integration/prerender/pages/user/[user]/profile.js +++ b/test/integration/prerender/pages/user/[user]/profile.js @@ -1,12 +1,10 @@ import React from 'react' import Link from 'next/link' -// eslint-disable-next-line camelcase export async function getStaticPaths() { return { paths: [], fallback: true } } -// eslint-disable-next-line camelcase export async function getStaticProps({ params }) { return { props: { From 709aac1e962eb825c7ff7ea52edc4400a29cd3cb Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Fri, 28 Feb 2020 14:46:18 -0800 Subject: [PATCH 19/29] Move upgrading guide to /docs (#10727) * Move UPGRADING.md to docs * Add upgrading guide to manifest.json * Update title * Move to docs/upgrading * Update UPGRADING.md * Update docs/manifest.json * Update UPGRADING.md Co-authored-by: Tim Neutkens --- UPGRADING.md | 214 +------------------------------------------ docs/manifest.json | 4 + docs/upgrading.md | 219 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 213 deletions(-) create mode 100644 docs/upgrading.md diff --git a/UPGRADING.md b/UPGRADING.md index 458bc5783e5d..83373e6ae0ad 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,213 +1 @@ -# Migrating from v8 to v9 - -## Preamble - -#### Production Deployment on ZEIT Now v2 - -If you previously configured `routes` in your `now.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](https://github.com/zeit/next.js#dynamic-routing). - -Next.js 9's dynamic routes are **automatically configured on [Now](https://zeit.co/now)** and do not require any `now.json` customization. - -You can read more about [Dynamic Routing here](https://github.com/zeit/next.js#dynamic-routing). - -#### Check your Custom (`pages/_app.js`) - -If you previously copied the [Custom ``](https://nextjs.org/docs#custom-app) example, you may be able to remove your `getInitialProps`. - -Removing `getInitialProps` from `pages/_app.js` (when possible) is important to leverage new Next.js features! - -The following `getInitialProps` does nothing and may be removed: - -```js -class MyApp extends App { - // Remove me, I do nothing! - static async getInitialProps({ Component, ctx }) { - let pageProps = {} - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx) - } - - return { pageProps } - } - - render() { - // ... etc - } -} -``` - -## Breaking Changes - -#### `@zeit/next-typescript` is no longer necessary - -Next.js will now ignore usage `@zeit/next-typescript` and warn you to remove it. Please remove this plugin from your `next.config.js`. - -Remove references to `@zeit/next-typescript/babel` from your custom `.babelrc` (if present). - -Usage of [`fork-ts-checker-webpack-plugin`](https://github.com/Realytics/fork-ts-checker-webpack-plugin/issues) should also be removed from your `next.config.js`. - -TypeScript Definitions are published with the `next` package, so you need to uninstall `@types/next` as they would conflict. - -The following types are different: - -> This list was created by the community to help you upgrade, if you find other differences please send a pull-request to this list to help other users. - -From: - -```tsx -import { NextContext } from 'next' -import { NextAppContext, DefaultAppIProps } from 'next/app' -import { NextDocumentContext, DefaultDocumentIProps } from 'next/document' -``` - -to - -```tsx -import { NextPageContext } from 'next' -import { AppContext, AppInitialProps } from 'next/app' -import { DocumentContext, DocumentInitialProps } from 'next/document' -``` - -#### The `config` key is now a special export on a page - -You may no longer export a custom variable named `config` from a page (i.e. `export { config }` / `export const config ...`). -This exported variable is now used to specify page-level Next.js configuration like Opt-in AMP and API Route features. - -You must rename a non-Next.js-purposed `config` export to something different. - -#### `next/dynamic` no longer renders "loading..." by default while loading - -Dynamic components will not render anything by default while loading. You can still customize this behavior by setting the `loading` property: - -```jsx -import dynamic from 'next/dynamic' - -const DynamicComponentWithCustomLoading = dynamic( - () => import('../components/hello2'), - { - loading: () =>

Loading

, - } -) -``` - -#### `withAmp` has been removed in favor of an exported configuration object - -Next.js now has the concept of page-level configuration, so the `withAmp` higher-order component has been removed for consistency. - -This change can be **automatically migrated by running the following commands in the root of your Next.js project:** - -```bash -curl -L https://github.com/zeit/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js -``` - -To perform this migration by hand, or view what the codemod will produce, see below: - -**Before** - -```jsx -import { withAmp } from 'next/amp' - -function Home() { - return

My AMP Page

-} - -export default withAmp(Home) -// or -export default withAmp(Home, { hybrid: true }) -``` - -**After** - -```jsx -export default function Home() { - return

My AMP Page

-} - -export const config = { - amp: true, - // or - amp: 'hybrid', -} -``` - -#### `next export` no longer exports pages as `index.html` - -Previously, exporting `pages/about.js` would result in `out/about/index.html`. This behavior has been changed to result in `out/about.html`. - -You can revert to the previous behavior by creating a `next.config.js` with the following content: - -```js -// next.config.js -module.exports = { - exportTrailingSlash: true, -} -``` - -#### `./pages/api/` is treated differently - -Pages in `./pages/api/` are now considered [API Routes](https://nextjs.org/blog/next-9#api-routes). -Pages in this directory will no longer contain a client-side bundle. - -## Deprecated Features - -#### `next/dynamic` has deprecated loading multiple modules at once - -The ability to load multiple modules at once has been deprecated in `next/dynamic` to be closer to React's implementation (`React.lazy` and `Suspense`). - -Updating code that relies on this behavior is relatively straightforward! We've provided an example of a before/after to help you migrate your application: - -**Before** - -```jsx -import dynamic from 'next/dynamic' - -const HelloBundle = dynamic({ - modules: () => { - const components = { - Hello1: () => import('../components/hello1').then(m => m.default), - Hello2: () => import('../components/hello2').then(m => m.default), - } - - return components - }, - render: (props, { Hello1, Hello2 }) => ( -
-

{props.title}

- - -
- ), -}) - -function DynamicBundle() { - return -} - -export default DynamicBundle -``` - -**After** - -```jsx -import dynamic from 'next/dynamic' - -const Hello1 = dynamic(() => import('../components/hello1')) -const Hello2 = dynamic(() => import('../components/hello2')) - -function HelloBundle({ title }) { - return ( -
-

{title}

- - -
- ) -} - -function DynamicBundle() { - return -} - -export default DynamicBundle -``` +This document has been moved to [nextjs.org/docs/upgrading](https://nextjs.org/docs/upgrading). It's also available in this repository on [/docs/upgrading.md](/docs/upgrading.md). diff --git a/docs/manifest.json b/docs/manifest.json index b3cccfe61edf..fb6dd32df41a 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -147,6 +147,10 @@ } ] }, + { + "title": "Upgrade Guide", + "path": "/docs/upgrading.md" + }, { "title": "FAQ", "path": "/docs/faq.md" } ] }, diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 000000000000..979d9c3195e6 --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,219 @@ +--- +description: Learn how to upgrade Next.js. +--- + +# Upgrade Guide + +## Upgrading from version 8 to 9.0.x + +### Preamble + +#### Production Deployment on ZEIT Now v2 + +If you previously configured `routes` in your `now.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](https://github.com/zeit/next.js#dynamic-routing). + +Next.js 9's dynamic routes are **automatically configured on [Now](https://zeit.co/now)** and do not require any `now.json` customization. + +You can read more about [Dynamic Routing here](https://github.com/zeit/next.js#dynamic-routing). + +#### Check your Custom (`pages/_app.js`) + +If you previously copied the [Custom ``](https://nextjs.org/docs#custom-app) example, you may be able to remove your `getInitialProps`. + +Removing `getInitialProps` from `pages/_app.js` (when possible) is important to leverage new Next.js features! + +The following `getInitialProps` does nothing and may be removed: + +```js +class MyApp extends App { + // Remove me, I do nothing! + static async getInitialProps({ Component, ctx }) { + let pageProps = {} + + if (Component.getInitialProps) { + pageProps = await Component.getInitialProps(ctx) + } + + return { pageProps } + } + + render() { + // ... etc + } +} +``` + +### Breaking Changes + +#### `@zeit/next-typescript` is no longer necessary + +Next.js will now ignore usage `@zeit/next-typescript` and warn you to remove it. Please remove this plugin from your `next.config.js`. + +Remove references to `@zeit/next-typescript/babel` from your custom `.babelrc` (if present). + +Usage of [`fork-ts-checker-webpack-plugin`](https://github.com/Realytics/fork-ts-checker-webpack-plugin/issues) should also be removed from your `next.config.js`. + +TypeScript Definitions are published with the `next` package, so you need to uninstall `@types/next` as they would conflict. + +The following types are different: + +> This list was created by the community to help you upgrade, if you find other differences please send a pull-request to this list to help other users. + +From: + +```tsx +import { NextContext } from 'next' +import { NextAppContext, DefaultAppIProps } from 'next/app' +import { NextDocumentContext, DefaultDocumentIProps } from 'next/document' +``` + +to + +```tsx +import { NextPageContext } from 'next' +import { AppContext, AppInitialProps } from 'next/app' +import { DocumentContext, DocumentInitialProps } from 'next/document' +``` + +#### The `config` key is now a special export on a page + +You may no longer export a custom variable named `config` from a page (i.e. `export { config }` / `export const config ...`). +This exported variable is now used to specify page-level Next.js configuration like Opt-in AMP and API Route features. + +You must rename a non-Next.js-purposed `config` export to something different. + +#### `next/dynamic` no longer renders "loading..." by default while loading + +Dynamic components will not render anything by default while loading. You can still customize this behavior by setting the `loading` property: + +```jsx +import dynamic from 'next/dynamic' + +const DynamicComponentWithCustomLoading = dynamic( + () => import('../components/hello2'), + { + loading: () =>

Loading

, + } +) +``` + +#### `withAmp` has been removed in favor of an exported configuration object + +Next.js now has the concept of page-level configuration, so the `withAmp` higher-order component has been removed for consistency. + +This change can be **automatically migrated by running the following commands in the root of your Next.js project:** + +```bash +curl -L https://github.com/zeit/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js +``` + +To perform this migration by hand, or view what the codemod will produce, see below: + +**Before** + +```jsx +import { withAmp } from 'next/amp' + +function Home() { + return

My AMP Page

+} + +export default withAmp(Home) +// or +export default withAmp(Home, { hybrid: true }) +``` + +**After** + +```jsx +export default function Home() { + return

My AMP Page

+} + +export const config = { + amp: true, + // or + amp: 'hybrid', +} +``` + +#### `next export` no longer exports pages as `index.html` + +Previously, exporting `pages/about.js` would result in `out/about/index.html`. This behavior has been changed to result in `out/about.html`. + +You can revert to the previous behavior by creating a `next.config.js` with the following content: + +```js +// next.config.js +module.exports = { + exportTrailingSlash: true, +} +``` + +#### `./pages/api/` is treated differently + +Pages in `./pages/api/` are now considered [API Routes](https://nextjs.org/blog/next-9#api-routes). +Pages in this directory will no longer contain a client-side bundle. + +## Deprecated Features + +#### `next/dynamic` has deprecated loading multiple modules at once + +The ability to load multiple modules at once has been deprecated in `next/dynamic` to be closer to React's implementation (`React.lazy` and `Suspense`). + +Updating code that relies on this behavior is relatively straightforward! We've provided an example of a before/after to help you migrate your application: + +**Before** + +```jsx +import dynamic from 'next/dynamic' + +const HelloBundle = dynamic({ + modules: () => { + const components = { + Hello1: () => import('../components/hello1').then(m => m.default), + Hello2: () => import('../components/hello2').then(m => m.default), + } + + return components + }, + render: (props, { Hello1, Hello2 }) => ( +
+

{props.title}

+ + +
+ ), +}) + +function DynamicBundle() { + return +} + +export default DynamicBundle +``` + +**After** + +```jsx +import dynamic from 'next/dynamic' + +const Hello1 = dynamic(() => import('../components/hello1')) +const Hello2 = dynamic(() => import('../components/hello2')) + +function HelloBundle({ title }) { + return ( +
+

{title}

+ + +
+ ) +} + +function DynamicBundle() { + return +} + +export default DynamicBundle +``` From 5a828125682b86c34636a07203d505ad358fb38b Mon Sep 17 00:00:00 2001 From: Prateek Bhatnagar Date: Fri, 28 Feb 2020 15:44:03 -0800 Subject: [PATCH 20/29] Adding new types of performance monitoring (#10421) * add new types of performance monitoring * adding LCP * adding cls to perf relayer * add test for cls and lcp * nit fixes * re-organizing code * fixing tests for lcp * updating size limits in test --- packages/next/client/index.js | 26 ++++--- packages/next/client/performance-relayer.js | 78 +++++++++++++++++++ .../integration/relay-analytics/pages/_app.js | 5 +- .../relay-analytics/pages/index.js | 7 +- .../relay-analytics/test/index.test.js | 23 +++++- .../integration/size-limit/test/index.test.js | 4 +- 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 packages/next/client/performance-relayer.js diff --git a/packages/next/client/index.js b/packages/next/client/index.js index 124b8ab35dc1..1e1721ae1abe 100644 --- a/packages/next/client/index.js +++ b/packages/next/client/index.js @@ -11,6 +11,11 @@ import { HeadManagerContext } from '../next-server/lib/head-manager-context' import { RouterContext } from '../next-server/lib/router-context' import { parse as parseQs, stringify as stringifyQs } from 'querystring' import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic' +import { + observeLayoutShift, + observeLargestContentfulPaint, + observePaint, +} from './performance-relayer' /// @@ -161,8 +166,14 @@ export default async ({ webpackHMR: passedWebpackHMR } = {}) => { const { page: app, mod } = await pageLoader.loadPageScript('/_app') App = app if (mod && mod.unstable_onPerformanceData) { - onPerfEntry = function({ name, startTime, value, duration }) { - mod.unstable_onPerformanceData({ name, startTime, value, duration }) + onPerfEntry = function({ name, startTime, value, duration, entryType }) { + mod.unstable_onPerformanceData({ + name, + startTime, + value, + duration, + entryType, + }) } } @@ -313,14 +324,9 @@ function renderReactElement(reactEl, domEl) { if (onPerfEntry && ST) { try { - const observer = new PerformanceObserver(list => { - list.getEntries().forEach(onPerfEntry) - }) - // Start observing paint entry types. - observer.observe({ - type: 'paint', - buffered: true, - }) + observeLayoutShift(onPerfEntry) + observeLargestContentfulPaint(onPerfEntry) + observePaint(onPerfEntry) } catch (e) { window.addEventListener('load', () => { performance.getEntriesByType('paint').forEach(onPerfEntry) diff --git a/packages/next/client/performance-relayer.js b/packages/next/client/performance-relayer.js new file mode 100644 index 000000000000..2d2c9dae326c --- /dev/null +++ b/packages/next/client/performance-relayer.js @@ -0,0 +1,78 @@ +function isTypeSupported(type) { + if (self.PerformanceObserver && PerformanceObserver.supportedEntryTypes) { + return PerformanceObserver.supportedEntryTypes.includes(type) + } + return false +} + +export function observeLayoutShift(onPerfEntry) { + if (isTypeSupported('layout-shift')) { + let cumulativeScore = 0 + const observer = new PerformanceObserver(list => { + for (const entry of list.getEntries()) { + // Only count layout shifts without recent user input. + if (!entry.hadRecentInput) { + cumulativeScore += entry.value + } + } + }) + observer.observe({ type: 'layout-shift', buffered: true }) + + document.addEventListener( + 'visibilitychange', + function clsObserver() { + if (document.visibilityState === 'hidden') { + // Force any pending records to be dispatched. + observer.takeRecords() + observer.disconnect() + removeEventListener('visibilitychange', clsObserver, true) + onPerfEntry({ + name: 'cumulative-layout-shift', + value: cumulativeScore, + }) + } + }, + true + ) + } +} + +export function observeLargestContentfulPaint(onPerfEntry) { + if (isTypeSupported('largest-contentful-paint')) { + // Create a variable to hold the latest LCP value (since it can change). + let lcp + + // Create the PerformanceObserver instance. + const observer = new PerformanceObserver(entryList => { + const entries = entryList.getEntries() + const lastEntry = entries[entries.length - 1] + lcp = lastEntry.renderTime || lastEntry.loadTime + }) + + observer.observe({ type: 'largest-contentful-paint', buffered: true }) + + document.addEventListener( + 'visibilitychange', + function lcpObserver() { + if (lcp && document.visibilityState === 'hidden') { + removeEventListener('visibilitychange', lcpObserver, true) + onPerfEntry({ + name: 'largest-contentful-paint', + value: lcp, + }) + } + }, + true + ) + } +} + +export function observePaint(onPerfEntry) { + const observer = new PerformanceObserver(list => { + list.getEntries().forEach(onPerfEntry) + }) + observer.observe({ + type: 'paint', + buffered: true, + }) +} diff --git a/test/integration/relay-analytics/pages/_app.js b/test/integration/relay-analytics/pages/_app.js index d8536b65b7a5..c9a40c8672c2 100644 --- a/test/integration/relay-analytics/pages/_app.js +++ b/test/integration/relay-analytics/pages/_app.js @@ -8,5 +8,8 @@ export default class MyApp extends App {} Method is experimental and will eventually be handled in a Next.js plugin */ export function unstable_onPerformanceData(data) { - localStorage.setItem(data.name, data.value || data.startTime) + localStorage.setItem( + data.name || data.entryType, + data.value !== undefined ? data.value : data.startTime + ) } diff --git a/test/integration/relay-analytics/pages/index.js b/test/integration/relay-analytics/pages/index.js index 7a227b55ed81..5730772dc4b9 100644 --- a/test/integration/relay-analytics/pages/index.js +++ b/test/integration/relay-analytics/pages/index.js @@ -1,3 +1,8 @@ export default () => { - return

Hello!

+ return ( +
+

Foo!

+

bar!

+
+ ) } diff --git a/test/integration/relay-analytics/test/index.test.js b/test/integration/relay-analytics/test/index.test.js index d5c02861ec2b..91263c21d076 100644 --- a/test/integration/relay-analytics/test/index.test.js +++ b/test/integration/relay-analytics/test/index.test.js @@ -30,13 +30,34 @@ describe('Analytics relayer', () => { const firstContentfulPaint = parseFloat( await browser.eval('localStorage.getItem("first-contentful-paint")') ) - expect(h1Text).toMatch(/Hello!/) + let largestContentfulPaint = await browser.eval( + 'localStorage.getItem("largest-contentful-paint")' + ) + let cls = await browser.eval( + 'localStorage.getItem("cumulative-layout-shift")' + ) + expect(h1Text).toMatch(/Foo!/) expect(data).not.toBeNaN() expect(data).toBeGreaterThan(0) expect(firstPaint).not.toBeNaN() expect(firstPaint).toBeGreaterThan(0) expect(firstContentfulPaint).not.toBeNaN() expect(firstContentfulPaint).toBeGreaterThan(0) + expect(largestContentfulPaint).toBeNull() + expect(cls).toBeNull() + // Create an artificial layout shift + await browser.eval('document.querySelector("h1").style.display = "none"') + await browser.refresh() + await browser.waitForElementByCss('h1') + largestContentfulPaint = parseFloat( + await browser.eval('localStorage.getItem("largest-contentful-paint")') + ) + cls = parseFloat( + await browser.eval('localStorage.getItem("cumulative-layout-shift")') + ) + expect(cls).not.toBeNull() + expect(largestContentfulPaint).not.toBeNaN() + expect(largestContentfulPaint).toBeGreaterThan(0) await browser.close() }) }) diff --git a/test/integration/size-limit/test/index.test.js b/test/integration/size-limit/test/index.test.js index 08a1262ded5a..0acf2859cfc5 100644 --- a/test/integration/size-limit/test/index.test.js +++ b/test/integration/size-limit/test/index.test.js @@ -80,7 +80,7 @@ describe('Production response size', () => { ) // These numbers are without gzip compression! - const delta = responseSizesBytes - 230 * 1024 + const delta = responseSizesBytes - 231 * 1024 expect(delta).toBeLessThanOrEqual(1024) // don't increase size more than 1kb expect(delta).toBeGreaterThanOrEqual(-1024) // don't decrease size more than 1kb without updating target }) @@ -100,7 +100,7 @@ describe('Production response size', () => { ) // These numbers are without gzip compression! - const delta = responseSizesBytes - 163 * 1024 + const delta = responseSizesBytes - 164 * 1024 expect(delta).toBeLessThanOrEqual(1024) // don't increase size more than 1kb expect(delta).toBeGreaterThanOrEqual(-1024) // don't decrease size more than 1kb without updating target }) From e608c86b59e9b504ff879f4b6eb2a4313afc1265 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sat, 29 Feb 2020 17:09:42 -0500 Subject: [PATCH 21/29] Separate Low Priority Files from Main Files (#10756) * Separate Low Priority Files from Main Files * Fix tests --- .../webpack/plugins/build-manifest-plugin.ts | 22 +++++++--------- packages/next/next-server/lib/utils.ts | 1 + .../next/next-server/server/get-page-files.ts | 2 ++ packages/next/next-server/server/render.tsx | 9 +++++-- packages/next/pages/_document.tsx | 25 ++++--------------- test/integration/chunking/test/index.test.js | 19 +++++++++++--- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index 75a3b2500d02..3b1a7f018fa8 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -1,7 +1,6 @@ import devalue from 'devalue' import { Compiler } from 'webpack' import { RawSource } from 'webpack-sources' - import { BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, @@ -10,19 +9,12 @@ import { IS_BUNDLED_PAGE_REGEX, ROUTE_NAME_REGEX, } from '../../../next-server/lib/constants' - -interface AssetMap { - devFiles: string[] - pages: { - '/_app': string[] - [s: string]: string[] - } -} +import { BuildManifest } from '../../../next-server/server/get-page-files' // This function takes the asset map generated in BuildManifestPlugin and creates a // reduced version to send to the client. const generateClientManifest = ( - assetMap: AssetMap, + assetMap: BuildManifest, isModern: boolean ): string => { const clientManifest: { [s: string]: string[] } = {} @@ -68,7 +60,11 @@ export default class BuildManifestPlugin { 'NextJsBuildManifest', (compilation, callback) => { const { chunks } = compilation - const assetMap: AssetMap = { devFiles: [], pages: { '/_app': [] } } + const assetMap: BuildManifest = { + devFiles: [], + lowPriorityFiles: [], + pages: { '/_app': [] }, + } const mainJsChunk = chunks.find( c => c.name === CLIENT_STATIC_FILES_RUNTIME_MAIN @@ -141,11 +137,11 @@ export default class BuildManifestPlugin { // as a dependency for the app. If the flag is false, the file won't be // downloaded by the client. if (this.clientManifest) { - assetMap.pages['/_app'].push( + assetMap.lowPriorityFiles.push( `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.js` ) if (this.modern) { - assetMap.pages['/_app'].push( + assetMap.lowPriorityFiles.push( `${CLIENT_STATIC_FILES_PATH}/${this.buildId}/_buildManifest.module.js` ) } diff --git a/packages/next/next-server/lib/utils.ts b/packages/next/next-server/lib/utils.ts index 8eb433737bb4..1114770b2aae 100644 --- a/packages/next/next-server/lib/utils.ts +++ b/packages/next/next-server/lib/utils.ts @@ -153,6 +153,7 @@ export type DocumentProps = DocumentInitialProps & { hasCssMode: boolean devFiles: string[] files: string[] + lowPriorityFiles: string[] polyfillFiles: string[] dynamicImports: ManifestItem[] assetPrefix?: string diff --git a/packages/next/next-server/server/get-page-files.ts b/packages/next/next-server/server/get-page-files.ts index 9d79e60d32b6..ada704db2ecb 100644 --- a/packages/next/next-server/server/get-page-files.ts +++ b/packages/next/next-server/server/get-page-files.ts @@ -2,7 +2,9 @@ import { normalizePagePath } from './normalize-page-path' export type BuildManifest = { devFiles: string[] + lowPriorityFiles: string[] pages: { + '/_app': string[] [page: string]: string[] } } diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 1b063dacdeb0..61dac77e4d4a 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -174,6 +174,7 @@ function renderDocument( staticMarkup, devFiles, files, + lowPriorityFiles, polyfillFiles, dynamicImports, htmlProps, @@ -191,8 +192,9 @@ function renderDocument( hybridAmp: boolean dynamicImportsIds: string[] dynamicImports: ManifestItem[] - files: string[] devFiles: string[] + files: string[] + lowPriorityFiles: string[] polyfillFiles: string[] htmlProps: any bodyTags: any @@ -229,6 +231,7 @@ function renderDocument( staticMarkup, devFiles, files, + lowPriorityFiles, polyfillFiles, dynamicImports, assetPrefix, @@ -579,6 +582,7 @@ export async function renderToHTML( ...getPageFiles(buildManifest, pathname), ]), ] + const lowPriorityFiles = buildManifest.lowPriorityFiles const polyfillFiles = getPageFiles(buildManifest, '/_polyfills') const renderElementToString = staticMarkup @@ -674,8 +678,9 @@ export async function renderToHTML( hybridAmp, dynamicImportsIds, dynamicImports, - files, devFiles, + files, + lowPriorityFiles, polyfillFiles, }) diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index f094406f21fe..23104cdb0cc5 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -44,10 +44,6 @@ function getOptionalModernScriptVariant(path: string) { return path } -function isLowPriority(file: string) { - return file.includes('_buildManifest') -} - /** * `Document` component handles the initial `document` markup and renders only on the server side. * Commonly used for implementing server side rendering for `css-in-js` libraries. @@ -260,13 +256,7 @@ export class Head extends Component< // `dynamicImports` will contain both `.js` and `.module.js` when // the feature is enabled. This clause will filter down to the // modern variants only. - // - // Also filter out low priority files because they should not be - // preloaded for performance reasons. - return ( - file.endsWith(getOptionalModernScriptVariant('.js')) && - !isLowPriority(file) - ) + return file.endsWith(getOptionalModernScriptVariant('.js')) }) : [] @@ -589,17 +579,12 @@ export class NextScript extends Component { } getScripts() { - const { assetPrefix, files } = this.context._documentProps - if (!files || files.length === 0) { - return null - } + const { assetPrefix, files, lowPriorityFiles } = this.context._documentProps const { _devOnlyInvalidateCacheQueryString } = this.context - const normalScripts = files.filter( - file => file.endsWith('.js') && !isLowPriority(file) - ) - const lowPriorityScripts = files.filter( - file => file.endsWith('.js') && isLowPriority(file) + const normalScripts = files?.filter(file => file.endsWith('.js')) + const lowPriorityScripts = lowPriorityFiles?.filter(file => + file.endsWith('.js') ) return [...normalScripts, ...lowPriorityScripts].map(file => { diff --git a/test/integration/chunking/test/index.test.js b/test/integration/chunking/test/index.test.js index f5563a6b554e..8d8b64fe72f6 100644 --- a/test/integration/chunking/test/index.test.js +++ b/test/integration/chunking/test/index.test.js @@ -1,12 +1,12 @@ /* eslint-env jest */ /* global jasmine */ -import { join } from 'path' +import cheerio from 'cheerio' import express from 'express' +import { access, readdir, readFile, unlink } from 'fs-extra' import http from 'http' import { nextBuild, nextServer, promiseCall, stopApp } from 'next-test-utils' -import { readdir, readFile, unlink, access } from 'fs-extra' -import cheerio from 'cheerio' import webdriver from 'next-webdriver' +import { join } from 'path' jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 @@ -90,6 +90,19 @@ describe('Chunking', () => { ).toBe(false) }) + it('should execute the build manifest', async () => { + const indexPage = await readFile( + join(appDir, '.next', 'server', 'static', buildId, 'pages', 'index.html') + ) + + const $ = cheerio.load(indexPage) + expect( + Array.from($('script')) + .map(e => e.attribs.src) + .some(entry => entry && entry.includes('_buildManifest')) + ).toBe(true) + }) + it('should not include more than one instance of react-dom', async () => { const misplacedReactDom = stats.chunks.some(chunk => { if (chunk.names.includes('framework')) { From b7f779058f7ef8bbccb3673db4122b128d669d15 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sat, 29 Feb 2020 17:36:49 -0500 Subject: [PATCH 22/29] Consistently Type GS(S)P (#10757) --- packages/next/next-server/server/render.tsx | 10 +++------- packages/next/types/index.d.ts | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 61dac77e4d4a..64e393d73717 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -471,11 +471,7 @@ export async function renderToHTML( // invoke, where we'd have to consider server & serverless. const previewData = tryGetPreviewData(req, res, previewProps) const data = await getStaticProps!({ - ...(pageIsDynamic - ? { - params: query as ParsedUrlQuery, - } - : { params: undefined }), + ...(pageIsDynamic ? { params: query as ParsedUrlQuery } : undefined), ...(previewData !== false ? { preview: true, previewData: previewData } : undefined), @@ -534,10 +530,10 @@ export async function renderToHTML( if (getServerSideProps && !isFallback) { const data = await getServerSideProps({ - params, - query, req, res, + ...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined), + query, }) const invalidKeys = Object.keys(data).filter(key => key !== 'props') diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index 986e551fec9e..0c77c3bedf79 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -65,7 +65,7 @@ export { } export type GetStaticProps = (ctx: { - params: ParsedUrlQuery | undefined + params?: ParsedUrlQuery preview?: boolean previewData?: any }) => Promise<{ @@ -79,9 +79,9 @@ export type GetStaticPaths = () => Promise<{ }> export type GetServerSideProps = (context: { - params: ParsedUrlQuery | undefined req: IncomingMessage res: ServerResponse + params?: ParsedUrlQuery query: ParsedUrlQuery }) => Promise<{ [key: string]: any }> From 24345c9d066b9fefe8116bb26dce6f3be4797746 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sat, 29 Feb 2020 18:06:18 -0500 Subject: [PATCH 23/29] Correctly Dedupe Prefetching (#10758) * Correctly Dedupe Prefetching * add test --- packages/next/client/link.tsx | 31 +++++++++++++------ .../preload-viewport/pages/not-de-duped.js | 11 +++++++ .../preload-viewport/test/index.test.js | 16 ++++++++++ 3 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 test/integration/preload-viewport/pages/not-de-duped.js diff --git a/packages/next/client/link.tsx b/packages/next/client/link.tsx index 9f846681accb..c13fd1bc2bfa 100644 --- a/packages/next/client/link.tsx +++ b/packages/next/client/link.tsx @@ -57,7 +57,7 @@ let observer: IntersectionObserver const listeners = new Map() const IntersectionObserver = typeof window !== 'undefined' ? (window as any).IntersectionObserver : null -const prefetched: { [href: string]: boolean } = {} +const prefetched: { [cacheKey: string]: boolean } = {} function getObserver() { // Return shared instance of IntersectionObserver if already created @@ -139,10 +139,16 @@ class Link extends Component { } handleRef(ref: Element) { - const isPrefetched = prefetched[this.getPaths()[0]] if (this.p && IntersectionObserver && ref && ref.tagName) { this.cleanUpListeners() + const isPrefetched = + prefetched[ + this.getPaths().join( + // Join on an invalid URI character + '%' + ) + ] if (!isPrefetched) { this.cleanUpListeners = listenToIntersections(ref, () => { this.prefetch() @@ -208,17 +214,24 @@ class Link extends Component { prefetch(options?: PrefetchOptions) { if (!this.p || typeof window === 'undefined') return // Prefetch the JSON page if asked (only in the client) - const [href, asPath] = this.getPaths() + const paths = this.getPaths() // We need to handle a prefetch error here since we may be // loading with priority which can reject but we don't // want to force navigation since this is only a prefetch - Router.prefetch(href, asPath, options).catch(err => { - if (process.env.NODE_ENV !== 'production') { - // rethrow to show invalid URL errors - throw err + Router.prefetch(paths[/* href */ 0], paths[/* asPath */ 1], options).catch( + err => { + if (process.env.NODE_ENV !== 'production') { + // rethrow to show invalid URL errors + throw err + } } - }) - prefetched[href] = true + ) + prefetched[ + paths.join( + // Join on an invalid URI character + '%' + ) + ] = true } render() { diff --git a/test/integration/preload-viewport/pages/not-de-duped.js b/test/integration/preload-viewport/pages/not-de-duped.js new file mode 100644 index 000000000000..c8b164baeb8f --- /dev/null +++ b/test/integration/preload-viewport/pages/not-de-duped.js @@ -0,0 +1,11 @@ +import Link from 'next/link' + +export default () => { + return ( +

+ + to /first + +

+ ) +} diff --git a/test/integration/preload-viewport/test/index.test.js b/test/integration/preload-viewport/test/index.test.js index 0df30ea2464f..25f40cb72595 100644 --- a/test/integration/preload-viewport/test/index.test.js +++ b/test/integration/preload-viewport/test/index.test.js @@ -228,4 +228,20 @@ describe('Prefetching Links in viewport', () => { const calledPrefetch = await browser.eval(`window.calledPrefetch`) expect(calledPrefetch).toBe(false) }) + + it('should prefetch with a different asPath for a prefetched page', async () => { + // info: both `/` and `/not-de-duped` ref the `/first` page, which we want + // to see prefetched twice. + const browser = await webdriver(appPort, '/') + await browser.eval(`(function() { + window.calledPrefetch = false + window.next.router.prefetch = function() { + window.calledPrefetch = true + } + window.next.router.push('/not-de-duped') + })()`) + await waitFor(2 * 1000) + const calledPrefetch = await browser.eval(`window.calledPrefetch`) + expect(calledPrefetch).toBe(true) + }) }) From c52c0389fd2eb554761ced854eb3c833859b6ec4 Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Sun, 1 Mar 2020 06:20:28 -0800 Subject: [PATCH 24/29] Add params to getStaticProps on err.sh (#10751) * Add params to getStaticProps * Update errors/invalid-getstaticprops-value.md Co-Authored-By: JJ Kasper Co-authored-by: JJ Kasper --- errors/invalid-getstaticprops-value.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/errors/invalid-getstaticprops-value.md b/errors/invalid-getstaticprops-value.md index ace1cdceb52b..ccbfd7232d07 100644 --- a/errors/invalid-getstaticprops-value.md +++ b/errors/invalid-getstaticprops-value.md @@ -9,7 +9,11 @@ In one of the page's `getStaticProps` the return value had the incorrect shape. Make sure to return the following shape from `getStaticProps`: ```js -export async function getStaticProps() { +export async function getStaticProps(ctx: { + params?: ParsedUrlQuery + preview?: boolean + previewData?: any +}) { return { props: { [key: string]: any } } From 241be2d5c148c28a2aa02264d7dd6f3041b0eac6 Mon Sep 17 00:00:00 2001 From: Ethan Ryan Date: Sun, 1 Mar 2020 09:23:03 -0500 Subject: [PATCH 25/29] updating links to dynamic-routes section of docs (#10759) --- docs/upgrading.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/upgrading.md b/docs/upgrading.md index 979d9c3195e6..d5be42132d86 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -10,11 +10,11 @@ description: Learn how to upgrade Next.js. #### Production Deployment on ZEIT Now v2 -If you previously configured `routes` in your `now.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](https://github.com/zeit/next.js#dynamic-routing). +If you previously configured `routes` in your `now.json` file for dynamic routes, these rules can be removed when leveraging Next.js 9's new [Dynamic Routing feature](https://nextjs.org/docs/routing/dynamic-routes). Next.js 9's dynamic routes are **automatically configured on [Now](https://zeit.co/now)** and do not require any `now.json` customization. -You can read more about [Dynamic Routing here](https://github.com/zeit/next.js#dynamic-routing). +You can read more about [Dynamic Routing here](https://nextjs.org/docs/routing/dynamic-routes). #### Check your Custom (`pages/_app.js`) From bc4f7bd2169405a05af8ae7c8aa89bcdd57e8df1 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 1 Mar 2020 11:44:44 -0500 Subject: [PATCH 26/29] v9.2.3-canary.18 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-google-analytics/package.json | 2 +- packages/next-plugin-material-ui/package.json | 2 +- packages/next-plugin-sentry/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next/package.json | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lerna.json b/lerna.json index 2d7932159f6a..bc9e216e3661 100644 --- a/lerna.json +++ b/lerna.json @@ -12,5 +12,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "9.2.3-canary.17" + "version": "9.2.3-canary.18" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 57935323fa29..4051a2bb69c8 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "keywords": [ "react", "next", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index eecce93a4713..5b0f0f22bf6e 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index bdcd79d3f929..01894dc3543b 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-google-analytics/package.json b/packages/next-plugin-google-analytics/package.json index 11dcfe74ebf5..0f20c11ef52a 100644 --- a/packages/next-plugin-google-analytics/package.json +++ b/packages/next-plugin-google-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-google-analytics", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "nextjs": { "name": "Google Analytics", "required-env": [ diff --git a/packages/next-plugin-material-ui/package.json b/packages/next-plugin-material-ui/package.json index d124dadf406e..5eb184ed4f97 100644 --- a/packages/next-plugin-material-ui/package.json +++ b/packages/next-plugin-material-ui/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-material-ui", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "nextjs": { "name": "Material UI", "required-env": [] diff --git a/packages/next-plugin-sentry/package.json b/packages/next-plugin-sentry/package.json index 548b58f3dd5d..7d640386e7f5 100644 --- a/packages/next-plugin-sentry/package.json +++ b/packages/next-plugin-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-sentry", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "nextjs": { "name": "Sentry", "required-env": [ diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 833c6cd0f09c..8c1b29da765c 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/package.json b/packages/next/package.json index 4c164d59c7e6..2886d9230305 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "9.2.3-canary.17", + "version": "9.2.3-canary.18", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -73,7 +73,7 @@ "@babel/preset-typescript": "7.7.2", "@babel/runtime": "7.7.2", "@babel/types": "7.7.4", - "@next/polyfill-nomodule": "9.2.3-canary.17", + "@next/polyfill-nomodule": "9.2.3-canary.18", "amphtml-validator": "1.0.30", "async-retry": "1.2.3", "async-sema": "3.0.0", From 302b55b43de0c431ed9fc3664f59aab13514b5f2 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 1 Mar 2020 12:14:10 -0500 Subject: [PATCH 27/29] Remove `dangerousAsPath` from `RenderOpts` (#10773) --- packages/next/next-server/server/render.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 64e393d73717..b92a19bc5ee1 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -129,7 +129,6 @@ type RenderOpts = LoadComponentsReturnType & { buildId: string canonicalBase: string runtimeConfig?: { [key: string]: any } - dangerousAsPath: string assetPrefix?: string hasCssMode: boolean err?: Error | null From 8999ef26604bd0b9e44b02a9408020f0223fd2f3 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 1 Mar 2020 12:26:31 -0500 Subject: [PATCH 28/29] Remove Dead Code from Next Server (#10772) * Remove Dead Code from Next Server * remove options * trigger --- .../next/next-server/server/next-server.ts | 20 ++++--------------- packages/next/server/next-dev-server.ts | 5 ++--- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 2717cf5218e8..80b4f5e8a5fa 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -780,7 +780,7 @@ export default class Server { return this.render404(req, res, parsedUrl) } - const html = await this.renderToHTML(req, res, pathname, query, {}) + const html = await this.renderToHTML(req, res, pathname, query) // Request was ended by the user if (html === null) { return @@ -1127,14 +1127,7 @@ export default class Server { req: IncomingMessage, res: ServerResponse, pathname: string, - query: ParsedUrlQuery = {}, - { - amphtml, - hasAmp, - }: { - amphtml?: boolean - hasAmp?: boolean - } = {} + query: ParsedUrlQuery = {} ): Promise { try { const result = await this.findPageComponents(pathname, query) @@ -1144,7 +1137,7 @@ export default class Server { res, pathname, result, - { ...this.renderOpts, amphtml, hasAmp } + { ...this.renderOpts } ) if (result2 !== false) { return result2 @@ -1169,12 +1162,7 @@ export default class Server { res, dynamicRoute.page, result, - { - ...this.renderOpts, - params, - amphtml, - hasAmp, - } + { ...this.renderOpts, params } ) if (result2 !== false) { return result2 diff --git a/packages/next/server/next-dev-server.ts b/packages/next/server/next-dev-server.ts index 2c9fa1631242..a8450ca03689 100644 --- a/packages/next/server/next-dev-server.ts +++ b/packages/next/server/next-dev-server.ts @@ -448,8 +448,7 @@ export default class DevServer extends Server { req: IncomingMessage, res: ServerResponse, pathname: string, - query: { [key: string]: string }, - options = {} + query: { [key: string]: string } ) { const compilationErr = await this.getCompilationError(pathname) if (compilationErr) { @@ -489,7 +488,7 @@ export default class DevServer extends Server { } if (!this.quiet) console.error(err) } - const html = await super.renderToHTML(req, res, pathname, query, options) + const html = await super.renderToHTML(req, res, pathname, query) return html } From 644a1ef187dada46cf411141b27813c1cb0219e2 Mon Sep 17 00:00:00 2001 From: Max Thirouin Date: Sun, 1 Mar 2020 18:32:48 +0100 Subject: [PATCH 29/29] examples: react-native-web: fix config to prefer .web.* exts (#10774) * [examples/with-react-native-web] fix config to get .web.* extensions resolved before other Simple issue if you have `Compo.js` & `Compo.web.js` (eg 1st for iOS/android & second for web): with current config, .web are resolved after. My change change this situation so web extensions are resolved before the others. I removed the `defaultLoaders` since it was not used. Tell me if that was intentional so I can add it back. * lint-fix Co-authored-by: Joe Haddad --- examples/with-react-native-web/next.config.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/with-react-native-web/next.config.js b/examples/with-react-native-web/next.config.js index 70dc75a1b219..cb7efa483410 100644 --- a/examples/with-react-native-web/next.config.js +++ b/examples/with-react-native-web/next.config.js @@ -1,11 +1,16 @@ module.exports = { - webpack: (config, { defaultLoaders }) => { + webpack: config => { config.resolve.alias = { ...(config.resolve.alias || {}), // Transform all direct `react-native` imports to `react-native-web` 'react-native$': 'react-native-web', } - config.resolve.extensions.push('.web.js', '.web.ts', '.web.tsx') + config.resolve.extensions = [ + '.web.js', + '.web.ts', + '.web.tsx', + ...config.resolve.extensions, + ] return config }, }