mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Change page export validity check on client and server in development (#5857)
Resolves #4055 Credit: https://github.com/zeit/next.js/pull/5095 I didn't use the ignore webpack plugin from the original PR and tested bundle size with https://github.com/zeit/next.js/pull/5339 - seems to be safe on that front. Was able to get tests to pass locally, unsure of what goes wrong in CI 🤷♂️ **Questions** 1) The initial PR didn't include changes to `next-server/lib/router` in `getRouteInfo()`. Should the same changes be made within? 2) Should we add a test for rendering a component created via `forwardRef()`? `component-with-forwardedRef`: ```javascript export default React.forwardRef((props, ref) => <span {...props} forwardedRef={ref}>This is a component with a forwarded ref</span>); ``` some test: ```javascript test('renders from forwardRef', async () => { const $ = await get$('/component-with-forwardedRef') const span = $('span') expect(span.text()).toMatch(/This is a component with a forwarded ref/) }) ```
This commit is contained in:
parent
b91a960182
commit
72e7929242
|
@ -260,9 +260,12 @@ export default class Router {
|
||||||
|
|
||||||
const { Component } = routeInfo
|
const { Component } = routeInfo
|
||||||
|
|
||||||
if (typeof Component !== 'function') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const { isValidElementType } = require('react-is')
|
||||||
|
if (!isValidElementType(Component)) {
|
||||||
throw new Error(`The default export is not a React Component in page: "${pathname}"`)
|
throw new Error(`The default export is not a React Component in page: "${pathname}"`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ctx = { pathname, query, asPath: as }
|
const ctx = { pathname, query, asPath: as }
|
||||||
routeInfo.props = await this.getInitialProps(Component, ctx)
|
routeInfo.props = await this.getInitialProps(Component, ctx)
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"@taskr/watch": "1.1.0",
|
"@taskr/watch": "1.1.0",
|
||||||
"@types/react": "16.7.13",
|
"@types/react": "16.7.13",
|
||||||
"@types/react-dom": "16.0.11",
|
"@types/react-dom": "16.0.11",
|
||||||
|
"@types/react-is": "16.5.0",
|
||||||
"@types/send": "0.14.4",
|
"@types/send": "0.14.4",
|
||||||
"taskr": "1.1.0",
|
"taskr": "1.1.0",
|
||||||
"typescript": "3.1.6"
|
"typescript": "3.1.6"
|
||||||
|
|
|
@ -131,9 +131,13 @@ export async function renderToHTML (req: IncomingMessage, res: ServerResponse, p
|
||||||
|
|
||||||
await Loadable.preloadAll() // Make sure all dynamic imports are loaded
|
await Loadable.preloadAll() // Make sure all dynamic imports are loaded
|
||||||
|
|
||||||
if (typeof Component !== 'function') {
|
if (dev) {
|
||||||
|
const { isValidElementType } = require('react-is')
|
||||||
|
if (!isValidElementType(Component)) {
|
||||||
throw new Error(`The default export is not a React Component in page: "${pathname}"`)
|
throw new Error(`The default export is not a React Component in page: "${pathname}"`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!Document.prototype || !Document.prototype.isReactComponent) throw new Error('_document.js is not exporting a React component')
|
if (!Document.prototype || !Document.prototype.isReactComponent) throw new Error('_document.js is not exporting a React component')
|
||||||
|
|
||||||
const asPath = req.url
|
const asPath = req.url
|
||||||
|
@ -141,7 +145,7 @@ export async function renderToHTML (req: IncomingMessage, res: ServerResponse, p
|
||||||
const router = new Router(pathname, query, asPath)
|
const router = new Router(pathname, query, asPath)
|
||||||
const props = await loadGetInitialProps(App, {Component, router, ctx})
|
const props = await loadGetInitialProps(App, {Component, router, ctx})
|
||||||
|
|
||||||
// the response might be finshed on the getInitialProps call
|
// the response might be finished on the getInitialProps call
|
||||||
if (isResSent(res)) return null
|
if (isResSent(res)) return null
|
||||||
|
|
||||||
const devFiles = buildManifest.devFiles
|
const devFiles = buildManifest.devFiles
|
||||||
|
|
|
@ -83,9 +83,12 @@ export default async ({
|
||||||
try {
|
try {
|
||||||
Component = await pageLoader.loadPage(page)
|
Component = await pageLoader.loadPage(page)
|
||||||
|
|
||||||
if (typeof Component !== 'function') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const { isValidElementType } = require('react-is')
|
||||||
|
if (!isValidElementType(Component)) {
|
||||||
throw new Error(`The default export is not a React Component in page: "${page}"`)
|
throw new Error(`The default export is not a React Component in page: "${page}"`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// This catches errors like throwing in the top level of a module
|
// This catches errors like throwing in the top level of a module
|
||||||
initialErr = error
|
initialErr = error
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"prop-types": "15.6.2",
|
"prop-types": "15.6.2",
|
||||||
"prop-types-exact": "1.2.0",
|
"prop-types-exact": "1.2.0",
|
||||||
"react-error-overlay": "4.0.0",
|
"react-error-overlay": "4.0.0",
|
||||||
|
"react-is": "16.6.3",
|
||||||
"recursive-copy": "2.0.6",
|
"recursive-copy": "2.0.6",
|
||||||
"resolve": "1.5.0",
|
"resolve": "1.5.0",
|
||||||
"strip-ansi": "3.0.1",
|
"strip-ansi": "3.0.1",
|
||||||
|
|
7
test/integration/basic/pages/forwardRef-component.js
Normal file
7
test/integration/basic/pages/forwardRef-component.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default React.forwardRef((props, ref) => (
|
||||||
|
<span {...props} forwardedRef={ref}>
|
||||||
|
This is a component with a forwarded ref
|
||||||
|
</span>
|
||||||
|
))
|
3
test/integration/basic/pages/memo-component.js
Normal file
3
test/integration/basic/pages/memo-component.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default React.memo((props) => <span {...props}>Memo component</span>)
|
|
@ -211,7 +211,7 @@ export default (context) => {
|
||||||
const text = await browser.elementByCss('p').text()
|
const text = await browser.elementByCss('p').text()
|
||||||
expect(text).toBe('This is the about page.')
|
expect(text).toBe('This is the about page.')
|
||||||
|
|
||||||
aboutPage.replace('export default', 'export default "not-a-page"\nexport const fn = ')
|
aboutPage.replace('export default', 'export default {};\nexport const fn =')
|
||||||
|
|
||||||
await check(
|
await check(
|
||||||
() => getBrowserBodyText(browser),
|
() => getBrowserBodyText(browser),
|
||||||
|
@ -250,7 +250,7 @@ export default (context) => {
|
||||||
const text = await browser.elementByCss('p').text()
|
const text = await browser.elementByCss('p').text()
|
||||||
expect(text).toBe('This is the about page.')
|
expect(text).toBe('This is the about page.')
|
||||||
|
|
||||||
aboutPage.replace('export default', 'export default () => /search/ \nexport const fn = ')
|
aboutPage.replace('export default', 'export default () => /search/;\nexport const fn =')
|
||||||
|
|
||||||
await check(
|
await check(
|
||||||
() => getBrowserBodyText(browser),
|
() => getBrowserBodyText(browser),
|
||||||
|
|
|
@ -22,6 +22,16 @@ export default function ({ app }, suiteName, render, fetch) {
|
||||||
expect(html.includes('My component!')).toBeTruthy()
|
expect(html.includes('My component!')).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('renders when component is a forwardRef instance', async () => {
|
||||||
|
const html = await render('/forwardRef-component')
|
||||||
|
expect(html.includes('This is a component with a forwarded ref')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('renders when component is a memo instance', async () => {
|
||||||
|
const html = await render('/memo-component')
|
||||||
|
expect(html.includes('Memo component')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
// default-head contains an empty <Head />.
|
// default-head contains an empty <Head />.
|
||||||
test('header renders default charset', async () => {
|
test('header renders default charset', async () => {
|
||||||
const html = await (render('/default-head'))
|
const html = await (render('/default-head'))
|
||||||
|
|
|
@ -1433,6 +1433,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-is@16.5.0":
|
||||||
|
version "16.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-16.5.0.tgz#6b0dd43e60fa7c82b48faf7b487543079a61015a"
|
||||||
|
integrity sha512-yUYPioB2Sh5d4csgpW/vJwxWM0RG1/QbGiwYap2m/bEAQKRwbagYRc5C7oK2AM9QC2vr2ZViCgpm0DpDpFQ6XA==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@16.7.13":
|
"@types/react@*", "@types/react@16.7.13":
|
||||||
version "16.7.13"
|
version "16.7.13"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.13.tgz#d2369ae78377356d42fb54275d30218e84f2247a"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.13.tgz#d2369ae78377356d42fb54275d30218e84f2247a"
|
||||||
|
@ -9245,7 +9252,7 @@ react-error-overlay@4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
|
||||||
integrity sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==
|
integrity sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==
|
||||||
|
|
||||||
react-is@^16.3.2:
|
react-is@16.6.3, react-is@^16.3.2:
|
||||||
version "16.6.3"
|
version "16.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
|
||||||
integrity sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==
|
integrity sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==
|
||||||
|
|
Loading…
Reference in a new issue