Skip to content

Commit

Permalink
fix(container): emit components as partials (#12239)
Browse files Browse the repository at this point in the history
Co-authored-by: Sarah Rainsberger <[email protected]>
  • Loading branch information
ematipico and sarah11918 authored Oct 16, 2024
1 parent a3d30a6 commit 2b6daa5
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 2 deletions.
22 changes: 22 additions & 0 deletions .changeset/soft-rabbits-draw.md
Original file line number Diff line number Diff line change
@@ -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>`
```

9 changes: 9 additions & 0 deletions packages/astro/src/container/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {}

/**
Expand All @@ -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(
Expand All @@ -93,6 +95,7 @@ export class RenderContext {
undefined,
undefined,
props,
partial,
);
}

Expand Down Expand Up @@ -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',
Expand Down
12 changes: 12 additions & 0 deletions packages/astro/test/container.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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>/);
});
});
Original file line number Diff line number Diff line change
@@ -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
});
}

0 comments on commit 2b6daa5

Please sign in to comment.