Fix race condition with directive definitions (#4375)
This commit is contained in:
parent
439f1d1c0d
commit
5e82f6c245
16 changed files with 128 additions and 2 deletions
5
.changeset/smooth-nails-remain.md
Normal file
5
.changeset/smooth-nails-remain.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes race condition between directives being defined
|
|
@ -0,0 +1,5 @@
|
|||
import preact from '@astrojs/preact';
|
||||
|
||||
export default {
|
||||
integrations: [preact()]
|
||||
};
|
13
packages/astro/e2e/fixtures/hydration-race/package.json
Normal file
13
packages/astro/e2e/fixtures/hydration-race/package.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "@e2e/hydration-race",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/preact": "workspace:*"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
import One from './One.jsx';
|
||||
---
|
||||
|
||||
<div>
|
||||
<span>Before one</span>
|
||||
<One name="One" client:idle />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
import Wrapper from './Wrapper.astro';
|
||||
---
|
||||
|
||||
<Wrapper />
|
||||
<slot />
|
|
@ -0,0 +1,8 @@
|
|||
import { h } from 'preact';
|
||||
|
||||
export default function({ name }) {
|
||||
const inTheClient = import.meta.env.SSR ? '' : ' in the client'
|
||||
return (
|
||||
<div id={name.toLowerCase()}>Hello {name}{inTheClient}</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
import One from './One.jsx';
|
||||
import Deeper from './Deeper.astro';
|
||||
---
|
||||
|
||||
|
||||
<One name="Two" client:visible />
|
||||
<Deeper />
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
import Layout from '../components/Layout.astro';
|
||||
import One from '../components/One.jsx';
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Testing</title>
|
||||
</head>
|
||||
<body>
|
||||
<One name="Four" client:idle />
|
||||
<Layout>
|
||||
<One name="Three" client:visible />
|
||||
</Layout>
|
||||
</body>
|
||||
</html>
|
36
packages/astro/e2e/hydration-race.test.js
Normal file
36
packages/astro/e2e/hydration-race.test.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { expect } from '@playwright/test';
|
||||
import { testFactory } from './test-utils.js';
|
||||
|
||||
const test = testFactory({
|
||||
root: './fixtures/hydration-race/',
|
||||
});
|
||||
|
||||
let devServer;
|
||||
|
||||
test.beforeEach(async ({ astro }) => {
|
||||
devServer = await astro.startDevServer();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await devServer.stop();
|
||||
});
|
||||
|
||||
test.describe('Hydration race', () => {
|
||||
test('Islands inside of slots hydrate', async ({ page, astro }) => {
|
||||
await page.goto('/slot');
|
||||
|
||||
const html = await page.content();
|
||||
|
||||
const one = page.locator('#one');
|
||||
await expect(one, 'updated text').toHaveText('Hello One in the client');
|
||||
|
||||
const two = page.locator('#two');
|
||||
await expect(two, 'updated text').toHaveText('Hello Two in the client');
|
||||
|
||||
const three = page.locator('#three');
|
||||
await expect(three, 'updated text').toHaveText('Hello Three in the client');
|
||||
|
||||
const four = page.locator('#four');
|
||||
await expect(four, 'updated text').toHaveText('Hello Four in the client');
|
||||
});
|
||||
});
|
|
@ -10,3 +10,4 @@
|
|||
setTimeout(cb, 200);
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:idle'));
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
await hydrate();
|
||||
})();
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:load'));
|
||||
|
|
|
@ -16,3 +16,4 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:media'));
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
await hydrate();
|
||||
})();
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:only'));
|
||||
|
|
|
@ -24,3 +24,4 @@
|
|||
io.observe(child);
|
||||
}
|
||||
};
|
||||
window.dispatchEvent(new Event('astro:visible'));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
type directiveAstroKeys = 'load' | 'idle' | 'visible' | 'media' | 'only';
|
||||
|
||||
declare const Astro: {
|
||||
[k in directiveAstroKeys]: (
|
||||
[k in directiveAstroKeys]?: (
|
||||
fn: () => Promise<() => void>,
|
||||
opts: Record<string, any>,
|
||||
root: HTMLElement
|
||||
|
@ -57,8 +57,16 @@ declare const Astro: {
|
|||
async childrenConnectedCallback() {
|
||||
window.addEventListener('astro:hydrate', this.hydrate);
|
||||
await import(this.getAttribute('before-hydration-url')!);
|
||||
this.start();
|
||||
}
|
||||
start() {
|
||||
const opts = JSON.parse(this.getAttribute('opts')!) as Record<string, any>;
|
||||
Astro[this.getAttribute('client') as directiveAstroKeys](
|
||||
const directive = this.getAttribute('client') as directiveAstroKeys;
|
||||
if(Astro[directive] === undefined) {
|
||||
window.addEventListener(`astro:${directive}`, () => this.start(), { once: true });
|
||||
return;
|
||||
}
|
||||
Astro[directive]!(
|
||||
async () => {
|
||||
const rendererUrl = this.getAttribute('renderer-url');
|
||||
const [componentModule, { default: hydrator }] = await Promise.all([
|
||||
|
|
|
@ -660,6 +660,14 @@ importers:
|
|||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
|
||||
packages/astro/e2e/fixtures/hydration-race:
|
||||
specifiers:
|
||||
'@astrojs/preact': workspace:*
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
'@astrojs/preact': link:../../../../integrations/preact
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/e2e/fixtures/invalidate-script-deps:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
|
|
Loading…
Reference in a new issue