From d1f4cbe64531c8db7f39391b7f404c076bdaae9d Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 6 May 2024 14:18:23 +0100 Subject: [PATCH 01/12] Container APIs --- proposals/0048-container-api.md | 221 ++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 proposals/0048-container-api.md diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md new file mode 100644 index 00000000..9a103bc9 --- /dev/null +++ b/proposals/0048-container-api.md @@ -0,0 +1,221 @@ +- Start Date: 2024-04-26 +- Reference Issues: https://github.com/withastro/roadmap/issues/533 +- Implementation PR: + +# Summary + +Astro components are tightly coupled to `astro` (the metaframework). This proposal introduces a possible server-side API for rendering `.astro` files in isolation, outside the full `astro` build pipeline. + +# Example + +```js +import { AstroContainer } from 'astro/container'; +import Component from './components/Component.astro'; + +const container = await AstroContainer.create(); +console.log(await container.renderToString(Component, { props, slots })) +``` + +The container can be optionally constructed with some settings that are typically reserved for Astro configuration. +```js +const container = await AstroContainer.create({ + mode: "production", + streaming: true, + astroConfig: { + site: "https://example.com", + trailingSlash: false + } +}) +``` + +The second argument to `renderToString` optionally provides render-specific data that may be exposed through the Astro global, like `props`, `slots`, `request`, and/or `params`. +```js +await astro.renderToString(Component, { props, slots, request, params }) +``` + +# Background & Motivation + +Some of our own proposals, as well as many third-party tool integrations, are blocked until we expose a proper server-side rendering API for `.astro` components. Other frameworks have tools like `preact-render-to-string` or `react-dom/server` that make rendering components on the server straight-forward. + +We've avoided doing this because... it's very hard! Part of the appeal of `.astro` components is that they have immediate access to a number of powerful APIs, context about your site, and a rich set of framework renderers. In the full `astro` build process, we are able to wire up all this context invisibly. Wiring up the context manually as a third-party is prohibitively complex. + + +# Goals + +- Provide a **low-level** API for rendering `.astro` components in isolation +- Expose a familiar, user-friendly API +- Surface enough control for full `astro` parity, but abstract away internal APIs +- Enable third-party tools to consume `.astro` files on the server +- Enable unit testing of `.astro` component output +- Possibly unblock `.mdx` compiledContent/html output? +- Support Astro framework renderers (`@astrojs/*`) if possible + +# Non-Goals + +- Provide a way to **import** `.astro` components. Users will be responsible to provide a compiled component. +- Provide a way to **import** `astro.config.(mjs|mts)` out of the box. + +# Detailed Design + +## Preface + +**This** RFC will have a smaller scoped compared to what users have envisioned. The reason why the scope shrunk is that +I want to ship a smaller feature on the surface, which will allow us, later to enhance it based on user's feedback and use cases. + +That's why the first iteration of the APIs won't provide a built-in way to compile Astro components. + +## Internal concepts + +The API took a lot of time to land because it required some internal refactors. Thanks to these refactors, we will be able +to land this API by using the **very same engine** that Astro uses under the hood. + +Eventually, we landed to a common and abstract concept called **Pipeline**. A pipeline is simply an abstract +class that is responsible to render any kind of Astro route: page, endpoint, redirect, virtual. + +Each pipeline inside the Astro codebase (dev, build, SSR and now test) is responsible to collect the important information in different way, +but eventually each route must be rendered using the same data, which are: +- **Manifest**: this is a serializable/deserializable version of our "configuration", however it also contains more information, such as renderers, client directives, styles. +- **Request**: a standard https://developer.mozilla.org/en-US/docs/Web/API/Request object. +- **RouteData**: a type that identifies a generic Astro route. It contains information such as the type of route, the associated component, etc. +- **ComponentInstance**: The instance of an Astro component. This is the **compiled** component. Internally, an Astro component undergoes various transformations via Vite, and it eventually gets consumed by our rendering engine (if it's a page). + +## Create a container + +A container is class exposed via the `astro/container` specifier. Users **shouldn't** create a container using the `new` instance, but they should use he static function `create`: + +```js +const container = await AstroContainer.create() +``` + +The reason why `create` returns a promise is because internally we will validate and parse the user configuration. As a developer, you will be able to pass the same configuration that Astro uses: + +```js +import astroConfig from "../src/astro.config.mjs"; + +const container = await AstroContainer.create({ + astroConfig +}) +``` + +The function `create` will throw an error if there's a validation error. + +> [!NOTE] +> If you use a TypeScript file for your configuration, you are responsible for loading and transforming it to JavaScript. + + +The `container` binding will expose a `renderToString` that accepts a Astro component and will return a `Response` object: + +```js +import astroConfig from "../src/astro.config.mjs"; + +const container = await AstroContainer.create({ + astroConfig +}) + +const response = await container.renderToString(AstroComponent); +``` + +## Options + +It will be possible to tweak the container APIs with options and such. The `.create` function will accept the following interface: + +```ts +type AstroContainerOptions = { + mode: "development" | "production"; + streaming: boolean; + renderers: SSRLoadedRenderer[]; + astroConfig: AstroUserConfig; + middleware: MiddlewareHandler +} +``` + +The `astroConfig` object is literally the same object exposed by the `defineConfig`, inside the `astro.config.mjs` file. This very configuration +will go under the same schema validation that Astro uses internally. This means that an invalid schema will result in an error of the `create` function. + +The `middleware` is the `onRequest` instance of the middleware, in case you're rendering a page that needs to trigger the middleware. + + +## `vite` configuration + +Astro exposes already its internal `vite` configuration, so users that already use `vite` can take compile Astro components using the same their pipeline. + +For example, if users use `vitest`, they can add Astro's `vite` configuration by importing it from `astro/config` specifier: + +```js +// vitest.config.js +import { getViteConfig } from "astro/config"; + +export default getViteConfig({ + test: {} +}) +``` + +```js +// Card.test.js +import { Card } from "../src/components/Card.astro" +import astroConfig from "../src/astro.config.mjs"; + +const container = await AstroContainer.create({ + astroConfig +}) + +const response = await container.renderToString(Card); +// assertions +``` + +## `renderToStringOptions` + +This function can accept an optional object as a second parameter: + +```js +import { onRequest } from "../src/middleware.js" + +const response = await container.renderToString(Card, { + slots: [ + (await container.renderToString(CardItem).text()), + "Footer of the card" + ], + request: new Request("https://example.com/blog/blog-slug"), + params: ["slug"], // in case your route is `pages/blog/[slug].astro` + locals: { + someFn() { + + }, + someString: "string", + someNumber: 100 + }, + status: 400 +}); +``` + +- `slots`: required in case your component is designed to render some slots inside of it. +- `request`: required in case your component/page access to some information such as `Astro.url` or `Astro.request`. +- `params` +- `locals`: initial value of the `Astro.locals`. +- `status`: useful in case you're rendering an error page. + +# Testing Strategy + +- Integration tests to cover the majority of the options. +- Example folders to show how to integrate inside an application + +# Drawbacks + +I have considered a more high-level API, however with a high-level API it gets more difficult to integrate it inside other testing frameworks. + +While a low-level API requires more maintenance and work to provide more work, the main idea is to provide a bare-metal API so users can tweak it as they see fit. + +# Alternatives + +I have considered the idea of exposing the compiler itself, with a thin layer of APIs, however users won't be able to compile the majority of formats like Markdown, MDX, etc. + +# Adoption strategy + +Considering the fact that this API doesn't involve any configuration or virtual module, the API +will be released with a `unstable_` prefix, e.g. `unstable_AstroContainer`. + +This implies that the public APIs of the class will be deemed unstable, and they can change anytime, in `patch` releases too. + +Once the API is deemed stable, the `unstable_` prefix will be removed. + +# Unresolved Questions From 8af8582b77a227d9ad294ab1b75fd9b1c4a15618 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 8 May 2024 15:49:11 +0100 Subject: [PATCH 02/12] Update proposals/0048-container-api.md Co-authored-by: Matthew Phillips --- proposals/0048-container-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 9a103bc9..23744b2c 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -4,7 +4,7 @@ # Summary -Astro components are tightly coupled to `astro` (the metaframework). This proposal introduces a possible server-side API for rendering `.astro` files in isolation, outside the full `astro` build pipeline. +Astro components are tightly coupled to `Astro` (the metaframework). This proposal introduces a possible server-side API for rendering `.astro` files in isolation, outside the full `astro` build pipeline. # Example From c29b1d148e59c8183512d5b55da2c1a55d75b560 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 8 May 2024 15:53:34 +0100 Subject: [PATCH 03/12] fix typo --- proposals/0048-container-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 23744b2c..41a80c91 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -103,7 +103,7 @@ The function `create` will throw an error if there's a validation error. > If you use a TypeScript file for your configuration, you are responsible for loading and transforming it to JavaScript. -The `container` binding will expose a `renderToString` that accepts a Astro component and will return a `Response` object: +The `container` binding will expose a `renderToString` that accepts a Astro component and will return a `string`: ```js import astroConfig from "../src/astro.config.mjs"; @@ -112,7 +112,7 @@ const container = await AstroContainer.create({ astroConfig }) -const response = await container.renderToString(AstroComponent); +const content = await container.renderToString(AstroComponent); ``` ## Options From 78597592b89eacfc1a5cb87fca4948cff0480f06 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 10:34:13 +0100 Subject: [PATCH 04/12] udpate the RFC --- proposals/0048-container-api.md | 71 +++++++++++---------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 41a80c91..41e0ca73 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -79,7 +79,13 @@ but eventually each route must be rendered using the same data, which are: - **RouteData**: a type that identifies a generic Astro route. It contains information such as the type of route, the associated component, etc. - **ComponentInstance**: The instance of an Astro component. This is the **compiled** component. Internally, an Astro component undergoes various transformations via Vite, and it eventually gets consumed by our rendering engine (if it's a page). -## Create a container +## Public APIs + +- `AstroContainer::create`: creates a new instance of the container. +- `AstroContainer::renderToString`: renders a component and return a string. +- `AstroContainer::renderToResponse`: renders a component and returns the `Response` emitted by the rendering phase. + +### `create` function A container is class exposed via the `astro/container` specifier. Users **shouldn't** create a container using the `new` instance, but they should use he static function `create`: @@ -115,55 +121,23 @@ const container = await AstroContainer.create({ const content = await container.renderToString(AstroComponent); ``` -## Options +#### Options It will be possible to tweak the container APIs with options and such. The `.create` function will accept the following interface: ```ts type AstroContainerOptions = { - mode: "development" | "production"; streaming: boolean; - renderers: SSRLoadedRenderer[]; + renderers: AstroRenderer[]; astroConfig: AstroUserConfig; - middleware: MiddlewareHandler } ``` The `astroConfig` object is literally the same object exposed by the `defineConfig`, inside the `astro.config.mjs` file. This very configuration will go under the same schema validation that Astro uses internally. This means that an invalid schema will result in an error of the `create` function. -The `middleware` is the `onRequest` instance of the middleware, in case you're rendering a page that needs to trigger the middleware. - - -## `vite` configuration - -Astro exposes already its internal `vite` configuration, so users that already use `vite` can take compile Astro components using the same their pipeline. - -For example, if users use `vitest`, they can add Astro's `vite` configuration by importing it from `astro/config` specifier: - -```js -// vitest.config.js -import { getViteConfig } from "astro/config"; - -export default getViteConfig({ - test: {} -}) -``` - -```js -// Card.test.js -import { Card } from "../src/components/Card.astro" -import astroConfig from "../src/astro.config.mjs"; -const container = await AstroContainer.create({ - astroConfig -}) - -const response = await container.renderToString(Card); -// assertions -``` - -## `renderToStringOptions` +### `renderToString` and `renderToResponse` options This function can accept an optional object as a second parameter: @@ -171,12 +145,15 @@ This function can accept an optional object as a second parameter: import { onRequest } from "../src/middleware.js" const response = await container.renderToString(Card, { - slots: [ - (await container.renderToString(CardItem).text()), - "Footer of the card" - ], - request: new Request("https://example.com/blog/blog-slug"), - params: ["slug"], // in case your route is `pages/blog/[slug].astro` + slots: { + default: await container.renderToString(CardItem).text() + }, + request: new Request("https://example.com/blog/blog-slug", { + headers: {} + }), + params: { + "slug": "blog-slug" + }, // in case your route is `pages/blog/[slug].astro` locals: { someFn() { @@ -188,9 +165,9 @@ const response = await container.renderToString(Card, { }); ``` -- `slots`: required in case your component is designed to render some slots inside of it. +- `slots`: required in case your component is designed to render some slots inside of it. It supposed named slots too. - `request`: required in case your component/page access to some information such as `Astro.url` or `Astro.request`. -- `params` +- `params`: the `Astro.params` to provide to the components - `locals`: initial value of the `Astro.locals`. - `status`: useful in case you're rendering an error page. @@ -212,10 +189,8 @@ I have considered the idea of exposing the compiler itself, with a thin layer of # Adoption strategy Considering the fact that this API doesn't involve any configuration or virtual module, the API -will be released with a `unstable_` prefix, e.g. `unstable_AstroContainer`. +will be released with a `experimental_` prefix, e.g. `experimental_AstroContainer`. This implies that the public APIs of the class will be deemed unstable, and they can change anytime, in `patch` releases too. -Once the API is deemed stable, the `unstable_` prefix will be removed. - -# Unresolved Questions +Once the API is deemed stable, the `experimental_` prefix will be removed. From 0b114322f6785602dc49e0a8fa52987e21cd7dca Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 14:57:09 +0100 Subject: [PATCH 05/12] Update proposals/0048-container-api.md Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> --- proposals/0048-container-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 41e0ca73..38063a8e 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -69,7 +69,7 @@ That's why the first iteration of the APIs won't provide a built-in way to compi The API took a lot of time to land because it required some internal refactors. Thanks to these refactors, we will be able to land this API by using the **very same engine** that Astro uses under the hood. -Eventually, we landed to a common and abstract concept called **Pipeline**. A pipeline is simply an abstract +Eventually, we landed to a common and abstract concept called **Pipeline**. A pipeline is an abstract class that is responsible to render any kind of Astro route: page, endpoint, redirect, virtual. Each pipeline inside the Astro codebase (dev, build, SSR and now test) is responsible to collect the important information in different way, From cac3f4246e15ec1fa89aef6f7c55c7238fa3f9bc Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 14:57:18 +0100 Subject: [PATCH 06/12] Update proposals/0048-container-api.md Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> --- proposals/0048-container-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 38063a8e..60dfe359 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -87,7 +87,7 @@ but eventually each route must be rendered using the same data, which are: ### `create` function -A container is class exposed via the `astro/container` specifier. Users **shouldn't** create a container using the `new` instance, but they should use he static function `create`: +A container is class exposed via the `astro/container` specifier. Users **shouldn't** create a container using the `new` instance, but they should use the static function `create`: ```js const container = await AstroContainer.create() From e5b02d252cdbeb8ae684d01b9b7f24287cec6a09 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 14:58:17 +0100 Subject: [PATCH 07/12] address feedback --- proposals/0048-container-api.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 60dfe359..1901885f 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -19,7 +19,6 @@ console.log(await container.renderToString(Component, { props, slots })) The container can be optionally constructed with some settings that are typically reserved for Astro configuration. ```js const container = await AstroContainer.create({ - mode: "production", streaming: true, astroConfig: { site: "https://example.com", @@ -43,16 +42,14 @@ We've avoided doing this because... it's very hard! Part of the appeal of `.astr # Goals - Provide a **low-level** API for rendering `.astro` components in isolation -- Expose a familiar, user-friendly API - Surface enough control for full `astro` parity, but abstract away internal APIs - Enable third-party tools to consume `.astro` files on the server - Enable unit testing of `.astro` component output -- Possibly unblock `.mdx` compiledContent/html output? -- Support Astro framework renderers (`@astrojs/*`) if possible +- Support Astro framework renderers (`@astrojs/*`) # Non-Goals -- Provide a way to **import** `.astro` components. Users will be responsible to provide a compiled component. +- Provide a way to **import** `.astro` components in a non-vite environment. Users will be responsible to provide a compiled component. - Provide a way to **import** `astro.config.(mjs|mts)` out of the box. # Detailed Design @@ -60,7 +57,8 @@ We've avoided doing this because... it's very hard! Part of the appeal of `.astr ## Preface **This** RFC will have a smaller scoped compared to what users have envisioned. The reason why the scope shrunk is that -I want to ship a smaller feature on the surface, which will allow us, later to enhance it based on user's feedback and use cases. + +We want to ship a smaller feature on the surface, which will allow us, later to enhance it based on user's feedback and use cases. That's why the first iteration of the APIs won't provide a built-in way to compile Astro components. @@ -146,7 +144,7 @@ import { onRequest } from "../src/middleware.js" const response = await container.renderToString(Card, { slots: { - default: await container.renderToString(CardItem).text() + default: await container.renderToString(CardItem) }, request: new Request("https://example.com/blog/blog-slug", { headers: {} From 013ca1dc4a95848c2f9d017b4121ba22824c8a47 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 14:59:06 +0100 Subject: [PATCH 08/12] Apply suggestions from code review Co-authored-by: Matthew Phillips Co-authored-by: Bjorn Lu --- proposals/0048-container-api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 1901885f..86ea97a0 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -4,7 +4,7 @@ # Summary -Astro components are tightly coupled to `Astro` (the metaframework). This proposal introduces a possible server-side API for rendering `.astro` files in isolation, outside the full `astro` build pipeline. +Astro components are tightly coupled to `Astro` (the metaframework). This proposal introduces a server-side API for rendering `.astro` files in isolation, outside the full `astro` build pipeline. # Example @@ -80,8 +80,8 @@ but eventually each route must be rendered using the same data, which are: ## Public APIs - `AstroContainer::create`: creates a new instance of the container. -- `AstroContainer::renderToString`: renders a component and return a string. -- `AstroContainer::renderToResponse`: renders a component and returns the `Response` emitted by the rendering phase. +- `AstroContainer.renderToString`: renders a component and return a string. +- `AstroContainer.renderToResponse`: renders a component and returns the `Response` emitted by the rendering phase. ### `create` function From 390afac394356422da369df65f03132680e8947b Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 15 May 2024 16:09:42 +0100 Subject: [PATCH 09/12] remove status --- proposals/0048-container-api.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 86ea97a0..7b83d387 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -159,7 +159,6 @@ const response = await container.renderToString(Card, { someString: "string", someNumber: 100 }, - status: 400 }); ``` @@ -167,7 +166,7 @@ const response = await container.renderToString(Card, { - `request`: required in case your component/page access to some information such as `Astro.url` or `Astro.request`. - `params`: the `Astro.params` to provide to the components - `locals`: initial value of the `Astro.locals`. -- `status`: useful in case you're rendering an error page. +- `routeType`: useful to test endpoints. # Testing Strategy From 155bfce9195ae6f9e52ed561146df0464791207a Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 27 May 2024 07:23:58 -0400 Subject: [PATCH 10/12] add props to the RFC --- proposals/0048-container-api.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 7b83d387..4cc2ed2f 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -159,12 +159,16 @@ const response = await container.renderToString(Card, { someString: "string", someNumber: 100 }, + props: { + "someState": true + } }); ``` - `slots`: required in case your component is designed to render some slots inside of it. It supposed named slots too. - `request`: required in case your component/page access to some information such as `Astro.url` or `Astro.request`. - `params`: the `Astro.params` to provide to the components +- `props`: the `Astro.props` to provide to the components - `locals`: initial value of the `Astro.locals`. - `routeType`: useful to test endpoints. From a5e85d030a237ed6d22a09165bd1c0450ec8d6fb Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 25 Jun 2024 10:57:53 +0100 Subject: [PATCH 11/12] add chapter for renderers --- proposals/0048-container-api.md | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 4cc2ed2f..19012e73 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -82,6 +82,7 @@ but eventually each route must be rendered using the same data, which are: - `AstroContainer::create`: creates a new instance of the container. - `AstroContainer.renderToString`: renders a component and return a string. - `AstroContainer.renderToResponse`: renders a component and returns the `Response` emitted by the rendering phase. +- `AstroContainer.addServerRenderer`: to programmatically store a renderer inside the container. ### `create` function @@ -126,7 +127,7 @@ It will be possible to tweak the container APIs with options and such. The `.cre ```ts type AstroContainerOptions = { streaming: boolean; - renderers: AstroRenderer[]; + renderers: AddServerRenderer[]; astroConfig: AstroUserConfig; } ``` @@ -134,6 +135,39 @@ type AstroContainerOptions = { The `astroConfig` object is literally the same object exposed by the `defineConfig`, inside the `astro.config.mjs` file. This very configuration will go under the same schema validation that Astro uses internally. This means that an invalid schema will result in an error of the `create` function. +#### Add a renderer + +Adding a renderer can be done manually or automatically. + +The automatic way is meant for static applications, or cases where the container isn't called at runtime. The renderers maintained by the Astro org will expose +a method called `getContainerRenderer` that will return the correct information that will tell the container **how to import the renderer**. The importing of the renderer is done via a function called `loadRenderers`, exported by a new virtual module called `astro:container`: + +```js +import { getContainerRenderer as reactRenderer } from "@astrojs/react"; +import { getContainerRenderer as vueRenderer } from "@astrojs/vue"; +import { laodRenderers } from "astro:container"; +import { AstroContainer } from "astro/container"; + +const renderers = loadRenderers([ + reactRenderer(), + vueRenderer() +]); +const container = await AstroContainer.create({ renderers }) +``` + +The manual way is meant for on-demand applications, or cases where the container is called at runtime or inside other "shells" (PHP, Ruby, Java, etc.). +The developer is in charge of **importing** the server renderer and store it inside the container via `AstroContainer.addServerRenderer`: + +The renderers maintained by the Astro org will ship proper types for importing server renderers. + +```js +import reactRenderer from "@astrojs/react/server.js"; +import vueRenderer from "@astrojs/vue/server.js"; + +const container = await AstroContainer.create(); +container.addServerRenderer({renderer: reactRenderer}); +container.addServerRenderer({renderer: vueRenderer}); +``` ### `renderToString` and `renderToResponse` options From 36d89479ec02d229d99eb1abb33331fd16cba626 Mon Sep 17 00:00:00 2001 From: "Sergio C. Orozco Torres" Date: Fri, 24 Jan 2025 11:12:02 -0500 Subject: [PATCH 12/12] fix typo on import loadRenderers instead of laodRenderers (#1109) --- proposals/0048-container-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0048-container-api.md b/proposals/0048-container-api.md index 19012e73..1650b2be 100644 --- a/proposals/0048-container-api.md +++ b/proposals/0048-container-api.md @@ -145,7 +145,7 @@ a method called `getContainerRenderer` that will return the correct information ```js import { getContainerRenderer as reactRenderer } from "@astrojs/react"; import { getContainerRenderer as vueRenderer } from "@astrojs/vue"; -import { laodRenderers } from "astro:container"; +import { loadRenderers } from "astro:container"; import { AstroContainer } from "astro/container"; const renderers = loadRenderers([