0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

fix(app): provide adapter locals to error pages (#10427)

This commit is contained in:
Arsh 2024-03-14 02:02:14 +05:30 committed by GitHub
parent c70aa6849f
commit 128c7a3639
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 48 additions and 5 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Fixes an issue where error pages did not have access to the `Astro.locals` fields provided by the adapter.

View file

@ -66,6 +66,7 @@ export interface RenderOptions {
} }
export interface RenderErrorOptions { export interface RenderErrorOptions {
locals?: App.Locals,
routeData?: RouteData; routeData?: RouteData;
response?: Response; response?: Response;
status: 404 | 500; status: 404 | 500;
@ -295,7 +296,7 @@ export class App {
routeData = this.match(request); routeData = this.match(request);
} }
if (!routeData) { if (!routeData) {
return this.#renderError(request, { status: 404 }); return this.#renderError(request, { locals, status: 404 });
} }
const pathname = this.#getPathnameFromRequest(request); const pathname = this.#getPathnameFromRequest(request);
const defaultStatus = this.#getDefaultStatusCode(routeData, pathname); const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
@ -314,7 +315,7 @@ export class App {
response = await renderContext.render(await mod.page()); response = await renderContext.render(await mod.page());
} catch (err: any) { } catch (err: any) {
this.#logger.error(null, err.stack || err.message || String(err)); this.#logger.error(null, err.stack || err.message || String(err));
return this.#renderError(request, { status: 500 }); return this.#renderError(request, { locals, status: 500 });
} }
if ( if (
@ -322,6 +323,7 @@ export class App {
response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no' response.headers.get(REROUTE_DIRECTIVE_HEADER) !== 'no'
) { ) {
return this.#renderError(request, { return this.#renderError(request, {
locals,
response, response,
status: response.status as 404 | 500, status: response.status as 404 | 500,
}); });
@ -374,7 +376,7 @@ export class App {
*/ */
async #renderError( async #renderError(
request: Request, request: Request,
{ status, response: originalResponse, skipMiddleware = false }: RenderErrorOptions { locals, status, response: originalResponse, skipMiddleware = false }: RenderErrorOptions
): Promise<Response> { ): Promise<Response> {
const errorRoutePath = `/${status}${this.#manifest.trailingSlash === 'always' ? '/' : ''}`; const errorRoutePath = `/${status}${this.#manifest.trailingSlash === 'always' ? '/' : ''}`;
const errorRouteData = matchRoute(errorRoutePath, this.#manifestData); const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
@ -397,6 +399,7 @@ export class App {
const mod = await this.#getModuleForRoute(errorRouteData); const mod = await this.#getModuleForRoute(errorRouteData);
try { try {
const renderContext = RenderContext.create({ const renderContext = RenderContext.create({
locals,
pipeline: this.#pipeline, pipeline: this.#pipeline,
middleware: skipMiddleware ? (_, next) => next() : undefined, middleware: skipMiddleware ? (_, next) => next() : undefined,
pathname: this.#getPathnameFromRequest(request), pathname: this.#getPathnameFromRequest(request),
@ -410,6 +413,7 @@ export class App {
// Middleware may be the cause of the error, so we try rendering 404/500.astro without it. // Middleware may be the cause of the error, so we try rendering 404/500.astro without it.
if (skipMiddleware === false) { if (skipMiddleware === false) {
return this.#renderError(request, { return this.#renderError(request, {
locals,
status, status,
response: originalResponse, response: originalResponse,
skipMiddleware: true, skipMiddleware: true,

View file

@ -0,0 +1,4 @@
---
const { foo } = Astro.locals;
---
<h1 id="foo">{ foo }</h1>

View file

@ -0,0 +1,4 @@
---
const { foo } = Astro.locals;
---
<h1 id="foo">{ foo }</h1>

View file

@ -0,0 +1,3 @@
---
throw new Error
---

View file

@ -7,6 +7,8 @@ import { loadFixture } from './test-utils.js';
describe('SSR Astro.locals from server', () => { describe('SSR Astro.locals from server', () => {
/** @type {import('./test-utils').Fixture} */ /** @type {import('./test-utils').Fixture} */
let fixture; let fixture;
/** @type {import('./test-utils.js').App} */
let app;
before(async () => { before(async () => {
fixture = await loadFixture({ fixture = await loadFixture({
@ -15,10 +17,10 @@ describe('SSR Astro.locals from server', () => {
adapter: testAdapter(), adapter: testAdapter(),
}); });
await fixture.build(); await fixture.build();
app = await fixture.loadTestAdapterApp();
}); });
it('Can access Astro.locals in page', async () => { it('Can access Astro.locals in page', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/foo'); const request = new Request('http://example.com/foo');
const locals = { foo: 'bar' }; const locals = { foo: 'bar' };
const response = await app.render(request, { locals }); const response = await app.render(request, { locals });
@ -29,7 +31,6 @@ describe('SSR Astro.locals from server', () => {
}); });
it('Can access Astro.locals in api context', async () => { it('Can access Astro.locals in api context', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/api'); const request = new Request('http://example.com/api');
const locals = { foo: 'bar' }; const locals = { foo: 'bar' };
const response = await app.render(request, undefined, locals); const response = await app.render(request, undefined, locals);
@ -38,4 +39,26 @@ describe('SSR Astro.locals from server', () => {
assert.equal(body.foo, 'bar'); assert.equal(body.foo, 'bar');
}); });
it('404.astro can access locals provided to app.render()', async () => {
const request = new Request('http://example.com/slkfnasf');
const locals = { foo: 'par' };
const response = await app.render(request, { locals });
assert.equal(response.status, 404);
const html = await response.text();
const $ = cheerio.load(html);
assert.equal($('#foo').text(), 'par');
});
it('500.astro can access locals provided to app.render()', async () => {
const request = new Request('http://example.com/go-to-error-page');
const locals = { foo: 'par' };
const response = await app.render(request, { locals });
assert.equal(response.status, 500);
const html = await response.text();
const $ = cheerio.load(html);
assert.equal($('#foo').text(), 'par');
});
}); });