0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

feat: add new method insertPageRoute to container (#13358)

* feat: add new method `insertPageRoute` to container

* chore: add back the correct use

* Update .changeset/ninety-cups-decide.md

Co-authored-by: Matt Kane <m@mk.gg>

---------

Co-authored-by: Matt Kane <m@mk.gg>

Co-authored-by: ascorbic <213306+ascorbic@users.noreply.github.com>
Co-authored-by: florian-lefebvre <69633530+florian-lefebvre@users.noreply.github.com>
This commit is contained in:
Emanuele Stoppa 2025-03-18 09:32:24 +00:00 committed by GitHub
parent defad33140
commit 8c21663c4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 98 additions and 3 deletions

View file

@ -0,0 +1,22 @@
---
'astro': patch
---
Adds a new function called `insertPageRoute` to the Astro Container API.
The new function is useful when testing routes that, for some business logic, use `Astro.rewrite`.
For example, if you have a route `/blog/post` and for some business decision there's a rewrite to `/generic-error`, the container API implementation will look like this:
```js
import Post from "../src/pages/Post.astro";
import GenericError from "../src/pages/GenericError.astro";
import { experimental_AstroContainer as AstroContainer } from "astro/container";
const container = await AstroContainer.create();
container.insertPageRoute("/generic-error", GenericError);
const result = await container.renderToString(Post);
console.log(result) // this should print the response from GenericError.astro
```
This new method only works for page routes, which means that endpoints aren't supported.

View file

@ -18,7 +18,7 @@ export function PostComment({
<form
method="POST"
data-testid="client"
action={actions.blog.comment}
action={actions.blog.comment.toString()}
onSubmit={async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;

View file

@ -539,6 +539,30 @@ export class experimental_AstroContainer {
return renderContext.render(componentInstance, slots);
}
/**
* It stores an Astro **page** route. The first argument, `route`, gets associated to the `component`.
*
* This function can be useful when you want to render a route via `AstroContainer.renderToString`, where that
* route eventually renders another route via `Astro.rewrite`.
*
* @param {string} route - The URL that will render the component.
* @param {AstroComponentFactory} component - The component factory to be used for rendering the route.
* @param {Record<string, string | undefined>} params - An object containing key-value pairs of route parameters.
*/
public insertPageRoute(route: string,component: AstroComponentFactory, params?: Record<string, string | undefined>) {
const url = new URL(route, 'https://example.com/');
const routeData: RouteData = this.#createRoute(url, params ?? {}, 'page');
this.#pipeline.manifest.routes.push({
routeData,
file: '',
links: [],
styles: [],
scripts: [],
});
const componentInstance = this.#wrapComponent(component, params);
this.#pipeline.insertRoute(routeData, componentInstance);
}
#createRoute(url: URL, params: Record<string, string | undefined>, type: RouteType): RouteData {
const segments = removeLeadingForwardSlash(url.pathname)
.split(posix.sep)

View file

@ -89,6 +89,11 @@ export class ContainerPipeline extends Pipeline {
}
// At the moment it's not used by the container via any public API
// @ts-expect-error It needs to be implemented.
async getComponentByRoute(_routeData: RouteData): Promise<ComponentInstance> {}
async getComponentByRoute(routeData: RouteData): Promise<ComponentInstance> {
const page = this.#componentsInterner.get(routeData);
if (page) {
return page.page();
}
throw new Error("Couldn't find component for route " + routeData.pathname);
}
}

View file

@ -11,6 +11,7 @@ import {
renderHead,
renderSlot,
renderTemplate,
createAstro
} from '../dist/runtime/server/index.js';
import testAdapter from './test-adapter.js';
import { loadFixture } from './test-utils.js';
@ -59,6 +60,43 @@ describe('Container', () => {
assert.match(response, /hello world/);
});
it('Renders a div with hello world text', async () => {
const $$Astro = createAstro();
const Page = createComponent((result, props, slots) => {
const Astro = result.createAstro($$Astro, props, slots);
return Astro.rewrite('/example')
});
const Example = createComponent((result) => {
return render`${renderComponent(
result,
'BaseLayout',
BaseLayout,
{},
{
default: () => render`${maybeRenderHead(result)}<div>hello world</div>`,
head: () => render`
${renderComponent(
result,
'Fragment',
Fragment,
{ slot: 'head' },
{
default: () => render`<meta charset="utf-8">`,
},
)}
`,
},
)}`;
});
const container = await experimental_AstroContainer.create();
container.insertPageRoute('/example', Example);
const response = await container.renderToString(Page);
assert.match(response, /hello world/);
});
it('Renders a slot', async () => {
const Page = createComponent(
(result, _props, slots) => {

6
pnpm-lock.yaml generated
View file

@ -2514,6 +2514,12 @@ importers:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/container:
dependencies:
astro:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/container-custom-renderers:
dependencies:
'@astrojs/react':