1
0
Fork 0
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:
nkzawa 2016-10-09 18:25:38 +09:00
parent 429668b464
commit 6718b05347
7 changed files with 108 additions and 39 deletions

View file

@ -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: {

View file

@ -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
View 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
}
})

View file

@ -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

View file

@ -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) => {

View file

@ -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)

View file

@ -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)