@llui/vike 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__llui_deps.json +56 -0
- package/dist/on-render-client.d.ts +2 -2
- package/dist/on-render-client.d.ts.map +1 -1
- package/dist/on-render-client.js.map +1 -1
- package/dist/on-render-html.d.ts +2 -1
- package/dist/on-render-html.d.ts.map +1 -1
- package/dist/on-render-html.js.map +1 -1
- package/dist/page-slot.d.ts +4 -3
- package/dist/page-slot.d.ts.map +1 -1
- package/dist/page-slot.js +23 -17
- package/dist/page-slot.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerVersion": "0.3.0",
|
|
3
|
+
"components": {},
|
|
4
|
+
"helpers": {
|
|
5
|
+
"on-render-client#_mountChainSuffix": {
|
|
6
|
+
"helperLocalPaths": [],
|
|
7
|
+
"kind": "view-helper",
|
|
8
|
+
"viaParams": [
|
|
9
|
+
{
|
|
10
|
+
"index": 0,
|
|
11
|
+
"shape": "opaque"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"index": 1,
|
|
15
|
+
"shape": "opaque"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"index": 2,
|
|
19
|
+
"shape": "opaque"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"index": 3,
|
|
23
|
+
"shape": "opaque"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"index": 4,
|
|
27
|
+
"shape": "opaque"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"index": 5,
|
|
31
|
+
"reads": [
|
|
32
|
+
"mode",
|
|
33
|
+
"runInitEffectsOnHydrate",
|
|
34
|
+
"serverStateEnvelope"
|
|
35
|
+
],
|
|
36
|
+
"shape": "state-value"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"on-render-client#fromTransition": {
|
|
41
|
+
"helperLocalPaths": [],
|
|
42
|
+
"kind": "view-helper",
|
|
43
|
+
"viaParams": [
|
|
44
|
+
{
|
|
45
|
+
"index": 0,
|
|
46
|
+
"reads": [
|
|
47
|
+
"enter",
|
|
48
|
+
"leave"
|
|
49
|
+
],
|
|
50
|
+
"shape": "state-value"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"version": 2
|
|
56
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SignalComponentHandle } from '@llui/dom';
|
|
2
|
-
import type { TransitionOptions } from '@llui/dom';
|
|
2
|
+
import type { TransitionOptions, Renderable } from '@llui/dom';
|
|
3
3
|
import type { VikePageContextData } from './vike-namespace.js';
|
|
4
4
|
export { pageSlot } from './page-slot.js';
|
|
5
5
|
/** A type-erased signal component as the adapter handles it (type params unused
|
|
@@ -10,7 +10,7 @@ export interface AnyLayer {
|
|
|
10
10
|
readonly name?: string;
|
|
11
11
|
init(): unknown;
|
|
12
12
|
update(state: unknown, msg: unknown): unknown;
|
|
13
|
-
view(bag: unknown):
|
|
13
|
+
view(bag: unknown): Renderable;
|
|
14
14
|
onEffect?(effect: unknown, api: unknown): void | (() => void);
|
|
15
15
|
}
|
|
16
16
|
/** The live handle a mounted/hydrated layer exposes (send/getState/subscribe). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAe,MAAM,WAAW,CAAA;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 { 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"]}
|
|
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, Renderable } 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): Renderable\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,3 +1,4 @@
|
|
|
1
|
+
import type { Renderable } from '@llui/dom';
|
|
1
2
|
import type { DomEnv } from '@llui/dom/ssr';
|
|
2
3
|
import type { VikePageContextData } from './vike-namespace.js';
|
|
3
4
|
/**
|
|
@@ -21,7 +22,7 @@ export interface AnyLayer {
|
|
|
21
22
|
readonly name?: string;
|
|
22
23
|
init(): unknown;
|
|
23
24
|
update(state: unknown, msg: unknown): unknown;
|
|
24
|
-
view(bag: unknown):
|
|
25
|
+
view(bag: unknown): Renderable;
|
|
25
26
|
onEffect?(effect: unknown, api: unknown): void | (() => void);
|
|
26
27
|
}
|
|
27
28
|
type LayoutChain = ReadonlyArray<AnyLayer>;
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"on-render-html.d.ts","sourceRoot":"","sources":["../src/on-render-html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,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,UAAU,CAAA;IAC9B,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"}
|
|
@@ -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 { 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"]}
|
|
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 { Renderable } 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): Renderable\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,3 +1,4 @@
|
|
|
1
|
+
import type { Mountable } from '@llui/dom';
|
|
1
2
|
/**
|
|
2
3
|
* Transient handoff between a layout layer's render pass and the vike
|
|
3
4
|
* adapter that's mounting the chain. `pageSlot()` populates this during
|
|
@@ -57,13 +58,13 @@ interface PendingSlot {
|
|
|
57
58
|
* })
|
|
58
59
|
* ```
|
|
59
60
|
*
|
|
60
|
-
* Returns the anchor comment
|
|
61
|
+
* Returns a `Mountable` (the slot's anchor comment) — drop it straight into
|
|
61
62
|
* a children array (`main([pageSlot()])`); no spread needed.
|
|
62
63
|
*
|
|
63
64
|
* Call exactly once per layout. Calling more than once in a single
|
|
64
|
-
* view throws.
|
|
65
|
+
* view throws (when both are placed).
|
|
65
66
|
*/
|
|
66
|
-
export declare function pageSlot():
|
|
67
|
+
export declare function pageSlot(): Mountable;
|
|
67
68
|
/**
|
|
68
69
|
* @internal — vike adapter only. Read and clear the slot registered by
|
|
69
70
|
* 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":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAS1C;;;;;;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,SAAS,CAyBpC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,WAAW,GAAG,IAAI,CAIxD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
package/dist/page-slot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __currentBuildInfo } from '@llui/dom';
|
|
1
|
+
import { __currentBuildInfo, mountable } from '@llui/dom';
|
|
2
2
|
let pendingSlot = null;
|
|
3
3
|
/**
|
|
4
4
|
* Declare where a persistent layout renders its nested content — either
|
|
@@ -40,27 +40,33 @@ let pendingSlot = null;
|
|
|
40
40
|
* })
|
|
41
41
|
* ```
|
|
42
42
|
*
|
|
43
|
-
* Returns the anchor comment
|
|
43
|
+
* Returns a `Mountable` (the slot's anchor comment) — drop it straight into
|
|
44
44
|
* a children array (`main([pageSlot()])`); no spread needed.
|
|
45
45
|
*
|
|
46
46
|
* Call exactly once per layout. Calling more than once in a single
|
|
47
|
-
* view throws.
|
|
47
|
+
* view throws (when both are placed).
|
|
48
48
|
*/
|
|
49
49
|
export function pageSlot() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
// Deferred to placement (like every authoring helper): the anchor is created
|
|
51
|
+
// and the pending-slot handoff registered when the Mountable is materialized,
|
|
52
|
+
// which still happens during the layout's view build — before the adapter reads
|
|
53
|
+
// `_consumePendingSlot()`.
|
|
54
|
+
return mountable(() => {
|
|
55
|
+
if (pendingSlot !== null) {
|
|
56
|
+
throw new Error('[llui/vike] pageSlot() was called more than once in the same layout. ' +
|
|
57
|
+
'A layout has exactly one nested-content slot — if you need two independent ' +
|
|
58
|
+
'regions that swap on navigation, build them as sibling nested layouts in ' +
|
|
59
|
+
'the Vike routing tree and use context to share state between them.');
|
|
60
|
+
}
|
|
61
|
+
const info = __currentBuildInfo();
|
|
62
|
+
if (!info) {
|
|
63
|
+
throw new Error('[llui/vike] pageSlot() was called outside a signal build. It must run inside ' +
|
|
64
|
+
'a layout component view rendered by the @llui/vike adapter.');
|
|
65
|
+
}
|
|
66
|
+
const anchor = info.doc.createComment('llui-page-slot');
|
|
67
|
+
pendingSlot = { anchor, contexts: info.contexts };
|
|
68
|
+
return anchor;
|
|
69
|
+
});
|
|
64
70
|
}
|
|
65
71
|
/**
|
|
66
72
|
* @internal — vike adapter only. Read and clear the slot registered by
|
package/dist/page-slot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-slot.js","sourceRoot":"","sources":["../src/page-slot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"page-slot.js","sourceRoot":"","sources":["../src/page-slot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AA8BzD,IAAI,WAAW,GAAuB,IAAI,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,UAAU,QAAQ;IACtB,6EAA6E;IAC7E,8EAA8E;IAC9E,gFAAgF;IAChF,2BAA2B;IAC3B,OAAO,SAAS,CAAC,GAAG,EAAE;QACpB,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,uEAAuE;gBACrE,6EAA6E;gBAC7E,2EAA2E;gBAC3E,oEAAoE,CACvE,CAAA;QACH,CAAC;QACD,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAA;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,+EAA+E;gBAC7E,6DAA6D,CAChE,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAY,CAAA;QAClE,WAAW,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;QACjD,OAAO,MAAM,CAAA;IACf,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GAAG,WAAW,CAAA;IACxB,WAAW,GAAG,IAAI,CAAA;IAClB,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,WAAW,GAAG,IAAI,CAAA;AACpB,CAAC","sourcesContent":["import { __currentBuildInfo, mountable } from '@llui/dom'\nimport type { Mountable } from '@llui/dom'\n\n// `@llui/dom`'s `__currentBuildInfo()` is the adapter-layer hook a\n// framework adapter like `@llui/vike` uses to participate in the signal build:\n// it exposes the in-progress build's `doc` (to create anchor nodes that belong\n// to the same document) plus a SNAPSHOT of the context values in scope at the\n// call site (so contexts provided ABOVE the slot reach the nested page's\n// separate build/mount pass). Not part of the public app-author API.\n\n/**\n * Transient handoff between a layout layer's render pass and the vike\n * adapter that's mounting the chain. `pageSlot()` populates this during\n * the layout's view() call; `_consumePendingSlot()` reads and clears it\n * immediately after the mount returns. One slot per mount pass — calling\n * `pageSlot()` twice in the same layout is a bug the primitive reports.\n */\ninterface PendingSlot {\n /** the slot's insertion anchor (a `<!-- llui-page-slot -->` comment) */\n anchor: Comment\n /**\n * Snapshot of the context values in scope at the `pageSlot()` call site.\n * The adapter replays these into the NESTED layer's build (via the signal\n * `contexts` mount/render option) so a layout-provided context — e.g. a\n * toast dispatcher provided ABOVE the slot — is reachable from inside the\n * nested page, even though the page builds in a separate pass.\n */\n contexts: ReadonlyMap<symbol, unknown>\n}\n\nlet pendingSlot: PendingSlot | null = null\n\n/**\n * Declare where a persistent layout renders its nested content — either\n * a nested layout or the route's page component. The vike adapter's\n * client and server render paths walk the layout chain, and each layer's\n * `pageSlot()` call records the position where the next layer mounts.\n *\n * Emits a single `<!-- llui-page-slot -->` comment as an insertion\n * anchor. The nested layer's DOM lives as siblings of this comment\n * within the layout's own parent element; a synthesized end sentinel\n * (`<!-- llui-mount-end -->`) brackets the owned region.\n *\n * Contexts provided by the layout (via `provide()`) ABOVE the slot are\n * reachable from inside the nested page: `pageSlot()` snapshots the\n * in-scope context values and the adapter replays them into the nested\n * layer's build. That's how patterns like a layout-owned toast\n * dispatcher work — the page does `useContext(ToastContext)` and reads\n * the value the layout provided above the slot.\n *\n * Do NOT name the file `+Layout.ts` — Vike reserves the `+` prefix for\n * its own framework config conventions. Use `Layout.ts`, `app-layout.ts`,\n * or anywhere outside `/pages` that Vike won't scan.\n *\n * ```ts\n * // pages/Layout.ts ← not +Layout.ts\n * import { component, div, main, header } from '@llui/dom'\n * import { pageSlot } from '@llui/vike/client'\n *\n * export const AppLayout = component<LayoutState, LayoutMsg>({\n * name: 'AppLayout',\n * init: () => ({ ... }),\n * update: layoutUpdate,\n * view: ({ send }) => [\n * div({ class: 'app-shell' }, [\n * header([...]),\n * main([pageSlot()]), // ← here the page goes (no wrapper div)\n * ]),\n * ],\n * })\n * ```\n *\n * Returns a `Mountable` (the slot's anchor comment) — drop it straight into\n * a children array (`main([pageSlot()])`); no spread needed.\n *\n * Call exactly once per layout. Calling more than once in a single\n * view throws (when both are placed).\n */\nexport function pageSlot(): Mountable {\n // Deferred to placement (like every authoring helper): the anchor is created\n // and the pending-slot handoff registered when the Mountable is materialized,\n // which still happens during the layout's view build — before the adapter reads\n // `_consumePendingSlot()`.\n return mountable(() => {\n if (pendingSlot !== null) {\n throw new Error(\n '[llui/vike] pageSlot() was called more than once in the same layout. ' +\n 'A layout has exactly one nested-content slot — if you need two independent ' +\n 'regions that swap on navigation, build them as sibling nested layouts in ' +\n 'the Vike routing tree and use context to share state between them.',\n )\n }\n const info = __currentBuildInfo()\n if (!info) {\n throw new Error(\n '[llui/vike] pageSlot() was called outside a signal build. It must run inside ' +\n 'a layout component view rendered by the @llui/vike adapter.',\n )\n }\n const anchor = info.doc.createComment('llui-page-slot') as Comment\n pendingSlot = { anchor, contexts: info.contexts }\n return anchor\n })\n}\n\n/**\n * @internal — vike adapter only. Read and clear the slot registered by\n * the most recent `pageSlot()` call. Returns null if the layer being\n * mounted didn't call `pageSlot()` (meaning it's the innermost layer\n * and owns no nested content).\n */\nexport function _consumePendingSlot(): PendingSlot | null {\n const slot = pendingSlot\n pendingSlot = null\n return slot\n}\n\n/**\n * @internal — vike adapter only. Reset the pending slot without reading\n * it. Used defensively in error paths to avoid leaking a pending slot\n * registration into a subsequent mount attempt.\n */\nexport function _resetPendingSlot(): void {\n pendingSlot = null\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llui/vike",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@llui/dom": "^0.
|
|
24
|
+
"@llui/dom": "^0.7.0",
|
|
25
25
|
"jsdom": "*"
|
|
26
26
|
},
|
|
27
27
|
"peerDependenciesMeta": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@llui/dom": "0.
|
|
33
|
+
"@llui/dom": "0.7.0"
|
|
34
34
|
},
|
|
35
35
|
"description": "LLui Vike SSR adapter — onRenderHtml, onRenderClient hooks",
|
|
36
36
|
"keywords": [
|