mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
add _error.js, fix error handlings
This commit is contained in:
parent
429668b464
commit
6718b05347
|
@ -3,7 +3,7 @@
|
|||
import { resolve } from 'path'
|
||||
import parseArgs from 'minimist'
|
||||
import Server from '../server'
|
||||
import build from '../server/build/index'
|
||||
import build from '../server/build'
|
||||
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { createElement } from 'react'
|
||||
import { render } from 'react-dom'
|
||||
import evalScript from './eval-script'
|
||||
import HeadManager from './head-manager'
|
||||
import Router from '../lib/router'
|
||||
import DefaultApp from '../lib/app'
|
||||
import evalScript from '../lib/eval-script'
|
||||
|
||||
const {
|
||||
__NEXT_DATA__: { app, component, props }
|
||||
|
|
64
lib/pages/_error.js
Normal file
64
lib/pages/_error.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
import React from 'react'
|
||||
import { css, StyleSheet } from 'next/css'
|
||||
|
||||
export default class Error extends React.Component {
|
||||
static getInitialProps ({ res, xhr }) {
|
||||
const statusCode = res ? res.statusCode : xhr.status
|
||||
return { statusCode }
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusCode } = this.props
|
||||
const title = 404 === statusCode ? 'This page could not be found' : 'Internal Server Error'
|
||||
|
||||
return <div className={css(styles.error, styles['error_' + statusCode])}>
|
||||
<div className={css(styles.text)}>
|
||||
<h1 className={css(styles.h1)}>{statusCode}</h1>
|
||||
<div className={css(styles.desc)}>
|
||||
<h2 className={css(styles.h2)}>{title}.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
error: {
|
||||
color: '#000',
|
||||
background: '#fff',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
position: 'absolute',
|
||||
fontDamily: '"SF UI Text", "Helvetica Neue", "Lucida Grande"',
|
||||
textAlign: 'center',
|
||||
paddingTop: '20%'
|
||||
},
|
||||
|
||||
desc: {
|
||||
display: 'inline-block',
|
||||
textAlign: 'left',
|
||||
lineHeight: '49px',
|
||||
height: '49px',
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
|
||||
h1: {
|
||||
display: 'inline-block',
|
||||
borderRight: '1px solid rgba(0, 0, 0,.3)',
|
||||
margin: 0,
|
||||
marginRight: '20px',
|
||||
padding: '10px 23px',
|
||||
fontSize: '24px',
|
||||
fontWeight: 500,
|
||||
verticalAlign: 'top'
|
||||
},
|
||||
|
||||
h2: {
|
||||
fontSize: '14px',
|
||||
fontWeight: 'normal',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}
|
||||
})
|
|
@ -33,7 +33,7 @@ export default class Router {
|
|||
const data = await this.fetchComponent(route)
|
||||
let props
|
||||
if (route !== this.route) {
|
||||
props = await this.getInitialProps(data.Component)
|
||||
props = await this.getInitialProps(data.Component, data.ctx)
|
||||
}
|
||||
|
||||
this.route = route
|
||||
|
@ -57,7 +57,7 @@ export default class Router {
|
|||
if (route === this.route) {
|
||||
let props
|
||||
try {
|
||||
props = await this.getInitialProps(data.Component)
|
||||
props = await this.getInitialProps(data.Component, {})
|
||||
} catch (err) {
|
||||
if (err.cancelled) return false
|
||||
throw err
|
||||
|
@ -89,7 +89,7 @@ export default class Router {
|
|||
try {
|
||||
data = await this.fetchComponent(route)
|
||||
if (route !== this.route) {
|
||||
props = await this.getInitialProps(data.Component)
|
||||
props = await this.getInitialProps(data.Component, data.ctx)
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.cancelled) return false
|
||||
|
@ -129,16 +129,16 @@ export default class Router {
|
|||
data = await new Promise((resolve, reject) => {
|
||||
this.componentLoadCancel = cancel = () => {
|
||||
cancelled = true
|
||||
if (componentXHR.abort) componentXHR.abort()
|
||||
if (xhr.abort) xhr.abort()
|
||||
|
||||
const err = new Error('Cancelled')
|
||||
err.cancelled = true
|
||||
reject(err)
|
||||
}
|
||||
|
||||
const componentXHR = loadComponent(componentUrl, (err, data) => {
|
||||
const xhr = loadComponent(componentUrl, (err, data) => {
|
||||
if (err) return reject(err)
|
||||
resolve(data)
|
||||
resolve({ ...data, ctx: { xhr } })
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -158,12 +158,12 @@ export default class Router {
|
|||
return data
|
||||
}
|
||||
|
||||
async getInitialProps (Component) {
|
||||
async getInitialProps (Component, ctx) {
|
||||
let cancelled = false
|
||||
const cancel = () => { cancelled = true }
|
||||
this.componentLoadCancel = cancel
|
||||
|
||||
const props = await (Component.getInitialProps ? Component.getInitialProps({}) : {})
|
||||
const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
||||
|
||||
if (cancel === this.componentLoadCancel) {
|
||||
this.componentLoadCancel = null
|
||||
|
|
|
@ -5,6 +5,14 @@ import bundle from './bundle'
|
|||
|
||||
export default async function build (dir) {
|
||||
const dstDir = resolve(dir, '.next')
|
||||
const templateDir = resolve(__dirname, '..', '..', 'lib', 'pages')
|
||||
|
||||
// create `.next/pages/_error.js`
|
||||
// which may be overwriten by the user sciprt, `pages/_error.js`
|
||||
const templatPaths = await glob('**/*.js', { cwd: templateDir })
|
||||
await Promise.all(templatPaths.map(async (p) => {
|
||||
await transpile(resolve(templateDir, p), resolve(dstDir, 'pages', p))
|
||||
}))
|
||||
|
||||
const paths = await glob('**/*.js', { cwd: dir, ignore: 'node_modules/**' })
|
||||
await Promise.all(paths.map(async (p) => {
|
||||
|
|
|
@ -11,8 +11,11 @@ export default class Server {
|
|||
this.router = new Router()
|
||||
|
||||
this.http = http.createServer((req, res) => {
|
||||
this.run(req, res).catch((err) => {
|
||||
this.renderError(req, res, err)
|
||||
this.run(req, res)
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
res.status(500);
|
||||
res.end('error');
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -30,12 +33,12 @@ export default class Server {
|
|||
|
||||
this.router.get('/:path+.json', async (req, res, params) => {
|
||||
const path = (params.path || []).join('/')
|
||||
await this.renderJSON(req, res, path)
|
||||
await this.renderJSON(path, req, res)
|
||||
})
|
||||
|
||||
this.router.get('/:path*', async (req, res, params) => {
|
||||
const path = (params.path || []).join('/')
|
||||
await this.render(req, res, path)
|
||||
await this.render(path, req, res)
|
||||
})
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
|
@ -55,32 +58,41 @@ export default class Server {
|
|||
}
|
||||
}
|
||||
|
||||
async render (req, res, path) {
|
||||
async render (path, req, res) {
|
||||
const { dir, dev } = this
|
||||
let html
|
||||
try {
|
||||
html = await render(path, req, res, { dir, dev })
|
||||
html = await render(path, { req, res }, { dir, dev })
|
||||
} catch (err) {
|
||||
let statusCode
|
||||
if ('ENOENT' === err.code) {
|
||||
return this.render404(req, res)
|
||||
statusCode = 404
|
||||
} else {
|
||||
console.error(err)
|
||||
statusCode = 500
|
||||
}
|
||||
throw err
|
||||
res.statusCode = err.statusCode = statusCode
|
||||
html = await render('_error', { req, res, err }, { dir, dev })
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||
res.end(html)
|
||||
}
|
||||
|
||||
async renderJSON (req, res, path) {
|
||||
async renderJSON (path, req, res) {
|
||||
const { dir } = this
|
||||
let json
|
||||
try {
|
||||
json = await renderJSON(path, { dir })
|
||||
} catch (err) {
|
||||
if ('ENOENT' === err.code) {
|
||||
return this.render404(req, res)
|
||||
res.statusCode = 404
|
||||
} else {
|
||||
console.error(err)
|
||||
res.statusCode = 500
|
||||
}
|
||||
throw err
|
||||
json = await renderJSON('_error', { dir })
|
||||
}
|
||||
|
||||
const data = JSON.stringify(json)
|
||||
|
@ -89,17 +101,6 @@ export default class Server {
|
|||
res.end(data)
|
||||
}
|
||||
|
||||
async render404 (req, res) {
|
||||
res.writeHead(404)
|
||||
res.end('Not Found')
|
||||
}
|
||||
|
||||
async renderError (req, res, err) {
|
||||
console.error(err)
|
||||
res.writeHead(500)
|
||||
res.end('Error')
|
||||
}
|
||||
|
||||
serveStatic (req, res, path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
send(req, path)
|
||||
|
|
|
@ -10,23 +10,19 @@ import Head from '../lib/head'
|
|||
import App from '../lib/app'
|
||||
import { StyleSheetServer } from '../lib/css'
|
||||
|
||||
export async function render (path, req, res, { dir = process.cwd(), dev = false } = {}) {
|
||||
export async function render (path, ctx, { dir = process.cwd(), dev = false } = {}) {
|
||||
const p = await requireResolve(resolve(dir, '.next', 'pages', path))
|
||||
const mod = require(p)
|
||||
const Component = mod.default || mod
|
||||
|
||||
let props = {}
|
||||
if (Component.getInitialProps) {
|
||||
props = await Component.getInitialProps({ req, res })
|
||||
}
|
||||
|
||||
const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {})
|
||||
const component = await read(resolve(dir, '.next', '_bundles', 'pages', path))
|
||||
|
||||
const { html, css } = StyleSheetServer.renderStatic(() => {
|
||||
const app = createElement(App, {
|
||||
Component,
|
||||
props,
|
||||
router: new Router(req.url)
|
||||
router: new Router(ctx.req.url)
|
||||
})
|
||||
|
||||
return renderToString(app)
|
||||
|
|
Loading…
Reference in a new issue