fix: store hydration meta info in local variable

This commit is contained in:
Nate Moore 2023-08-17 14:16:07 -05:00
parent 9d48d35db0
commit 57a6f5b3b4
2 changed files with 29 additions and 20 deletions

View file

@ -10,14 +10,27 @@ declare const Astro: {
opts: Record<string, any>, opts: Record<string, any>,
root: HTMLElement root: HTMLElement
) => void; ) => void;
}; }
interface IslandMeta {
componentUrl: string;
rendererUrl: string;
beforeHydrationUrl?: string;
componentExport?: string;
props: Record<string, any>;
opts: Record<string, any>;
}
{ {
// @ts-ignore // island meta info (for all islands) is stored here
(self.Astro || (self.Astro = {})).assign = (script,value) => { var $meta: Record<string, IslandMeta> = {};
Object.assign(script.parentElement,value); // Astro.assign is called in injected scripts
script.remove(); (self as any).Astro = Object.assign((self as any).Astro || {}, {
}; assign(script: HTMLElement, value: any) {
$meta[(script.parentElement as any).getAttribute('uid')] = value;
script.remove();
}
})
if (!customElements.get('astro-island')) { if (!customElements.get('astro-island')) {
customElements.define( customElements.define(
@ -25,16 +38,11 @@ declare const Astro: {
class extends HTMLElement { class extends HTMLElement {
public Component: any; public Component: any;
public hydrator: any; public hydrator: any;
public uid: any;
// These props are injected by the Astro runtime via inline `<script>` static get observedAttributes() { return ['ssr'] }
public opts: Record<string, any> = {}; get meta() { return $meta[this.uid]; }
public props: Record<string, any> = {};
public rendererUrl = '';
public componentUrl = '';
public componentExport = 'default';
public beforeHydrationUrl = '';
connectedCallback() { connectedCallback() {
this.uid = this.getAttribute('uid')!;
if (!this.hasAttribute('await-children') || this.firstChild) { if (!this.hasAttribute('await-children') || this.firstChild) {
this.childrenConnectedCallback(); this.childrenConnectedCallback();
} else { } else {
@ -49,7 +57,7 @@ declare const Astro: {
} }
} }
async childrenConnectedCallback() { async childrenConnectedCallback() {
let { beforeHydrationUrl } = this; let { beforeHydrationUrl } = this.meta;
if (beforeHydrationUrl) { if (beforeHydrationUrl) {
await import(beforeHydrationUrl); await import(beforeHydrationUrl);
} }
@ -57,7 +65,7 @@ declare const Astro: {
this.removeAttribute('await-children'); this.removeAttribute('await-children');
} }
start() { start() {
const { opts } = this; const { opts = {} } = this.meta;
const client = this.getAttribute('client') as directiveAstroKeys; const client = this.getAttribute('client') as directiveAstroKeys;
if (Astro[client] === undefined) { if (Astro[client] === undefined) {
window.addEventListener(`astro:${client}`, () => this.start(), { once: true }); window.addEventListener(`astro:${client}`, () => this.start(), { once: true });
@ -65,7 +73,7 @@ declare const Astro: {
} }
Astro[client]!( Astro[client]!(
async () => { async () => {
const { rendererUrl, componentUrl, componentExport = 'default' } = this; const { rendererUrl, componentUrl, componentExport = 'default' } = this.meta;
const [componentModule, { default: hydrator }] = await Promise.all([ const [componentModule, { default: hydrator }] = await Promise.all([
import(componentUrl), import(componentUrl),
rendererUrl ? import(rendererUrl) : () => () => {}, rendererUrl ? import(rendererUrl) : () => () => {},
@ -117,7 +125,8 @@ declare const Astro: {
if (!closest?.isSameNode(this)) continue; if (!closest?.isSameNode(this)) continue;
slots[slot.getAttribute('name') || 'default'] = slot.innerHTML; slots[slot.getAttribute('name') || 'default'] = slot.innerHTML;
} }
await this.hydrator(this)(this.Component, this.props, slots, { const { props = {} } = this.meta;
await this.hydrator(this)(this.Component, props, slots, {
client: this.getAttribute('client'), client: this.getAttribute('client'),
}); });
this.removeAttribute('ssr'); this.removeAttribute('ssr');

View file

@ -186,7 +186,7 @@ export async function generateHydrateScript(
const serializedProps = serialize(scriptProps) const serializedProps = serialize(scriptProps)
const script: SSRElement = { const script: SSRElement = {
children: `Astro.assign(document.currentScript,${serializedProps})`, children: `Astro.assign(document.currentScript,${serializedProps})`,
props: { async: '' } props: {}
} }
return [island, script]; return [island, script];