Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Commit

Permalink
Render server errors in the browser in dev mode
Browse files Browse the repository at this point in the history
Currently we just display internal server error. Rendering the error in the browser should lead to a better developer UX.

Refs #108
  • Loading branch information
KevinGrandon committed Jan 8, 2018
1 parent 600099d commit 1dc1648
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 15 deletions.
3 changes: 2 additions & 1 deletion build/dev-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ module.exports.DevelopmentRuntime = function({
req.pipe(proxyReq).pipe(res);
},
error => {
renderError(res, error);
res.write(renderError(error));
res.end();
}
);
});
Expand Down
14 changes: 7 additions & 7 deletions build/server-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ const React = require('react');
const RedBox = require('redbox-react').RedBoxError;
const ReactDOMServer = require('react-dom/server');

function renderError(res, error) {
res.write(
'<!DOCTYPE html><html><head><title>Server error</title></head><body>'
);
function renderError(error) {
const content = [
'<!DOCTYPE html><html><head><title>Server error</title></head><body>',
];

const displayError = typeof error === 'string' ? new Error(error) : error;
const errorComponent = ReactDOMServer.renderToString(
React.createElement(RedBox, {error: displayError})
);
res.write(errorComponent);
content.push(errorComponent);

res.write('</body></html>');
res.end();
content.push('</body></html>');
return content.join('');
}

module.exports.renderError = renderError;
5 changes: 5 additions & 0 deletions entries/server-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import main from '__FRAMEWORK_SHARED_ENTRY__';
import CompilationMetaDataFactory from '../plugins/compilation-metadata-plugin';
import AssetsFactory from '../plugins/assets-plugin';
import ContextFactory from '../plugins/context-plugin';
import ServerErrorFactory from '../plugins/server-error-plugin';

const CompilationMetaData = CompilationMetaDataFactory();
const Assets = AssetsFactory();
const Context = ContextFactory();
const ServerErrorHandling = ServerErrorFactory();

/*
Webpack has a configuration option called `publicPath`, which determines the
Expand Down Expand Up @@ -59,6 +61,9 @@ export async function start({port}) {
async function reload() {
const app = await initialize();
app.plugins = [Assets, Context].concat(app.plugins);
if (__DEV__) {
app.plugins.unshift(ServerErrorHandling);
}
state.serve = app.callback();
state.app = app;
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"compose-middleware": "4.0.0",
"compression-webpack-plugin": "^1.1.3",
"core-js": "^2.5.3",
"debug": "^3.1.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.0",
"es6-object-assign": "^1.1.0",
Expand Down
14 changes: 14 additions & 0 deletions plugins/server-error-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* eslint-env node */

const renderError = require('../build/server-error').renderError;

module.exports = function() {
return async function middleware(ctx, next) {
try {
await next();
} catch (err) {
ctx.status = err.statusCode || err.status || 500;
ctx.body = renderError(err);
}
};
};
15 changes: 10 additions & 5 deletions test/cli/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,17 @@ test('`fusion dev` works with assets with cdnUrl', async t => {
});

test('`fusion dev` top-level error', async t => {
const dir = path.resolve(
__dirname,
'../fixtures/server-error-route-component'
);
const dir = path.resolve(__dirname, '../fixtures/server-startup-error');
const {res, proc} = await dev(`--dir=${dir}`);
t.ok(res.includes('server-startup-error'));
proc.kill();
t.end();
});

test('`fusion dev` server render error', async t => {
const dir = path.resolve(__dirname, '../fixtures/server-render-error');
const {res, proc} = await dev(`--dir=${dir}`);
t.ok(res.includes('top-level-route-error'));
t.ok(res.includes('server-render-error'));
proc.kill();
t.end();
});
1 change: 0 additions & 1 deletion test/fixtures/server-error-route-component/src/home.js

This file was deleted.

7 changes: 7 additions & 0 deletions test/fixtures/server-render-error/src/home.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';

const Home = () => {
throw new Error('server-render-error');
return null;
};
export default Home;
13 changes: 13 additions & 0 deletions test/fixtures/server-render-error/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import App from 'fusion-react';
import Router, {Route, Switch} from 'fusion-plugin-react-router';

import Home from './home.js';

export default () => {
const app = new App(<Switch>
<Route exact path="/" component={Home} />
</Switch>);
app.plugin(Router, {});
return app;
};
1 change: 1 addition & 0 deletions test/fixtures/server-startup-error/src/home.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
throw new Error('server-startup-error');
8 changes: 7 additions & 1 deletion test/run-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ async function waitForServer(port) {
});
started = true;
} catch (e) {
numTries++;
// Allow returning true for 500 status code errors to test error states
if (e.statusCode === 500) {
started = true;
res = e.response.body;
} else {
numTries++;
}
}
}
if (!started) {
Expand Down

0 comments on commit 1dc1648

Please sign in to comment.