Skip to content

Commit

Permalink
Add handling for invalid pages during autoExport (#7574)
Browse files Browse the repository at this point in the history
* Add handling for invalid pages during autoExport

* Add err.sh link for invalid page export

* Fix typo

* Apply suggestions from code review

Co-Authored-By: Tim Neutkens <tim@timneutkens.nl>
  • Loading branch information
ijjk and timneutkens committed Jun 14, 2019
1 parent feb31f4 commit 090a06b
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 12 deletions.
9 changes: 9 additions & 0 deletions errors/page-without-valid-component.md
@@ -0,0 +1,9 @@
# Page Without Valid React Component

#### Why This Error Occurred

While auto exporting a page a valid React Component wasn't found. This could mean you have a file in `pages` that exports something that is not a React Component.

#### Possible Ways to Fix It

Move any non-page files that don't export a React Component as the default export to a different folder like `components` or `lib`.
24 changes: 21 additions & 3 deletions packages/next/build/index.ts
Expand Up @@ -280,6 +280,7 @@ export default async function build(dir: string, conf = null): Promise<void> {

const { autoExport } = config.experimental
const staticPages = new Set<string>()
const invalidPages = new Set<string>()
const pageInfos = new Map<string, PageInfo>()
let pagesManifest: any = {}
let customAppGetInitialProps: boolean | undefined
Expand Down Expand Up @@ -336,16 +337,33 @@ export default async function build(dir: string, conf = null): Promise<void> {
}

if (customAppGetInitialProps === false && nonReservedPage) {
if (isPageStatic(serverBundle, runtimeEnvConfig)) {
staticPages.add(page)
isStatic = true
try {
if (isPageStatic(serverBundle, runtimeEnvConfig)) {
staticPages.add(page)
isStatic = true
}
} catch (err) {
if (err.code !== 'INVALID_DEFAULT_EXPORT') throw err
invalidPages.add(page)
}
}
}

pageInfos.set(page, { size, chunks, serverBundle, static: isStatic })
}

if (invalidPages.size > 0) {
throw new Error(
`autoExport failed: found page${
invalidPages.size === 1 ? '' : 's'
} without React Component as default export\n${[...invalidPages]
.map(pg => `pages${pg}`)
.join(
'\n'
)}\n\nSee https://err.sh/zeit/next.js/page-without-valid-component for more info.\n`
)
}

if (Array.isArray(configs[0].plugins)) {
configs[0].plugins.some((plugin: any) => {
if (!plugin.ampPages) {
Expand Down
15 changes: 6 additions & 9 deletions packages/next/build/utils.ts
Expand Up @@ -7,6 +7,7 @@ import path from 'path'
import stripAnsi from 'strip-ansi'
import { promisify } from 'util'

import { isValidElementType } from 'react-is'
import prettyBytes from '../lib/pretty-bytes'
import { recursiveReadDir } from '../lib/recursive-readdir'
import { getPageChunks } from './webpack/plugins/chunk-graph-plugin'
Expand Down Expand Up @@ -262,16 +263,12 @@ export function isPageStatic(
try {
nextEnvConfig.setConfig(runtimeEnvConfig)
const Comp = require(serverBundle).default
if (!Comp) {
const pageStartIdx = serverBundle.indexOf('pages/') + 5
console.log(
'not exporting invalid page',
serverBundle.substr(pageStartIdx),
'(no default export)'
)
return false
if (!Comp || !isValidElementType(Comp) || typeof Comp === 'string') {
const invalidPage = new Error('invalid-page')
;(invalidPage as any).code = 'INVALID_DEFAULT_EXPORT'
throw invalidPage
}
return typeof Comp.getInitialProps !== 'function'
return typeof (Comp as any).getInitialProps !== 'function'
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') return false
throw err
Expand Down
5 changes: 5 additions & 0 deletions test/integration/invalid-page-autoExport/next.config.js
@@ -0,0 +1,5 @@
module.exports = {
experimental: {
autoExport: true
}
}
@@ -0,0 +1 @@
export default 'just a string'
7 changes: 7 additions & 0 deletions test/integration/invalid-page-autoExport/pages/also-valid.js
@@ -0,0 +1,7 @@
import React, { Component } from 'react'

export default class AlsoValid extends Component {
render () {
return <div>Hi there</div>
}
}
5 changes: 5 additions & 0 deletions test/integration/invalid-page-autoExport/pages/invalid.js
@@ -0,0 +1,5 @@
const obj = {
something: 'idk'
}

export default obj
1 change: 1 addition & 0 deletions test/integration/invalid-page-autoExport/pages/valid.js
@@ -0,0 +1 @@
export default () => <p>Hello world</p>
19 changes: 19 additions & 0 deletions test/integration/invalid-page-autoExport/test/index.test.js
@@ -0,0 +1,19 @@
/* eslint-env jest */
/* global jasmine, test */
import path from 'path'
import { nextBuild } from 'next-test-utils'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
const appDir = path.join(__dirname, '..')

describe('Invalid Page autoExport', () => {
it('Fails softly with descriptive error', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true })

expect(stderr).toMatch(
/autoExport failed: found pages without React Component as default export/
)
expect(stderr).toMatch(/pages\/invalid/)
expect(stderr).toMatch(/pages\/also-invalid/)
})
})

0 comments on commit 090a06b

Please sign in to comment.