diff options
Diffstat (limited to 'packages/astro/src')
-rw-r--r-- | packages/astro/src/@types/astro.ts | 3 | ||||
-rw-r--r-- | packages/astro/src/@types/hydrate.ts | 4 | ||||
-rw-r--r-- | packages/astro/src/compiler/codegen/index.ts | 29 | ||||
-rw-r--r-- | packages/astro/src/frontend/hydrate/idle.ts | 4 | ||||
-rw-r--r-- | packages/astro/src/frontend/hydrate/load.ts | 4 | ||||
-rw-r--r-- | packages/astro/src/frontend/hydrate/media.ts | 23 | ||||
-rw-r--r-- | packages/astro/src/frontend/hydrate/visible.ts | 4 | ||||
-rw-r--r-- | packages/astro/src/internal/__astro_component.ts | 6 |
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>`; |