@llui/vike 0.4.10 → 0.5.1
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.
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/on-render-client.d.ts +80 -189
- package/dist/on-render-client.d.ts.map +1 -1
- package/dist/on-render-client.js +101 -223
- package/dist/on-render-client.js.map +1 -1
- package/dist/on-render-html.d.ts +36 -8
- package/dist/on-render-html.d.ts.map +1 -1
- package/dist/on-render-html.js +36 -31
- package/dist/on-render-html.js.map +1 -1
- package/dist/page-slot.d.ts +22 -12
- package/dist/page-slot.d.ts.map +1 -1
- package/dist/page-slot.js +20 -14
- package/dist/page-slot.js.map +1 -1
- package/package.json +3 -3
|
@@ -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 * 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"]}
|
|
1
|
+
{"version":3,"file":"on-render-client.js","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { mountSignalComponent, hydrateSignalApp } from '@llui/dom'\nimport type { SignalComponentHandle, MountTarget } from '@llui/dom'\nimport type { TransitionOptions } 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\n/** A type-erased signal component as the adapter handles it (type params unused\n * at runtime). Method syntax + a single `unknown` view-bag param so any concrete\n * `SignalComponentDef<S,M,E>` assigns in (see on-render-html's AnyLayer note —\n * ComponentBag's state/send variance rules out the `<unknown,…>` erasure). */\nexport interface AnyLayer {\n readonly name?: string\n init(): unknown\n update(state: unknown, msg: unknown): unknown\n view(bag: unknown): readonly Node[]\n onEffect?(effect: unknown, api: unknown): void | (() => void)\n}\n/** The live handle a mounted/hydrated layer exposes (send/getState/subscribe). */\nexport type LayerHandle = SignalComponentHandle<unknown, unknown>\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 flow\n * through to every callback here without a cast; unaugmented projects\n * fall back to `unknown`.\n *\n * In the signal runtime a component's `init()` takes no data argument, so\n * each layer's `data` slice is used directly as that layer's seed STATE\n * when present; when absent, the layer's own `init()` provides the seed.\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.\n */\nexport interface ClientPageContext {\n Page: AnyLayer\n data?: VikePageContextData\n lluiLayoutData?: readonly unknown[]\n isHydration?: boolean\n}\n\ntype LayoutChain = ReadonlyArray<AnyLayer>\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.\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 AnyLayer]\n}\n\n/** Resolve a layer's seed state — a present data slice IS the seed state\n * (signal init() takes no data); an absent slice falls back to init(). */\nfunction seedFor(data: unknown): unknown | undefined {\n return data === undefined ? undefined : data\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 * 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 `SignalComponentDef` — becomes a one-layout chain.\n * - An array of `SignalComponentDef`s — outermost layout first,\n * innermost 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 *\n * Layers shared between the previous and next navigation stay mounted.\n * Only the divergent suffix is disposed and re-mounted.\n */\n Layout?: AnyLayer | 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. Return a promise to\n * defer the 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`. Fire-and-forget. Not called on\n * 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. Receives the live layout chain —\n * `[...layouts, page]`, outermost first — as `LayerHandle`s.\n */\n onMount?: (chain: readonly LayerHandle[]) => void\n\n /**\n * Called for each surviving layout layer whose `lluiLayoutData[i]`\n * slice changed across a client navigation. Surviving layers stay\n * mounted but need a fresh injection of nav-driven data. You decide how\n * to translate the new data into a message and dispatch it through\n * `handle.send(msg)`.\n *\n * Not called for unchanged slices, not on the initial hydration render,\n * and not for the page layer (it always disposes and remounts, so its\n * `init`/seed receives the fresh data directly).\n */\n onLayerDataChange?: (ctx: {\n def: AnyLayer\n handle: LayerHandle\n newData: unknown\n prevData: unknown\n }) => void\n\n /**\n * Forwarded to the signal hydrate path for every layer on initial\n * hydration. When `true`, effects returned by each component's `init()`\n * are dispatched post-swap on the client. When `false` (default), they\n * are skipped — the SSR pass already ran them.\n *\n * Subsequent client-side navigation always uses a fresh mount, which\n * always fires init effects regardless of this flag.\n */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Adapt a `TransitionOptions` object into the `onLeave` / `onEnter` pair\n * expected by `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 layer's\n * `pageSlot()` element.\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 SignalComponentHandle returned by mount/hydrate for\n * this layer. `slotAnchor` is set when the layer called `pageSlot()`\n * during its view pass; null for the innermost layer (the page, which\n * has no slot). `slotContexts` snapshots the context values in scope at\n * the slot, replayed into the nested layer's build.\n */\ninterface ChainEntry {\n def: AnyLayer\n handle: LayerHandle\n slotAnchor: Comment | null\n slotContexts: ReadonlyMap<symbol, unknown> | null\n /**\n * The data slice this layer was most recently mounted or updated with.\n * Compared shallow-key against the next nav's `lluiLayoutData[i]` to\n * decide whether a surviving layer needs `onLayerDataChange` to fire.\n */\n data: unknown\n}\n\n/**\n * Live chain of mounted layers — module-level singleton. Vike runs one\n * client-side adapter per browser tab; within one tab a single\n * `chainHandles` array holds the handle for every active layer, indexed\n * `[outermostLayout, ..., innerLayout, page]`. It mutates in place across\n * navigations: shared layout layers stay live, divergent suffix layers\n * dispose, new layers append.\n *\n * Module-level scope is correct for the browser (one consumer per page\n * load); a multi-tenant Node SSR worker importing the client adapter\n * would clobber it — that usage isn't supported.\n */\nlet chainHandles: ChainEntry[] = []\n\n/**\n * @internal — test helper. Disposes every layer in the current chain and\n * clears the module state so subsequent calls behave as a first mount.\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.\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\n * `RenderClientOptions` for the full option surface.\n *\n * **Do not name your layout file `+Layout.ts`.** Vike reserves the `+`\n * prefix for its own framework config conventions. Name the file\n * `Layout.ts`, `app-layout.ts`, or anywhere outside `/pages` that Vike\n * won't scan, and import it here by path.\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 is always the innermost entry.\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 — hydrate every layer against server-rendered HTML.\n mountChainSuffix(newChain, newChainData, 0, rootEl, undefined, {\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) is NEVER a surviving layer: every client\n // navigation disposes the current page and mounts fresh, even when the\n // incoming Page resolves to the same def reference. The persistent-layout\n // feature is about keeping app chrome alive; the page is the thing that\n // changes per route.\n let firstMismatch = 0\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 (the shared prefix). The\n // user-supplied `onLayerDataChange` receives the layer def, its handle,\n // and the new + previous data slices; it typically dispatches a message\n // through `handle.send`. Unchanged slices are skipped. Layouts-only by\n // construction (firstMismatch is bounded by layoutChain.length).\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({ def: entry.def, handle: entry.handle, newData, prevData })\n }\n }\n\n // `firstMismatch === 0` means a root swap (no layouts, or all diverging);\n // otherwise the surviving layer at firstMismatch-1 owns the slot we mount into.\n const isRootSwap = firstMismatch === 0\n\n // onLeave runs BEFORE any teardown — outgoing DOM still mounted. Skip on the\n // very first mount (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() runs\n // the layer's teardowns; anchor-mounted layers also remove their owned DOM\n // region (anchor → end sentinel). For a root swap the container is cleared\n // explicitly below since a container mount's dispose doesn't remove DOM.\n for (let i = chainHandles.length - 1; i >= firstMismatch; i--) {\n chainHandles[i]!.handle.dispose()\n }\n chainHandles = chainHandles.slice(0, firstMismatch)\n if (isRootSwap && !isFirstMount) rootEl.replaceChildren()\n\n // Mount the new suffix starting at firstMismatch.\n const mountTarget: HTMLElement | Comment =\n firstMismatch === 0 ? rootEl : chainHandles[firstMismatch - 1]!.slotAnchor!\n const mountContexts =\n firstMismatch === 0 ? undefined : (chainHandles[firstMismatch - 1]!.slotContexts ?? undefined)\n mountChainSuffix(newChain, newChainData, firstMismatch, mountTarget, mountContexts, {\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 — live `LayerHandle`s for\n * `[...layouts, page]`, outermost first. Empty before the first mount.\n */\nexport function getLayoutChain(): readonly LayerHandle[] {\n return snapshotLayoutChain()\n}\n\nfunction snapshotLayoutChain(): readonly LayerHandle[] {\n return chainHandles.map((entry) => entry.handle)\n}\n\ninterface MountOpts {\n mode: 'mount' | 'hydrate'\n /** For hydration: the full `window.__LLUI_STATE__` envelope. */\n serverStateEnvelope?: unknown\n /** Forwarded to the signal hydrate path. Mount mode ignores. */\n runInitEffectsOnHydrate?: boolean\n}\n\n/**\n * Mount (or hydrate) `chain[startAt..end]` into `initialTarget`, replaying\n * `initialContexts` into the first layer's build. Threads each layer's slot\n * (anchor + captured contexts) into the next layer's target + contexts.\n *\n * `initialTarget` is an `HTMLElement` for the outermost layer (container mount/\n * hydrate) and a `Comment` for inner layers mounting relative to a `pageSlot()`\n * anchor.\n *\n * Fails loudly if a non-innermost layer forgot to call `pageSlot()`, or if the\n * innermost layer called `pageSlot()` unnecessarily.\n *\n * @internal — test helper. Exported so `client-page-slot.test.ts` can exercise\n * anchor-mount/dispose contracts directly with hand-built DOM.\n */\nexport function _mountChainSuffix(\n chain: LayoutChain,\n chainData: readonly unknown[],\n startAt: number,\n initialTarget: HTMLElement | Comment,\n initialContexts: ReadonlyMap<symbol, unknown> | undefined,\n opts: MountOpts,\n): void {\n let mountTarget: HTMLElement | Comment = initialTarget\n let contexts: ReadonlyMap<symbol, unknown> | undefined = initialContexts\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 const isContainer = mountTarget.nodeType === 1\n const target: Element | MountTarget = isContainer\n ? (mountTarget as HTMLElement)\n : { anchor: mountTarget as Comment, mode: opts.mode === 'hydrate' ? 'replace' : 'append' }\n\n let handle: LayerHandle\n if (opts.mode === 'hydrate') {\n // Each layer pulls its own state slice from the envelope, matched by name\n // so a server/client mismatch throws clearly instead of binding wrong state.\n const layerState = extractHydrationState(opts.serverStateEnvelope, i, chain.length, def)\n handle = hydrateSignalApp(target, def, layerState, {\n runInitEffects: opts.runInitEffectsOnHydrate,\n contexts,\n })\n } else {\n handle = mountSignalComponent(target, def, {\n initialState: seedFor(layerData),\n contexts,\n })\n }\n\n const slot = _consumePendingSlot()\n\n if (isInnermost && slot !== null) {\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 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 slotContexts: slot?.contexts ?? null,\n data: layerData,\n })\n\n if (slot !== null) {\n // Next layer mounts relative to this slot's anchor, replaying the\n // contexts captured at the slot so providers above it stay reachable.\n mountTarget = slot.anchor\n contexts = slot.contexts\n }\n }\n}\n\n// Internal alias used by renderClient. The public-named export above carries\n// the @internal doc.\nconst mountChainSuffix = _mountChainSuffix\n\n/**\n * Shallow-key data diff for the surviving-layer prop-update path. Returns true\n * when `next` differs from `prev` enough to warrant dispatching the user's\n * `onLayerDataChange` hook.\n */\nfunction hasDataChanged(prev: unknown, next: unknown): boolean {\n if (Object.is(prev, next)) return false\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\n/**\n * Pull the per-layer state from the hydration envelope. Supports the chain-aware\n * shape (`{ layouts: [...], page: {...} }`) and the legacy flat shape (the state\n * object itself) for a single-layer page-only render.\n *\n * Throws on envelope shape mismatch — missing entries, wrong component name at a\n * given index — so server/client drift fails loud.\n */\nfunction extractHydrationState(\n envelope: unknown,\n layerIndex: number,\n chainLength: number,\n def: AnyLayer,\n): unknown {\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/dist/on-render-html.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DomEnv } from '@llui/dom/ssr';
|
|
2
2
|
import type { VikePageContextData } from './vike-namespace.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A type-erased signal component as the adapter sees it. Layouts and pages are
|
|
5
|
+
* `SignalComponentDef<S, M, E>` for concrete S/M/E; the adapter handles them
|
|
6
|
+
* uniformly with the type params erased — the runtime doesn't use them. Unlike
|
|
7
|
+
* the legacy `ComponentDef`, the signal `init()` takes NO data argument, so
|
|
8
|
+
* per-layer data flows in as a seed-STATE override (see `renderPage`).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Type-erased layer def at the adapter boundary. Declared with METHOD syntax and
|
|
12
|
+
* a single `unknown` view-bag param so a concrete `SignalComponentDef<S,M,E>`
|
|
13
|
+
* assigns in for ANY S/M/E — `SignalComponentDef<unknown,unknown,unknown>` can't
|
|
14
|
+
* be that erasure, because `view(bag: ComponentBag<S,M>)` couples covariant
|
|
15
|
+
* `state` with contravariant `send` and neither variance direction admits a
|
|
16
|
+
* heterogeneous chain. This interface is itself assignable to
|
|
17
|
+
* `SignalComponentDef<unknown,unknown,unknown>`, so `renderNodes(layer)` type-
|
|
18
|
+
* checks. Mirrors the legacy `AnyComponentDef`.
|
|
19
|
+
*/
|
|
20
|
+
export interface AnyLayer {
|
|
21
|
+
readonly name?: string;
|
|
22
|
+
init(): unknown;
|
|
23
|
+
update(state: unknown, msg: unknown): unknown;
|
|
24
|
+
view(bag: unknown): readonly Node[];
|
|
25
|
+
onEffect?(effect: unknown, api: unknown): void | (() => void);
|
|
26
|
+
}
|
|
27
|
+
type LayoutChain = ReadonlyArray<AnyLayer>;
|
|
4
28
|
/**
|
|
5
29
|
* Page context shape as seen by `@llui/vike`'s server hook. `Page` and
|
|
6
30
|
* `data` are whichever `+Page.ts` and `+data.ts` Vike resolved for the
|
|
@@ -11,9 +35,13 @@ type LayoutChain = ReadonlyArray<AnyComponentDef>;
|
|
|
11
35
|
* consumer-side augmentations (the Vike convention for typing data) flow
|
|
12
36
|
* into this hook's callbacks without any cast. When the consumer hasn't
|
|
13
37
|
* augmented the namespace, `data` falls back to `unknown`.
|
|
38
|
+
*
|
|
39
|
+
* In the signal runtime a component's `init()` takes no data argument, so
|
|
40
|
+
* each layer's `data` slice is used directly as that layer's seed STATE
|
|
41
|
+
* when present; when absent, the layer's own `init()` provides the seed.
|
|
14
42
|
*/
|
|
15
43
|
export interface PageContext {
|
|
16
|
-
Page:
|
|
44
|
+
Page: AnyLayer;
|
|
17
45
|
data?: VikePageContextData;
|
|
18
46
|
lluiLayoutData?: readonly unknown[];
|
|
19
47
|
head?: string;
|
|
@@ -47,8 +75,8 @@ export interface RenderHtmlOptions {
|
|
|
47
75
|
/**
|
|
48
76
|
* Persistent layout chain. One of:
|
|
49
77
|
*
|
|
50
|
-
* - A single `
|
|
51
|
-
* - An array of `
|
|
78
|
+
* - A single `SignalComponentDef` — becomes a one-layout chain.
|
|
79
|
+
* - An array of `SignalComponentDef`s — outermost first, innermost last.
|
|
52
80
|
* Every layer except the innermost must call `pageSlot()` in its view.
|
|
53
81
|
* - A function that returns a chain from the current `pageContext` —
|
|
54
82
|
* enables per-route chains (e.g. reading Vike's `urlPathname`).
|
|
@@ -57,7 +85,7 @@ export interface RenderHtmlOptions {
|
|
|
57
85
|
* hydration reads the matching envelope and reconstructs the chain
|
|
58
86
|
* layer-by-layer.
|
|
59
87
|
*/
|
|
60
|
-
Layout?:
|
|
88
|
+
Layout?: AnyLayer | LayoutChain | ((pageContext: PageContext) => LayoutChain);
|
|
61
89
|
/**
|
|
62
90
|
* Factory that returns the `DomEnv` backing SSR render. Call with
|
|
63
91
|
* either `jsdomEnv` (from `@llui/dom/ssr/jsdom`) or `linkedomEnv`
|
|
@@ -130,8 +158,8 @@ interface HydrationEnvelope {
|
|
|
130
158
|
* serialize. At each non-innermost layer, consume the pending
|
|
131
159
|
* `pageSlot()` registration and insert the next layer's nodes as
|
|
132
160
|
* siblings after the anchor comment, bracketed by an end sentinel.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
161
|
+
* Contexts provided above a slot are replayed into the nested layer's
|
|
162
|
+
* build so they reach the nested page.
|
|
135
163
|
*
|
|
136
164
|
* @internal — exported for unit testing only (`_renderChain`).
|
|
137
165
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-render-html.d.ts","sourceRoot":"","sources":["../src/on-render-html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"on-render-html.d.ts","sourceRoot":"","sources":["../src/on-render-html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAE9D;;;;;;GAMG;AACH;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,IAAI,IAAI,OAAO,CAAA;IACf,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAA;IAC7C,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,IAAI,EAAE,CAAA;IACnC,QAAQ,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAA;CAC9D;AAED,KAAK,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;AAE1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAA;IACd,IAAI,CAAC,EAAE,mBAAmB,CAAA;IAC1B,cAAc,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,WAAW,EAAE,WAAW,CAAA;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,WAAW,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAA;CACpC;AAcD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAA;IAE3C;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,CAAC,CAAA;IAE7E;;;;;;;;;;;;;;;OAeG;IACH,MAAM,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACvC;AAcD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGtF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,GACzB,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAEzD;AAED;;;;GAIG;AACH,UAAU,iBAAiB;IACzB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAChD,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CACvC;AAsCD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,SAAS,OAAO,EAAE,EAC7B,GAAG,EAAE,MAAM,GACV;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAiG/C"}
|
package/dist/on-render-html.js
CHANGED
|
@@ -80,13 +80,19 @@ async function renderPage(pageContext, options) {
|
|
|
80
80
|
pageContext: { lluiState: envelope },
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
+
/** Resolve a layer's seed state. In the signal runtime `init()` takes no data,
|
|
84
|
+
* so a present data slice IS the seed state; an absent slice falls back to the
|
|
85
|
+
* layer's own `init()` (renderNodes does this when given `undefined`). */
|
|
86
|
+
function seedFor(data) {
|
|
87
|
+
return data === undefined ? undefined : data;
|
|
88
|
+
}
|
|
83
89
|
/**
|
|
84
90
|
* Render every layer of the chain into one composed DOM tree, then
|
|
85
91
|
* serialize. At each non-innermost layer, consume the pending
|
|
86
92
|
* `pageSlot()` registration and insert the next layer's nodes as
|
|
87
93
|
* siblings after the anchor comment, bracketed by an end sentinel.
|
|
88
|
-
*
|
|
89
|
-
*
|
|
94
|
+
* Contexts provided above a slot are replayed into the nested layer's
|
|
95
|
+
* build so they reach the nested page.
|
|
90
96
|
*
|
|
91
97
|
* @internal — exported for unit testing only (`_renderChain`).
|
|
92
98
|
*/
|
|
@@ -96,38 +102,25 @@ export function _renderChain(chain, chainData, env) {
|
|
|
96
102
|
}
|
|
97
103
|
// Defensive: ensure no stale slot leaks in from a prior failed render.
|
|
98
104
|
_resetPendingSlot();
|
|
99
|
-
// Accumulate bindings from every layer — serializeNodes needs the
|
|
100
|
-
// full set so hydrate markers are correctly placed across the
|
|
101
|
-
// composed tree.
|
|
102
|
-
const allBindings = [];
|
|
103
105
|
const envelopeLayouts = [];
|
|
104
106
|
let envelopePage = null;
|
|
105
107
|
let outermostNodes = [];
|
|
108
|
+
const disposers = [];
|
|
106
109
|
let currentSlotAnchor = null;
|
|
107
|
-
let
|
|
110
|
+
let currentSlotContexts = undefined;
|
|
108
111
|
for (let i = 0; i < chain.length; i++) {
|
|
109
112
|
const def = chain[i];
|
|
110
113
|
const layerData = chainData[i];
|
|
111
114
|
const isInnermost = i === chain.length - 1;
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
// renderNodes expects. Same pattern as the client mount path —
|
|
118
|
-
// renderNodes is generic but the runtime doesn't use the type params.
|
|
119
|
-
const { nodes, inst } = renderNodes(def, initialState, env, currentSlotScope);
|
|
120
|
-
allBindings.push(...inst.allBindings);
|
|
115
|
+
// Build this layer's tree against the server DomEnv. Per-layer data is the
|
|
116
|
+
// seed state (signal init() takes no data); contexts captured at the parent
|
|
117
|
+
// layer's pageSlot() are replayed so providers above the slot reach here.
|
|
118
|
+
const { nodes, dispose } = renderNodes(def, seedFor(layerData), env, currentSlotContexts);
|
|
119
|
+
disposers.push(dispose);
|
|
121
120
|
if (i === 0) {
|
|
122
121
|
outermostNodes = nodes;
|
|
123
122
|
}
|
|
124
123
|
else {
|
|
125
|
-
// Insert this layer's nodes as siblings immediately after the
|
|
126
|
-
// anchor comment, then place an end sentinel after them.
|
|
127
|
-
// The anchor is already attached to the composed DOM tree (it was
|
|
128
|
-
// produced by the previous layer's pageSlot() call). We insert
|
|
129
|
-
// before the anchor's next sibling so nodes land right after the
|
|
130
|
-
// anchor, preserving any trailing siblings that may exist.
|
|
131
124
|
if (!currentSlotAnchor) {
|
|
132
125
|
// Unreachable given the error checks below, but defensive.
|
|
133
126
|
throw new Error(`[llui/vike] internal: chain layer ${i} (<${def.name}>) has no slot anchor`);
|
|
@@ -136,23 +129,23 @@ export function _renderChain(chain, chainData, env) {
|
|
|
136
129
|
if (!parentNode) {
|
|
137
130
|
throw new Error(`[llui/vike] internal: slot anchor for layer ${i} (<${def.name}>) is detached`);
|
|
138
131
|
}
|
|
139
|
-
//
|
|
140
|
-
//
|
|
132
|
+
// Insert this layer's nodes immediately after the anchor, then an end
|
|
133
|
+
// sentinel — preserving any trailing siblings of the anchor.
|
|
141
134
|
const insertPoint = currentSlotAnchor.nextSibling;
|
|
142
135
|
for (const node of nodes) {
|
|
143
136
|
parentNode.insertBefore(node, insertPoint);
|
|
144
137
|
}
|
|
145
|
-
// Synthesize an end sentinel that brackets the owned region.
|
|
146
138
|
const endSentinel = env.createComment('llui-mount-end');
|
|
147
139
|
parentNode.insertBefore(endSentinel, insertPoint);
|
|
148
140
|
}
|
|
149
|
-
// Record this layer's state in the envelope. Page goes under
|
|
150
|
-
//
|
|
141
|
+
// Record this layer's seed state in the envelope. Page goes under `page`,
|
|
142
|
+
// everything else under `layouts[]` ordered outer-to-inner.
|
|
143
|
+
const layerState = seedFor(layerData) ?? normalizeInitState(def);
|
|
151
144
|
if (isInnermost) {
|
|
152
|
-
envelopePage = { name: def.name, state:
|
|
145
|
+
envelopePage = { name: def.name ?? 'Page', state: layerState };
|
|
153
146
|
}
|
|
154
147
|
else {
|
|
155
|
-
envelopeLayouts.push({ name: def.name, state:
|
|
148
|
+
envelopeLayouts.push({ name: def.name ?? 'Layout', state: layerState });
|
|
156
149
|
}
|
|
157
150
|
// Consume this layer's pending slot registration (if any). Every
|
|
158
151
|
// non-innermost layer MUST declare a slot; the innermost MUST NOT.
|
|
@@ -167,9 +160,12 @@ export function _renderChain(chain, chainData, env) {
|
|
|
167
160
|
`layer(s) to mount and no slot to mount them into.`);
|
|
168
161
|
}
|
|
169
162
|
currentSlotAnchor = slot?.anchor ?? null;
|
|
170
|
-
|
|
163
|
+
currentSlotContexts = slot?.contexts;
|
|
171
164
|
}
|
|
172
|
-
const html = serializeNodes(outermostNodes
|
|
165
|
+
const html = serializeNodes(outermostNodes);
|
|
166
|
+
// Dispose every layer's build now that the composed tree is serialized.
|
|
167
|
+
for (const d of disposers)
|
|
168
|
+
d();
|
|
173
169
|
if (envelopePage === null) {
|
|
174
170
|
// Unreachable — chain is non-empty so the last iteration sets this.
|
|
175
171
|
throw new Error('[llui/vike] internal: renderChain produced no page entry');
|
|
@@ -182,4 +178,13 @@ export function _renderChain(chain, chainData, env) {
|
|
|
182
178
|
},
|
|
183
179
|
};
|
|
184
180
|
}
|
|
181
|
+
/** The seed state a layer's `init()` produces (used for the envelope when no
|
|
182
|
+
* data slice overrides it). `init()` may return `S` or `[S, E[]]`. */
|
|
183
|
+
function normalizeInitState(def) {
|
|
184
|
+
const r = def.init();
|
|
185
|
+
if (Array.isArray(r) && r.length === 2 && Array.isArray(r[1])) {
|
|
186
|
+
return r[0];
|
|
187
|
+
}
|
|
188
|
+
return r;
|
|
189
|
+
}
|
|
185
190
|
//# sourceMappingURL=on-render-html.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-render-html.js","sourceRoot":"","sources":["../src/on-render-html.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderNodes, serializeNodes } from '@llui/dom'\nimport type { AnyComponentDef, Binding, Lifetime, DomEnv } from '@llui/dom'\nimport { _consumePendingSlot, _resetPendingSlot } from './page-slot.js'\nimport type { VikePageContextData } from './vike-namespace.js'\n\ntype LayoutChain = ReadonlyArray<AnyComponentDef>\n\n/**\n * Page context shape as seen by `@llui/vike`'s server hook. `Page` and\n * `data` are whichever `+Page.ts` and `+data.ts` Vike resolved for the\n * current route; `lluiLayoutData` is an optional array of per-layer\n * layout data matching the chain configured on `createOnRenderHtml`.\n *\n * `data` is derived from the global `Vike.PageContext` namespace so that\n * consumer-side augmentations (the Vike convention for typing data) flow\n * into this hook's callbacks without any cast. When the consumer hasn't\n * augmented the namespace, `data` falls back to `unknown`.\n */\nexport interface PageContext {\n Page: AnyComponentDef\n data?: VikePageContextData\n lluiLayoutData?: readonly unknown[]\n head?: string\n}\n\nexport interface DocumentContext {\n /** Rendered component HTML (layout + page composed if a Layout is configured) */\n html: string\n /** JSON-serialized hydration envelope (chain-aware when Layout is configured) */\n state: string\n /** Head content from pageContext.head (e.g. from +Head.ts) */\n head: string\n /** Full page context for custom logic */\n pageContext: PageContext\n}\n\nexport interface RenderHtmlResult {\n documentHtml: string | { _escaped: string }\n pageContext: { lluiState: unknown }\n}\n\nconst DEFAULT_DOCUMENT = ({ html, state, head }: DocumentContext): string => `<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n ${head}\n </head>\n <body>\n <div id=\"app\">${html}</div>\n <script>window.__LLUI_STATE__ = ${state}</script>\n </body>\n</html>`\n\n/**\n * Options for the customized `createOnRenderHtml` factory. Mirrors\n * `@llui/vike/client`'s `RenderClientOptions.Layout` — the same chain\n * shape is accepted for consistency between server and client render.\n */\nexport interface RenderHtmlOptions {\n /** Custom HTML document template. Defaults to a minimal layout. */\n document?: (ctx: DocumentContext) => 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 first, innermost last.\n * Every layer except the innermost must call `pageSlot()` in its view.\n * - A function that returns a chain from the current `pageContext` —\n * enables per-route chains (e.g. reading Vike's `urlPathname`).\n *\n * The server renders the full chain as one composed HTML tree. Client\n * hydration reads the matching envelope and reconstructs the chain\n * layer-by-layer.\n */\n Layout?: AnyComponentDef | LayoutChain | ((pageContext: PageContext) => LayoutChain)\n\n /**\n * Factory that returns the `DomEnv` backing SSR render. Call with\n * either `jsdomEnv` (from `@llui/dom/ssr/jsdom`) or `linkedomEnv`\n * (from `@llui/dom/ssr/linkedom`). The factory is invoked once per\n * page render, so each request gets a fresh DOM — safe under\n * concurrency, no `globalThis` mutation.\n *\n * On Cloudflare Workers use `linkedomEnv` — jsdom's transitive deps\n * (whatwg-url, tr46, punycode) don't resolve under workerd.\n *\n * @example\n * ```ts\n * import { jsdomEnv } from '@llui/dom/ssr/jsdom'\n * createOnRenderHtml({ Layout: MyLayout, domEnv: jsdomEnv })\n * ```\n */\n domEnv: () => DomEnv | Promise<DomEnv>\n}\n\nfunction resolveLayoutChain(\n layoutOption: RenderHtmlOptions['Layout'],\n pageContext: PageContext,\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 * Default onRenderHtml hook — no layout, minimal document template,\n * jsdom-backed DOM env. For Cloudflare Workers (no jsdom support) or\n * a custom layout / document, use `createOnRenderHtml({ domEnv, … })`\n * with `linkedomEnv` from `@llui/dom/ssr/linkedom`.\n *\n * The lazy import below keeps jsdom out of the client bundle —\n * Rollup's graph walker only pulls it when this server hook executes.\n */\nexport async function onRenderHtml(pageContext: PageContext): Promise<RenderHtmlResult> {\n const { jsdomEnv } = await import('@llui/dom/ssr/jsdom')\n return renderPage(pageContext, { domEnv: jsdomEnv })\n}\n\n/**\n * Factory to create a customized onRenderHtml hook.\n *\n * **Do not name your layout file `+Layout.ts`.** Vike reserves `+Layout`\n * for its own framework-adapter config (`vike-react` / `vike-vue` /\n * `vike-solid`) and will conflict with `@llui/vike`'s `Layout` option.\n * Name the file `Layout.ts`, `app-layout.ts`, or anywhere outside\n * `/pages` that Vike won't scan, and import it here by path.\n *\n * ```ts\n * // pages/+onRenderHtml.ts\n * import { createOnRenderHtml } from '@llui/vike/server'\n * import { AppLayout } from './Layout.js' // ← NOT './+Layout'\n *\n * export const onRenderHtml = createOnRenderHtml({\n * Layout: AppLayout,\n * document: ({ html, state, head }) => `<!DOCTYPE html>\n * <html><head>${head}<link rel=\"stylesheet\" href=\"/styles.css\" /></head>\n * <body><div id=\"app\">${html}</div>\n * <script>window.__LLUI_STATE__ = ${state}</script></body></html>`,\n * })\n * ```\n */\nexport function createOnRenderHtml(\n options: RenderHtmlOptions,\n): (pageContext: PageContext) => Promise<RenderHtmlResult> {\n return (pageContext) => renderPage(pageContext, options)\n}\n\n/**\n * Hydration envelope emitted into `window.__LLUI_STATE__`. Chain-aware\n * by default — every layer (layouts + page) is represented by its own\n * entry, keyed by component name so server/client mismatches fail loud.\n */\ninterface HydrationEnvelope {\n layouts: Array<{ name: string; state: unknown }>\n page: { name: string; state: unknown }\n}\n\nasync function renderPage(\n pageContext: PageContext,\n options: RenderHtmlOptions,\n): Promise<RenderHtmlResult> {\n const env = await options.domEnv()\n\n const layoutChain = resolveLayoutChain(options.Layout, pageContext)\n const layoutData = pageContext.lluiLayoutData ?? []\n\n // Full chain: every layout, then the page. Always at least one entry\n // (the page) since Vike's pageContext always has a Page.\n const chain: LayoutChain = [...layoutChain, pageContext.Page]\n const chainData: readonly unknown[] = [...layoutData, pageContext.data]\n\n const { html, envelope } = _renderChain(chain, chainData, env)\n\n const document = options.document ?? DEFAULT_DOCUMENT\n const head = pageContext.head ?? ''\n const state = JSON.stringify(envelope)\n const documentHtml = document({ html, state, head, pageContext })\n\n return {\n // Vike's dangerouslySkipEscape format — the document template is\n // trusted (authored by the developer, not user input)\n documentHtml: { _escaped: documentHtml },\n pageContext: { lluiState: envelope },\n }\n}\n\n/**\n * Render every layer of the chain into one composed DOM tree, then\n * serialize. At each non-innermost layer, consume the pending\n * `pageSlot()` registration and insert the next layer's nodes as\n * siblings after the anchor comment, bracketed by an end sentinel.\n * Scopes are threaded so inner layers inherit the outer layer's scope\n * tree for context lookups.\n *\n * @internal — exported for unit testing only (`_renderChain`).\n */\nexport function _renderChain(\n chain: LayoutChain,\n chainData: readonly unknown[],\n env: DomEnv,\n): { html: string; envelope: HydrationEnvelope } {\n if (chain.length === 0) {\n throw new Error('[llui/vike] renderChain called with empty chain')\n }\n\n // Defensive: ensure no stale slot leaks in from a prior failed render.\n _resetPendingSlot()\n\n // Accumulate bindings from every layer — serializeNodes needs the\n // full set so hydrate markers are correctly placed across the\n // composed tree.\n const allBindings: Binding[] = []\n const envelopeLayouts: HydrationEnvelope['layouts'] = []\n let envelopePage: HydrationEnvelope['page'] | null = null\n\n let outermostNodes: Node[] = []\n let currentSlotAnchor: Comment | null = null\n let currentSlotScope: Lifetime | undefined = undefined\n\n for (let i = 0; i < chain.length; i++) {\n const def = chain[i]!\n const layerData = chainData[i]\n const isInnermost = i === chain.length - 1\n\n // Resolve the initial state from the layer's own init() applied to\n // its data slice, same as client-side mountApp(). renderNodes takes\n // the state post-init so that the view sees the right fields.\n const [initialState] = def.init(layerData)\n\n // Cross from type-erased AnyComponentDef into the concrete signature\n // renderNodes expects. Same pattern as the client mount path —\n // renderNodes is generic but the runtime doesn't use the type params.\n const { nodes, inst } = renderNodes(\n def as unknown as Parameters<typeof renderNodes>[0],\n initialState,\n env,\n currentSlotScope,\n )\n allBindings.push(...inst.allBindings)\n\n if (i === 0) {\n outermostNodes = nodes\n } else {\n // Insert this layer's nodes as siblings immediately after the\n // anchor comment, then place an end sentinel after them.\n // The anchor is already attached to the composed DOM tree (it was\n // produced by the previous layer's pageSlot() call). We insert\n // before the anchor's next sibling so nodes land right after the\n // anchor, preserving any trailing siblings that may exist.\n if (!currentSlotAnchor) {\n // Unreachable given the error checks below, but defensive.\n throw new Error(`[llui/vike] internal: chain layer ${i} (<${def.name}>) has no slot anchor`)\n }\n const parentNode = currentSlotAnchor.parentNode\n if (!parentNode) {\n throw new Error(\n `[llui/vike] internal: slot anchor for layer ${i} (<${def.name}>) is detached`,\n )\n }\n // insertPoint is the node currently after the anchor; inserting\n // before it keeps all new nodes in order immediately after anchor.\n const insertPoint = currentSlotAnchor.nextSibling\n for (const node of nodes) {\n parentNode.insertBefore(node, insertPoint)\n }\n // Synthesize an end sentinel that brackets the owned region.\n const endSentinel = env.createComment('llui-mount-end')\n parentNode.insertBefore(endSentinel, insertPoint)\n }\n\n // Record this layer's state in the envelope. Page goes under\n // `page`, everything else under `layouts[]` ordered outer-to-inner.\n if (isInnermost) {\n envelopePage = { name: def.name, state: initialState }\n } else {\n envelopeLayouts.push({ name: def.name, state: initialState })\n }\n\n // Consume this layer's pending slot registration (if any). Every\n // non-innermost layer MUST declare a slot; the innermost MUST NOT.\n const slot = _consumePendingSlot()\n if (isInnermost && slot !== null) {\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 )\n }\n if (!isInnermost && slot === null) {\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.`,\n )\n }\n\n currentSlotAnchor = slot?.anchor ?? null\n currentSlotScope = slot?.slotLifetime\n }\n\n const html = serializeNodes(outermostNodes, allBindings)\n\n if (envelopePage === null) {\n // Unreachable — chain is non-empty so the last iteration sets this.\n throw new Error('[llui/vike] internal: renderChain produced no page entry')\n }\n\n return {\n html,\n envelope: {\n layouts: envelopeLayouts,\n page: envelopePage,\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"on-render-html.js","sourceRoot":"","sources":["../src/on-render-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEvD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAoEvE,MAAM,gBAAgB,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAmB,EAAU,EAAE,CAAC;;;;MAIvE,IAAI;;;oBAGU,IAAI;sCACc,KAAK;;QAEnC,CAAA;AA6CR,SAAS,kBAAkB,CACzB,YAAyC,EACzC,WAAwB;IAExB,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,CAAC;QACvC,OAAO,YAAY,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;IACxC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;QAAE,OAAO,YAAY,CAAA;IACpD,OAAO,CAAC,YAAwB,CAAC,CAAA;AACnC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAwB;IACzD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAA;IACxD,OAAO,UAAU,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;AACtD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA0B;IAE1B,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;AAC1D,CAAC;AAYD,KAAK,UAAU,UAAU,CACvB,WAAwB,EACxB,OAA0B;IAE1B,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAA;IAElC,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACnE,MAAM,UAAU,GAAG,WAAW,CAAC,cAAc,IAAI,EAAE,CAAA;IAEnD,qEAAqE;IACrE,yDAAyD;IACzD,MAAM,KAAK,GAAgB,CAAC,GAAG,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC,CAAA;IAC7D,MAAM,SAAS,GAAuB,CAAC,GAAG,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,CAAA;IAEvE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAA;IAE9D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,gBAAgB,CAAA;IACrD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,IAAI,EAAE,CAAA;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IACtC,MAAM,YAAY,GAAG,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IAEjE,OAAO;QACL,iEAAiE;QACjE,sDAAsD;QACtD,YAAY,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE;QACxC,WAAW,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;KACrC,CAAA;AACH,CAAC;AAED;;0EAE0E;AAC1E,SAAS,OAAO,CAAC,IAAa;IAC5B,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAkB,EAClB,SAA6B,EAC7B,GAAW;IAEX,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IAED,uEAAuE;IACvE,iBAAiB,EAAE,CAAA;IAEnB,MAAM,eAAe,GAAiC,EAAE,CAAA;IACxD,IAAI,YAAY,GAAqC,IAAI,CAAA;IAEzD,IAAI,cAAc,GAAoB,EAAE,CAAA;IACxC,MAAM,SAAS,GAAsB,EAAE,CAAA;IACvC,IAAI,iBAAiB,GAAmB,IAAI,CAAA;IAC5C,IAAI,mBAAmB,GAA6C,SAAS,CAAA;IAE7E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QACrB,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;QAC9B,MAAM,WAAW,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;QAE1C,2EAA2E;QAC3E,4EAA4E;QAC5E,0EAA0E;QAC1E,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,mBAAmB,CAAC,CAAA;QACzF,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAEvB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACZ,cAAc,GAAG,KAAK,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACvB,2DAA2D;gBAC3D,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,MAAM,GAAG,CAAC,IAAI,uBAAuB,CAAC,CAAA;YAC9F,CAAC;YACD,MAAM,UAAU,GAAG,iBAAiB,CAAC,UAAU,CAAA;YAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CACb,+CAA+C,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAC/E,CAAA;YACH,CAAC;YACD,sEAAsE;YACtE,6DAA6D;YAC7D,MAAM,WAAW,GAAG,iBAAiB,CAAC,WAAW,CAAA;YACjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,UAAU,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;YAC5C,CAAC;YACD,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAA;YACvD,UAAU,CAAC,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;QACnD,CAAC;QAED,0EAA0E;QAC1E,4DAA4D;QAC5D,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAA;QAChE,IAAI,WAAW,EAAE,CAAC;YAChB,YAAY,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAA;QAChE,CAAC;aAAM,CAAC;YACN,eAAe,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;QACzE,CAAC;QAED,iEAAiE;QACjE,mEAAmE;QACnE,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAA;QAClC,IAAI,WAAW,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,gBAAgB,GAAG,CAAC,IAAI,4CAA4C;gBAClE,sEAAsE,CACzE,CAAA;QACH,CAAC;QACD,IAAI,CAAC,WAAW,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,gBAAgB,GAAG,CAAC,IAAI,gCAAgC,CAAC,eAAe;gBACtE,4CAA4C,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ;gBACxE,mDAAmD,CACtD,CAAA;QACH,CAAC;QAED,iBAAiB,GAAG,IAAI,EAAE,MAAM,IAAI,IAAI,CAAA;QACxC,mBAAmB,GAAG,IAAI,EAAE,QAAQ,CAAA;IACtC,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,cAAc,CAAC,CAAA;IAE3C,wEAAwE;IACxE,KAAK,MAAM,CAAC,IAAI,SAAS;QAAE,CAAC,EAAE,CAAA;IAE9B,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,oEAAoE;QACpE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IAED,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE;YACR,OAAO,EAAE,eAAe;YACxB,IAAI,EAAE,YAAY;SACnB;KACF,CAAA;AACH,CAAC;AAED;sEACsE;AACtE,SAAS,kBAAkB,CAAC,GAAa;IACvC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACpB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAE,CAA0B,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,OAAQ,CAA0B,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC","sourcesContent":["import { renderNodes, serializeNodes } from '@llui/dom'\nimport type { DomEnv } from '@llui/dom/ssr'\nimport { _consumePendingSlot, _resetPendingSlot } from './page-slot.js'\nimport type { VikePageContextData } from './vike-namespace.js'\n\n/**\n * A type-erased signal component as the adapter sees it. Layouts and pages are\n * `SignalComponentDef<S, M, E>` for concrete S/M/E; the adapter handles them\n * uniformly with the type params erased — the runtime doesn't use them. Unlike\n * the legacy `ComponentDef`, the signal `init()` takes NO data argument, so\n * per-layer data flows in as a seed-STATE override (see `renderPage`).\n */\n/**\n * Type-erased layer def at the adapter boundary. Declared with METHOD syntax and\n * a single `unknown` view-bag param so a concrete `SignalComponentDef<S,M,E>`\n * assigns in for ANY S/M/E — `SignalComponentDef<unknown,unknown,unknown>` can't\n * be that erasure, because `view(bag: ComponentBag<S,M>)` couples covariant\n * `state` with contravariant `send` and neither variance direction admits a\n * heterogeneous chain. This interface is itself assignable to\n * `SignalComponentDef<unknown,unknown,unknown>`, so `renderNodes(layer)` type-\n * checks. Mirrors the legacy `AnyComponentDef`.\n */\nexport interface AnyLayer {\n readonly name?: string\n init(): unknown\n update(state: unknown, msg: unknown): unknown\n view(bag: unknown): readonly Node[]\n onEffect?(effect: unknown, api: unknown): void | (() => void)\n}\n\ntype LayoutChain = ReadonlyArray<AnyLayer>\n\n/**\n * Page context shape as seen by `@llui/vike`'s server hook. `Page` and\n * `data` are whichever `+Page.ts` and `+data.ts` Vike resolved for the\n * current route; `lluiLayoutData` is an optional array of per-layer\n * layout data matching the chain configured on `createOnRenderHtml`.\n *\n * `data` is derived from the global `Vike.PageContext` namespace so that\n * consumer-side augmentations (the Vike convention for typing data) flow\n * into this hook's callbacks without any cast. When the consumer hasn't\n * augmented the namespace, `data` falls back to `unknown`.\n *\n * In the signal runtime a component's `init()` takes no data argument, so\n * each layer's `data` slice is used directly as that layer's seed STATE\n * when present; when absent, the layer's own `init()` provides the seed.\n */\nexport interface PageContext {\n Page: AnyLayer\n data?: VikePageContextData\n lluiLayoutData?: readonly unknown[]\n head?: string\n}\n\nexport interface DocumentContext {\n /** Rendered component HTML (layout + page composed if a Layout is configured) */\n html: string\n /** JSON-serialized hydration envelope (chain-aware when Layout is configured) */\n state: string\n /** Head content from pageContext.head (e.g. from +Head.ts) */\n head: string\n /** Full page context for custom logic */\n pageContext: PageContext\n}\n\nexport interface RenderHtmlResult {\n documentHtml: string | { _escaped: string }\n pageContext: { lluiState: unknown }\n}\n\nconst DEFAULT_DOCUMENT = ({ html, state, head }: DocumentContext): string => `<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n ${head}\n </head>\n <body>\n <div id=\"app\">${html}</div>\n <script>window.__LLUI_STATE__ = ${state}</script>\n </body>\n</html>`\n\n/**\n * Options for the customized `createOnRenderHtml` factory. Mirrors\n * `@llui/vike/client`'s `RenderClientOptions.Layout` — the same chain\n * shape is accepted for consistency between server and client render.\n */\nexport interface RenderHtmlOptions {\n /** Custom HTML document template. Defaults to a minimal layout. */\n document?: (ctx: DocumentContext) => string\n\n /**\n * Persistent layout chain. One of:\n *\n * - A single `SignalComponentDef` — becomes a one-layout chain.\n * - An array of `SignalComponentDef`s — outermost first, innermost last.\n * Every layer except the innermost must call `pageSlot()` in its view.\n * - A function that returns a chain from the current `pageContext` —\n * enables per-route chains (e.g. reading Vike's `urlPathname`).\n *\n * The server renders the full chain as one composed HTML tree. Client\n * hydration reads the matching envelope and reconstructs the chain\n * layer-by-layer.\n */\n Layout?: AnyLayer | LayoutChain | ((pageContext: PageContext) => LayoutChain)\n\n /**\n * Factory that returns the `DomEnv` backing SSR render. Call with\n * either `jsdomEnv` (from `@llui/dom/ssr/jsdom`) or `linkedomEnv`\n * (from `@llui/dom/ssr/linkedom`). The factory is invoked once per\n * page render, so each request gets a fresh DOM — safe under\n * concurrency, no `globalThis` mutation.\n *\n * On Cloudflare Workers use `linkedomEnv` — jsdom's transitive deps\n * (whatwg-url, tr46, punycode) don't resolve under workerd.\n *\n * @example\n * ```ts\n * import { jsdomEnv } from '@llui/dom/ssr/jsdom'\n * createOnRenderHtml({ Layout: MyLayout, domEnv: jsdomEnv })\n * ```\n */\n domEnv: () => DomEnv | Promise<DomEnv>\n}\n\nfunction resolveLayoutChain(\n layoutOption: RenderHtmlOptions['Layout'],\n pageContext: PageContext,\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 AnyLayer]\n}\n\n/**\n * Default onRenderHtml hook — no layout, minimal document template,\n * jsdom-backed DOM env. For Cloudflare Workers (no jsdom support) or\n * a custom layout / document, use `createOnRenderHtml({ domEnv, … })`\n * with `linkedomEnv` from `@llui/dom/ssr/linkedom`.\n *\n * The lazy import below keeps jsdom out of the client bundle —\n * Rollup's graph walker only pulls it when this server hook executes.\n */\nexport async function onRenderHtml(pageContext: PageContext): Promise<RenderHtmlResult> {\n const { jsdomEnv } = await import('@llui/dom/ssr/jsdom')\n return renderPage(pageContext, { domEnv: jsdomEnv })\n}\n\n/**\n * Factory to create a customized onRenderHtml hook.\n *\n * **Do not name your layout file `+Layout.ts`.** Vike reserves `+Layout`\n * for its own framework-adapter config (`vike-react` / `vike-vue` /\n * `vike-solid`) and will conflict with `@llui/vike`'s `Layout` option.\n * Name the file `Layout.ts`, `app-layout.ts`, or anywhere outside\n * `/pages` that Vike won't scan, and import it here by path.\n *\n * ```ts\n * // pages/+onRenderHtml.ts\n * import { createOnRenderHtml } from '@llui/vike/server'\n * import { AppLayout } from './Layout.js' // ← NOT './+Layout'\n *\n * export const onRenderHtml = createOnRenderHtml({\n * Layout: AppLayout,\n * document: ({ html, state, head }) => `<!DOCTYPE html>\n * <html><head>${head}<link rel=\"stylesheet\" href=\"/styles.css\" /></head>\n * <body><div id=\"app\">${html}</div>\n * <script>window.__LLUI_STATE__ = ${state}</script></body></html>`,\n * })\n * ```\n */\nexport function createOnRenderHtml(\n options: RenderHtmlOptions,\n): (pageContext: PageContext) => Promise<RenderHtmlResult> {\n return (pageContext) => renderPage(pageContext, options)\n}\n\n/**\n * Hydration envelope emitted into `window.__LLUI_STATE__`. Chain-aware\n * by default — every layer (layouts + page) is represented by its own\n * entry, keyed by component name so server/client mismatches fail loud.\n */\ninterface HydrationEnvelope {\n layouts: Array<{ name: string; state: unknown }>\n page: { name: string; state: unknown }\n}\n\nasync function renderPage(\n pageContext: PageContext,\n options: RenderHtmlOptions,\n): Promise<RenderHtmlResult> {\n const env = await options.domEnv()\n\n const layoutChain = resolveLayoutChain(options.Layout, pageContext)\n const layoutData = pageContext.lluiLayoutData ?? []\n\n // Full chain: every layout, then the page. Always at least one entry\n // (the page) since Vike's pageContext always has a Page.\n const chain: LayoutChain = [...layoutChain, pageContext.Page]\n const chainData: readonly unknown[] = [...layoutData, pageContext.data]\n\n const { html, envelope } = _renderChain(chain, chainData, env)\n\n const document = options.document ?? DEFAULT_DOCUMENT\n const head = pageContext.head ?? ''\n const state = JSON.stringify(envelope)\n const documentHtml = document({ html, state, head, pageContext })\n\n return {\n // Vike's dangerouslySkipEscape format — the document template is\n // trusted (authored by the developer, not user input)\n documentHtml: { _escaped: documentHtml },\n pageContext: { lluiState: envelope },\n }\n}\n\n/** Resolve a layer's seed state. In the signal runtime `init()` takes no data,\n * so a present data slice IS the seed state; an absent slice falls back to the\n * layer's own `init()` (renderNodes does this when given `undefined`). */\nfunction seedFor(data: unknown): unknown | undefined {\n return data === undefined ? undefined : data\n}\n\n/**\n * Render every layer of the chain into one composed DOM tree, then\n * serialize. At each non-innermost layer, consume the pending\n * `pageSlot()` registration and insert the next layer's nodes as\n * siblings after the anchor comment, bracketed by an end sentinel.\n * Contexts provided above a slot are replayed into the nested layer's\n * build so they reach the nested page.\n *\n * @internal — exported for unit testing only (`_renderChain`).\n */\nexport function _renderChain(\n chain: LayoutChain,\n chainData: readonly unknown[],\n env: DomEnv,\n): { html: string; envelope: HydrationEnvelope } {\n if (chain.length === 0) {\n throw new Error('[llui/vike] renderChain called with empty chain')\n }\n\n // Defensive: ensure no stale slot leaks in from a prior failed render.\n _resetPendingSlot()\n\n const envelopeLayouts: HydrationEnvelope['layouts'] = []\n let envelopePage: HydrationEnvelope['page'] | null = null\n\n let outermostNodes: readonly Node[] = []\n const disposers: Array<() => void> = []\n let currentSlotAnchor: Comment | null = null\n let currentSlotContexts: ReadonlyMap<symbol, unknown> | undefined = undefined\n\n for (let i = 0; i < chain.length; i++) {\n const def = chain[i]!\n const layerData = chainData[i]\n const isInnermost = i === chain.length - 1\n\n // Build this layer's tree against the server DomEnv. Per-layer data is the\n // seed state (signal init() takes no data); contexts captured at the parent\n // layer's pageSlot() are replayed so providers above the slot reach here.\n const { nodes, dispose } = renderNodes(def, seedFor(layerData), env, currentSlotContexts)\n disposers.push(dispose)\n\n if (i === 0) {\n outermostNodes = nodes\n } else {\n if (!currentSlotAnchor) {\n // Unreachable given the error checks below, but defensive.\n throw new Error(`[llui/vike] internal: chain layer ${i} (<${def.name}>) has no slot anchor`)\n }\n const parentNode = currentSlotAnchor.parentNode\n if (!parentNode) {\n throw new Error(\n `[llui/vike] internal: slot anchor for layer ${i} (<${def.name}>) is detached`,\n )\n }\n // Insert this layer's nodes immediately after the anchor, then an end\n // sentinel — preserving any trailing siblings of the anchor.\n const insertPoint = currentSlotAnchor.nextSibling\n for (const node of nodes) {\n parentNode.insertBefore(node, insertPoint)\n }\n const endSentinel = env.createComment('llui-mount-end')\n parentNode.insertBefore(endSentinel, insertPoint)\n }\n\n // Record this layer's seed state in the envelope. Page goes under `page`,\n // everything else under `layouts[]` ordered outer-to-inner.\n const layerState = seedFor(layerData) ?? normalizeInitState(def)\n if (isInnermost) {\n envelopePage = { name: def.name ?? 'Page', state: layerState }\n } else {\n envelopeLayouts.push({ name: def.name ?? 'Layout', state: layerState })\n }\n\n // Consume this layer's pending slot registration (if any). Every\n // non-innermost layer MUST declare a slot; the innermost MUST NOT.\n const slot = _consumePendingSlot()\n if (isInnermost && slot !== null) {\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 )\n }\n if (!isInnermost && slot === null) {\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.`,\n )\n }\n\n currentSlotAnchor = slot?.anchor ?? null\n currentSlotContexts = slot?.contexts\n }\n\n const html = serializeNodes(outermostNodes)\n\n // Dispose every layer's build now that the composed tree is serialized.\n for (const d of disposers) d()\n\n if (envelopePage === null) {\n // Unreachable — chain is non-empty so the last iteration sets this.\n throw new Error('[llui/vike] internal: renderChain produced no page entry')\n }\n\n return {\n html,\n envelope: {\n layouts: envelopeLayouts,\n page: envelopePage,\n },\n }\n}\n\n/** The seed state a layer's `init()` produces (used for the envelope when no\n * data slice overrides it). `init()` may return `S` or `[S, E[]]`. */\nfunction normalizeInitState(def: AnyLayer): unknown {\n const r = def.init()\n if (Array.isArray(r) && r.length === 2 && Array.isArray((r as [unknown, unknown[]])[1])) {\n return (r as [unknown, unknown[]])[0]\n }\n return r\n}\n"]}
|
package/dist/page-slot.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import type { Lifetime } from '@llui/dom';
|
|
2
1
|
/**
|
|
3
2
|
* Transient handoff between a layout layer's render pass and the vike
|
|
4
3
|
* adapter that's mounting the chain. `pageSlot()` populates this during
|
|
5
|
-
* the layout's view() call; `
|
|
4
|
+
* the layout's view() call; `_consumePendingSlot()` reads and clears it
|
|
6
5
|
* immediately after the mount returns. One slot per mount pass — calling
|
|
7
6
|
* `pageSlot()` twice in the same layout is a bug the primitive reports.
|
|
8
7
|
*/
|
|
9
8
|
interface PendingSlot {
|
|
10
|
-
|
|
9
|
+
/** the slot's insertion anchor (a `<!-- llui-page-slot -->` comment) */
|
|
11
10
|
anchor: Comment;
|
|
11
|
+
/**
|
|
12
|
+
* Snapshot of the context values in scope at the `pageSlot()` call site.
|
|
13
|
+
* The adapter replays these into the NESTED layer's build (via the signal
|
|
14
|
+
* `contexts` mount/render option) so a layout-provided context — e.g. a
|
|
15
|
+
* toast dispatcher provided ABOVE the slot — is reachable from inside the
|
|
16
|
+
* nested page, even though the page builds in a separate pass.
|
|
17
|
+
*/
|
|
18
|
+
contexts: ReadonlyMap<symbol, unknown>;
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
21
|
* Declare where a persistent layout renders its nested content — either
|
|
@@ -21,12 +28,12 @@ interface PendingSlot {
|
|
|
21
28
|
* within the layout's own parent element; a synthesized end sentinel
|
|
22
29
|
* (`<!-- llui-mount-end -->`) brackets the owned region.
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* the page does `useContext(ToastContext)` and
|
|
29
|
-
*
|
|
31
|
+
* Contexts provided by the layout (via `provide()`) ABOVE the slot are
|
|
32
|
+
* reachable from inside the nested page: `pageSlot()` snapshots the
|
|
33
|
+
* in-scope context values and the adapter replays them into the nested
|
|
34
|
+
* layer's build. That's how patterns like a layout-owned toast
|
|
35
|
+
* dispatcher work — the page does `useContext(ToastContext)` and reads
|
|
36
|
+
* the value the layout provided above the slot.
|
|
30
37
|
*
|
|
31
38
|
* Do NOT name the file `+Layout.ts` — Vike reserves the `+` prefix for
|
|
32
39
|
* its own framework config conventions. Use `Layout.ts`, `app-layout.ts`,
|
|
@@ -39,9 +46,9 @@ interface PendingSlot {
|
|
|
39
46
|
*
|
|
40
47
|
* export const AppLayout = component<LayoutState, LayoutMsg>({
|
|
41
48
|
* name: 'AppLayout',
|
|
42
|
-
* init: () =>
|
|
49
|
+
* init: () => ({ ... }),
|
|
43
50
|
* update: layoutUpdate,
|
|
44
|
-
* view: (
|
|
51
|
+
* view: ({ send }) => [
|
|
45
52
|
* div({ class: 'app-shell' }, [
|
|
46
53
|
* header([...]),
|
|
47
54
|
* main([pageSlot()]), // ← here the page goes (no wrapper div)
|
|
@@ -50,10 +57,13 @@ interface PendingSlot {
|
|
|
50
57
|
* })
|
|
51
58
|
* ```
|
|
52
59
|
*
|
|
60
|
+
* Returns the anchor comment as a single `Node` — drop it straight into
|
|
61
|
+
* a children array (`main([pageSlot()])`); no spread needed.
|
|
62
|
+
*
|
|
53
63
|
* Call exactly once per layout. Calling more than once in a single
|
|
54
64
|
* view throws.
|
|
55
65
|
*/
|
|
56
|
-
export declare function pageSlot(): Node
|
|
66
|
+
export declare function pageSlot(): Node;
|
|
57
67
|
/**
|
|
58
68
|
* @internal — vike adapter only. Read and clear the slot registered by
|
|
59
69
|
* the most recent `pageSlot()` call. Returns null if the layer being
|
package/dist/page-slot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-slot.d.ts","sourceRoot":"","sources":["../src/page-slot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"page-slot.d.ts","sourceRoot":"","sources":["../src/page-slot.ts"],"names":[],"mappings":"AASA;;;;;;GAMG;AACH,UAAU,WAAW;IACnB,wEAAwE;IACxE,MAAM,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACvC;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CAmB/B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,WAAW,GAAG,IAAI,CAIxD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|