summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/flat-baboons-nail.md5
-rw-r--r--packages/astro/src/content/types-generator.ts11
-rw-r--r--packages/astro/test/units/content-collections/frontmatter.test.js72
-rw-r--r--packages/astro/test/units/test-utils.js6
4 files changed, 92 insertions, 2 deletions
diff --git a/.changeset/flat-baboons-nail.md b/.changeset/flat-baboons-nail.md
new file mode 100644
index 000000000..128d4234b
--- /dev/null
+++ b/.changeset/flat-baboons-nail.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Prevent frontmatter errors from crashing the dev server
diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts
index e695f9e60..ab0e254ad 100644
--- a/packages/astro/src/content/types-generator.ts
+++ b/packages/astro/src/content/types-generator.ts
@@ -226,8 +226,17 @@ export async function createContentTypesGenerator({
events.push(event);
debounceTimeout && clearTimeout(debounceTimeout);
+ const runEventsSafe = async () => {
+ try {
+ await runEvents(opts);
+ } catch {
+ // Prevent frontmatter errors from crashing the server. The errors
+ // are still reported on page reflects as desired.
+ // Errors still crash dev from *starting*.
+ }
+ };
debounceTimeout = setTimeout(
- async () => runEvents(opts),
+ runEventsSafe,
50 /* debounce to batch chokidar events */
);
}
diff --git a/packages/astro/test/units/content-collections/frontmatter.test.js b/packages/astro/test/units/content-collections/frontmatter.test.js
new file mode 100644
index 000000000..d4a9f1ece
--- /dev/null
+++ b/packages/astro/test/units/content-collections/frontmatter.test.js
@@ -0,0 +1,72 @@
+import { fileURLToPath } from 'node:url';
+import nodeFS from 'node:fs';
+import path from 'node:path';
+import slash from 'slash';
+
+import { runInContainer } from '../../../dist/core/dev/index.js';
+import { attachContentServerListeners } from '../../../dist/content/index.js';
+import { createFs, triggerFSEvent } from '../test-utils.js';
+
+const root = new URL('../../fixtures/alias/', import.meta.url);
+
+function getTypesDts() {
+ const typesdtsURL = new URL('../../../src/content/template/types.d.ts', import.meta.url);
+ const relpath = slash(path.relative(fileURLToPath(root), fileURLToPath(typesdtsURL)));
+ return {
+ [relpath]: nodeFS.readFileSync(typesdtsURL, 'utf-8')
+ };
+}
+
+describe('frontmatter', () => {
+ it('errors in content/ does not crash server', async () => {
+ const fs = createFs(
+ {
+ ...getTypesDts(),
+ '/src/content/posts/blog.md': `
+ ---
+ title: One
+ ---
+ `,
+ '/src/content/config.ts': `
+ import { defineCollection, z } from 'astro:content';
+
+ const posts = defineCollection({
+ schema: z.string()
+ });
+
+ export const collections = {
+ posts
+ };
+ `,
+ '/src/pages/index.astro': `
+ ---
+ ---
+ <html>
+ <head><title>Test</title></head>
+ <body class="one">
+ <h1>Test</h1>
+ </body>
+ </html>
+ `,
+ },
+ root
+ );
+
+ await runInContainer({ fs, root }, async (container) => {
+ await attachContentServerListeners(container);
+
+ fs.writeFileFromRootSync(
+ '/src/content/posts/blog.md',
+ `
+ ---
+ title: One
+ title: two
+ ---
+ `
+ );
+ triggerFSEvent(container, fs, '/src/content/posts/blog.md', 'change');
+ await new Promise(resolve => setTimeout(resolve, 100));
+ // Note, if we got here, it didn't crash
+ });
+ })
+});
diff --git a/packages/astro/test/units/test-utils.js b/packages/astro/test/units/test-utils.js
index f142fceaf..68a86d475 100644
--- a/packages/astro/test/units/test-utils.js
+++ b/packages/astro/test/units/test-utils.js
@@ -3,7 +3,7 @@ import { Volume } from 'memfs';
import httpMocks from 'node-mocks-http';
import realFS from 'node:fs';
import npath from 'path';
-import { fileURLToPath } from 'url';
+import { fileURLToPath, pathToFileURL } from 'url';
import { unixify } from './correct-path.js';
class VirtualVolume extends Volume {
@@ -26,6 +26,10 @@ class VirtualVolume extends Volume {
return npath.posix.join(this.#root, pth);
}
+ readFile(p, ...args) {
+ return super.readFile(this.#forcePath(p), ...args);
+ }
+
existsSync(p) {
return super.existsSync(this.#forcePath(p));
}