Skip to content

Commit

Permalink
fix: 🐛 Fixed server-side runtime errors
Browse files Browse the repository at this point in the history
  • Loading branch information
jsimck committed Mar 12, 2022
1 parent 27d3b56 commit b8512eb
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 72 deletions.
3 changes: 2 additions & 1 deletion packages/error-overlay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ImaErrorOverlay extends HTMLElement {

// Get component attributes
const publicUrl = this.getAttribute('public-url');
const serverError = this.getAttribute('server-error');

// Render App
ReactDOM.render(
Expand All @@ -30,7 +31,7 @@ class ImaErrorOverlay extends HTMLElement {
publicUrl: publicUrl ?? defaultOverlayContext.publicUrl,
}}
>
<App />
<App serverError={serverError} />
</OverlayContext.Provider>,
root
);
Expand Down
9 changes: 7 additions & 2 deletions packages/error-overlay/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { FunctionComponent, useCallback } from 'react';
import { Overlay, CompileError, RuntimeError, Header } from '#/components';
import { useConnect } from '#/hooks';

const App: FunctionComponent = () => {
const { error, setError } = useConnect();
export interface AppProps {
serverError: string | null;
}

const App: FunctionComponent<AppProps> = ({ serverError }) => {
const { error, setError } = useConnect(serverError);
const handleClose = useCallback(() => setError(null), []);

if (!error) {
Expand All @@ -18,6 +22,7 @@ const App: FunctionComponent = () => {
message={error.message}
type={error.type}
onClose={handleClose}
hasCloseButton={!serverError}
/>
{error.type === 'compile' && <CompileError error={error} />}
{error.type === 'runtime' && <RuntimeError error={error} />}
Expand Down
4 changes: 3 additions & 1 deletion packages/error-overlay/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ export type HeaderProps = {
message: ParsedError['message'];
type: ParsedError['type'];
onClose: () => void;
hasCloseButton: boolean;
};

const Header: FunctionComponent<HeaderProps> = ({
name,
message,
type,
onClose,
hasCloseButton,
}) => {
return (
<div className='ima-header'>
Expand All @@ -26,7 +28,7 @@ const Header: FunctionComponent<HeaderProps> = ({
<span className='ima-header__name'>{name}: </span>
<span className='ima-header__message'>{message}</span>
</div>
<Close onClose={() => onClose()} />
{hasCloseButton && <Close onClose={() => onClose()} />}
</div>
);
};
Expand Down
143 changes: 87 additions & 56 deletions packages/error-overlay/src/hooks/useConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { mapCompileStackFrame, mapStackFramesToOriginal } from '#/utils';
/**
* Connects error overlay to __IMA_HMR interface.
*/
function useConnect() {
function useConnect(serverError: string | null) {
const { publicUrl } = useContext(OverlayContext);
const [error, setError] = useState<ParsedError | null>(null);
const sourceStorage = new SourceStorage(publicUrl);
Expand All @@ -23,72 +23,103 @@ function useConnect() {
'lib/mappings.wasm': `${publicUrl}/__error-overlay-static/mappings.wasm`,
});

window.__IMA_HMR.on('close', async () => {
setError(null);
});

window.__IMA_HMR.on('clear', async () => {
setError(null);
});

window.__IMA_HMR.on('error', async data => {
if (!data) {
// Parse server error
(async () => {
if (!serverError) {
return;
}

const { type, error } = data;

try {
// Parse compile error
if (type === 'compile') {
const parsedError = await parseCompileError(error);
// Parse runtime error
const { name, message, stack } = JSON.parse(
serverError
) as unknown as Error;

if (!parsedError) {
return;
}
const frames = await mapStackFramesToOriginal(stack, sourceStorage);

const { message, name, column, fileUri, line } = parsedError;
const frame = await mapCompileStackFrame(
fileUri,
line,
column,
sourceStorage
);
if (!frames) {
return;
}

if (!frame) {
return;
}
setError({
name,
message,
type: 'runtime',
frames,
});
} catch (err) {
console.error('Unable to parse server error in ima-error-overlay.');
console.error(err);
}
})();

setError({
name,
message,
type,
frames: [frame],
});
} else if (type === 'runtime') {
// Parse runtime error
const { name, message, stack } = error;
const frames = await mapStackFramesToOriginal(stack, sourceStorage);

if (!frames) {
return;
}
// Connect to IMA HMR
if (window?.__IMA_HMR) {
window.__IMA_HMR.on('close', async () => {
setError(null);
});

setError({
name,
message,
type,
frames,
});
window.__IMA_HMR.on('clear', async () => {
setError(null);
});

window.__IMA_HMR.on('error', async data => {
if (!data) {
return;
}

// Cleanup wasm allocated sourcemaps
sourceStorage.cleanup();
} catch (error) {
console.error('Unable to parse an error in ima-error-overlay.');
console.error(error);
}
});
try {
// Parse compile error
if (data.type === 'compile') {
const parsedError = await parseCompileError(data.error);

if (!parsedError) {
return;
}

const { message, name, column, fileUri, line } = parsedError;
const frame = await mapCompileStackFrame(
fileUri,
line,
column,
sourceStorage
);

if (!frame) {
return;
}

setError({
name,
message,
type: data.type,
frames: [frame],
});
} else if (data.type === 'runtime') {
// Parse runtime error
const { name, message, stack } = data.error;
const frames = await mapStackFramesToOriginal(stack, sourceStorage);

if (!frames) {
return;
}

setError({
name,
message,
type: data.type,
frames,
});
}

// Cleanup wasm allocated sourcemaps
sourceStorage.cleanup();
} catch (err) {
console.error('Unable to parse an error in ima-error-overlay.');
console.error(err);
}
});
}
}, []);

return { error, setError };
Expand Down
2 changes: 1 addition & 1 deletion packages/server/lib/clientApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ module.exports = (environment, logger, languageLoader, appFactory) => {
} else {
res.send(
errorTemplate({
devServerPublic: process.env.IMA_CLI_DEV_SERVER_PUBLIC,
devServerPublic: process.env.IMA_CLI_DEV_SERVER_PUBLIC_URL,
serverError: {
name: err.name,
message: err.message,
Expand Down
17 changes: 6 additions & 11 deletions packages/server/lib/error-view/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<link rel="stylesheet" href="http://<%= devServerPublic %>/__error-overlay-static/overlay.css">
<title>IMA.js - Server Error</title>
</head>

<body>
<div id="root"></div>
<script>
window.__ima_server_error = <%- JSON.stringify(serverError) %>;
window.__ima_hmr = {
options: {
publicUrl: '<%= devServerPublic %>'
}
};
</script>
<script src="http://<%= devServerPublic %>/__error-overlay-static/overlay.js"></script>
<script src="<%= devServerPublic %>/__error-overlay-static/overlay.js"></script>
<ima-error-overlay
public-url="<%= devServerPublic %>"
server-error="<%= JSON.stringify(serverError) %>"
type="compile"
></ima-error-overlay>
<script>
(() => {
function debounce(func, timeout) {
Expand Down

0 comments on commit b8512eb

Please sign in to comment.