summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/afraid-cups-deliver.md15
-rw-r--r--packages/astro/src/container/index.ts50
-rw-r--r--packages/astro/src/runtime/server/render/component.ts5
-rw-r--r--packages/astro/test/container.test.js9
-rw-r--r--packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro8
-rw-r--r--packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts11
-rw-r--r--packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts2
-rw-r--r--packages/integrations/react/server.d.ts4
8 files changed, 99 insertions, 5 deletions
diff --git a/.changeset/afraid-cups-deliver.md b/.changeset/afraid-cups-deliver.md
new file mode 100644
index 000000000..05cfb5677
--- /dev/null
+++ b/.changeset/afraid-cups-deliver.md
@@ -0,0 +1,15 @@
+---
+'astro': patch
+---
+
+Adds a new function called `addClientRenderer` to the Container API.
+
+This function should be used when rendering components using the `client:*` directives. The `addClientRenderer` API must be used
+*after* the use of the `addServerRenderer`:
+
+```js
+const container = await experimental_AstroContainer.create();
+container.addServerRenderer({renderer});
+container.addClientRenderer({name: '@astrojs/react', entrypoint: '@astrojs/react/client.js'});
+const response = await container.renderToResponse(Component);
+```
diff --git a/packages/astro/src/container/index.ts b/packages/astro/src/container/index.ts
index a8641b5ea..7e88f00e5 100644
--- a/packages/astro/src/container/index.ts
+++ b/packages/astro/src/container/index.ts
@@ -14,6 +14,7 @@ import type {
SSRManifest,
SSRResult,
} from '../@types/astro.js';
+import { getDefaultClientDirectives } from '../core/client-directive/index.js';
import { ASTRO_CONFIG_DEFAULTS } from '../core/config/schema.js';
import { validateConfig } from '../core/config/validate.js';
import { Logger } from '../core/logger/core.js';
@@ -96,6 +97,11 @@ export type AddServerRenderer =
name: string;
};
+export type AddClientRenderer = {
+ name: string;
+ entrypoint: string;
+};
+
function createManifest(
manifest?: AstroContainerManifest,
renderers?: SSRLoadedRenderer[],
@@ -116,7 +122,7 @@ function createManifest(
entryModules: manifest?.entryModules ?? {},
routes: manifest?.routes ?? [],
adapterName: '',
- clientDirectives: manifest?.clientDirectives ?? new Map(),
+ clientDirectives: manifest?.clientDirectives ?? getDefaultClientDirectives(),
renderers: renderers ?? manifest?.renderers ?? [],
base: manifest?.base ?? ASTRO_CONFIG_DEFAULTS.base,
componentMetadata: manifest?.componentMetadata ?? new Map(),
@@ -283,7 +289,7 @@ export class experimental_AstroContainer {
}
/**
- * Use this function to manually add a renderer to the container.
+ * Use this function to manually add a **server** renderer to the container.
*
* This function is preferred when you require to use the container with a renderer in environments such as on-demand pages.
*
@@ -326,6 +332,46 @@ export class experimental_AstroContainer {
}
}
+ /**
+ * Use this function to manually add a **client** renderer to the container.
+ *
+ * When rendering components that use the `client:*` directives, you need to use this function.
+ *
+ * ## Example
+ *
+ * ```js
+ * import reactRenderer from "@astrojs/react/server.js";
+ * import { experimental_AstroContainer as AstroContainer } from "astro/container"
+ *
+ * const container = await AstroContainer.create();
+ * container.addServerRenderer(reactRenderer);
+ * container.addClientRenderer({
+ * name: "@astrojs/react",
+ * entrypoint: "@astrojs/react/client.js"
+ * });
+ * ```
+ *
+ * @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.entrypoint The entrypoint of the client renderer.
+ */
+ public addClientRenderer(options: AddClientRenderer): void {
+ const { entrypoint, name } = options;
+
+ const rendererIndex = this.#pipeline.manifest.renderers.findIndex((r) => r.name === name);
+ if (rendererIndex === -1) {
+ throw new Error(
+ 'You tried to add the ' +
+ name +
+ " client renderer, but its server renderer wasn't added. You must add the server renderer first. Use the `addServerRenderer` function."
+ );
+ }
+ const renderer = this.#pipeline.manifest.renderers[rendererIndex];
+ renderer.clientEntrypoint = entrypoint;
+
+ this.#pipeline.manifest.renderers[rendererIndex] = renderer;
+ }
+
// NOTE: we keep this private via TS instead via `#` so it's still available on the surface, so we can play with it.
// @ematipico: I plan to use it for a possible integration that could help people
private static async createFromManifest(
diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts
index 0b552b950..e6c9f3acb 100644
--- a/packages/astro/src/runtime/server/render/component.ts
+++ b/packages/astro/src/runtime/server/render/component.ts
@@ -521,7 +521,10 @@ export async function renderComponent(
);
function handleCancellation(e: unknown) {
- if (result.cancelled) return { render() {} };
+ if (result.cancelled)
+ return {
+ render() {},
+ };
throw e;
}
}
diff --git a/packages/astro/test/container.test.js b/packages/astro/test/container.test.js
index 72d233ce4..6af5dc1f9 100644
--- a/packages/astro/test/container.test.js
+++ b/packages/astro/test/container.test.js
@@ -261,4 +261,13 @@ describe('Container with renderers', () => {
assert.match(html, /I am a vue button/);
});
+
+ it('Should render a component with directives', async () => {
+ const request = new Request('https://example.com/button-directive');
+ const response = await app.render(request);
+ const html = await response.text();
+
+ assert.match(html, /Button not rendered/);
+ assert.match(html, /I am a react button/);
+ });
});
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro b/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
new file mode 100644
index 000000000..48c6cc386
--- /dev/null
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
@@ -0,0 +1,8 @@
+---
+import Button from "./button.jsx"
+---
+
+<div>
+ <p>Button not rendered</p>
+ <Button client:idle/>
+</div>
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts b/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
new file mode 100644
index 000000000..c3a2d377f
--- /dev/null
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
@@ -0,0 +1,11 @@
+import type { APIRoute, SSRLoadedRenderer } from 'astro';
+import { experimental_AstroContainer } from 'astro/container';
+import renderer from '@astrojs/react/server.js';
+import Component from '../components/buttonDirective.astro';
+
+export const GET: APIRoute = async (ctx) => {
+ const container = await experimental_AstroContainer.create();
+ container.addServerRenderer({ renderer });
+ container.addClientRenderer({ name: '@astrojs/react', entrypoint: '@astrojs/react/client.js' });
+ return await container.renderToResponse(Component);
+};
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts b/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
index d77eaead1..b8d44a355 100644
--- a/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
@@ -1,4 +1,4 @@
-import type {APIRoute, SSRLoadedRenderer} from "astro";
+import type {APIRoute} from "astro";
import { experimental_AstroContainer } from "astro/container";
import renderer from '@astrojs/react/server.js';
import Component from "../components/button.jsx"
diff --git a/packages/integrations/react/server.d.ts b/packages/integrations/react/server.d.ts
index bb2f29556..75cc3eb64 100644
--- a/packages/integrations/react/server.d.ts
+++ b/packages/integrations/react/server.d.ts
@@ -1,2 +1,4 @@
import type { NamedSSRLoadedRendererValue } from 'astro';
-export default NamedSSRLoadedRendererValue;
+
+declare const renderer: NamedSSRLoadedRendererValue;
+export default renderer;