Skip to content

Commit

Permalink
add runtime error overlay reporting (#1142)
Browse files Browse the repository at this point in the history
  • Loading branch information
FredKSchott authored Sep 28, 2020
1 parent 037961f commit e373499
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 44 deletions.
38 changes: 32 additions & 6 deletions snowpack/assets/hmr-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@ function log(...args) {
function reload() {
location.reload(true);
}
/** Clear all error overlays from the page */
function clearErrorOverlay() {
document.querySelectorAll('hmr-error-overlay').forEach((el) => el.remove());
}
/** Create an error overlay (if custom element exists on the page). */
function createNewErrorOverlay(data) {
const HmrErrorOverlay = customElements.get('hmr-error-overlay');
if (HmrErrorOverlay) {
const overlay = new HmrErrorOverlay(data);
clearErrorOverlay();
document.body.appendChild(overlay);
}
}

let SOCKET_MESSAGE_QUEUE = [];
function _sendSocketMessage(msg) {
Expand Down Expand Up @@ -146,12 +156,7 @@ socket.addEventListener('message', ({data: _data}) => {
return;
}
if (data.type === 'error') {
const HmrErrorOverlay = customElements.get('hmr-error-overlay');
if (HmrErrorOverlay) {
const overlay = new HmrErrorOverlay(data);
clearErrorOverlay();
document.body.appendChild(overlay);
}
createNewErrorOverlay(data);
console.error(
`[ESM-HMR] ${data.fileLoc ? data.fileLoc + '\n' : ''}`,
data.title + '\n' + data.errorMessage,
Expand All @@ -176,3 +181,24 @@ socket.addEventListener('message', ({data: _data}) => {
log('message: unknown', data);
});
log('listening for file changes...');

/** Runtime error reporting: If a runtime error occurs, show it in an overlay. */
window.addEventListener('error', function (event) {
// Generate an "error location" string
let fileLoc;
if (event.filename) {
fileLoc = event.filename;
if (event.lineno !== undefined) {
fileLoc += `:${event.lineno}`;
if (event.colno !== undefined) {
fileLoc += `:${event.colno}`;
}
}
}
createNewErrorOverlay({
title: 'Unhandled Runtime Error',
fileLoc,
errorMessage: event.message,
errorStackTrace: event.error.stack,
});
});
2 changes: 1 addition & 1 deletion snowpack/assets/hmr-error-overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ const ERROR_OVERLAY_TEMPLATE = `
<button type="button" disabled="" aria-disabled="true"><svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.99996 1.16666L1.16663 6.99999L6.99996 12.8333M12.8333 6.99999H1.99996H12.8333Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></button><button type="button" disabled="" aria-disabled="true"><svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.99996 1.16666L12.8333 6.99999L6.99996 12.8333M1.16663 6.99999H12H1.16663Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></button>
&nbsp;<small><span>1</span> of <span>1</span> unhandled error</small>
</nav>
<button id="close-button" type="button" aria-label="Close"><span aria-hidden="true"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span></button></div><h1 id="nextjs__container_errors_label">Unhandled Runtime Error</h1><p id="nextjs__container_errors_desc">ReferenceError: Head is not defined</p></div><div data-nextjs-dialog-body="true" class="nextjs-container-errors-body"><h5>Source</h5><div data-nextjs-codeframe="true">
<button id="close-button" type="button" aria-label="Close"><span aria-hidden="true"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span></button></div><h1 id="nextjs__container_errors_label">Error</h1><p id="nextjs__container_errors_desc"></p></div><div data-nextjs-dialog-body="true" class="nextjs-container-errors-body"><h5>Source</h5><div data-nextjs-codeframe="true">
<p>
<span id="error-file-loc">Loading...</span>
<!--<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>-->
Expand Down
4 changes: 2 additions & 2 deletions snowpack/src/build/build-import-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type CSSModuleLoader from 'css-modules-loader-core';
import path from 'path';
import {SnowpackConfig} from '../types/snowpack';
import {appendHTMLToBody, getExt} from '../util';
import {appendHtmlToHead, getExt} from '../util';
import {logger} from '../logger';

export function getMetaUrlPath(urlPath: string, config: SnowpackConfig): string {
Expand Down Expand Up @@ -76,7 +76,7 @@ export function wrapHtmlResponse({

if (hmr) {
const hmrScript = `<script type="module" src="${getMetaUrlPath('hmr-client.js', config)}"></script><script type="module" src="${getMetaUrlPath('hmr-error-overlay.js', config)}"></script>`;
code = appendHTMLToBody(code, hmrScript);
code = appendHtmlToHead(code, hmrScript);
}
return code;
}
Expand Down
5 changes: 4 additions & 1 deletion snowpack/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,10 @@ export async function command(commandOptions: CommandOptions) {
hmrEngine &&
hmrEngine.broadcastMessage({
type: 'error',
title: 'Build Error',
title:
`Build Error` + err.__snowpackBuildDetails
? `: ${err.__snowpackBuildDetails.name}`
: '',
errorMessage: err.toString(),
fileLoc,
errorStackTrace: err.stack,
Expand Down
25 changes: 8 additions & 17 deletions snowpack/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -815,23 +815,14 @@ If Snowpack is having trouble detecting the import, add ${colors.bold(
responseOutput = await buildFile(fileLoc);
} catch (err) {
logger.error(err.toString(), {name: err.__snowpackBuildDetails?.name});
if (err.__snowpackBuildDetails) {
hmrEngine.broadcastMessage({
type: 'error',
title: `Build Error: ${err.__snowpackBuildDetails.name}`,
errorMessage: err.toString(),
fileLoc,
errorStackTrace: err.stack,
});
} else {
hmrEngine.broadcastMessage({
type: 'error',
title: 'Build Error',
errorMessage: err.toString(),
fileLoc,
errorStackTrace: err.stack,
});
}
hmrEngine.broadcastMessage({
type: 'error',
title:
`Build Error` + err.__snowpackBuildDetails ? `: ${err.__snowpackBuildDetails.name}` : '',
errorMessage: err.toString(),
fileLoc,
errorStackTrace: err.stack,
});
sendError(req, res, 500);
return;
}
Expand Down
18 changes: 1 addition & 17 deletions snowpack/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ export function relativeURL(path1: string, path2: string): string {
const CLOSING_HEAD_TAG = /<\s*\/\s*head\s*>/gi;

/** Append HTML before closing </head> tag */
export function appendHTMLToHead(doc: string, htmlToAdd: string) {
export function appendHtmlToHead(doc: string, htmlToAdd: string) {
const closingHeadMatch = doc.match(CLOSING_HEAD_TAG);
// if no <head> tag found, throw an error (we can’t load your app properly)
if (!closingHeadMatch) {
Expand All @@ -377,22 +377,6 @@ export function appendHTMLToHead(doc: string, htmlToAdd: string) {
return doc.replace(closingHeadMatch[0], htmlToAdd + closingHeadMatch[0]);
}

const CLOSING_BODY_TAG = /<\s*\/\s*body\s*>/gi;

/** Append HTML before closing </body> tag */
export function appendHTMLToBody(doc: string, htmlToAdd: string) {
const closingBodyMatch = doc.match(CLOSING_BODY_TAG);
// if no <body> tag found, throw an error (we can’t load your app properly)
if (!closingBodyMatch) {
throw new Error(`No <body> tag found in HTML (this is needed to load your app):\n\n${doc}`);
}
// if multiple <body> tags found, also freak out
if (closingBodyMatch.length > 1) {
throw new Error(`Multiple <body> tags found in HTML (perhaps commented out?):\n\n${doc}`);
}
return doc.replace(closingBodyMatch[0], htmlToAdd + closingBodyMatch[0]);
}

/** Add / to beginning of string (but don’t double-up) */
export function addLeadingSlash(path: string) {
return path.replace(/^\/?/, '/');
Expand Down

1 comment on commit e373499

@vercel
Copy link

@vercel vercel bot commented on e373499 Sep 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.