summaryrefslogtreecommitdiff
path: root/packages/astro/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/astro/src')
-rw-r--r--packages/astro/src/@types/astro.ts3
-rw-r--r--packages/astro/src/@types/hydrate.ts4
-rw-r--r--packages/astro/src/compiler/codegen/index.ts29
-rw-r--r--packages/astro/src/frontend/hydrate/idle.ts4
-rw-r--r--packages/astro/src/frontend/hydrate/load.ts4
-rw-r--r--packages/astro/src/frontend/hydrate/media.ts23
-rw-r--r--packages/astro/src/frontend/hydrate/visible.ts4
-rw-r--r--packages/astro/src/internal/__astro_component.ts6
8 files changed, 57 insertions, 20 deletions
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index ce2e2bb03..3ff0ceb7e 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -180,9 +180,10 @@ export type Components = Map<string, ComponentInfo>;
export interface AstroComponentMetadata {
displayName: string;
- hydrate?: 'load' | 'idle' | 'visible';
+ hydrate?: 'load' | 'idle' | 'visible' | 'media';
componentUrl?: string;
componentExport?: { value: string; namespace?: boolean };
+ value?: undefined | string;
}
type AsyncRendererComponentFn<U> = (Component: any, props: any, children: string | undefined, metadata?: AstroComponentMetadata) => Promise<U>;
diff --git a/packages/astro/src/@types/hydrate.ts b/packages/astro/src/@types/hydrate.ts
index ff1ab0781..88d6a0cc3 100644
--- a/packages/astro/src/@types/hydrate.ts
+++ b/packages/astro/src/@types/hydrate.ts
@@ -1 +1,5 @@
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
+
+export interface HydrateOptions {
+ value?: string;
+} \ No newline at end of file
diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts
index f264b5878..52f7fd576 100644
--- a/packages/astro/src/compiler/codegen/index.ts
+++ b/packages/astro/src/compiler/codegen/index.ts
@@ -48,20 +48,25 @@ interface CodeGenOptions {
}
interface HydrationAttributes {
- method?: 'load' | 'idle' | 'visible';
+ method?: 'load' | 'idle' | 'visible' | 'media';
+ value?: undefined | string
}
/** Searches through attributes to extract hydration-rlated attributes */
function findHydrationAttributes(attrs: Record<string, string>): HydrationAttributes {
let method: HydrationAttributes['method'];
+ let value: undefined | string;
- const hydrationDirectives = new Set(['client:load', 'client:idle', 'client:visible']);
+ const hydrationDirectives = new Set(['client:load', 'client:idle', 'client:visible', 'client:media']);
for (const [key, val] of Object.entries(attrs)) {
- if (hydrationDirectives.has(key)) method = key.slice(7) as HydrationAttributes['method'];
+ if (hydrationDirectives.has(key)) {
+ method = key.slice(7) as HydrationAttributes['method'];
+ value = val === "true" ? undefined : val;
+ }
}
- return { method };
+ return { method, value };
}
/** Retrieve attributes from TemplateNode */
@@ -220,15 +225,17 @@ function getComponentWrapper(_name: string, hydration: HydrationAttributes, { ur
}
};
- const importInfo = method
- ? {
- componentUrl: getComponentUrl(astroConfig, url, pathToFileURL(filename)),
- componentExport: getComponentExport(),
- }
- : {};
+ let metadata: string = '';
+ if(method) {
+ const componentUrl = getComponentUrl(astroConfig, url, pathToFileURL(filename));
+ const componentExport = getComponentExport();
+ metadata = `{ hydrate: "${method}", displayName: "${name}", componentUrl: "${componentUrl}", componentExport: ${JSON.stringify(componentExport)}, value: ${hydration.value || 'null'} }`;
+ } else {
+ metadata = `{ hydrate: undefined, displayName: "${name}", value: ${hydration.value || 'null'} }`
+ }
return {
- wrapper: `__astro_component(${name}, ${JSON.stringify({ hydrate: method, displayName: _name, ...importInfo })})`,
+ wrapper: `__astro_component(${name}, ${metadata})`,
wrapperImports: [`import {__astro_component} from 'astro/dist/internal/__astro_component.js';`],
};
}
diff --git a/packages/astro/src/frontend/hydrate/idle.ts b/packages/astro/src/frontend/hydrate/idle.ts
index 2fd96b9cb..f270d0928 100644
--- a/packages/astro/src/frontend/hydrate/idle.ts
+++ b/packages/astro/src/frontend/hydrate/idle.ts
@@ -1,10 +1,10 @@
-import type { GetHydrateCallback } from '../../@types/hydrate';
+import type { GetHydrateCallback, HydrateOptions } from '../../@types/hydrate';
/**
* Hydrate this component as soon as the main thread is free
* (or after a short delay, if `requestIdleCallback`) isn't supported
*/
-export default async function onIdle(astroId: string, getHydrateCallback: GetHydrateCallback) {
+export default async function onIdle(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
const cb = async () => {
const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
diff --git a/packages/astro/src/frontend/hydrate/load.ts b/packages/astro/src/frontend/hydrate/load.ts
index 38ac1a0ea..62e90b660 100644
--- a/packages/astro/src/frontend/hydrate/load.ts
+++ b/packages/astro/src/frontend/hydrate/load.ts
@@ -1,9 +1,9 @@
-import type { GetHydrateCallback } from '../../@types/hydrate';
+import type { GetHydrateCallback, HydrateOptions } from '../../@types/hydrate';
/**
* Hydrate this component immediately
*/
-export default async function onLoad(astroId: string, getHydrateCallback: GetHydrateCallback) {
+export default async function onLoad(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
const hydrate = await getHydrateCallback();
diff --git a/packages/astro/src/frontend/hydrate/media.ts b/packages/astro/src/frontend/hydrate/media.ts
new file mode 100644
index 000000000..39c57c4f9
--- /dev/null
+++ b/packages/astro/src/frontend/hydrate/media.ts
@@ -0,0 +1,23 @@
+import type { GetHydrateCallback, HydrateOptions } from '../../@types/hydrate';
+
+/**
+ * Hydrate this component when a matching media query is found
+ */
+export default async function onMedia(astroId: string, options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
+ const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
+ const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
+
+ const cb = async () => {
+ const hydrate = await getHydrateCallback();
+ for (const root of roots) {
+ hydrate(root, innerHTML);
+ }
+ };
+
+ const mql = matchMedia(options.value!);
+ if(mql.matches) {
+ cb();
+ } else {
+ mql.addEventListener('change', cb, {once:true});
+ }
+}
diff --git a/packages/astro/src/frontend/hydrate/visible.ts b/packages/astro/src/frontend/hydrate/visible.ts
index d4dacdf51..eb55e63e9 100644
--- a/packages/astro/src/frontend/hydrate/visible.ts
+++ b/packages/astro/src/frontend/hydrate/visible.ts
@@ -1,11 +1,11 @@
-import type { GetHydrateCallback } from '../../@types/hydrate';
+import type { GetHydrateCallback, HydrateOptions } from '../../@types/hydrate';
/**
* Hydrate this component when one of it's children becomes visible.
* We target the children because `astro-root` is set to `display: contents`
* which doesn't work with IntersectionObserver
*/
-export default async function onVisible(astroId: string, getHydrateCallback: GetHydrateCallback) {
+export default async function onVisible(astroId: string, _options: HydrateOptions, getHydrateCallback: GetHydrateCallback) {
const roots = document.querySelectorAll(`astro-root[uid="${astroId}"]`);
const innerHTML = roots[0].querySelector(`astro-fragment`)?.innerHTML ?? null;
diff --git a/packages/astro/src/internal/__astro_component.ts b/packages/astro/src/internal/__astro_component.ts
index 6738600e3..1ddd40b05 100644
--- a/packages/astro/src/internal/__astro_component.ts
+++ b/packages/astro/src/internal/__astro_component.ts
@@ -72,8 +72,10 @@ interface HydrateScriptOptions {
}
/** For hydrated components, generate a <script type="module"> to load the component */
-async function generateHydrateScript({ instance, astroId, props }: HydrateScriptOptions, { hydrate, componentUrl, componentExport }: Required<AstroComponentMetadata>) {
+async function generateHydrateScript(scriptOptions: HydrateScriptOptions, metadata: Required<AstroComponentMetadata>) {
+ const { instance, astroId, props } = scriptOptions;
const { source } = instance;
+ const { hydrate, componentUrl, componentExport } = metadata;
let hydrationSource = '';
if (instance.hydrationPolyfills.length) {
@@ -92,7 +94,7 @@ async function generateHydrateScript({ instance, astroId, props }: HydrateScript
const hydrationScript = `<script type="module">
import setup from '/_astro_frontend/hydrate/${hydrate}.js';
-setup("${astroId}", async () => {
+setup("${astroId}", {${metadata.value ? `value: "${metadata.value}"` : ''}}, async () => {
${hydrationSource}
});
</script>`;