diff --git a/.changeset/orange-cycles-serve.md b/.changeset/orange-cycles-serve.md
new file mode 100644
index 0000000000..1235931167
--- /dev/null
+++ b/.changeset/orange-cycles-serve.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Makes the dev server more resilient to crashes
diff --git a/packages/astro/src/core/errors.ts b/packages/astro/src/core/errors.ts
index d96fbe8854..9b2c7c174e 100644
--- a/packages/astro/src/core/errors.ts
+++ b/packages/astro/src/core/errors.ts
@@ -48,7 +48,10 @@ export function cleanErrorStack(stack: string) {
export function fixViteErrorMessage(_err: unknown, server?: ViteDevServer, filePath?: URL) {
const err = createSafeError(_err);
// 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,
// 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.') {
@@ -57,14 +60,16 @@ export function fixViteErrorMessage(_err: unknown, server?: ViteDevServer, fileP
if (filePath && /failed to load module for ssr:/.test(err.message)) {
const importName = err.message.split('for ssr:').at(1)?.trim();
if (importName) {
- const content = fs.readFileSync(fileURLToPath(filePath)).toString();
- const lns = content.split('\n');
- const line = lns.findIndex((ln) => ln.includes(importName));
- if (line == -1) return err;
- const column = lns[line]?.indexOf(importName);
- if (!(err as any).id) {
- (err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
- }
+ try {
+ const content = fs.readFileSync(fileURLToPath(filePath)).toString();
+ const lns = content.split('\n');
+ const line = lns.findIndex((ln) => ln.includes(importName));
+ if (line == -1) return err;
+ const column = lns[line]?.indexOf(importName);
+ if (!(err as any).id) {
+ (err as any).id = `${fileURLToPath(filePath)}:${line + 1}:${column + 1}`;
+ }
+ } catch {}
}
}
return err;
diff --git a/packages/astro/src/vite-plugin-astro-server/index.ts b/packages/astro/src/vite-plugin-astro-server/index.ts
index 5a550ec18f..05f2b893a1 100644
--- a/packages/astro/src/vite-plugin-astro-server/index.ts
+++ b/packages/astro/src/vite-plugin-astro-server/index.ts
@@ -348,7 +348,7 @@ async function handleRequest(
return await writeSSRResult(result, res);
}
} catch (_err) {
- const err = fixViteErrorMessage(createSafeError(_err), viteServer, filePath);
+ const err = fixViteErrorMessage(_err, viteServer, filePath);
const errorWithMetadata = collectErrorMetadata(err);
error(logging, null, msg.formatErrorMessage(errorWithMetadata));
handle500Response(viteServer, origin, req, res, errorWithMetadata);
diff --git a/packages/astro/test/error-bad-js.test.js b/packages/astro/test/error-bad-js.test.js
new file mode 100644
index 0000000000..0f4c12e6da
--- /dev/null
+++ b/packages/astro/test/error-bad-js.test.js
@@ -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');
+ });
+});
diff --git a/packages/astro/test/fixtures/error-bad-js/astro.config.mjs b/packages/astro/test/fixtures/error-bad-js/astro.config.mjs
new file mode 100644
index 0000000000..86dbfb9248
--- /dev/null
+++ b/packages/astro/test/fixtures/error-bad-js/astro.config.mjs
@@ -0,0 +1,3 @@
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({});
diff --git a/packages/astro/test/fixtures/error-bad-js/package.json b/packages/astro/test/fixtures/error-bad-js/package.json
new file mode 100644
index 0000000000..6b7af585e3
--- /dev/null
+++ b/packages/astro/test/fixtures/error-bad-js/package.json
@@ -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:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/error-bad-js/src/env.d.ts b/packages/astro/test/fixtures/error-bad-js/src/env.d.ts
new file mode 100644
index 0000000000..f964fe0cff
--- /dev/null
+++ b/packages/astro/test/fixtures/error-bad-js/src/env.d.ts
@@ -0,0 +1 @@
+///