Skip to content

Commit

Permalink
feat: refine container APIs for renderers (#11251)
Browse files Browse the repository at this point in the history
  • Loading branch information
ematipico authored Jun 14, 2024
1 parent 1b42229 commit fd9da98
Show file tree
Hide file tree
Showing 28 changed files with 160 additions and 31 deletions.
22 changes: 20 additions & 2 deletions .changeset/dull-carpets-breathe.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
---
'astro': patch
'@astrojs/preact': minor
'@astrojs/svelte': minor
'@astrojs/react': minor
'@astrojs/solid-js': minor
'@astrojs/lit': minor
'@astrojs/vue': minor
---

Adds a new function called `addServerRenderer` to the Container API. Use this function to manually store renderers inside the instance of your container.
Expand All @@ -14,10 +20,22 @@ import vueRenderer from '@astrojs/vue/server.js';
import ReactComponent from "../components/button.jsx"
import VueComponent from "../components/button.vue"

// MDX runtime is contained inside the Astro
import mdxRenderer from "@astrojs/jsx/serverr.js"

This comment has been minimized.

Copy link
@syhily

syhily Jun 14, 2024

Contributor

@ematipico Hi, I think this is a typo. Right?

This comment has been minimized.

Copy link
@ematipico

ematipico Jun 14, 2024

Author Member

Good catch


// In case you need to import a custom renderer
import customRenderer from "../renderers/custoRender.js";

export const GET: APIRoute = async (ctx) => {
const container = await experimental_AstroContainer.create();
container.addServerRenderer("@astrojs/react", reactRenderer);
container.addServerRenderer("@astrojs/vue", vueRenderer);
container.addServerRenderer({ renderer: reactRenderer });
container.addServerRenderer({ renderer: vueRenderer });
container.addServerRenderer({ renderer: customRenderer });
// You can pass a custom name too
container.addServerRenderer({
name: "customRenderer",
renderer: customRenderer
})
const vueComponent = await container.renderToString(VueComponent)
return await container.renderToResponse(Component);
}
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2977,7 +2977,12 @@ export interface AstroRenderer {
jsxTransformOptions?: JSXTransformFn;
}

export type SSRLoadedRendererValue = {
export interface NamedSSRLoadedRendererValue extends SSRLoadedRendererValue {
name: string;
}

export interface SSRLoadedRendererValue {
name?: string;
check: AsyncRendererComponentFn<boolean>;
renderToStaticMarkup: AsyncRendererComponentFn<{
html: string;
Expand Down
45 changes: 35 additions & 10 deletions packages/astro/src/container/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ComponentInstance,
ContainerImportRendererFn,
MiddlewareHandler,
NamedSSRLoadedRendererValue,
Props,
RouteData,
RouteType,
Expand Down Expand Up @@ -84,6 +85,16 @@ export type ContainerRenderOptions = {
props?: Props;
};

export type AddServerRenderer =
| {
renderer: NamedSSRLoadedRendererValue;
name: never;
}
| {
renderer: SSRLoadedRendererValue;
name: string;
};

function createManifest(
manifest?: AstroContainerManifest,
renderers?: SSRLoadedRenderer[],
Expand Down Expand Up @@ -279,28 +290,38 @@ export class experimental_AstroContainer {
* ```js
* import reactRenderer from "@astrojs/react/server.js";
* import vueRenderer from "@astrojs/vue/server.js";
* import customRenderer from "../renderer/customRenderer.js";
* import { experimental_AstroContainer as AstroContainer } from "astro/container"
*
* const container = await AstroContainer.create();
* container.addServerRenderer("@astrojs/react", reactRenderer);
* container.addServerRenderer("@astrojs/vue", vueRenderer);
* container.addServerRenderer(reactRenderer);
* container.addServerRenderer(vueRenderer);
* container.addServerRenderer("customRenderer", customRenderer);
* ```
*
* @param name The name of the renderer. The name **isn't** arbitrary, and it should match the name of the package.
* @param renderer The server renderer exported by integration.
* @param options {object}
* @param options.name The name of the renderer. The name **isn't** arbitrary, and it should match the name of the package.
* @param options.renderer The server renderer exported by integration.
*/
public addServerRenderer(name: string, renderer: SSRLoadedRendererValue) {
public addServerRenderer(options: AddServerRenderer): void {
const { renderer, name } = options;
if (!renderer.check || !renderer.renderToStaticMarkup) {
throw new Error(
"The renderer you passed isn't valid. A renderer is usually an object that exposes the `check` and `renderToStaticMarkup` functions.\n" +
"Usually, the renderer is exported by a /server.js entrypoint e.g. `import renderer from '@astrojs/react/server.js'`"
);
}

this.#pipeline.manifest.renderers.push({
name,
ssr: renderer,
});
if (isNamedRenderer(renderer)) {
this.#pipeline.manifest.renderers.push({
name: renderer.name,
ssr: renderer,
});
} else {
this.#pipeline.manifest.renderers.push({
name,
ssr: renderer,
});
}
}

// NOTE: we keep this private via TS instead via `#` so it's still available on the surface, so we can play with it.
Expand Down Expand Up @@ -473,3 +494,7 @@ export class experimental_AstroContainer {
return { default: componentFactory };
}
}

function isNamedRenderer(renderer: any): renderer is NamedSSRLoadedRendererValue {
return !!renderer?.name;
}
6 changes: 5 additions & 1 deletion packages/astro/src/jsx/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AstroError, AstroUserError } from '../core/errors/errors.js';
import { AstroJSX, jsx } from '../jsx-runtime/index.js';
import { renderJSX } from '../runtime/server/jsx.js';
import type { NamedSSRLoadedRendererValue } from '../@types/astro.js';

const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());

Expand Down Expand Up @@ -64,7 +65,10 @@ function throwEnhancedErrorIfMdxComponent(error: Error, Component: any) {
}
}

export default {
const renderer: NamedSSRLoadedRendererValue = {
name: 'astro:jsx',
check,
renderToStaticMarkup,
};

export default renderer;
12 changes: 10 additions & 2 deletions packages/astro/test/container.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ describe('Container with renderers', () => {
let app;
before(async () => {
fixture = await loadFixture({
root: new URL('./fixtures/container-react/', import.meta.url),
root: new URL('./fixtures/container-custom-renderers/', import.meta.url),
output: 'server',
adapter: testAdapter(),
});
Expand All @@ -247,10 +247,18 @@ describe('Container with renderers', () => {
});

it('the endpoint should return the HTML of the React component', async () => {
const request = new Request('https://example.com/api');
const request = new Request('https://example.com/react');
const response = await app.render(request);
const html = await response.text();

assert.match(html, /I am a react button/);
});

it('the endpoint should return the HTML of the Vue component', async () => {
const request = new Request('https://example.com/vue');
const response = await app.render(request);
const html = await response.text();

assert.match(html, /I am a vue button/);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
integrations: [react()],
integrations: [react(), vue()],
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"type": "module",
"dependencies": {
"@astrojs/react": "workspace:*",
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"vue": "^3.4.27"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<button id="arrow-fn-component">I am a vue button</button>
</template>
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {APIRoute, SSRLoadedRenderer} from "astro";
import { experimental_AstroContainer } from "astro/container";
import server from '@astrojs/react/server.js';
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("@astrojs/react", server);
container.addServerRenderer({ renderer });
return await container.renderToResponse(Component);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type {APIRoute, SSRLoadedRenderer} from "astro";
import { experimental_AstroContainer } from "astro/container";
import renderer from '@astrojs/vue/server.js';
import Component from "../components/button.vue"

export const GET: APIRoute = async (ctx) => {
const container = await experimental_AstroContainer.create();
container.addServerRenderer({ renderer });
return await container.renderToResponse(Component);
}
6 changes: 5 additions & 1 deletion packages/integrations/lit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"homepage": "https://docs.astro.build/en/guides/integrations-guide/lit/",
"exports": {
".": "./dist/index.js",
"./server.js": "./server.js",
"./server.js": {
"default": "./server.js",
"types": "./server.d.ts"
},
"./client-shim.js": "./client-shim.js",
"./dist/client.js": "./dist/client.js",
"./hydration-support.js": "./hydration-support.js",
Expand All @@ -33,6 +36,7 @@
"client-shim.min.js",
"hydration-support.js",
"server.js",
"server.d.ts",
"server-shim.js"
],
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/lit/server.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { NamedSSRLoadedRendererValue } from 'astro';
export default NamedSSRLoadedRendererValue;
1 change: 1 addition & 0 deletions packages/integrations/lit/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ async function renderToStaticMarkup(Component, props, slots) {
}

export default {
name: '@astrojs/lit',
check,
renderToStaticMarkup,
};
7 changes: 5 additions & 2 deletions packages/integrations/preact/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AstroComponentMetadata } from 'astro';
import type { AstroComponentMetadata, NamedSSRLoadedRendererValue } from 'astro';
import { Component as BaseComponent, type VNode, h } from 'preact';
import { render } from 'preact-render-to-string';
import prepass from 'preact-ssr-prepass';
Expand Down Expand Up @@ -147,8 +147,11 @@ function filteredConsoleError(msg: string, ...rest: any[]) {
originalConsoleError(msg, ...rest);
}

export default {
const renderer: NamedSSRLoadedRendererValue = {
name: '@astrojs/preact',
check,
renderToStaticMarkup,
supportsAstroStaticSlot: true,
};

export default renderer;
12 changes: 10 additions & 2 deletions packages/integrations/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@
"./actions": "./dist/actions.js",
"./client.js": "./client.js",
"./client-v17.js": "./client-v17.js",
"./server.js": "./server.js",
"./server-v17.js": "./server-v17.js",
"./server.js": {
"default": "./server.js",
"types": "./server.d.ts"
},
"./server-v17.js": {
"default": "./server-v17.js",
"types": "./server-v17.d.ts"
},
"./package.json": "./package.json",
"./jsx-runtime": "./jsx-runtime.js"
},
Expand All @@ -36,7 +42,9 @@
"context.js",
"jsx-runtime.js",
"server.js",
"server.d.ts",
"server-v17.js",
"server-v17.d.ts",
"static-html.js",
"vnode-children.js"
],
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/react/server-v17.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function renderToStaticMarkup(Component, props, { default: children, ...slotted
}

export default {
name: '@astrojs/react',
check,
renderToStaticMarkup,
supportsAstroStaticSlot: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/react/server.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { NamedSSRLoadedRendererValue } from 'astro';

This comment has been minimized.

Copy link
@kirainmoe

kirainmoe Jul 5, 2024

Hi, is importing this as type intended? I got the error while import reactRender from @astrojs/react/server:

'reactRender' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
export default NamedSSRLoadedRendererValue;
1 change: 1 addition & 0 deletions packages/integrations/react/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ function isFormRequest(contentType) {
}

export default {
name: '@astrojs/react',
check,
renderToStaticMarkup,
supportsAstroStaticSlot: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/react/server17.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { NamedSSRLoadedRendererValue } from 'astro';
export default NamedSSRLoadedRendererValue;
6 changes: 5 additions & 1 deletion packages/integrations/solid/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'solid-js/web';
import { getContext, incrementId } from './context.js';
import type { RendererContext } from './types.js';
import type { NamedSSRLoadedRendererValue } from 'astro';

const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());

Expand Down Expand Up @@ -123,9 +124,12 @@ async function renderToStaticMarkup(
};
}

export default {
const renderer: NamedSSRLoadedRendererValue = {
name: '@astrojs/solid',
check,
renderToStaticMarkup,
supportsAstroStaticSlot: true,
renderHydrationScript: () => generateHydrationScript(),
};

export default renderer;
14 changes: 11 additions & 3 deletions packages/integrations/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,24 @@
"./*": "./*",
"./client.js": "./client.js",
"./client-v5.js": "./client-v5.js",
"./server.js": "./server.js",
"./server-v5.js": "./server-v5.js",
"./server.js": {
"default": "./server.js",
"types": "./server.d.ts"
},
"./server-v5.js": {
"default": "./server-v5.js",
"types": "./server-v5.d.ts"
},
"./package.json": "./package.json"
},
"files": [
"dist",
"client.js",
"client-v5.js",
"server.js",
"server-v5.js"
"server.d.ts",
"server-v5.js",
"server-v5.d.ts"
],
"scripts": {
"build": "astro-scripts build \"src/index.ts\" && astro-scripts build \"src/editor.cts\" --force-cjs --no-clean-dist && tsc",
Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/svelte/server-v5.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { NamedSSRLoadedRendererValue } from 'astro';
export default NamedSSRLoadedRendererValue;
2 changes: 2 additions & 0 deletions packages/integrations/svelte/server.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { NamedSSRLoadedRendererValue } from 'astro';
export default NamedSSRLoadedRendererValue;
Loading

0 comments on commit fd9da98

Please sign in to comment.