mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
fix: prevent client hydration when rendering via Container API
This commit is contained in:
parent
0df81422a8
commit
ed69f8f98e
12 changed files with 50 additions and 6 deletions
5
.changeset/afraid-cups-deliver.md
Normal file
5
.changeset/afraid-cups-deliver.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes a bug in the Container API that was triggered when trying to render a client component with some `client:*` directives.
|
|
@ -2,4 +2,4 @@
|
|||
import Counter from './Counter.jsx';
|
||||
---
|
||||
|
||||
<Counter initialCount={5} />
|
||||
<Counter initialCount={5} client:idle />
|
||||
|
|
|
@ -3220,6 +3220,12 @@ export interface SSRResult {
|
|||
response: AstroGlobal['response'];
|
||||
request: AstroGlobal['request'];
|
||||
actionResult?: ReturnType<AstroGlobal['getActionResult']>;
|
||||
// Metadata used to signal Astro renderer to skip any client hydration, such as:
|
||||
// - client directories
|
||||
// - client entrypoint
|
||||
// - renderer-url
|
||||
skipHydration: boolean;
|
||||
|
||||
renderers: SSRLoadedRenderer[];
|
||||
/**
|
||||
* Map of directive name (e.g. `load`) to the directive script code
|
||||
|
|
|
@ -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';
|
||||
|
@ -114,7 +115,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(),
|
||||
|
@ -435,6 +436,8 @@ export class experimental_AstroContainer {
|
|||
pathname: url.pathname,
|
||||
locals: options?.locals ?? {},
|
||||
});
|
||||
// client directives aren't needed in this case
|
||||
renderContext.skipHydration = true;
|
||||
if (options.params) {
|
||||
renderContext.params = options.params;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ export class RenderContext {
|
|||
// The first route that this instance of the context attempts to render
|
||||
originalRoute: RouteData;
|
||||
|
||||
// Metadata used to signal Astro renderer to skip any client hydration
|
||||
skipHydration: boolean;
|
||||
|
||||
private constructor(
|
||||
readonly pipeline: Pipeline,
|
||||
public locals: App.Locals,
|
||||
|
@ -56,6 +59,7 @@ export class RenderContext {
|
|||
public props: Props = {}
|
||||
) {
|
||||
this.originalRoute = routeData;
|
||||
this.skipHydration = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,7 +301,7 @@ export class RenderContext {
|
|||
}
|
||||
|
||||
async createResult(mod: ComponentInstance) {
|
||||
const { cookies, pathname, pipeline, routeData, status } = this;
|
||||
const { cookies, pathname, pipeline, routeData, status, skipHydration } = this;
|
||||
const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } =
|
||||
pipeline;
|
||||
const { links, scripts, styles } = await pipeline.headElements(routeData);
|
||||
|
@ -343,6 +347,7 @@ export class RenderContext {
|
|||
request: this.request,
|
||||
scripts,
|
||||
styles,
|
||||
skipHydration,
|
||||
actionResult,
|
||||
_metadata: {
|
||||
hasHydrationScript: false,
|
||||
|
|
|
@ -156,7 +156,7 @@ export async function generateHydrateScript(
|
|||
island.props['component-url'] = await result.resolve(decodeURI(componentUrl));
|
||||
|
||||
// Add renderer url
|
||||
if (renderer.clientEntrypoint) {
|
||||
if (renderer.clientEntrypoint && !result.skipHydration) {
|
||||
island.props['component-export'] = componentExport.value;
|
||||
island.props['renderer-url'] = await result.resolve(decodeURI(renderer.clientEntrypoint));
|
||||
island.props['props'] = escapeHTML(serializeProps(props, metadata));
|
||||
|
|
|
@ -295,7 +295,8 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
|
|||
renderer &&
|
||||
!renderer.clientEntrypoint &&
|
||||
renderer.name !== '@astrojs/lit' &&
|
||||
metadata.hydrate
|
||||
metadata.hydrate &&
|
||||
!result.skipHydration
|
||||
) {
|
||||
throw new AstroError({
|
||||
...AstroErrorData.NoClientEntrypoint,
|
||||
|
|
|
@ -261,4 +261,12 @@ 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, /I am a react button/);
|
||||
});
|
||||
});
|
||||
|
|
5
packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
vendored
Normal file
5
packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
import Button from "./button.jsx"
|
||||
---
|
||||
|
||||
<Button client:idle />
|
10
packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
vendored
Normal file
10
packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
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 });
|
||||
return await container.renderToResponse(Component);
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -60,6 +60,7 @@ function getViteConfiguration(
|
|||
'react/jsx-runtime',
|
||||
'react/jsx-dev-runtime',
|
||||
'react-dom',
|
||||
``,
|
||||
],
|
||||
exclude: [reactConfig.server],
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue