UNPKG

vite-plugin-shopify-theme-islands

Version:
164 lines (163 loc) 6.06 kB
/** * Plugin ↔ Runtime contract (deep module). * * Single source of truth for the payload shape, key→tag derivation, and defaults. * Plugin and runtime both depend on this module in-process. */ import type { InteractionEventName } from "./interaction-events.js"; /** Loader function for one island chunk. */ export type IslandLoader = () => Promise<unknown>; /** Directive config for the runtime (built-in + no plugin-only `custom` entrypoints). */ export interface RuntimeDirectivesConfig { visible?: { attribute?: string; rootMargin?: string; threshold?: number; }; idle?: { attribute?: string; timeout?: number; }; media?: { attribute?: string; }; defer?: { attribute?: string; delay?: number; }; interaction?: { attribute?: string; events?: readonly InteractionEventName[]; }; } /** Retry configuration. */ export interface RetryConfig { retries?: number; delay?: number; } /** Options passed from plugin to runtime (subset of plugin options). */ export interface ReviveOptions { directives?: RuntimeDirectivesConfig; debug?: boolean; retry?: RetryConfig; /** * Milliseconds before a custom directive that never calls `load()` is considered timed out. * When exceeded, `islands:error` is dispatched and the island is abandoned. * Default: `0` (disabled). */ directiveTimeout?: number; } /** 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; } export interface ClientDirectiveContext { /** Aborted when the directive should stop waiting and clean up any side effects. */ signal: AbortSignal; /** Registers cleanup work that should run when the directive is aborted or resolves. */ onCleanup(cleanup: () => void): void; } /** Custom directive function at runtime (load, opts, element, ctx). */ export type ClientDirective = (load: () => Promise<void>, options: ClientDirectiveOptions, el: HTMLElement, ctx: ClientDirectiveContext) => void | Promise<void>; /** 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 { "islands:load": CustomEvent<IslandLoadDetail>; "islands:error": CustomEvent<IslandErrorDetail>; } } /** * Payload the plugin emits and the runtime consumes. * Islands: glob key → loader (e.g. "/frontend/js/islands/product-form.ts" → loader). * Custom directives: attribute name → directive implementation (resolved at build). * Options may be partial; runtime uses normalizeReviveOptions() to fill defaults. */ export interface RevivePayload { islands: Record<string, IslandLoader>; options?: ReviveOptions; customDirectives?: Map<string, ClientDirective>; resolvedTags?: Record<string, string | false>; } /** Fully resolved options; all directive and retry fields have defaults applied. */ export interface NormalizedReviveOptions { directives: { visible: { attribute: string; rootMargin: string; threshold: number; }; idle: { attribute: string; timeout: number; }; media: { attribute: string; }; defer: { attribute: string; delay: number; }; interaction: { attribute: string; events: readonly InteractionEventName[]; }; }; debug: boolean; retry: { retries: number; delay: number; }; directiveTimeout: number; } /** Default directive config. Single source of truth for plugin merge and runtime normalization. */ export declare const DEFAULT_DIRECTIVES: NormalizedReviveOptions["directives"]; /** * Applies default values for all runtime options. * Single source of truth so plugin and runtime do not duplicate defaults. */ export declare function normalizeReviveOptions(options?: ReviveOptions): NormalizedReviveOptions; export type KeyToTagResult = { tag: string; skip?: boolean; }; export type ResolvedTagOverride = string | false; export type ResolveTagInput = { filePath: string; defaultTag: string; }; export type ResolveTagOverrideFn = (input: ResolveTagInput) => ResolvedTagOverride; /** * Maps a glob key (e.g. "/frontend/js/islands/product-form.ts") to a custom element tag. * Return { tag, skip: true } to exclude this entry from the island map. */ export type KeyToTagFn = (key: string) => KeyToTagResult; /** Derives the default tag name from a glob key without warning or skipping. */ export declare function deriveDefaultTag(key: string): string; /** Default: last path segment, extension stripped; skip (and warn) when tag has no hyphen. */ export declare function defaultKeyToTag(key: string): KeyToTagResult; export declare function compileResolvedTags(filePaths: Iterable<string>, resolveTag: ResolveTagOverrideFn): Record<string, ResolvedTagOverride> | null; /** * Builds tag → loader map from payload. * Applies the default key→tag derivation and requires unique tag ownership. */ export declare function buildIslandMap(payload: RevivePayload): Map<string, IslandLoader>;