UNPKG

vite-plugin-shopify-theme-islands

Version:
188 lines (133 loc) 5.87 kB
--- name: lifecycle description: > Island lifecycle events and SPA teardown. onIslandLoad and onIslandError helpers from vite-plugin-shopify-theme-islands/events — prefer these over raw document.addEventListener for guaranteed type safety. Raw DOM events islands:load and islands:error on document. islands:load detail includes tag, duration (ms), and attempt (1-based). islands:error detail includes tag, error, and attempt. disconnect() from the virtual module revive for SPA navigation teardown. type: core library: vite-plugin-shopify-theme-islands library_version: "1.1.0" sources: - Rees1993/vite-plugin-shopify-theme-islands:src/events.ts - Rees1993/vite-plugin-shopify-theme-islands:src/index.ts - Rees1993/vite-plugin-shopify-theme-islands:src/runtime.ts --- ## Setup ```ts import { onIslandLoad, onIslandError } from "vite-plugin-shopify-theme-islands/events"; const offLoad = onIslandLoad(({ tag, duration, attempt }) => { console.log("loaded:", tag, `${duration.toFixed(1)}ms`, `attempt ${attempt}`); }); const offError = onIslandError(({ tag, error, attempt }) => { console.error("failed:", tag, `attempt ${attempt}`, error); }); // Remove listeners when no longer needed offLoad(); offError(); ``` ## Core Patterns ### Track island load for analytics ```ts import { onIslandLoad } from "vite-plugin-shopify-theme-islands/events"; onIslandLoad(({ tag, duration, attempt }) => { analytics.track("island_loaded", { component: tag, duration, attempt }); }); ``` `tag` is the lowercased custom element tag name (e.g. `"product-form"`). `duration` is the time in milliseconds from when all directives resolved to when the module finished loading. `attempt` is 1 on the first successful load, 2 if it succeeded on the first retry, etc. ### Track load performance ```ts import { onIslandLoad } from "vite-plugin-shopify-theme-islands/events"; onIslandLoad(({ tag, duration }) => { if (duration > 3000) { performance.mark(`island-slow:${tag}`); } }); ``` `duration` measures only the chunk fetch time — time spent waiting on directives (e.g. `client:visible`) is not included. ### Report errors to a monitoring service ```ts import { onIslandError } from "vite-plugin-shopify-theme-islands/events"; onIslandError(({ tag, error, attempt }) => { Sentry.captureException(error, { extra: { island: tag, attempt } }); }); ``` `onIslandError` fires on each retry attempt and on custom directive failures. `attempt` tells you which attempt failed — 1 is the initial load, 2 is the first retry, etc. ### Teardown for SPA navigation ```ts import { disconnect } from "vite-plugin-shopify-theme-islands/revive"; // Before navigating away / unmounting the page disconnect(); ``` `disconnect()` stops the MutationObserver and prevents new islands from activating. Call it before SPA page transitions to avoid activating islands from the previous page's DOM. ### Raw DOM events (when type augmentation is in scope) ```ts // DocumentEventMap augmentation is exported from the main package import type {} from "vite-plugin-shopify-theme-islands"; document.addEventListener("islands:load", (e) => { console.log(e.detail.tag, e.detail.duration, e.detail.attempt); }); ``` The `DocumentEventMap` augmentation is declared in the main package's `index.ts`. It is only in scope when the import is present in the same tsconfig compilation. ## Common Mistakes ### HIGH Raw `addEventListener` without types — `e.detail` is untyped Wrong: ```ts // No import from the package — e is Event, detail is unknown document.addEventListener("islands:load", (e) => { console.log(e.detail.tag); // TypeScript error or any }); ``` Correct: ```ts import { onIslandLoad } from "vite-plugin-shopify-theme-islands/events"; onIslandLoad(({ tag }) => { console.log(tag); // string, always typed }); ``` `onIslandLoad` and `onIslandError` are typed unconditionally regardless of tsconfig setup. Use them instead of raw `document.addEventListener` unless the `DocumentEventMap` augmentation is confirmed to be in scope. Source: src/events.ts ### CRITICAL `disconnect` imported from wrong entry point Wrong: ```ts import { disconnect } from "vite-plugin-shopify-theme-islands/runtime"; import { disconnect } from "vite-plugin-shopify-theme-islands/island"; ``` Correct: ```ts import { disconnect } from "vite-plugin-shopify-theme-islands/revive"; ``` Only the virtual module (`/revive`) exports the `disconnect` bound to the plugin-managed `revive()` instance. Importing from other entry points references a different or nonexistent instance. Source: src/index.ts — virtual module `export const { disconnect } = _islands(...)` ### MEDIUM `onIslandError` fires on every retry, not just final failure Wrong: ```ts onIslandError(({ tag }) => { // Assuming this fires once when the island permanently fails markIslandBroken(tag); }); ``` Correct: ```ts onIslandError(({ tag, error, attempt }) => { // attempt === 1 is the first failure; higher values are retries if (attempt === 1) { reportFirstFailure(tag, error); } }); ``` With `retry: { retries: 3 }`, a single island can fire `islands:error` up to 4 times before exhausting retries. Use `attempt` to distinguish the initial failure from retries. Source: src/runtime.ts — `dispatch("islands:error", ...)` inside `.catch()` before retry check ### MEDIUM `islands:error` fires for custom directive failures too Wrong assumption: ```ts onIslandError(({ tag, error }) => { // Assuming this only fires for failed dynamic import() reportChunkLoadFailure(tag); }); ``` `islands:error` fires when any custom directive throws or rejects, not only when the island module's `import()` fails. The `error` value may be a directive error rather than a network or chunk error. Source: src/runtime.ts — handleDirectiveError dispatches `islands:error`