UNPKG

vite-plugin-shopify-theme-islands

Version:
143 lines (142 loc) 5.96 kB
import type { Plugin } from "vite"; /** A function that triggers the load of an island module. */ export type ClientDirectiveLoader = () => Promise<void>; /** Options passed to a custom client directive function. */ export interface ClientDirectiveOptions { /** The matched attribute name, e.g. `'client:on-click'` */ name: string; /** The attribute value; empty string if no value was set */ value: string; } /** * A custom client directive function. * * Called by the runtime when a matching attribute is found on an island element. * The function is responsible for calling `load()` when the desired condition is met. * * @example * ```ts * // src/directives/hash.ts * import type { ClientDirective } from 'vite-plugin-shopify-theme-islands'; * * const hashDirective: ClientDirective = (load, opts) => { * const target = opts.value; * if (location.hash === target) { load(); return; } * window.addEventListener('hashchange', () => { * if (location.hash === target) load(); * }); * }; * * export default hashDirective; * ``` * * Register it in `vite.config.ts`: * ```ts * shopifyThemeIslands({ * directives: { * custom: [{ name: 'client:hash', entrypoint: './src/directives/hash.ts' }], * }, * }) * ``` */ export type ClientDirective = (load: ClientDirectiveLoader, options: ClientDirectiveOptions, el: HTMLElement) => void | Promise<void>; /** Plugin option entry for registering a custom client directive. */ export interface ClientDirectiveDefinition { /** HTML attribute name, e.g. `'client:on-click'` */ name: string; /** Path to the directive module (supports Vite aliases) */ entrypoint: string; } /** Shared directive configuration shape used by both the plugin and the runtime. */ export interface DirectivesConfig { /** Configuration for the `client:visible` directive (IntersectionObserver). */ visible?: { /** HTML attribute name. Default: `'client:visible'` */ attribute?: string; /** Passed to IntersectionObserver — loads islands before they scroll into view. Default: `'200px'` */ rootMargin?: string; /** Passed to IntersectionObserver — ratio of element that must be visible. Default: `0` */ threshold?: number; }; /** Configuration for the `client:idle` directive (requestIdleCallback). */ idle?: { /** HTML attribute name. Default: `'client:idle'` */ attribute?: string; /** Deadline (ms) passed to requestIdleCallback; also used as the setTimeout fallback delay. Default: `500` */ timeout?: number; }; /** Configuration for the `client:media` directive (matchMedia). */ media?: { /** HTML attribute name. Default: `'client:media'` */ attribute?: string; }; /** Configuration for the `client:defer` directive (fixed setTimeout delay). */ defer?: { /** HTML attribute name. Default: `'client:defer'` */ attribute?: string; /** Fallback delay (ms) when the attribute has no value. Default: `3000` */ delay?: number; }; /** Configuration for the `client:interaction` directive (mouseenter/touchstart/focusin). */ interaction?: { /** HTML attribute name. Default: `'client:interaction'` */ attribute?: string; /** DOM event names to listen for. Default: `['mouseenter', 'touchstart', 'focusin']` */ events?: string[]; }; /** Custom client directives to register. Each entry maps an attribute name to a module entrypoint. */ custom?: ClientDirectiveDefinition[]; } /** Runtime-facing directive configuration — omits plugin-only `custom` directives. */ export type RuntimeDirectivesConfig = Omit<DirectivesConfig, "custom">; /** Retry configuration for failed island loads. */ export interface RetryConfig { /** Number of times to retry after the initial failure. Default: `0` (no auto-retry) */ retries?: number; /** Base delay in ms between retries; doubles each attempt. Default: `1000` */ delay?: number; } /** Event detail for the `islands:load` DOM event. */ export interface IslandLoadDetail { /** The custom element tag name, e.g. `'product-form'` */ tag: string; /** Milliseconds from directive resolution to successful module load (chunk fetch time). */ duration: number; /** Which attempt succeeded. 1 = first try, 2 = first retry, etc. */ attempt: number; } /** Event detail for the `islands:error` DOM event. */ export interface IslandErrorDetail { /** The custom element tag name, e.g. `'product-form'` */ tag: string; /** The error thrown by the loader or custom directive */ error: unknown; /** Which attempt failed. 1 = initial attempt, 2 = first retry, etc. */ attempt: number; } declare global { interface DocumentEventMap { /** Fired after an island module resolves successfully. */ "islands:load": CustomEvent<IslandLoadDetail>; /** Fired when an island load or custom directive fails. Fired on each retry attempt. */ "islands:error": CustomEvent<IslandErrorDetail>; } } export interface ShopifyThemeIslandsOptions { /** Directories to scan for island files. Accepts paths or Vite aliases. Default: `['/frontend/js/islands/']` */ directories?: string | string[]; /** Log discovered islands and generated virtual module. Default: `false` */ debug?: boolean; /** Per-directive configuration. */ directives?: DirectivesConfig; /** Automatic retry behaviour for failed island loads. */ retry?: RetryConfig; } export interface ReviveOptions { directives?: RuntimeDirectivesConfig; /** Log island activation and directive events to the console. Default: `false` */ debug?: boolean; /** Automatic retry behaviour for failed island loads. */ retry?: RetryConfig; } export default function shopifyThemeIslands(options?: ShopifyThemeIslandsOptions): Plugin;