diff --git a/.changeset/chatty-cows-attack.md b/.changeset/chatty-cows-attack.md new file mode 100644 index 000000000..7fe9fb8c2 --- /dev/null +++ b/.changeset/chatty-cows-attack.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Allow components to return a Response diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 7f314f721..b9970d59d 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -412,7 +412,8 @@ async function replaceHeadInjection(result: SSRResult, html: string): Promise { const Component = await componentFactory(result, props, children); if (!isAstroComponent(Component)) { - throw new Error('Cannot return a Response from a nested component.'); + const response: Response = Component; + throw response; } let template = await renderAstroComponent(Component); @@ -425,20 +426,31 @@ export async function renderPage( props: any, children: any ): Promise<{ type: 'html'; html: string } | { type: 'response'; response: Response }> { - const response = await componentFactory(result, props, children); + try { + const response = await componentFactory(result, props, children); - if (isAstroComponent(response)) { - let template = await renderAstroComponent(response); - const html = await replaceHeadInjection(result, template); - return { - type: 'html', - html, - }; - } else { - return { - type: 'response', - response, - }; + if (isAstroComponent(response)) { + let template = await renderAstroComponent(response); + const html = await replaceHeadInjection(result, template); + return { + type: 'html', + html, + }; + } else { + return { + type: 'response', + response, + }; + } + } catch(err) { + if(err instanceof Response) { + return { + type: 'response', + response: err + }; + } else { + throw err; + } } } diff --git a/packages/astro/test/astro-response.test.js b/packages/astro/test/astro-response.test.js new file mode 100644 index 000000000..719d14d30 --- /dev/null +++ b/packages/astro/test/astro-response.test.js @@ -0,0 +1,32 @@ +import { expect } from 'chai'; +import { load as cheerioLoad } from 'cheerio'; +import { loadFixture } from './test-utils.js'; + +// Asset bundling +describe('Returning responses', () => { + let fixture; + /** @type {import('./test-utils').DevServer} */ + let devServer; + + before(async () => { + fixture = await loadFixture({ + projectRoot: './fixtures/astro-response/', + }); + + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('Works from a page', async () => { + let response = await fixture.fetch('/not-found'); + expect(response.status).to.equal(404); + }); + + it('Works from a component', async () => { + let response = await fixture.fetch('/not-found-component'); + expect(response.status).to.equal(404); + }); +}); diff --git a/packages/astro/test/fixtures/astro-response/src/components/not-found.astro b/packages/astro/test/fixtures/astro-response/src/components/not-found.astro new file mode 100644 index 000000000..dd339e72b --- /dev/null +++ b/packages/astro/test/fixtures/astro-response/src/components/not-found.astro @@ -0,0 +1,6 @@ +--- +return new Response(null, { + status: 404, + statusText: `Not found` +}); +--- diff --git a/packages/astro/test/fixtures/astro-response/src/pages/not-found-component.astro b/packages/astro/test/fixtures/astro-response/src/pages/not-found-component.astro new file mode 100644 index 000000000..e1077e9c9 --- /dev/null +++ b/packages/astro/test/fixtures/astro-response/src/pages/not-found-component.astro @@ -0,0 +1,4 @@ +--- +import NotFound from '../components/not-found.astro'; +--- + diff --git a/packages/astro/test/fixtures/astro-response/src/pages/not-found.astro b/packages/astro/test/fixtures/astro-response/src/pages/not-found.astro new file mode 100644 index 000000000..dd339e72b --- /dev/null +++ b/packages/astro/test/fixtures/astro-response/src/pages/not-found.astro @@ -0,0 +1,6 @@ +--- +return new Response(null, { + status: 404, + statusText: `Not found` +}); +---