From 4dcdcec35148ec580a2c91dd54078e5eb6ef20b4 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 3 Apr 2020 17:48:56 -0500 Subject: [PATCH] Add error when GSSP methods are added as page component members --- errors/gssp-component-member.md | 47 ++++++++ packages/next/lib/constants.ts | 2 + packages/next/next-server/server/render.tsx | 13 +++ .../pages/index.js | 2 + .../test/index.test.js | 106 ++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100644 errors/gssp-component-member.md create mode 100644 test/integration/ssg-component-members-error/pages/index.js create mode 100644 test/integration/ssg-component-members-error/test/index.test.js diff --git a/errors/gssp-component-member.md b/errors/gssp-component-member.md new file mode 100644 index 0000000000000..802416079ffe3 --- /dev/null +++ b/errors/gssp-component-member.md @@ -0,0 +1,47 @@ +# getStaticProps/getServerProps can not be attached to the page component + +#### Why This Error Occurred + +On one of your page's components you attached either `getStaticProps`, `getStaticPaths`, or `getServerSideProps` as a member instead of as a separate export. + +These methods can not be attached in the same way as `getInitialProps` and must be their own export + +#### Possible Ways to Fix It + +Move the method to it's own export from your page. + +**Before** + +```jsx +function Page(props) { + return

hello world

+} + +Page.getStaticProps = () => ({ + props: { + hello: 'world', + }, +}) + +export default Page +``` + +**After** + +```jsx +function Page(props) { + return

hello world

+} + +export default Page + +export const getStaticProps = () => ({ + props: { + hello: 'world', + }, +}) +``` + +### Useful Links + +- [Data Fetching Docs](https://nextjs.org/docs/basic-features/data-fetching) diff --git a/packages/next/lib/constants.ts b/packages/next/lib/constants.ts index b4efb3466f6d0..d648b3f1fdd50 100644 --- a/packages/next/lib/constants.ts +++ b/packages/next/lib/constants.ts @@ -39,3 +39,5 @@ export const UNSTABLE_REVALIDATE_RENAME_ERROR = 'To try the experimental implementation, please use `unstable_revalidate` instead.\n' + "We're excited for you to try this feature—please share all feedback on the RFC:\n" + 'https://nextjs.link/issg' + +export const GSSP_COMPONENT_MEMBER_ERROR = `can not be attached to a page's component and must be exported from the page. See more info here: https://err.sh/next.js/gssp-component-member` diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index ec4178b5987bb..31bdb9ff244c4 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -8,6 +8,7 @@ import { SERVER_PROPS_SSG_CONFLICT, SSG_GET_INITIAL_PROPS_CONFLICT, UNSTABLE_REVALIDATE_RENAME_ERROR, + GSSP_COMPONENT_MEMBER_ERROR, } from '../../lib/constants' import { isSerializableProps } from '../../lib/is-serializable-props' import { isInAmpMode } from '../lib/amp' @@ -349,6 +350,18 @@ export async function renderToHTML( !isSSG && !getServerSideProps + for (const methodName of [ + 'getStaticProps', + 'getServerSideProps', + 'getStaticPaths', + ]) { + if ((Component as any)[methodName]) { + throw new Error( + `page ${pathname} ${methodName} ${GSSP_COMPONENT_MEMBER_ERROR}` + ) + } + } + if ( process.env.NODE_ENV !== 'production' && (isAutoExport || isFallback) && diff --git a/test/integration/ssg-component-members-error/pages/index.js b/test/integration/ssg-component-members-error/pages/index.js new file mode 100644 index 0000000000000..410f652f8018b --- /dev/null +++ b/test/integration/ssg-component-members-error/pages/index.js @@ -0,0 +1,2 @@ +const Page = () => 'hi' +export default Page diff --git a/test/integration/ssg-component-members-error/test/index.test.js b/test/integration/ssg-component-members-error/test/index.test.js new file mode 100644 index 0000000000000..783504923a55e --- /dev/null +++ b/test/integration/ssg-component-members-error/test/index.test.js @@ -0,0 +1,106 @@ +/* eslint-env jest */ +/* global jasmine */ +import fs from 'fs-extra' +import { join } from 'path' +import { + nextBuild, + launchApp, + findPort, + renderViaHTTP, + killApp, +} from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2 + +const appDir = join(__dirname, '..') +const indexPage = join(appDir, 'pages/index.js') +let app +let appPort +let origIndexPage = '' + +const runTests = (isDev = false) => { + const getStderr = async () => { + if (isDev) { + let stderr = '' + appPort = await findPort() + app = await launchApp(appDir, appPort, { + onStderr(msg) { + stderr += msg || '' + }, + }) + await renderViaHTTP(appPort, '/') + await killApp(app) + return stderr + } else { + const { stderr } = await nextBuild(appDir, undefined, { stderr: true }) + return stderr + } + } + + it('should show error for getStaticProps as component member', async () => { + await fs.writeFile( + indexPage, + ` + const Page = () => 'hi' + Page.getStaticProps = () => ({ props: { hello: 'world' }}) + export default Page + ` + ) + expect(await getStderr()).toContain( + `getStaticProps can not be attached to a page's component and must be exported from the page` + ) + }) + + it('should show error for getServerSideProps as component member', async () => { + await fs.writeFile( + indexPage, + ` + import React from 'react' + export default class MyPage extends React.Component { + static async getServerSideProps() { + return { + props: { + hello: 'world' + } + } + } + render() { + return 'hi' + } + } + ` + ) + expect(await getStderr()).toContain( + `getServerSideProps can not be attached to a page's component and must be exported from the page` + ) + }) + + it('should show error for getStaticPaths as component member', async () => { + await fs.writeFile( + indexPage, + ` + const Page = () => 'hi' + Page.getStaticPaths = () => ({ paths: [], fallback: true }) + export default Page + ` + ) + expect(await getStderr()).toContain( + `getStaticPaths can not be attached to a page's component and must be exported from the page` + ) + }) +} + +describe('GSSP Page Component Member Error', () => { + beforeAll(async () => { + origIndexPage = await fs.readFile(indexPage, 'utf8') + }) + afterAll(() => fs.writeFile(indexPage, origIndexPage)) + + describe('dev mode', () => { + runTests(true) + }) + + describe('production mode', () => { + runTests() + }) +})