Make error handling more resilient in the dev server (#4723)

* Make error handling more resilient in the dev server

* Better approach

* Add a changeset
This commit is contained in:
Matthew Phillips 2022-09-12 14:40:31 -04:00 committed by GitHub
parent 562147a5b4
commit 0dba3b6f3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 10 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Makes the dev server more resilient to crashes

View file

@ -48,7 +48,10 @@ export function cleanErrorStack(stack: string) {
export function fixViteErrorMessage(_err: unknown, server?: ViteDevServer, filePath?: URL) { export function fixViteErrorMessage(_err: unknown, server?: ViteDevServer, filePath?: URL) {
const err = createSafeError(_err); const err = createSafeError(_err);
// Vite will give you better stacktraces, using sourcemaps. // Vite will give you better stacktraces, using sourcemaps.
server?.ssrFixStacktrace(err); try {
server?.ssrFixStacktrace(err);
} catch {}
// Fix: Astro.glob() compiles to import.meta.glob() by the time Vite sees it, // Fix: Astro.glob() compiles to import.meta.glob() by the time Vite sees it,
// so we need to update this error message in case it originally came from Astro.glob(). // so we need to update this error message in case it originally came from Astro.glob().
if (err.message === 'import.meta.glob() can only accept string literals.') { if (err.message === 'import.meta.glob() can only accept string literals.') {
@ -57,14 +60,16 @@ export function fixViteErrorMessage(_err: unknown, server?: ViteDevServer, fileP
if (filePath && /failed to load module for ssr:/.test(err.message)) { if (filePath && /failed to load module for ssr:/.test(err.message)) {
const importName = err.message.split('for ssr:').at(1)?.trim(); const importName = err.message.split('for ssr:').at(1)?.trim();
if (importName) { if (importName) {
const content = fs.readFileSync(fileURLToPath(filePath)).toString(); try {
const lns = content.split('\n'); const content = fs.readFileSync(fileURLToPath(filePath)).toString();
const line = lns.findIndex((ln) => ln.includes(importName)); const lns = content.split('\n');
if (line == -1) return err; const line = lns.findIndex((ln) => ln.includes(importName));
const column = lns[line]?.indexOf(importName); if (line == -1) return err;
if (!(err as any).id) { const column = lns[line]?.indexOf(importName);
(err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`; if (!(err as any).id) {
} (err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
}
} catch {}
} }
} }
return err; return err;

View file

@ -348,7 +348,7 @@ async function handleRequest(
return await writeSSRResult(result, res); return await writeSSRResult(result, res);
} }
} catch (_err) { } catch (_err) {
const err = fixViteErrorMessage(createSafeError(_err), viteServer, filePath); const err = fixViteErrorMessage(_err, viteServer, filePath);
const errorWithMetadata = collectErrorMetadata(err); const errorWithMetadata = collectErrorMetadata(err);
error(logging, null, msg.formatErrorMessage(errorWithMetadata)); error(logging, null, msg.formatErrorMessage(errorWithMetadata));
handle500Response(viteServer, origin, req, res, errorWithMetadata); handle500Response(viteServer, origin, req, res, errorWithMetadata);

View file

@ -0,0 +1,34 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Errors in JavaScript', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
/** @type {import('./test-utils').DevServer} */
let devServer;
before(async () => {
fixture = await loadFixture({
root: './fixtures/error-bad-js',
});
devServer = await fixture.startDevServer();
});
after(async() => {
await devServer.stop();
});
it('Does not crash the dev server', async () => {
let res = await fixture.fetch('/');
let html = await res.text();
expect(html).to.include('ReferenceError');
res = await fixture.fetch('/');
await res.text();
expect(html).to.include('ReferenceError');
});
});

View file

@ -0,0 +1,3 @@
import { defineConfig } from 'astro/config';
export default defineConfig({});

View file

@ -0,0 +1,15 @@
{
"name": "@test/error-bad-js",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "workspace:*"
}
}

View file

@ -0,0 +1 @@
/// <reference types="astro/client" />

View file

@ -0,0 +1,16 @@
---
import something from '../something.js';
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>

View file

@ -0,0 +1 @@
export var foo = bar;

View file

@ -1492,6 +1492,12 @@ importers:
'@astrojs/preact': link:../../../../integrations/preact '@astrojs/preact': link:../../../../integrations/preact
astro: link:../../.. astro: link:../../..
packages/astro/test/fixtures/error-bad-js:
specifiers:
astro: workspace:*
dependencies:
astro: link:../../..
packages/astro/test/fixtures/fetch: packages/astro/test/fixtures/fetch:
specifiers: specifiers:
'@astrojs/preact': workspace:* '@astrojs/preact': workspace:*