@llui/vike 0.0.42 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -136,6 +136,45 @@ export interface RenderClientOptions {
|
|
|
136
136
|
* layers stay live but disposed layers do not.
|
|
137
137
|
*/
|
|
138
138
|
onMount?: (chain: readonly AppHandle[]) => void;
|
|
139
|
+
/**
|
|
140
|
+
* Called for each surviving layout layer whose `lluiLayoutData[i]`
|
|
141
|
+
* slice changed across a client navigation. Surviving layers are
|
|
142
|
+
* layers shared between the previous and current chain — they stay
|
|
143
|
+
* mounted, but their state needs a fresh injection of nav-driven
|
|
144
|
+
* data (pathname, breadcrumbs, session, …).
|
|
145
|
+
*
|
|
146
|
+
* The framework gives you the layer's `AppHandle` and the changed
|
|
147
|
+
* data; you decide how to translate it into a state-update message
|
|
148
|
+
* and dispatch it through `handle.send(msg)`. Typically:
|
|
149
|
+
*
|
|
150
|
+
* ```ts
|
|
151
|
+
* createOnRenderClient({
|
|
152
|
+
* Layout: NavAwareLayout,
|
|
153
|
+
* onLayerDataChange: ({ def, handle, newData }) => {
|
|
154
|
+
* if (def === NavAwareLayout) {
|
|
155
|
+
* handle.send({ type: 'navChanged', data: newData as NavData })
|
|
156
|
+
* }
|
|
157
|
+
* },
|
|
158
|
+
* })
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* Not called for layers whose data slice is unchanged (shallow-key
|
|
162
|
+
* `Object.is` diff on records, whole-value `Object.is` for
|
|
163
|
+
* primitives). Not called on the initial hydration render — `init`
|
|
164
|
+
* handles the initial data injection there. Not called for the
|
|
165
|
+
* page layer (the innermost entry); the page always disposes and
|
|
166
|
+
* remounts, so its `init(data)` receives the fresh data directly.
|
|
167
|
+
*
|
|
168
|
+
* If omitted, surviving layers retain their existing state across
|
|
169
|
+
* navigations. Opt in only when the layout needs to react to
|
|
170
|
+
* nav-scoped data changes.
|
|
171
|
+
*/
|
|
172
|
+
onLayerDataChange?: (ctx: {
|
|
173
|
+
def: AnyComponentDef;
|
|
174
|
+
handle: AppHandle;
|
|
175
|
+
newData: unknown;
|
|
176
|
+
prevData: unknown;
|
|
177
|
+
}) => void;
|
|
139
178
|
/**
|
|
140
179
|
* Forwarded to `@llui/dom`'s `hydrateApp` / `hydrateAtAnchor` for
|
|
141
180
|
* every layer in the layout chain on initial hydration. When `true`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAExF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAI9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,CAAC,EAAE,OAAO,CAAA;KACzB;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,CAAC,EAAE,mBAAmB,CAAA;IAC1B,cAAc,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,KAAK,WAAW,GAAG,aAAa,CAAC,eAAe,CAAC,CAAA;AAoBjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,EAAE,eAAe,GAAG,WAAW,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,KAAK,WAAW,CAAC,CAAA;IAE1F;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEnD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;IAEnC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,CAAA;IAE/C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,iBAAiB,GACnB,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,SAAS,CAAC,CAgBlD;
|
|
1
|
+
{"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"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"}
|
package/dist/on-render-client.js
CHANGED
|
@@ -182,10 +182,11 @@ async function renderClient(pageContext, options) {
|
|
|
182
182
|
// those sites visually while the URL advances — a regression that
|
|
183
183
|
// shipped in 0.0.26 and was reported against the llui.dev site.
|
|
184
184
|
//
|
|
185
|
-
// `
|
|
186
|
-
// nav-scoped data (pathname, session,
|
|
187
|
-
// remounting — see the loop below. The page is
|
|
188
|
-
// from that path because `init(data)` always
|
|
185
|
+
// The user-supplied `onLayerDataChange` callback fires for *layouts*
|
|
186
|
+
// that want to react to nav-scoped data (pathname, session,
|
|
187
|
+
// breadcrumbs) without remounting — see the loop below. The page is
|
|
188
|
+
// deliberately excluded from that path because `init(data)` always
|
|
189
|
+
// re-runs for it.
|
|
189
190
|
let firstMismatch = 0;
|
|
190
191
|
// `chainHandles` stores `[...layouts, page]`, so the layout prefix
|
|
191
192
|
// length is `chainHandles.length - 1` (or 0 on first fresh mount, when
|
|
@@ -199,31 +200,33 @@ async function renderClient(pageContext, options) {
|
|
|
199
200
|
// Push fresh data into surviving layers (layers in the shared prefix).
|
|
200
201
|
// Without this, persistent layouts can't react to nav-driven data
|
|
201
202
|
// changes — pathname, breadcrumbs, session, nav-highlight state all
|
|
202
|
-
// belong to the layout but change on every client navigation.
|
|
203
|
-
// surviving layer's def can opt in via `propsMsg(data) => Msg`; we
|
|
204
|
-
// dispatch the resulting message through the handle's `send` so the
|
|
205
|
-
// layout's update loop processes it like any other state change.
|
|
203
|
+
// belong to the layout but change on every client navigation.
|
|
206
204
|
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
205
|
+
// The user-supplied `onLayerDataChange` callback receives the layer
|
|
206
|
+
// def, its AppHandle, the new data slice, and the previously-seen
|
|
207
|
+
// data slice. The user typically dispatches a state-update message
|
|
208
|
+
// through `handle.send`. Layers whose data slice is unchanged
|
|
209
|
+
// (shallow-key Object.is on records, whole-value Object.is for
|
|
210
|
+
// primitives) are skipped without calling the hook.
|
|
211
211
|
//
|
|
212
212
|
// This loop is layouts-only by construction: `firstMismatch` is
|
|
213
213
|
// bounded by `layoutChain.length` above, so indices [0, firstMismatch)
|
|
214
|
-
// never reach the page slot.
|
|
214
|
+
// never reach the page slot. If `onLayerDataChange` is undefined,
|
|
215
|
+
// surviving layers retain their existing state — opt-in.
|
|
215
216
|
for (let i = 0; i < firstMismatch; i++) {
|
|
216
217
|
const entry = chainHandles[i];
|
|
217
218
|
const newData = newChainData[i];
|
|
218
219
|
if (!hasDataChanged(entry.data, newData))
|
|
219
220
|
continue;
|
|
221
|
+
const prevData = entry.data;
|
|
220
222
|
entry.data = newData;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
if (options.onLayerDataChange) {
|
|
224
|
+
options.onLayerDataChange({
|
|
225
|
+
def: entry.def,
|
|
226
|
+
handle: entry.handle,
|
|
227
|
+
newData,
|
|
228
|
+
prevData,
|
|
229
|
+
});
|
|
227
230
|
}
|
|
228
231
|
}
|
|
229
232
|
// Determine whether this nav replaces the entire root or only a suffix.
|
|
@@ -402,7 +405,7 @@ const mountChainSuffix = _mountChainSuffix;
|
|
|
402
405
|
/**
|
|
403
406
|
* Shallow-key data diff for the persistent-layer prop-update path.
|
|
404
407
|
* Returns true when `next` differs from `prev` enough to warrant
|
|
405
|
-
* dispatching
|
|
408
|
+
* dispatching the user's `onLayerDataChange` hook:
|
|
406
409
|
*
|
|
407
410
|
* - `Object.is(prev, next)` short-circuits identical references.
|
|
408
411
|
* - For two plain-object records, walks the union of keys and returns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-render-client.js","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { hydrateApp, mountApp, mountAtAnchor, hydrateAtAnchor } from '@llui/dom'\nimport type { AnyComponentDef, AppHandle, TransitionOptions, Lifetime } from '@llui/dom'\nimport { _consumePendingSlot, _resetPendingSlot } from './page-slot.js'\nimport type { VikePageContextData } from './vike-namespace.js'\n\n// Re-exported so `@llui/vike/client` is a one-stop-shop for everything\n// a pages/+onRenderClient.ts / +Layout.ts file needs.\nexport { pageSlot } from './page-slot.js'\n\ndeclare global {\n interface Window {\n __LLUI_STATE__?: unknown\n }\n}\n\n/**\n * Page context shape as seen by `@llui/vike`'s client-side hooks. The\n * `Page` and `data` fields come from whichever `+Page.ts` and `+data.ts`\n * Vike resolved for the current route.\n *\n * `data` is derived from the global `Vike.PageContext` namespace — the\n * convention users already know from Vike. Consumer augmentations of\n * `Vike.PageContext { interface PageContext { data?: MyData } }` flow\n * through to every callback here without a cast. Unaugmented projects\n * fall back to `unknown`.\n *\n * `lluiLayoutData` is optional and carries per-layer data for the layout\n * chain configured via `createOnRenderClient({ Layout })`. It's indexed\n * outermost-to-innermost, one entry per layout layer. Absent entries\n * mean the corresponding layout's `init()` receives `undefined`. Users\n * wire this from their Vike `+data.ts` files by merging layout-owned\n * data under the `lluiLayoutData` key.\n */\nexport interface ClientPageContext {\n Page: AnyComponentDef\n data?: VikePageContextData\n lluiLayoutData?: readonly unknown[]\n isHydration?: boolean\n}\n\ntype LayoutChain = ReadonlyArray<AnyComponentDef>\n\n/**\n * Resolves the layout chain for a given pageContext. A single layout\n * becomes a one-element chain; a function resolver gives callers full\n * control to return different chains for different routes (e.g. nested\n * layouts keyed on Vike's `pageContext.urlPathname`).\n */\nfunction resolveLayoutChain(\n layoutOption: RenderClientOptions['Layout'],\n pageContext: ClientPageContext,\n): LayoutChain {\n if (!layoutOption) return []\n if (typeof layoutOption === 'function') {\n return layoutOption(pageContext) ?? []\n }\n if (Array.isArray(layoutOption)) return layoutOption\n return [layoutOption as AnyComponentDef]\n}\n\n/**\n * Page-lifecycle hooks that fire around the dispose → mount cycle on\n * client navigation. With persistent layouts in play the cycle only\n * tears down the *divergent* suffix of the layout chain — any layers\n * shared between the old and new routes stay mounted.\n *\n * Navigation sequence for an already-mounted app:\n *\n * ```\n * client nav triggered\n * │\n * ▼\n * compare old chain to new chain → find first mismatch index K\n * │\n * ▼\n * onLeave(leaveTarget) ← awaited; leaveTarget is the slot element\n * │ at depth K-1 (or the root container if K=0)\n * │ whose contents are about to be replaced\n * ▼\n * dispose chainHandles[K..end] innermost first\n * │\n * ▼\n * leaveTarget.textContent = ''\n * │\n * ▼\n * mount newChain[K..end] into leaveTarget, outermost first\n * │\n * ▼\n * onEnter(leaveTarget) ← fire-and-forget; fresh DOM in place\n * │\n * ▼\n * onMount()\n * ```\n *\n * On the initial hydration render, `onLeave` and `onEnter` are NOT\n * called — there's no outgoing page to leave and no animation to enter.\n * Use `onMount` for code that should run on every render including the\n * initial one.\n */\nexport interface RenderClientOptions {\n /** CSS selector for the mount container. Default: `'#app'`. */\n container?: string\n\n /**\n * Persistent layout chain. One of:\n *\n * - A single `ComponentDef` — becomes a one-layout chain.\n * - An array of `ComponentDef`s — outermost layout first, innermost\n * layout last. Every layer except the innermost must call\n * `pageSlot()` in its view to declare where nested content renders.\n * - A function that returns a chain from the current `pageContext` —\n * lets different routes use different chains, e.g. by reading\n * Vike's `pageContext.urlPathname` or `pageContext.config.Layout`.\n *\n * Layers that are shared between the previous and next navigation\n * stay mounted. Only the divergent suffix is disposed and re-mounted.\n * Dialogs, focus traps, and effect subscriptions rooted in a surviving\n * layer are unaffected by the nav.\n */\n Layout?: AnyComponentDef | LayoutChain | ((pageContext: ClientPageContext) => LayoutChain)\n\n /**\n * Called on the slot element whose contents are about to be replaced,\n * BEFORE the divergent suffix is disposed and re-mounted. The slot's\n * current DOM is still attached when this runs — the only moment a\n * leave animation can read/write it. Return a promise to defer the\n * swap until the animation completes.\n *\n * For a plain no-layout setup, the slot element is the root container.\n * Not called on the initial hydration render.\n */\n onLeave?: (el: HTMLElement) => void | Promise<void>\n\n /**\n * Called after the new divergent suffix is mounted, on the same slot\n * element that was passed to `onLeave`. Use this to kick off an enter\n * animation. Fire-and-forget — promise returns are ignored.\n *\n * Not called on the initial hydration render.\n */\n onEnter?: (el: HTMLElement) => void\n\n /**\n * Called after mount or hydration completes. Fires on every render\n * including the initial hydration. Use for per-render side effects\n * that don't fit the animation hooks.\n *\n * Receives the live layout chain — `[...layouts, page]`, outermost\n * first — as `AppHandle`s. Consumers wiring observability bridges,\n * the LAP agent client, custom devtools, or any tool that needs\n * `getState` / `send` / `subscribe` for the *outermost layout*\n * (which `window.__lluiComponents` did not reliably expose for\n * hydrated apps until @llui/dom@0.0.31) can read it from here:\n *\n * ```ts\n * createOnRenderClient({\n * Layout: AppLayout,\n * onMount: (chain) => {\n * const layout = chain[0] // outermost layout\n * const page = chain.at(-1) // current page\n * },\n * })\n * ```\n *\n * The array is a snapshot at call time; consumers should not retain\n * references to handles past the next navigation, since surviving\n * layers stay live but disposed layers do not.\n */\n onMount?: (chain: readonly AppHandle[]) => void\n\n /**\n * Forwarded to `@llui/dom`'s `hydrateApp` / `hydrateAtAnchor` for\n * every layer in the layout chain on initial hydration. When `true`,\n * effects returned by each component's `init()` are dispatched\n * post-swap on the client. When `false` (default), they are skipped\n * — the SSR pass already ran them on the server, and re-running on\n * the client typically produces duplicate fetches / subscriptions.\n *\n * Opt in only when:\n * - `init()` returns no effects, OR\n * - all returned effects are idempotent / client-only (e.g. attaching\n * a `window` listener), AND\n * - the SSR path didn't run them (typically because `init()` checks\n * a `loaded` flag in state and returns `[]` when serverState\n * already has the data loaded).\n *\n * Subsequent client-side navigation always uses `mountApp` /\n * `mountAtAnchor` (fresh mount), which always fires init effects\n * regardless of this flag.\n */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Adapt a `TransitionOptions` object (e.g. the output of\n * `routeTransition()` from `@llui/transitions`, or a preset like `fade`\n * / `slide`) into the `onLeave` / `onEnter` pair expected by\n * `createOnRenderClient`.\n *\n * ```ts\n * import { createOnRenderClient, fromTransition } from '@llui/vike/client'\n * import { routeTransition } from '@llui/transitions'\n *\n * export const onRenderClient = createOnRenderClient({\n * Layout: AppLayout,\n * ...fromTransition(routeTransition({ duration: 200 })),\n * })\n * ```\n *\n * The transition operates on the slot element — in a no-layout setup,\n * the root container; in a layout setup, the innermost surviving\n * layer's `pageSlot()` element. Opacity / transform fades apply to the\n * outgoing page content, then the new page fades in.\n */\nexport function fromTransition(\n t: TransitionOptions,\n): Pick<RenderClientOptions, 'onLeave' | 'onEnter'> {\n return {\n onLeave: t.leave\n ? (el): void | Promise<void> => {\n const result = t.leave!([el])\n return result && typeof (result as Promise<void>).then === 'function'\n ? (result as Promise<void>)\n : undefined\n }\n : undefined,\n onEnter: t.enter\n ? (el): void => {\n t.enter!([el])\n }\n : undefined,\n }\n}\n\n/**\n * One element of the live chain the adapter keeps between navs.\n * `handle` is the AppHandle returned by mountApp/hydrateApp for this\n * layer. `slotAnchor` / `slotLifetime` are set when the layer called\n * `pageSlot()` during its view pass; they're null for the innermost\n * layer (typically the page component, which doesn't have a slot).\n */\ninterface ChainEntry {\n def: AnyComponentDef\n handle: AppHandle\n slotAnchor: Comment | null\n slotLifetime: Lifetime | null\n /**\n * The data slice this layer was most recently mounted or updated\n * with. Compared shallow-key against the next nav's `lluiLayoutData[i]`\n * to decide whether a surviving layer needs a `propsMsg` dispatch.\n * Layers that didn't receive any layout data carry `undefined` here.\n */\n data: unknown\n}\n\n/**\n * Live chain of mounted layers — module-level singleton.\n *\n * Vike runs one client-side adapter per browser tab. Within one tab,\n * a single `chainHandles` array holds the AppHandle for every active\n * layer, indexed `[outermostLayout, ..., innerLayout, page]`. The\n * array mutates in place across navigations: shared layout layers\n * stay live, divergent suffix layers dispose, new layers append.\n *\n * **Module-level scope is correct for the browser**, where the\n * adapter has exactly one consumer per page load. It would be\n * INCORRECT in a long-running multi-tenant Node SSR worker that\n * imports `@llui/vike/client` and tries to render multiple requests\n * concurrently — every request would clobber the same array. That\n * usage isn't supported today (the client adapter assumes a browser\n * runtime; the SSR side lives in `@llui/vike/server`'s `_renderChain`\n * which keeps state per-call), but the constraint should be made\n * explicit if the adapter ever grows a Node SSR consumer. If you're\n * here to add such a consumer: convert `chainHandles` and the\n * pending-slot register to per-call locals threaded through the\n * adapter API instead of module state, and audit `getLayoutChain`\n * and `_resetChainForTest` for the same change.\n */\nlet chainHandles: ChainEntry[] = []\n\n/**\n * @internal — test helper. Disposes every layer in the current chain\n * and clears the module state so subsequent calls behave as a first\n * mount. Not part of the public API; subject to change without notice.\n */\nexport function _resetChainForTest(): void {\n // Dispose innermost-first to match the normal teardown path.\n for (let i = chainHandles.length - 1; i >= 0; i--) {\n chainHandles[i]!.handle.dispose()\n }\n chainHandles = []\n _resetPendingSlot()\n}\n\n/**\n * Back-compat alias for the pre-layout test helper name.\n * @internal\n * @deprecated — use `_resetChainForTest` instead.\n */\nexport function _resetCurrentHandleForTest(): void {\n _resetChainForTest()\n}\n\n/**\n * Default onRenderClient hook — no layout, no animation hooks. Hydrates\n * on first load, mounts fresh on subsequent navs. Use `createOnRenderClient`\n * for the customizable factory form.\n */\nexport async function onRenderClient(pageContext: ClientPageContext): Promise<void> {\n await renderClient(pageContext, {})\n}\n\n/**\n * Factory to create a customized onRenderClient hook. See `RenderClientOptions`\n * for the full option surface — this is the entry point for persistent\n * layouts, route transitions, and lifecycle hooks.\n *\n * **Do not name your layout file `+Layout.ts`.** Vike reserves the `+`\n * prefix for its own framework config conventions, and `+Layout.ts` is\n * interpreted by `vike-react` / `vike-vue` / `vike-solid` framework\n * adapters as a native layout config. `@llui/vike` isn't a framework\n * adapter in that sense — it's a render adapter, and `createOnRenderClient`\n * consumes the layout component directly via the `Layout` option. Name\n * the file `Layout.ts`, `app-layout.ts`, or anywhere outside `/pages`\n * that Vike won't scan, and import it here by path.\n *\n * ```ts\n * // pages/+onRenderClient.ts\n * import { createOnRenderClient, fromTransition } from '@llui/vike/client'\n * import { routeTransition } from '@llui/transitions'\n * import { AppLayout } from './Layout.js' // ← NOT './+Layout'\n *\n * export const onRenderClient = createOnRenderClient({\n * Layout: AppLayout,\n * ...fromTransition(routeTransition({ duration: 200 })),\n * onMount: () => console.log('page rendered'),\n * })\n * ```\n */\nexport function createOnRenderClient(\n options: RenderClientOptions,\n): (pageContext: ClientPageContext) => Promise<void> {\n return (pageContext) => renderClient(pageContext, options)\n}\n\nasync function renderClient(\n pageContext: ClientPageContext,\n options: RenderClientOptions,\n): Promise<void> {\n const selector = options.container ?? '#app'\n const container = document.querySelector(selector)\n if (!container) {\n throw new Error(`@llui/vike: container \"${selector}\" not found in DOM`)\n }\n const rootEl = container as HTMLElement\n\n // Resolve the chain for this render. The page component is always\n // the innermost entry, regardless of layout configuration.\n const layoutChain = resolveLayoutChain(options.Layout, pageContext)\n const layoutData = pageContext.lluiLayoutData ?? []\n const newChain: LayoutChain = [...layoutChain, pageContext.Page]\n const newChainData: readonly unknown[] = [...layoutData, pageContext.data]\n\n if (pageContext.isHydration) {\n // First load — the chain starts empty and we hydrate every layer\n // against server-rendered HTML. No onLeave/onEnter on hydration.\n await mountOrHydrateChain(newChain, newChainData, rootEl, {\n mode: 'hydrate',\n serverStateEnvelope: window.__LLUI_STATE__,\n runInitEffectsOnHydrate: options.runInitEffectsOnHydrate,\n })\n options.onMount?.(snapshotLayoutChain())\n return\n }\n\n // Subsequent nav — diff the layout chain to find the divergent suffix.\n //\n // The page (innermost entry, always stored at chainHandles[length-1])\n // is NEVER considered a surviving layer: every client navigation\n // disposes the current page and mounts fresh, even when the incoming\n // `pageContext.Page` happens to resolve to the same `ComponentDef`\n // reference as the outgoing one.\n //\n // Rationale: the persistent-layout feature is about keeping app\n // *chrome* alive across navigation — headers, sidebars, focus traps,\n // session state. The page, by definition, is the thing that changes\n // per route. Content-driven sites routinely share one `ComponentDef`\n // across many routes (e.g. a docs site where every `+Page.ts`\n // re-exports the same `DocPage` and per-route `+data.ts` supplies\n // the content). Treating same-def page navs as no-ops would freeze\n // those sites visually while the URL advances — a regression that\n // shipped in 0.0.26 and was reported against the llui.dev site.\n //\n // `propsMsg` is still honored for *layouts* that want to react to\n // nav-scoped data (pathname, session, breadcrumbs) without\n // remounting — see the loop below. The page is deliberately excluded\n // from that path because `init(data)` always re-runs for it.\n let firstMismatch = 0\n // `chainHandles` stores `[...layouts, page]`, so the layout prefix\n // length is `chainHandles.length - 1` (or 0 on first fresh mount, when\n // the chain is still empty). Bounding `minLen` by this length keeps\n // `firstMismatch` from ever advancing into the page slot.\n const prevLayoutLen = chainHandles.length === 0 ? 0 : chainHandles.length - 1\n const minLen = Math.min(prevLayoutLen, layoutChain.length)\n while (firstMismatch < minLen && chainHandles[firstMismatch]!.def === newChain[firstMismatch]) {\n firstMismatch++\n }\n\n // Push fresh data into surviving layers (layers in the shared prefix).\n // Without this, persistent layouts can't react to nav-driven data\n // changes — pathname, breadcrumbs, session, nav-highlight state all\n // belong to the layout but change on every client navigation. Each\n // surviving layer's def can opt in via `propsMsg(data) => Msg`; we\n // dispatch the resulting message through the handle's `send` so the\n // layout's update loop processes it like any other state change.\n //\n // Diff is shallow-key Object.is on record-shaped data, falling back\n // to whole-value Object.is for primitives / non-records. This matches\n // child()'s prop-diff behavior, which is what the report asked us to\n // mirror. Layers without `propsMsg` are skipped silently — opt-in.\n //\n // This loop is layouts-only by construction: `firstMismatch` is\n // bounded by `layoutChain.length` above, so indices [0, firstMismatch)\n // never reach the page slot.\n for (let i = 0; i < firstMismatch; i++) {\n const entry = chainHandles[i]!\n const newData = newChainData[i]\n if (!hasDataChanged(entry.data, newData)) continue\n entry.data = newData\n const propsMsg = (entry.def as { propsMsg?: (data: unknown) => unknown }).propsMsg\n if (typeof propsMsg !== 'function') continue\n const msg = propsMsg(newData)\n if (msg !== null && msg !== undefined) {\n entry.handle.send(msg)\n }\n }\n\n // Determine whether this nav replaces the entire root or only a suffix.\n // For the root swap, the outermost layer mounts/hydrates via mountApp/\n // hydrateApp on rootEl. For a deeper swap, the mount target is an\n // anchor comment owned by the surviving layer's slot. `firstMismatch\n // === 0` covers two cases: no layouts configured (page-only chain,\n // every nav is a root swap) and all layouts diverging (full re-render).\n const isRootSwap = firstMismatch === 0\n\n // onLeave runs BEFORE any teardown. Outgoing DOM still mounted here.\n // Skip on the very first mount — there's no outgoing page to leave.\n const isFirstMount = chainHandles.length === 0\n if (options.onLeave && !isFirstMount) {\n const leaveTargetEl = isRootSwap\n ? rootEl\n : (chainHandles[firstMismatch - 1]!.slotAnchor?.parentElement ?? rootEl)\n await options.onLeave(leaveTargetEl)\n }\n\n // Dispose the divergent suffix, innermost first. Each handle.dispose()\n // calls disposeLifetime on that layer's rootLifetime, which cascades through\n // every child scope the layer owned (bindings, portals, onMount\n // cleanups, dialog focus traps, etc.). The surviving layers are\n // untouched because their scopes live above the disposal roots.\n // For anchor-based mounts, dispose() also removes the owned DOM region\n // between the anchor and end sentinel — no additional textContent clear needed.\n for (let i = chainHandles.length - 1; i >= firstMismatch; i--) {\n chainHandles[i]!.handle.dispose()\n }\n chainHandles = chainHandles.slice(0, firstMismatch)\n\n // Mount the new suffix starting at firstMismatch.\n // For a root swap, the target is the container HTMLElement.\n // For a deeper swap, the target is the surviving layer's slot anchor (Comment).\n const parentLifetime =\n firstMismatch === 0 ? undefined : (chainHandles[firstMismatch - 1]!.slotLifetime ?? undefined)\n const mountTargetArg: HTMLElement | Comment =\n firstMismatch === 0 ? rootEl : chainHandles[firstMismatch - 1]!.slotAnchor!\n mountChainSuffix(newChain, newChainData, firstMismatch, mountTargetArg, parentLifetime, {\n mode: 'mount',\n })\n\n // onEnter fires after the new suffix is in place. Fire-and-forget.\n if (options.onEnter) {\n const enterTargetEl = isRootSwap\n ? rootEl\n : (chainHandles[firstMismatch - 1]!.slotAnchor?.parentElement ?? rootEl)\n options.onEnter(enterTargetEl)\n }\n options.onMount?.(snapshotLayoutChain())\n}\n\n/**\n * Public read of the current layout chain. Returns the live\n * `AppHandle`s for `[...layouts, page]`, outermost first. Empty array\n * before the first mount; updates after every navigation.\n *\n * Returns a fresh array each call, but the AppHandle references are\n * shared with the live chain — calling `.send()` / `.dispose()` /\n * `.subscribe()` operates on the same instance the framework manages.\n *\n * Prefer the `onMount(chain)` callback for lifecycle-coupled wiring\n * (the framework guarantees the chain is fully populated when it\n * fires); use this getter for ad-hoc reads where the caller can't\n * thread state through `onMount`.\n */\nexport function getLayoutChain(): readonly AppHandle[] {\n return snapshotLayoutChain()\n}\n\nfunction snapshotLayoutChain(): readonly AppHandle[] {\n return chainHandles.map((entry) => entry.handle)\n}\n\n/**\n * Walk the full chain for the first mount or hydration. Starts from\n * depth 0 at the root container, threads each layer's slot into the\n * next layer's mount target + parentLifetime.\n */\nasync function mountOrHydrateChain(\n chain: LayoutChain,\n chainData: readonly unknown[],\n rootEl: HTMLElement,\n opts: MountOpts,\n): Promise<void> {\n mountChainSuffix(chain, chainData, 0, rootEl, undefined, opts)\n}\n\ninterface MountOpts {\n mode: 'mount' | 'hydrate'\n /** For hydration: the full `window.__LLUI_STATE__` envelope. */\n serverStateEnvelope?: unknown\n /** Forwarded to `hydrateApp` / `hydrateAtAnchor`. Mount mode ignores. */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Mount (or hydrate) `chain[startAt..end]` into `initialTarget`, with\n * the initial layer's rootLifetime parented at `initialParentLifetime`.\n * Threads slot → next-target → next-parentLifetime through the chain.\n *\n * `initialTarget` is `HTMLElement` for the outermost layer (container-\n * based mount/hydrate) and `Comment` for inner layers that mount relative\n * to a `pageSlot()` anchor.\n *\n * Fails loudly if a non-innermost layer forgot to call `pageSlot()`,\n * or if the innermost layer called `pageSlot()` unnecessarily.\n *\n * @internal — test helper. Exported so `client-page-slot.test.ts` can\n * test anchor-mount/dispose contracts directly with hand-built DOM.\n * Not part of the public API.\n */\nexport function _mountChainSuffix(\n chain: LayoutChain,\n chainData: readonly unknown[],\n startAt: number,\n initialTarget: HTMLElement | Comment,\n initialParentLifetime: Lifetime | undefined,\n opts: MountOpts,\n): void {\n let mountTarget: HTMLElement | Comment = initialTarget\n let parentLifetime: Lifetime | undefined = initialParentLifetime\n\n for (let i = startAt; i < chain.length; i++) {\n const def = chain[i]!\n const layerData = chainData[i]\n const isInnermost = i === chain.length - 1\n\n // Defensive: clear any stale slot from a prior failed mount.\n _resetPendingSlot()\n\n let handle: AppHandle\n if (opts.mode === 'hydrate') {\n // Hydration envelope: each layer pulls its own state slice. The\n // envelope shape is `{ layouts: [...], page: {...} }` with each\n // entry carrying `{ name, state }`. We match by name so a server/\n // client mismatch throws with a clear error instead of silently\n // hydrating the wrong state into the wrong instance.\n const layerState = extractHydrationState(opts.serverStateEnvelope, i, chain.length, def)\n if (mountTarget.nodeType === 1) {\n // HTMLElement — outermost layer, use hydrateApp (container-based).\n // Cross from the type-erased AnyComponentDef back into a concrete\n // ComponentDef<unknown, unknown, unknown, unknown> for the mount\n // primitive's signature. The cast is safe — mountApp / hydrateApp\n // don't use the type parameters at runtime.\n handle = hydrateApp(\n mountTarget as HTMLElement,\n def as unknown as Parameters<typeof hydrateApp>[1],\n layerState,\n { parentLifetime, runInitEffectsOnHydrate: opts.runInitEffectsOnHydrate },\n )\n } else {\n // Comment anchor — inner layer, use hydrateAtAnchor.\n handle = hydrateAtAnchor(\n mountTarget as Comment,\n def as unknown as Parameters<typeof hydrateAtAnchor>[1],\n layerState as never,\n { parentLifetime, runInitEffectsOnHydrate: opts.runInitEffectsOnHydrate },\n )\n }\n } else {\n if (mountTarget.nodeType === 1) {\n // HTMLElement — outermost layer, use mountApp (container-based).\n handle = mountApp(\n mountTarget as HTMLElement,\n def as unknown as Parameters<typeof mountApp>[1],\n layerData,\n { parentLifetime },\n )\n } else {\n // Comment anchor — inner layer, use mountAtAnchor.\n handle = mountAtAnchor(\n mountTarget as Comment,\n def as unknown as Parameters<typeof mountAtAnchor>[1],\n layerData,\n { parentLifetime },\n )\n }\n }\n\n const slot = _consumePendingSlot()\n\n if (isInnermost && slot !== null) {\n // Innermost layer declared a slot with nothing to fill it —\n // probably a misuse of pageSlot() in the page component itself.\n handle.dispose()\n throw new Error(\n `[llui/vike] <${def.name}> is the innermost component in the chain ` +\n `but called pageSlot(). pageSlot() only belongs in layout components ` +\n `that wrap a nested page or layout — not in the page itself.`,\n )\n }\n if (!isInnermost && slot === null) {\n // Non-innermost layer didn't declare a slot — there's nowhere to\n // mount the remaining chain.\n handle.dispose()\n throw new Error(\n `[llui/vike] <${def.name}> is a layout layer at depth ${i} but did not ` +\n `call pageSlot() in its view(). There are ${chain.length - i - 1} more ` +\n `layer(s) to mount and no slot to mount them into. Add pageSlot() from ` +\n `@llui/vike/client to the view at the position where nested content renders.`,\n )\n }\n\n chainHandles.push({\n def,\n handle,\n slotAnchor: slot?.anchor ?? null,\n slotLifetime: slot?.slotLifetime ?? null,\n data: layerData,\n })\n\n if (slot !== null) {\n // Next layer mounts relative to the slot's comment anchor.\n mountTarget = slot.anchor\n parentLifetime = slot.slotLifetime\n }\n }\n}\n\n// Internal alias used by renderClient and mountOrHydrateChain.\n// The public-named export above carries the @internal doc.\nconst mountChainSuffix = _mountChainSuffix\n\n/**\n * Pull the per-layer state from the hydration envelope. Supports both\n * the new chain-aware shape (`{ layouts: [...], page: {...} }`) and the\n * legacy flat shape (`window.__LLUI_STATE__` is the state object itself)\n * for backward compatibility with pages written against 0.0.15 or earlier.\n *\n * Throws on envelope shape mismatch — missing entries, wrong component\n * name at a given index — so server/client drift fails loud instead of\n * silently binding the wrong state to the wrong instance.\n */\n/**\n * Shallow-key data diff for the persistent-layer prop-update path.\n * Returns true when `next` differs from `prev` enough to warrant\n * dispatching a `propsMsg`. Mirrors `child()`'s prop-diff semantics:\n *\n * - `Object.is(prev, next)` short-circuits identical references.\n * - For two plain-object records, walks the union of keys and returns\n * true on the first `Object.is` mismatch.\n * - For anything else (primitives, arrays, class instances), falls\n * back to the top-level `Object.is` result — covers the cases where\n * the host populates `lluiLayoutData[i]` with a primitive or a\n * referentially-stable object.\n */\nfunction hasDataChanged(prev: unknown, next: unknown): boolean {\n if (Object.is(prev, next)) return false\n // Both must be plain object records to do a key walk; otherwise the\n // Object.is above is the only signal.\n if (\n prev === null ||\n next === null ||\n typeof prev !== 'object' ||\n typeof next !== 'object' ||\n Array.isArray(prev) ||\n Array.isArray(next)\n ) {\n return true\n }\n const prevRec = prev as Record<string, unknown>\n const nextRec = next as Record<string, unknown>\n const seen = new Set<string>()\n for (const k of Object.keys(prevRec)) {\n seen.add(k)\n if (!Object.is(prevRec[k], nextRec[k])) return true\n }\n for (const k of Object.keys(nextRec)) {\n if (!seen.has(k)) return true\n }\n return false\n}\n\nfunction extractHydrationState(\n envelope: unknown,\n layerIndex: number,\n chainLength: number,\n def: AnyComponentDef,\n): unknown {\n // Legacy flat envelope — no layout chain at render time. Only valid\n // when the chain has a single layer (the page).\n const isLegacyFlat =\n envelope !== null &&\n typeof envelope === 'object' &&\n !('layouts' in (envelope as object)) &&\n !('page' in (envelope as object))\n\n if (isLegacyFlat) {\n if (chainLength !== 1) {\n throw new Error(\n `[llui/vike] Hydration envelope is in the legacy flat shape but the ` +\n `current render has ${chainLength} chain layers. The server must emit ` +\n `the chain-aware shape ({ layouts, page }) when rendering with a layout.`,\n )\n }\n return envelope\n }\n\n const chainEnvelope = envelope as\n | { layouts?: Array<{ name: string; state: unknown }>; page?: { name: string; state: unknown } }\n | undefined\n if (!chainEnvelope) {\n throw new Error(\n `[llui/vike] Hydration envelope is missing. Server-side onRenderHtml must ` +\n `populate window.__LLUI_STATE__ with the full chain before client hydration.`,\n )\n }\n\n const isPageLayer = layerIndex === chainLength - 1\n const layoutEntries = chainEnvelope.layouts ?? []\n const expected = isPageLayer ? chainEnvelope.page : layoutEntries[layerIndex]\n\n if (!expected) {\n throw new Error(\n `[llui/vike] Hydration envelope has no entry for chain layer ${layerIndex} ` +\n `(<${def.name}>). Server rendered ${layoutEntries.length} layouts + ${\n chainEnvelope.page ? 'a page' : 'no page'\n }, client expected ${chainLength} total entries.`,\n )\n }\n\n if (expected.name !== def.name) {\n throw new Error(\n `[llui/vike] Hydration mismatch at chain layer ${layerIndex}: server ` +\n `rendered <${expected.name}> but client is trying to hydrate <${def.name}>. ` +\n `This usually means the layout chain resolver returns different layouts ` +\n `on the server and the client for the same route.`,\n )\n }\n\n return expected.state\n}\n"]}
|
|
1
|
+
{"version":3,"file":"on-render-client.js","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { hydrateApp, mountApp, mountAtAnchor, hydrateAtAnchor } from '@llui/dom'\nimport type { AnyComponentDef, AppHandle, TransitionOptions, Lifetime } from '@llui/dom'\nimport { _consumePendingSlot, _resetPendingSlot } from './page-slot.js'\nimport type { VikePageContextData } from './vike-namespace.js'\n\n// Re-exported so `@llui/vike/client` is a one-stop-shop for everything\n// a pages/+onRenderClient.ts / +Layout.ts file needs.\nexport { pageSlot } from './page-slot.js'\n\ndeclare global {\n interface Window {\n __LLUI_STATE__?: unknown\n }\n}\n\n/**\n * Page context shape as seen by `@llui/vike`'s client-side hooks. The\n * `Page` and `data` fields come from whichever `+Page.ts` and `+data.ts`\n * Vike resolved for the current route.\n *\n * `data` is derived from the global `Vike.PageContext` namespace — the\n * convention users already know from Vike. Consumer augmentations of\n * `Vike.PageContext { interface PageContext { data?: MyData } }` flow\n * through to every callback here without a cast. Unaugmented projects\n * fall back to `unknown`.\n *\n * `lluiLayoutData` is optional and carries per-layer data for the layout\n * chain configured via `createOnRenderClient({ Layout })`. It's indexed\n * outermost-to-innermost, one entry per layout layer. Absent entries\n * mean the corresponding layout's `init()` receives `undefined`. Users\n * wire this from their Vike `+data.ts` files by merging layout-owned\n * data under the `lluiLayoutData` key.\n */\nexport interface ClientPageContext {\n Page: AnyComponentDef\n data?: VikePageContextData\n lluiLayoutData?: readonly unknown[]\n isHydration?: boolean\n}\n\ntype LayoutChain = ReadonlyArray<AnyComponentDef>\n\n/**\n * Resolves the layout chain for a given pageContext. A single layout\n * becomes a one-element chain; a function resolver gives callers full\n * control to return different chains for different routes (e.g. nested\n * layouts keyed on Vike's `pageContext.urlPathname`).\n */\nfunction resolveLayoutChain(\n layoutOption: RenderClientOptions['Layout'],\n pageContext: ClientPageContext,\n): LayoutChain {\n if (!layoutOption) return []\n if (typeof layoutOption === 'function') {\n return layoutOption(pageContext) ?? []\n }\n if (Array.isArray(layoutOption)) return layoutOption\n return [layoutOption as AnyComponentDef]\n}\n\n/**\n * Page-lifecycle hooks that fire around the dispose → mount cycle on\n * client navigation. With persistent layouts in play the cycle only\n * tears down the *divergent* suffix of the layout chain — any layers\n * shared between the old and new routes stay mounted.\n *\n * Navigation sequence for an already-mounted app:\n *\n * ```\n * client nav triggered\n * │\n * ▼\n * compare old chain to new chain → find first mismatch index K\n * │\n * ▼\n * onLeave(leaveTarget) ← awaited; leaveTarget is the slot element\n * │ at depth K-1 (or the root container if K=0)\n * │ whose contents are about to be replaced\n * ▼\n * dispose chainHandles[K..end] innermost first\n * │\n * ▼\n * leaveTarget.textContent = ''\n * │\n * ▼\n * mount newChain[K..end] into leaveTarget, outermost first\n * │\n * ▼\n * onEnter(leaveTarget) ← fire-and-forget; fresh DOM in place\n * │\n * ▼\n * onMount()\n * ```\n *\n * On the initial hydration render, `onLeave` and `onEnter` are NOT\n * called — there's no outgoing page to leave and no animation to enter.\n * Use `onMount` for code that should run on every render including the\n * initial one.\n */\nexport interface RenderClientOptions {\n /** CSS selector for the mount container. Default: `'#app'`. */\n container?: string\n\n /**\n * Persistent layout chain. One of:\n *\n * - A single `ComponentDef` — becomes a one-layout chain.\n * - An array of `ComponentDef`s — outermost layout first, innermost\n * layout last. Every layer except the innermost must call\n * `pageSlot()` in its view to declare where nested content renders.\n * - A function that returns a chain from the current `pageContext` —\n * lets different routes use different chains, e.g. by reading\n * Vike's `pageContext.urlPathname` or `pageContext.config.Layout`.\n *\n * Layers that are shared between the previous and next navigation\n * stay mounted. Only the divergent suffix is disposed and re-mounted.\n * Dialogs, focus traps, and effect subscriptions rooted in a surviving\n * layer are unaffected by the nav.\n */\n Layout?: AnyComponentDef | LayoutChain | ((pageContext: ClientPageContext) => LayoutChain)\n\n /**\n * Called on the slot element whose contents are about to be replaced,\n * BEFORE the divergent suffix is disposed and re-mounted. The slot's\n * current DOM is still attached when this runs — the only moment a\n * leave animation can read/write it. Return a promise to defer the\n * swap until the animation completes.\n *\n * For a plain no-layout setup, the slot element is the root container.\n * Not called on the initial hydration render.\n */\n onLeave?: (el: HTMLElement) => void | Promise<void>\n\n /**\n * Called after the new divergent suffix is mounted, on the same slot\n * element that was passed to `onLeave`. Use this to kick off an enter\n * animation. Fire-and-forget — promise returns are ignored.\n *\n * Not called on the initial hydration render.\n */\n onEnter?: (el: HTMLElement) => void\n\n /**\n * Called after mount or hydration completes. Fires on every render\n * including the initial hydration. Use for per-render side effects\n * that don't fit the animation hooks.\n *\n * Receives the live layout chain — `[...layouts, page]`, outermost\n * first — as `AppHandle`s. Consumers wiring observability bridges,\n * the LAP agent client, custom devtools, or any tool that needs\n * `getState` / `send` / `subscribe` for the *outermost layout*\n * (which `window.__lluiComponents` did not reliably expose for\n * hydrated apps until @llui/dom@0.0.31) can read it from here:\n *\n * ```ts\n * createOnRenderClient({\n * Layout: AppLayout,\n * onMount: (chain) => {\n * const layout = chain[0] // outermost layout\n * const page = chain.at(-1) // current page\n * },\n * })\n * ```\n *\n * The array is a snapshot at call time; consumers should not retain\n * references to handles past the next navigation, since surviving\n * layers stay live but disposed layers do not.\n */\n onMount?: (chain: readonly AppHandle[]) => void\n\n /**\n * Called for each surviving layout layer whose `lluiLayoutData[i]`\n * slice changed across a client navigation. Surviving layers are\n * layers shared between the previous and current chain — they stay\n * mounted, but their state needs a fresh injection of nav-driven\n * data (pathname, breadcrumbs, session, …).\n *\n * The framework gives you the layer's `AppHandle` and the changed\n * data; you decide how to translate it into a state-update message\n * and dispatch it through `handle.send(msg)`. Typically:\n *\n * ```ts\n * createOnRenderClient({\n * Layout: NavAwareLayout,\n * onLayerDataChange: ({ def, handle, newData }) => {\n * if (def === NavAwareLayout) {\n * handle.send({ type: 'navChanged', data: newData as NavData })\n * }\n * },\n * })\n * ```\n *\n * Not called for layers whose data slice is unchanged (shallow-key\n * `Object.is` diff on records, whole-value `Object.is` for\n * primitives). Not called on the initial hydration render — `init`\n * handles the initial data injection there. Not called for the\n * page layer (the innermost entry); the page always disposes and\n * remounts, so its `init(data)` receives the fresh data directly.\n *\n * If omitted, surviving layers retain their existing state across\n * navigations. Opt in only when the layout needs to react to\n * nav-scoped data changes.\n */\n onLayerDataChange?: (ctx: {\n def: AnyComponentDef\n handle: AppHandle\n newData: unknown\n prevData: unknown\n }) => void\n\n /**\n * Forwarded to `@llui/dom`'s `hydrateApp` / `hydrateAtAnchor` for\n * every layer in the layout chain on initial hydration. When `true`,\n * effects returned by each component's `init()` are dispatched\n * post-swap on the client. When `false` (default), they are skipped\n * — the SSR pass already ran them on the server, and re-running on\n * the client typically produces duplicate fetches / subscriptions.\n *\n * Opt in only when:\n * - `init()` returns no effects, OR\n * - all returned effects are idempotent / client-only (e.g. attaching\n * a `window` listener), AND\n * - the SSR path didn't run them (typically because `init()` checks\n * a `loaded` flag in state and returns `[]` when serverState\n * already has the data loaded).\n *\n * Subsequent client-side navigation always uses `mountApp` /\n * `mountAtAnchor` (fresh mount), which always fires init effects\n * regardless of this flag.\n */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Adapt a `TransitionOptions` object (e.g. the output of\n * `routeTransition()` from `@llui/transitions`, or a preset like `fade`\n * / `slide`) into the `onLeave` / `onEnter` pair expected by\n * `createOnRenderClient`.\n *\n * ```ts\n * import { createOnRenderClient, fromTransition } from '@llui/vike/client'\n * import { routeTransition } from '@llui/transitions'\n *\n * export const onRenderClient = createOnRenderClient({\n * Layout: AppLayout,\n * ...fromTransition(routeTransition({ duration: 200 })),\n * })\n * ```\n *\n * The transition operates on the slot element — in a no-layout setup,\n * the root container; in a layout setup, the innermost surviving\n * layer's `pageSlot()` element. Opacity / transform fades apply to the\n * outgoing page content, then the new page fades in.\n */\nexport function fromTransition(\n t: TransitionOptions,\n): Pick<RenderClientOptions, 'onLeave' | 'onEnter'> {\n return {\n onLeave: t.leave\n ? (el): void | Promise<void> => {\n const result = t.leave!([el])\n return result && typeof (result as Promise<void>).then === 'function'\n ? (result as Promise<void>)\n : undefined\n }\n : undefined,\n onEnter: t.enter\n ? (el): void => {\n t.enter!([el])\n }\n : undefined,\n }\n}\n\n/**\n * One element of the live chain the adapter keeps between navs.\n * `handle` is the AppHandle returned by mountApp/hydrateApp for this\n * layer. `slotAnchor` / `slotLifetime` are set when the layer called\n * `pageSlot()` during its view pass; they're null for the innermost\n * layer (typically the page component, which doesn't have a slot).\n */\ninterface ChainEntry {\n def: AnyComponentDef\n handle: AppHandle\n slotAnchor: Comment | null\n slotLifetime: Lifetime | null\n /**\n * The data slice this layer was most recently mounted or updated\n * with. Compared shallow-key against the next nav's `lluiLayoutData[i]`\n * to decide whether a surviving layer needs the `onLayerDataChange`\n * hook to fire. Layers that didn't receive any layout data carry\n * `undefined` here.\n */\n data: unknown\n}\n\n/**\n * Live chain of mounted layers — module-level singleton.\n *\n * Vike runs one client-side adapter per browser tab. Within one tab,\n * a single `chainHandles` array holds the AppHandle for every active\n * layer, indexed `[outermostLayout, ..., innerLayout, page]`. The\n * array mutates in place across navigations: shared layout layers\n * stay live, divergent suffix layers dispose, new layers append.\n *\n * **Module-level scope is correct for the browser**, where the\n * adapter has exactly one consumer per page load. It would be\n * INCORRECT in a long-running multi-tenant Node SSR worker that\n * imports `@llui/vike/client` and tries to render multiple requests\n * concurrently — every request would clobber the same array. That\n * usage isn't supported today (the client adapter assumes a browser\n * runtime; the SSR side lives in `@llui/vike/server`'s `_renderChain`\n * which keeps state per-call), but the constraint should be made\n * explicit if the adapter ever grows a Node SSR consumer. If you're\n * here to add such a consumer: convert `chainHandles` and the\n * pending-slot register to per-call locals threaded through the\n * adapter API instead of module state, and audit `getLayoutChain`\n * and `_resetChainForTest` for the same change.\n */\nlet chainHandles: ChainEntry[] = []\n\n/**\n * @internal — test helper. Disposes every layer in the current chain\n * and clears the module state so subsequent calls behave as a first\n * mount. Not part of the public API; subject to change without notice.\n */\nexport function _resetChainForTest(): void {\n // Dispose innermost-first to match the normal teardown path.\n for (let i = chainHandles.length - 1; i >= 0; i--) {\n chainHandles[i]!.handle.dispose()\n }\n chainHandles = []\n _resetPendingSlot()\n}\n\n/**\n * Back-compat alias for the pre-layout test helper name.\n * @internal\n * @deprecated — use `_resetChainForTest` instead.\n */\nexport function _resetCurrentHandleForTest(): void {\n _resetChainForTest()\n}\n\n/**\n * Default onRenderClient hook — no layout, no animation hooks. Hydrates\n * on first load, mounts fresh on subsequent navs. Use `createOnRenderClient`\n * for the customizable factory form.\n */\nexport async function onRenderClient(pageContext: ClientPageContext): Promise<void> {\n await renderClient(pageContext, {})\n}\n\n/**\n * Factory to create a customized onRenderClient hook. See `RenderClientOptions`\n * for the full option surface — this is the entry point for persistent\n * layouts, route transitions, and lifecycle hooks.\n *\n * **Do not name your layout file `+Layout.ts`.** Vike reserves the `+`\n * prefix for its own framework config conventions, and `+Layout.ts` is\n * interpreted by `vike-react` / `vike-vue` / `vike-solid` framework\n * adapters as a native layout config. `@llui/vike` isn't a framework\n * adapter in that sense — it's a render adapter, and `createOnRenderClient`\n * consumes the layout component directly via the `Layout` option. Name\n * the file `Layout.ts`, `app-layout.ts`, or anywhere outside `/pages`\n * that Vike won't scan, and import it here by path.\n *\n * ```ts\n * // pages/+onRenderClient.ts\n * import { createOnRenderClient, fromTransition } from '@llui/vike/client'\n * import { routeTransition } from '@llui/transitions'\n * import { AppLayout } from './Layout.js' // ← NOT './+Layout'\n *\n * export const onRenderClient = createOnRenderClient({\n * Layout: AppLayout,\n * ...fromTransition(routeTransition({ duration: 200 })),\n * onMount: () => console.log('page rendered'),\n * })\n * ```\n */\nexport function createOnRenderClient(\n options: RenderClientOptions,\n): (pageContext: ClientPageContext) => Promise<void> {\n return (pageContext) => renderClient(pageContext, options)\n}\n\nasync function renderClient(\n pageContext: ClientPageContext,\n options: RenderClientOptions,\n): Promise<void> {\n const selector = options.container ?? '#app'\n const container = document.querySelector(selector)\n if (!container) {\n throw new Error(`@llui/vike: container \"${selector}\" not found in DOM`)\n }\n const rootEl = container as HTMLElement\n\n // Resolve the chain for this render. The page component is always\n // the innermost entry, regardless of layout configuration.\n const layoutChain = resolveLayoutChain(options.Layout, pageContext)\n const layoutData = pageContext.lluiLayoutData ?? []\n const newChain: LayoutChain = [...layoutChain, pageContext.Page]\n const newChainData: readonly unknown[] = [...layoutData, pageContext.data]\n\n if (pageContext.isHydration) {\n // First load — the chain starts empty and we hydrate every layer\n // against server-rendered HTML. No onLeave/onEnter on hydration.\n await mountOrHydrateChain(newChain, newChainData, rootEl, {\n mode: 'hydrate',\n serverStateEnvelope: window.__LLUI_STATE__,\n runInitEffectsOnHydrate: options.runInitEffectsOnHydrate,\n })\n options.onMount?.(snapshotLayoutChain())\n return\n }\n\n // Subsequent nav — diff the layout chain to find the divergent suffix.\n //\n // The page (innermost entry, always stored at chainHandles[length-1])\n // is NEVER considered a surviving layer: every client navigation\n // disposes the current page and mounts fresh, even when the incoming\n // `pageContext.Page` happens to resolve to the same `ComponentDef`\n // reference as the outgoing one.\n //\n // Rationale: the persistent-layout feature is about keeping app\n // *chrome* alive across navigation — headers, sidebars, focus traps,\n // session state. The page, by definition, is the thing that changes\n // per route. Content-driven sites routinely share one `ComponentDef`\n // across many routes (e.g. a docs site where every `+Page.ts`\n // re-exports the same `DocPage` and per-route `+data.ts` supplies\n // the content). Treating same-def page navs as no-ops would freeze\n // those sites visually while the URL advances — a regression that\n // shipped in 0.0.26 and was reported against the llui.dev site.\n //\n // The user-supplied `onLayerDataChange` callback fires for *layouts*\n // that want to react to nav-scoped data (pathname, session,\n // breadcrumbs) without remounting — see the loop below. The page is\n // deliberately excluded from that path because `init(data)` always\n // re-runs for it.\n let firstMismatch = 0\n // `chainHandles` stores `[...layouts, page]`, so the layout prefix\n // length is `chainHandles.length - 1` (or 0 on first fresh mount, when\n // the chain is still empty). Bounding `minLen` by this length keeps\n // `firstMismatch` from ever advancing into the page slot.\n const prevLayoutLen = chainHandles.length === 0 ? 0 : chainHandles.length - 1\n const minLen = Math.min(prevLayoutLen, layoutChain.length)\n while (firstMismatch < minLen && chainHandles[firstMismatch]!.def === newChain[firstMismatch]) {\n firstMismatch++\n }\n\n // Push fresh data into surviving layers (layers in the shared prefix).\n // Without this, persistent layouts can't react to nav-driven data\n // changes — pathname, breadcrumbs, session, nav-highlight state all\n // belong to the layout but change on every client navigation.\n //\n // The user-supplied `onLayerDataChange` callback receives the layer\n // def, its AppHandle, the new data slice, and the previously-seen\n // data slice. The user typically dispatches a state-update message\n // through `handle.send`. Layers whose data slice is unchanged\n // (shallow-key Object.is on records, whole-value Object.is for\n // primitives) are skipped without calling the hook.\n //\n // This loop is layouts-only by construction: `firstMismatch` is\n // bounded by `layoutChain.length` above, so indices [0, firstMismatch)\n // never reach the page slot. If `onLayerDataChange` is undefined,\n // surviving layers retain their existing state — opt-in.\n for (let i = 0; i < firstMismatch; i++) {\n const entry = chainHandles[i]!\n const newData = newChainData[i]\n if (!hasDataChanged(entry.data, newData)) continue\n const prevData = entry.data\n entry.data = newData\n if (options.onLayerDataChange) {\n options.onLayerDataChange({\n def: entry.def,\n handle: entry.handle,\n newData,\n prevData,\n })\n }\n }\n\n // Determine whether this nav replaces the entire root or only a suffix.\n // For the root swap, the outermost layer mounts/hydrates via mountApp/\n // hydrateApp on rootEl. For a deeper swap, the mount target is an\n // anchor comment owned by the surviving layer's slot. `firstMismatch\n // === 0` covers two cases: no layouts configured (page-only chain,\n // every nav is a root swap) and all layouts diverging (full re-render).\n const isRootSwap = firstMismatch === 0\n\n // onLeave runs BEFORE any teardown. Outgoing DOM still mounted here.\n // Skip on the very first mount — there's no outgoing page to leave.\n const isFirstMount = chainHandles.length === 0\n if (options.onLeave && !isFirstMount) {\n const leaveTargetEl = isRootSwap\n ? rootEl\n : (chainHandles[firstMismatch - 1]!.slotAnchor?.parentElement ?? rootEl)\n await options.onLeave(leaveTargetEl)\n }\n\n // Dispose the divergent suffix, innermost first. Each handle.dispose()\n // calls disposeLifetime on that layer's rootLifetime, which cascades through\n // every child scope the layer owned (bindings, portals, onMount\n // cleanups, dialog focus traps, etc.). The surviving layers are\n // untouched because their scopes live above the disposal roots.\n // For anchor-based mounts, dispose() also removes the owned DOM region\n // between the anchor and end sentinel — no additional textContent clear needed.\n for (let i = chainHandles.length - 1; i >= firstMismatch; i--) {\n chainHandles[i]!.handle.dispose()\n }\n chainHandles = chainHandles.slice(0, firstMismatch)\n\n // Mount the new suffix starting at firstMismatch.\n // For a root swap, the target is the container HTMLElement.\n // For a deeper swap, the target is the surviving layer's slot anchor (Comment).\n const parentLifetime =\n firstMismatch === 0 ? undefined : (chainHandles[firstMismatch - 1]!.slotLifetime ?? undefined)\n const mountTargetArg: HTMLElement | Comment =\n firstMismatch === 0 ? rootEl : chainHandles[firstMismatch - 1]!.slotAnchor!\n mountChainSuffix(newChain, newChainData, firstMismatch, mountTargetArg, parentLifetime, {\n mode: 'mount',\n })\n\n // onEnter fires after the new suffix is in place. Fire-and-forget.\n if (options.onEnter) {\n const enterTargetEl = isRootSwap\n ? rootEl\n : (chainHandles[firstMismatch - 1]!.slotAnchor?.parentElement ?? rootEl)\n options.onEnter(enterTargetEl)\n }\n options.onMount?.(snapshotLayoutChain())\n}\n\n/**\n * Public read of the current layout chain. Returns the live\n * `AppHandle`s for `[...layouts, page]`, outermost first. Empty array\n * before the first mount; updates after every navigation.\n *\n * Returns a fresh array each call, but the AppHandle references are\n * shared with the live chain — calling `.send()` / `.dispose()` /\n * `.subscribe()` operates on the same instance the framework manages.\n *\n * Prefer the `onMount(chain)` callback for lifecycle-coupled wiring\n * (the framework guarantees the chain is fully populated when it\n * fires); use this getter for ad-hoc reads where the caller can't\n * thread state through `onMount`.\n */\nexport function getLayoutChain(): readonly AppHandle[] {\n return snapshotLayoutChain()\n}\n\nfunction snapshotLayoutChain(): readonly AppHandle[] {\n return chainHandles.map((entry) => entry.handle)\n}\n\n/**\n * Walk the full chain for the first mount or hydration. Starts from\n * depth 0 at the root container, threads each layer's slot into the\n * next layer's mount target + parentLifetime.\n */\nasync function mountOrHydrateChain(\n chain: LayoutChain,\n chainData: readonly unknown[],\n rootEl: HTMLElement,\n opts: MountOpts,\n): Promise<void> {\n mountChainSuffix(chain, chainData, 0, rootEl, undefined, opts)\n}\n\ninterface MountOpts {\n mode: 'mount' | 'hydrate'\n /** For hydration: the full `window.__LLUI_STATE__` envelope. */\n serverStateEnvelope?: unknown\n /** Forwarded to `hydrateApp` / `hydrateAtAnchor`. Mount mode ignores. */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Mount (or hydrate) `chain[startAt..end]` into `initialTarget`, with\n * the initial layer's rootLifetime parented at `initialParentLifetime`.\n * Threads slot → next-target → next-parentLifetime through the chain.\n *\n * `initialTarget` is `HTMLElement` for the outermost layer (container-\n * based mount/hydrate) and `Comment` for inner layers that mount relative\n * to a `pageSlot()` anchor.\n *\n * Fails loudly if a non-innermost layer forgot to call `pageSlot()`,\n * or if the innermost layer called `pageSlot()` unnecessarily.\n *\n * @internal — test helper. Exported so `client-page-slot.test.ts` can\n * test anchor-mount/dispose contracts directly with hand-built DOM.\n * Not part of the public API.\n */\nexport function _mountChainSuffix(\n chain: LayoutChain,\n chainData: readonly unknown[],\n startAt: number,\n initialTarget: HTMLElement | Comment,\n initialParentLifetime: Lifetime | undefined,\n opts: MountOpts,\n): void {\n let mountTarget: HTMLElement | Comment = initialTarget\n let parentLifetime: Lifetime | undefined = initialParentLifetime\n\n for (let i = startAt; i < chain.length; i++) {\n const def = chain[i]!\n const layerData = chainData[i]\n const isInnermost = i === chain.length - 1\n\n // Defensive: clear any stale slot from a prior failed mount.\n _resetPendingSlot()\n\n let handle: AppHandle\n if (opts.mode === 'hydrate') {\n // Hydration envelope: each layer pulls its own state slice. The\n // envelope shape is `{ layouts: [...], page: {...} }` with each\n // entry carrying `{ name, state }`. We match by name so a server/\n // client mismatch throws with a clear error instead of silently\n // hydrating the wrong state into the wrong instance.\n const layerState = extractHydrationState(opts.serverStateEnvelope, i, chain.length, def)\n if (mountTarget.nodeType === 1) {\n // HTMLElement — outermost layer, use hydrateApp (container-based).\n // Cross from the type-erased AnyComponentDef back into a concrete\n // ComponentDef<unknown, unknown, unknown, unknown> for the mount\n // primitive's signature. The cast is safe — mountApp / hydrateApp\n // don't use the type parameters at runtime.\n handle = hydrateApp(\n mountTarget as HTMLElement,\n def as unknown as Parameters<typeof hydrateApp>[1],\n layerState,\n { parentLifetime, runInitEffectsOnHydrate: opts.runInitEffectsOnHydrate },\n )\n } else {\n // Comment anchor — inner layer, use hydrateAtAnchor.\n handle = hydrateAtAnchor(\n mountTarget as Comment,\n def as unknown as Parameters<typeof hydrateAtAnchor>[1],\n layerState as never,\n { parentLifetime, runInitEffectsOnHydrate: opts.runInitEffectsOnHydrate },\n )\n }\n } else {\n if (mountTarget.nodeType === 1) {\n // HTMLElement — outermost layer, use mountApp (container-based).\n handle = mountApp(\n mountTarget as HTMLElement,\n def as unknown as Parameters<typeof mountApp>[1],\n layerData,\n { parentLifetime },\n )\n } else {\n // Comment anchor — inner layer, use mountAtAnchor.\n handle = mountAtAnchor(\n mountTarget as Comment,\n def as unknown as Parameters<typeof mountAtAnchor>[1],\n layerData,\n { parentLifetime },\n )\n }\n }\n\n const slot = _consumePendingSlot()\n\n if (isInnermost && slot !== null) {\n // Innermost layer declared a slot with nothing to fill it —\n // probably a misuse of pageSlot() in the page component itself.\n handle.dispose()\n throw new Error(\n `[llui/vike] <${def.name}> is the innermost component in the chain ` +\n `but called pageSlot(). pageSlot() only belongs in layout components ` +\n `that wrap a nested page or layout — not in the page itself.`,\n )\n }\n if (!isInnermost && slot === null) {\n // Non-innermost layer didn't declare a slot — there's nowhere to\n // mount the remaining chain.\n handle.dispose()\n throw new Error(\n `[llui/vike] <${def.name}> is a layout layer at depth ${i} but did not ` +\n `call pageSlot() in its view(). There are ${chain.length - i - 1} more ` +\n `layer(s) to mount and no slot to mount them into. Add pageSlot() from ` +\n `@llui/vike/client to the view at the position where nested content renders.`,\n )\n }\n\n chainHandles.push({\n def,\n handle,\n slotAnchor: slot?.anchor ?? null,\n slotLifetime: slot?.slotLifetime ?? null,\n data: layerData,\n })\n\n if (slot !== null) {\n // Next layer mounts relative to the slot's comment anchor.\n mountTarget = slot.anchor\n parentLifetime = slot.slotLifetime\n }\n }\n}\n\n// Internal alias used by renderClient and mountOrHydrateChain.\n// The public-named export above carries the @internal doc.\nconst mountChainSuffix = _mountChainSuffix\n\n/**\n * Pull the per-layer state from the hydration envelope. Supports both\n * the new chain-aware shape (`{ layouts: [...], page: {...} }`) and the\n * legacy flat shape (`window.__LLUI_STATE__` is the state object itself)\n * for backward compatibility with pages written against 0.0.15 or earlier.\n *\n * Throws on envelope shape mismatch — missing entries, wrong component\n * name at a given index — so server/client drift fails loud instead of\n * silently binding the wrong state to the wrong instance.\n */\n/**\n * Shallow-key data diff for the persistent-layer prop-update path.\n * Returns true when `next` differs from `prev` enough to warrant\n * dispatching the user's `onLayerDataChange` hook:\n *\n * - `Object.is(prev, next)` short-circuits identical references.\n * - For two plain-object records, walks the union of keys and returns\n * true on the first `Object.is` mismatch.\n * - For anything else (primitives, arrays, class instances), falls\n * back to the top-level `Object.is` result — covers the cases where\n * the host populates `lluiLayoutData[i]` with a primitive or a\n * referentially-stable object.\n */\nfunction hasDataChanged(prev: unknown, next: unknown): boolean {\n if (Object.is(prev, next)) return false\n // Both must be plain object records to do a key walk; otherwise the\n // Object.is above is the only signal.\n if (\n prev === null ||\n next === null ||\n typeof prev !== 'object' ||\n typeof next !== 'object' ||\n Array.isArray(prev) ||\n Array.isArray(next)\n ) {\n return true\n }\n const prevRec = prev as Record<string, unknown>\n const nextRec = next as Record<string, unknown>\n const seen = new Set<string>()\n for (const k of Object.keys(prevRec)) {\n seen.add(k)\n if (!Object.is(prevRec[k], nextRec[k])) return true\n }\n for (const k of Object.keys(nextRec)) {\n if (!seen.has(k)) return true\n }\n return false\n}\n\nfunction extractHydrationState(\n envelope: unknown,\n layerIndex: number,\n chainLength: number,\n def: AnyComponentDef,\n): unknown {\n // Legacy flat envelope — no layout chain at render time. Only valid\n // when the chain has a single layer (the page).\n const isLegacyFlat =\n envelope !== null &&\n typeof envelope === 'object' &&\n !('layouts' in (envelope as object)) &&\n !('page' in (envelope as object))\n\n if (isLegacyFlat) {\n if (chainLength !== 1) {\n throw new Error(\n `[llui/vike] Hydration envelope is in the legacy flat shape but the ` +\n `current render has ${chainLength} chain layers. The server must emit ` +\n `the chain-aware shape ({ layouts, page }) when rendering with a layout.`,\n )\n }\n return envelope\n }\n\n const chainEnvelope = envelope as\n | { layouts?: Array<{ name: string; state: unknown }>; page?: { name: string; state: unknown } }\n | undefined\n if (!chainEnvelope) {\n throw new Error(\n `[llui/vike] Hydration envelope is missing. Server-side onRenderHtml must ` +\n `populate window.__LLUI_STATE__ with the full chain before client hydration.`,\n )\n }\n\n const isPageLayer = layerIndex === chainLength - 1\n const layoutEntries = chainEnvelope.layouts ?? []\n const expected = isPageLayer ? chainEnvelope.page : layoutEntries[layerIndex]\n\n if (!expected) {\n throw new Error(\n `[llui/vike] Hydration envelope has no entry for chain layer ${layerIndex} ` +\n `(<${def.name}>). Server rendered ${layoutEntries.length} layouts + ${\n chainEnvelope.page ? 'a page' : 'no page'\n }, client expected ${chainLength} total entries.`,\n )\n }\n\n if (expected.name !== def.name) {\n throw new Error(\n `[llui/vike] Hydration mismatch at chain layer ${layerIndex}: server ` +\n `rendered <${expected.name}> but client is trying to hydrate <${def.name}>. ` +\n `This usually means the layout chain resolver returns different layouts ` +\n `on the server and the client for the same route.`,\n )\n }\n\n return expected.state\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llui/vike",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@llui/dom": "^0.0
|
|
24
|
+
"@llui/dom": "^0.1.0",
|
|
25
25
|
"jsdom": "*"
|
|
26
26
|
},
|
|
27
27
|
"peerDependenciesMeta": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@llui/dom": "0.0
|
|
33
|
+
"@llui/dom": "0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"description": "LLui Vike SSR adapter — onRenderHtml, onRenderClient hooks",
|
|
36
36
|
"keywords": [
|