mirror of
https://github.com/withastro/astro.git
synced 2025-02-17 22:44:24 -05:00
fix(container): emit components as partials (#12239)
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
a3d30a602a
commit
2b6daa5840
5 changed files with 60 additions and 2 deletions
22
.changeset/soft-rabbits-draw.md
Normal file
22
.changeset/soft-rabbits-draw.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
"astro": patch
|
||||
---
|
||||
|
||||
**BREAKING CHANGE to the experimental Container API only**
|
||||
|
||||
Changes the default page rendering behavior of Astro components in containers, and adds a new option `partial: false` to render full Astro pages as before.
|
||||
|
||||
Previously, the Container API was rendering all Astro components as if they were full Astro pages containing `<!DOCTYPE html>` by default. This was not intended, and now by default, all components will render as [page partials](https://docs.astro.build/en/basics/astro-pages/#page-partials): only the contents of the components without a page shell.
|
||||
|
||||
To render the component as a full-fledged Astro page, pass a new option called `partial: false` to `renderToString()` and `renderToResponse()`:
|
||||
|
||||
```js
|
||||
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
|
||||
import Card from "../src/components/Card.astro";
|
||||
|
||||
const container = AstroContainer.create();
|
||||
|
||||
await container.renderToString(Card); // the string will not contain `<!DOCTYPE html>`
|
||||
await container.renderToString(Card, { partial: false }); // the string will contain `<!DOCTYPE html>`
|
||||
```
|
||||
|
|
@ -89,6 +89,14 @@ export type ContainerRenderOptions = {
|
|||
* ```
|
||||
*/
|
||||
props?: Props;
|
||||
|
||||
/**
|
||||
* When `false`, it forces to render the component as it was a full-fledged page.
|
||||
*
|
||||
* By default, the container API render components as [partials](https://docs.astro.build/en/basics/astro-pages/#page-partials).
|
||||
*
|
||||
*/
|
||||
partial?: boolean;
|
||||
};
|
||||
|
||||
export type AddServerRenderer =
|
||||
|
@ -487,6 +495,7 @@ export class experimental_AstroContainer {
|
|||
request,
|
||||
pathname: url.pathname,
|
||||
locals: options?.locals ?? {},
|
||||
partial: options?.partial ?? true,
|
||||
});
|
||||
if (options.params) {
|
||||
renderContext.params = options.params;
|
||||
|
|
|
@ -56,6 +56,7 @@ export class RenderContext {
|
|||
public params = getParams(routeData, pathname),
|
||||
protected url = new URL(request.url),
|
||||
public props: Props = {},
|
||||
public partial: undefined | boolean = undefined,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
@ -76,9 +77,10 @@ export class RenderContext {
|
|||
routeData,
|
||||
status = 200,
|
||||
props,
|
||||
partial = undefined,
|
||||
}: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData'> &
|
||||
Partial<
|
||||
Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props'>
|
||||
Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props' | 'partial'>
|
||||
>): Promise<RenderContext> {
|
||||
const pipelineMiddleware = await pipeline.getMiddleware();
|
||||
return new RenderContext(
|
||||
|
@ -93,6 +95,7 @@ export class RenderContext {
|
|||
undefined,
|
||||
undefined,
|
||||
props,
|
||||
partial,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -319,7 +322,7 @@ export class RenderContext {
|
|||
const componentMetadata =
|
||||
(await pipeline.componentMetadata(routeData)) ?? manifest.componentMetadata;
|
||||
const headers = new Headers({ 'Content-Type': 'text/html' });
|
||||
const partial = Boolean(mod.partial);
|
||||
const partial = typeof this.partial === 'boolean' ? this.partial : Boolean(mod.partial);
|
||||
const response = {
|
||||
status,
|
||||
statusText: 'OK',
|
||||
|
|
|
@ -252,6 +252,16 @@ describe('Container with renderers', () => {
|
|||
const html = await response.text();
|
||||
|
||||
assert.match(html, /I am a react button/);
|
||||
assert.doesNotMatch(html, /<!DOCTYPE html>/);
|
||||
});
|
||||
|
||||
it('the endpoint should return the HTML of the React component, with DOCTYPE when rendered when partial is off', async () => {
|
||||
const request = new Request('https://example.com/react-as-page');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
|
||||
assert.match(html, /I am a react button/);
|
||||
assert.match(html, /<!DOCTYPE html>/);
|
||||
});
|
||||
|
||||
it('the endpoint should return the HTML of the Vue component', async () => {
|
||||
|
@ -260,6 +270,7 @@ describe('Container with renderers', () => {
|
|||
const html = await response.text();
|
||||
|
||||
assert.match(html, /I am a vue button/);
|
||||
assert.doesNotMatch(html, /<!DOCTYPE html>/);
|
||||
});
|
||||
|
||||
it('Should render a component with directives', async () => {
|
||||
|
@ -269,5 +280,6 @@ describe('Container with renderers', () => {
|
|||
|
||||
assert.match(html, /Button not rendered/);
|
||||
assert.match(html, /I am a react button/);
|
||||
assert.doesNotMatch(html, /<!DOCTYPE html>/);
|
||||
});
|
||||
});
|
||||
|
|
12
packages/astro/test/fixtures/container-custom-renderers/src/pages/react-as-page.ts
vendored
Normal file
12
packages/astro/test/fixtures/container-custom-renderers/src/pages/react-as-page.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type {APIRoute} from "astro";
|
||||
import { experimental_AstroContainer } from "astro/container";
|
||||
import renderer from '@astrojs/react/server.js';
|
||||
import Component from "../components/button.jsx"
|
||||
|
||||
export const GET: APIRoute = async (ctx) => {
|
||||
const container = await experimental_AstroContainer.create();
|
||||
container.addServerRenderer({ renderer });
|
||||
return await container.renderToResponse(Component, {
|
||||
partial: false
|
||||
});
|
||||
}
|
Loading…
Add table
Reference in a new issue