From 3d7d63aa8a106b2af0474c74ec3a047c66c9ec7b Mon Sep 17 00:00:00 2001
From: Drew Powers <1369770+drwpow@users.noreply.github.com>
Date: Thu, 11 Nov 2021 13:04:57 -0700
Subject: [PATCH] Improve WASM panic error (#1782)
* Improve WASM panic error
* Add panic test
---
packages/astro/src/core/dev/index.ts | 4 +--
packages/astro/src/core/dev/template/error.ts | 28 ++++++++++++++-----
packages/astro/src/core/ssr/index.ts | 3 +-
packages/astro/src/vite-plugin-astro/index.ts | 28 ++++++++++++++++++-
.../wasm-panic-error/src/pages/index.astro | 3 ++
packages/astro/test/wasm-panic-error.test.js | 26 +++++++++++++++++
6 files changed, 80 insertions(+), 12 deletions(-)
create mode 100644 packages/astro/test/fixtures/wasm-panic-error/src/pages/index.astro
create mode 100644 packages/astro/test/wasm-panic-error.test.js
diff --git a/packages/astro/src/core/dev/index.ts b/packages/astro/src/core/dev/index.ts
index 572ab72c3..756e2a8ca 100644
--- a/packages/astro/src/core/dev/index.ts
+++ b/packages/astro/src/core/dev/index.ts
@@ -178,7 +178,6 @@ export class AstroDevServer {
return [];
} catch (e) {
const err = e as Error;
- viteServer.ssrFixStacktrace(err);
// eslint-disable-next-line
console.error(err.stack);
viteServer.ws.send({
@@ -302,7 +301,6 @@ export class AstroDevServer {
res.write(html);
res.end();
} catch (err: any) {
- this.viteServer.ssrFixStacktrace(err);
this.viteServer.ws.send({ type: 'error', err });
const statusCode = 500;
const html = errorTemplate({
@@ -310,6 +308,8 @@ export class AstroDevServer {
title: 'Internal Error',
tabTitle: '500: Error',
message: stripAnsi(err.message),
+ url: err.url || undefined,
+ stack: stripAnsi(err.stack),
});
info(this.logging, 'astro', msg.req({ url: pathname, statusCode: 500, reqTime: performance.now() - reqStart }));
res.writeHead(statusCode, {
diff --git a/packages/astro/src/core/dev/template/error.ts b/packages/astro/src/core/dev/template/error.ts
index 7060e93a0..7f611328c 100644
--- a/packages/astro/src/core/dev/template/error.ts
+++ b/packages/astro/src/core/dev/template/error.ts
@@ -1,14 +1,23 @@
import { encode } from 'html-entities';
interface ErrorTemplateOptions {
- statusCode?: number;
- tabTitle: string;
- title: string;
+ /** a short description of the error */
message: string;
+ /** information about where the error occurred */
+ stack?: string;
+ /** HTTP error code */
+ statusCode?: number;
+ /** HTML
*/
+ tabTitle: string;
+ /** page title */
+ title: string;
+ /** show user a URL for more info or action to take */
+ url?: string;
}
/** Display internal 404 page (if user didn’t provide one) */
-export function errorTemplate({ title, message, statusCode, tabTitle }: ErrorTemplateOptions): string {
+export function errorTemplate({ title, url, message, stack, statusCode, tabTitle }: ErrorTemplateOptions): string {
+ let error = url ? message.replace(url, '') : message;
return `
@@ -19,13 +28,16 @@ export function errorTemplate({ title, message, statusCode, tabTitle }: ErrorTem
background-color: #101010;
color: #d0d0d0;
font-family: monospace;
- line-height: 1.6;
+ line-height: 1.5;
margin: 0;
}
.wrapper {
padding-left: 2rem;
padding-right: 2rem;
}
+ a {
+ color: #ff5d01;
+ }
h1 {
font-weight: 800;
margin-top: 1rem;
@@ -33,7 +45,7 @@ export function errorTemplate({ title, message, statusCode, tabTitle }: ErrorTem
}
pre {
color: #999;
- font-size: 1.4em;
+ font-size: 1.2em;
margin-top: 0;
max-width: 60em;
}
@@ -45,7 +57,9 @@ export function errorTemplate({ title, message, statusCode, tabTitle }: ErrorTem
${statusCode ? `${statusCode} ` : ''}${title}
- ${encode(message)}
+ ${encode(error)}
+ ${url ? `${url}` : ''}
+ ${encode(stack)}
diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts
index 598497144..720acbad6 100644
--- a/packages/astro/src/core/ssr/index.ts
+++ b/packages/astro/src/core/ssr/index.ts
@@ -81,7 +81,7 @@ async function errorHandler(e: unknown, viteServer: vite.ViteDevServer, filePath
const anyError = e as any;
if (anyError.errors) {
const { location, pluginName, text } = (e as BuildResult).errors[0];
- const err = new Error(text) as SSRError;
+ const err = e as SSRError;
if (location) err.loc = { file: location.file, line: location.line, column: location.column };
const frame = codeFrame(await fs.promises.readFile(filePath, 'utf8'), err.loc);
err.frame = frame;
@@ -89,7 +89,6 @@ async function errorHandler(e: unknown, viteServer: vite.ViteDevServer, filePath
err.message = `${location?.file}: ${text}
${frame}
`;
- err.stack = anyError.stack;
if (pluginName) err.plugin = pluginName;
throw err;
}
diff --git a/packages/astro/src/vite-plugin-astro/index.ts b/packages/astro/src/vite-plugin-astro/index.ts
index 8e68d9504..891c88996 100644
--- a/packages/astro/src/vite-plugin-astro/index.ts
+++ b/packages/astro/src/vite-plugin-astro/index.ts
@@ -94,7 +94,32 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
map,
};
} catch (err: any) {
- // if esbuild threw the error, find original code source to display (if it’s mapped)
+ // improve compiler errors
+ if (err.stack.includes('wasm-function')) {
+ const search = new URLSearchParams({
+ labels: 'compiler',
+ title: '🐛 BUG: `@astrojs/compiler` panic',
+ body: `### Describe the Bug
+
+\`@astrojs/compiler\` encountered an unrecoverable error when compiling the following file.
+
+**${id.replace(fileURLToPath(config.projectRoot), '')}**
+\`\`\`astro
+${source}
+\`\`\`
+`,
+ });
+ err.url = `https://github.com/snowpackjs/astro/issues/new?${search.toString()}`;
+ err.message = `Error: Uh oh, the Astro compiler encountered an unrecoverable error!
+
+Please open
+a GitHub issue using the link below:
+${err.url}`;
+ // TODO: remove stack replacement when compiler throws better errors
+ err.stack = ` at ${id}`;
+ }
+
+ // improve esbuild errors
if (err.errors && tsResult?.map) {
const json = JSON.parse(tsResult.map);
const mappings = decode(json.mappings);
@@ -103,6 +128,7 @@ export default function astro({ config, devServer }: AstroPluginOptions): vite.P
err.sourceLoc = { file: id, line: (focusMapping[0][2] || 0) + 1, column: (focusMapping[0][3] || 0) + 1 };
}
}
+
throw err;
}
},
diff --git a/packages/astro/test/fixtures/wasm-panic-error/src/pages/index.astro b/packages/astro/test/fixtures/wasm-panic-error/src/pages/index.astro
new file mode 100644
index 000000000..fb2847eed
--- /dev/null
+++ b/packages/astro/test/fixtures/wasm-panic-error/src/pages/index.astro
@@ -0,0 +1,3 @@
+
+
+{[...new Array(20)].map(() => ())}
diff --git a/packages/astro/test/wasm-panic-error.test.js b/packages/astro/test/wasm-panic-error.test.js
new file mode 100644
index 000000000..4ee1e3479
--- /dev/null
+++ b/packages/astro/test/wasm-panic-error.test.js
@@ -0,0 +1,26 @@
+import { expect } from 'chai';
+import cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
+
+let fixture;
+
+before(async () => {
+ fixture = await loadFixture({ projectRoot: './fixtures/wasm-panic-error' });
+});
+
+describe('compiler error', () => {
+ it('throws helpful error', async () => {
+ try {
+ await fixture.build();
+
+ // should err
+ expect(true).to.be.false;
+ } catch (err) {
+ // test err thrown contains filepath
+ expect(err.stack).to.include('wasm-panic-error/src/pages/index.astro');
+
+ // test err thrown contains "unrecoverable error"
+ expect(err.message || err.toString()).to.include('Uh oh, the Astro compiler encountered an unrecoverable error!');
+ }
+ });
+});