@tanstack/react-start-client
Advanced tools
| import { HydrateProps } from './Hydrate.js'; | ||
| import * as React from 'react'; | ||
| export declare function GenericHydrate(props: HydrateProps): React.JSX.Element; |
| "use client"; | ||
| import { reactUse, useHydrated, useLayoutEffect } from "@tanstack/react-router"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| import * as React from "react"; | ||
| import { isServer } from "@tanstack/router-core/isServer"; | ||
| import { listenForDelegatedHydrationIntent } from "@tanstack/start-client-core/hydration"; | ||
| import { hydrateIdAttribute, hydrateWhenAttribute } from "@tanstack/start-client-core/hydration/constants"; | ||
| import { createResolvedGate, getFallbackHtml, getOrCreateGate, onGateResolve, releaseGate, runHydrationStrategyCleanup, saveFallbackHtml, waitForHydrationPrefetchStrategy } from "@tanstack/start-client-core/hydration/runtime"; | ||
| //#region src/GenericHydrate.tsx | ||
| var dynamicType = "dynamic"; | ||
| var dynamicHydrateStrategy = { | ||
| _t: dynamicType, | ||
| _d: () => true | ||
| }; | ||
| function shouldDeferHydration(strategy) { | ||
| return strategy._d ? strategy._d() : strategy._t !== "load"; | ||
| } | ||
| function useLatest(value) { | ||
| const ref = React.useRef(value); | ||
| ref.current = value; | ||
| return ref; | ||
| } | ||
| function useHydrationGate(props) { | ||
| const hydrated = useHydrated(); | ||
| const reactId = React.useId(); | ||
| const id = props.h ? `${props.h}${reactId}` : reactId; | ||
| const when = props.when; | ||
| const isDynamicHydrate = typeof when === "function"; | ||
| const dynamicHydrateStrategyRef = React.useRef(void 0); | ||
| if (isDynamicHydrate) dynamicHydrateStrategyRef.current ??= isServer ?? typeof window === "undefined" ? dynamicHydrateStrategy : when(); | ||
| const hydrateStrategy = isDynamicHydrate ? dynamicHydrateStrategyRef.current : when; | ||
| const markerHydrateType = isDynamicHydrate ? dynamicType : hydrateStrategy._t; | ||
| const [prefetchError, setPrefetchError] = React.useState(); | ||
| const latestRef = useLatest({ | ||
| prefetch: props.prefetch, | ||
| preload: props.p | ||
| }); | ||
| const gateRef = React.useRef(void 0); | ||
| const markerElementRef = React.useRef(null); | ||
| const shouldPreserveServerHTMLRef = React.useRef(void 0); | ||
| const shouldDeferInitialHydrationRef = React.useRef(void 0); | ||
| const didPrefetchRef = React.useRef(false); | ||
| const prefetchControllerRef = React.useRef(void 0); | ||
| prefetchControllerRef.current ??= { | ||
| abortController: new AbortController(), | ||
| hydrationRequested: false, | ||
| hydrationListeners: /* @__PURE__ */ new Set(), | ||
| hydrationResolvePending: false, | ||
| started: false | ||
| }; | ||
| shouldPreserveServerHTMLRef.current ??= (isServer ?? typeof window === "undefined") || !hydrated; | ||
| shouldDeferInitialHydrationRef.current ??= !hydrated && shouldDeferHydration(hydrateStrategy); | ||
| if (!gateRef.current) gateRef.current = isServer ?? typeof window === "undefined" ? createResolvedGate(id, hydrateStrategy._t) : getOrCreateGate(id, hydrateStrategy._t); | ||
| gateRef.current.when = hydrateStrategy._t; | ||
| if (!(isServer ?? typeof window === "undefined") && hydrateStrategy._t !== "never" && (!shouldDeferInitialHydrationRef.current || !shouldDeferHydration(hydrateStrategy))) gateRef.current.resolve(); | ||
| const markerRef = React.useCallback((element) => { | ||
| markerElementRef.current = element; | ||
| if (element) { | ||
| if (hydrateStrategy._t === "never" && !shouldPreserveServerHTMLRef.current) element.replaceChildren(); | ||
| saveFallbackHtml(id, element); | ||
| } | ||
| }, [hydrateStrategy._t, id]); | ||
| React.useEffect(() => { | ||
| const gate = gateRef.current; | ||
| return () => { | ||
| const controller = prefetchControllerRef.current; | ||
| controller?.abortController.abort(); | ||
| controller?.cleanup?.(); | ||
| controller?.hydrationListeners.clear(); | ||
| releaseGate(gate); | ||
| }; | ||
| }, []); | ||
| React.useEffect(() => { | ||
| if ((isServer ?? typeof window === "undefined") || !latestRef.current.prefetch) return; | ||
| const controller = prefetchControllerRef.current; | ||
| if (controller.started) return; | ||
| controller.started = true; | ||
| const onHydrate = (listener) => { | ||
| if (controller.hydrationRequested) { | ||
| listener(); | ||
| return () => {}; | ||
| } | ||
| controller.hydrationListeners.add(listener); | ||
| return () => { | ||
| controller.hydrationListeners.delete(listener); | ||
| }; | ||
| }; | ||
| const preload = () => latestRef.current.preload?.() ?? Promise.resolve(); | ||
| const prefetchInput = latestRef.current.prefetch; | ||
| if (typeof prefetchInput === "function") { | ||
| const promise = Promise.resolve().then(() => prefetchInput({ | ||
| element: markerElementRef.current, | ||
| signal: controller.abortController.signal, | ||
| preload, | ||
| waitFor: (strategy) => waitForHydrationPrefetchStrategy(strategy, { | ||
| element: markerElementRef.current, | ||
| signal: controller.abortController.signal, | ||
| onHydrate | ||
| }) | ||
| })).then(() => void 0); | ||
| controller.promise = promise; | ||
| promise.catch((error) => { | ||
| if (!controller.abortController.signal.aborted) setPrefetchError(error); | ||
| }); | ||
| return; | ||
| } | ||
| if (!latestRef.current.preload) return; | ||
| const prefetch = () => { | ||
| if (didPrefetchRef.current) return; | ||
| didPrefetchRef.current = true; | ||
| preload(); | ||
| }; | ||
| controller.cleanup = runHydrationStrategyCleanup(prefetchInput._s?.({ | ||
| element: markerElementRef.current, | ||
| prefetch | ||
| })); | ||
| }, [hydrateStrategy, latestRef]); | ||
| useLayoutEffect(() => { | ||
| const gate = gateRef.current; | ||
| if (!shouldDeferInitialHydrationRef.current || hydrateStrategy._t === "never") return; | ||
| if (gate.resolved) return; | ||
| const cleanups = []; | ||
| let removeResolveListener = () => {}; | ||
| let disposed = false; | ||
| const resolveGate = gate.resolve; | ||
| const cleanup = () => { | ||
| if (disposed) return; | ||
| disposed = true; | ||
| if (gate.resolve === requestHydration) gate.resolve = resolveGate; | ||
| removeResolveListener(); | ||
| cleanups.forEach((fn) => fn()); | ||
| }; | ||
| const addCleanup = (fn) => { | ||
| if (!fn) return; | ||
| if (disposed || gate.resolved) { | ||
| fn(); | ||
| return; | ||
| } | ||
| cleanups.push(fn); | ||
| }; | ||
| const requestHydration = () => { | ||
| const controller = prefetchControllerRef.current; | ||
| if (!controller.hydrationRequested) { | ||
| controller.hydrationRequested = true; | ||
| controller.hydrationListeners.forEach((listener) => listener()); | ||
| controller.hydrationListeners.clear(); | ||
| } | ||
| if (!controller.promise) { | ||
| resolveGate(); | ||
| return; | ||
| } | ||
| if (controller.hydrationResolvePending) return; | ||
| controller.hydrationResolvePending = true; | ||
| controller.promise.then(() => resolveGate(), (error) => { | ||
| if (!controller.abortController.signal.aborted) setPrefetchError(error); | ||
| }); | ||
| }; | ||
| gate.resolve = requestHydration; | ||
| removeResolveListener = onGateResolve(gate, cleanup); | ||
| const context = { | ||
| element: markerElementRef.current, | ||
| gate | ||
| }; | ||
| addCleanup(runHydrationStrategyCleanup(hydrateStrategy._s?.(context))); | ||
| if (hydrateStrategy._t !== "interaction") addCleanup(runHydrationStrategyCleanup(markerElementRef.current ? listenForDelegatedHydrationIntent(markerElementRef.current, context) : void 0)); | ||
| return cleanup; | ||
| }, [hydrateStrategy, latestRef]); | ||
| return { | ||
| gate: gateRef.current, | ||
| markerRef, | ||
| markerElementRef, | ||
| hydrateStrategy, | ||
| markerHydrateType, | ||
| prefetchError, | ||
| shouldPreserveServerHTML: shouldPreserveServerHTMLRef.current | ||
| }; | ||
| } | ||
| function HydrationGate(props) { | ||
| if (isServer ?? typeof window === "undefined") return props.children; | ||
| if (props.gate.resolved) return props.children; | ||
| if (!reactUse) throw props.gate.promise; | ||
| reactUse(props.gate.promise); | ||
| return props.children; | ||
| } | ||
| function HydratedBoundary(props) { | ||
| const { id, onHydrated, onStrategyHydrated } = props; | ||
| const didHydrateRef = React.useRef(false); | ||
| React.useEffect(() => { | ||
| if (didHydrateRef.current) return; | ||
| didHydrateRef.current = true; | ||
| onHydrated?.(); | ||
| onStrategyHydrated?.(id); | ||
| }, [ | ||
| id, | ||
| onHydrated, | ||
| onStrategyHydrated | ||
| ]); | ||
| return props.children; | ||
| } | ||
| function GenericHydrate(props) { | ||
| const { gate, hydrateStrategy, markerHydrateType, markerElementRef, markerRef, prefetchError, shouldPreserveServerHTML } = useHydrationGate(props); | ||
| if (prefetchError) throw prefetchError; | ||
| const fallback = shouldPreserveServerHTML ? (() => { | ||
| const html = getFallbackHtml(gate.id); | ||
| return html ? /* @__PURE__ */ jsx("div", { | ||
| style: { display: "contents" }, | ||
| dangerouslySetInnerHTML: { __html: html } | ||
| }) : null; | ||
| })() : props.fallback ?? null; | ||
| const markerAttributes = markerHydrateType === dynamicType ? void 0 : hydrateStrategy._a?.(); | ||
| if (hydrateStrategy._t === "never" && !shouldPreserveServerHTML) return /* @__PURE__ */ jsx("div", { | ||
| ref: markerRef, | ||
| [hydrateIdAttribute]: gate.id, | ||
| [hydrateWhenAttribute]: markerHydrateType, | ||
| ...markerAttributes, | ||
| children: props.fallback ?? null | ||
| }); | ||
| return /* @__PURE__ */ jsx("div", { | ||
| ref: markerRef, | ||
| [hydrateIdAttribute]: gate.id, | ||
| [hydrateWhenAttribute]: markerHydrateType, | ||
| ...markerAttributes, | ||
| children: /* @__PURE__ */ jsx(React.Suspense, { | ||
| fallback, | ||
| children: /* @__PURE__ */ jsx(HydrationGate, { | ||
| gate, | ||
| children: /* @__PURE__ */ jsx(HydratedBoundary, { | ||
| id: gate.id, | ||
| onHydrated: props.onHydrated, | ||
| onStrategyHydrated: (id) => { | ||
| markerElementRef.current?.removeAttribute(hydrateWhenAttribute); | ||
| hydrateStrategy._o?.(id); | ||
| }, | ||
| children: props.children | ||
| }) | ||
| }) | ||
| }) | ||
| }); | ||
| } | ||
| //#endregion | ||
| export { GenericHydrate }; | ||
| //# sourceMappingURL=GenericHydrate.js.map |
| {"version":3,"file":"GenericHydrate.js","names":[],"sources":["../../src/GenericHydrate.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { reactUse, useHydrated, useLayoutEffect } from '@tanstack/react-router'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport {\n hydrateIdAttribute,\n hydrateWhenAttribute,\n} from '@tanstack/start-client-core/hydration/constants'\nimport {\n createResolvedGate,\n getFallbackHtml,\n getOrCreateGate,\n onGateResolve,\n releaseGate,\n runHydrationStrategyCleanup,\n saveFallbackHtml,\n waitForHydrationPrefetchStrategy,\n} from '@tanstack/start-client-core/hydration/runtime'\nimport { listenForDelegatedHydrationIntent } from '@tanstack/start-client-core/hydration'\nimport type {\n HydrationRuntimeContext,\n HydrationStrategy,\n HydrationWhen,\n} from '@tanstack/start-client-core/hydration'\nimport type { HydrationGateRecord } from '@tanstack/start-client-core/hydration/runtime'\nimport type { HydrateProps, InternalHydrateProps } from './Hydrate'\n\ntype Gate = HydrationGateRecord & { promise: Promise<void> }\ntype PrefetchController = {\n abortController: AbortController\n hydrationRequested: boolean\n hydrationListeners: Set<() => void>\n hydrationResolvePending: boolean\n started: boolean\n promise?: Promise<void>\n cleanup?: () => void\n}\n\nconst dynamicType = 'dynamic'\nconst dynamicHydrateStrategy = {\n _t: dynamicType,\n _d: () => true,\n} satisfies HydrationStrategy<typeof dynamicType, false>\n\nfunction shouldDeferHydration(strategy: HydrationStrategy) {\n return strategy._d ? strategy._d() : strategy._t !== 'load'\n}\n\nfunction useLatest<T>(value: T) {\n const ref = React.useRef(value)\n ref.current = value\n return ref\n}\n\nfunction useHydrationGate(props: InternalHydrateProps) {\n const hydrated = useHydrated()\n const reactId = React.useId()\n const id = props.h ? `${props.h}${reactId}` : reactId\n const when = props.when\n const isDynamicHydrate = typeof when === 'function'\n const dynamicHydrateStrategyRef = React.useRef<HydrationStrategy | undefined>(\n undefined,\n )\n if (isDynamicHydrate) {\n dynamicHydrateStrategyRef.current ??=\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (isServer ?? typeof window === 'undefined')\n ? dynamicHydrateStrategy\n : when()\n }\n const hydrateStrategy = isDynamicHydrate\n ? dynamicHydrateStrategyRef.current!\n : when\n const markerHydrateType: HydrationWhen = isDynamicHydrate\n ? dynamicType\n : hydrateStrategy._t!\n const [prefetchError, setPrefetchError] = React.useState<unknown>()\n const latestRef = useLatest({\n prefetch: props.prefetch,\n preload: props.p,\n })\n const gateRef = React.useRef<HydrationGateRecord | undefined>(undefined)\n const markerElementRef = React.useRef<HTMLDivElement | null>(null)\n const shouldPreserveServerHTMLRef = React.useRef<boolean | undefined>(\n undefined,\n )\n const shouldDeferInitialHydrationRef = React.useRef<boolean | undefined>(\n undefined,\n )\n const didPrefetchRef = React.useRef(false)\n const prefetchControllerRef = React.useRef<PrefetchController | undefined>(\n undefined,\n )\n\n prefetchControllerRef.current ??= {\n abortController: new AbortController(),\n hydrationRequested: false,\n hydrationListeners: new Set<() => void>(),\n hydrationResolvePending: false,\n started: false,\n }\n\n shouldPreserveServerHTMLRef.current ??=\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (isServer ?? typeof window === 'undefined') || !hydrated\n shouldDeferInitialHydrationRef.current ??=\n !hydrated && shouldDeferHydration(hydrateStrategy)\n\n if (!gateRef.current) {\n gateRef.current =\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (isServer ?? typeof window === 'undefined')\n ? createResolvedGate(id, hydrateStrategy._t!)\n : getOrCreateGate(id, hydrateStrategy._t!)\n }\n\n gateRef.current.when = hydrateStrategy._t!\n\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n !(isServer ?? typeof window === 'undefined') &&\n hydrateStrategy._t !== 'never' &&\n (!shouldDeferInitialHydrationRef.current ||\n !shouldDeferHydration(hydrateStrategy))\n ) {\n gateRef.current.resolve()\n }\n\n const markerRef = React.useCallback(\n (element: HTMLDivElement | null) => {\n markerElementRef.current = element\n if (element) {\n if (\n hydrateStrategy._t === 'never' &&\n !shouldPreserveServerHTMLRef.current\n ) {\n element.replaceChildren()\n }\n saveFallbackHtml(id, element)\n }\n },\n [hydrateStrategy._t, id],\n )\n\n React.useEffect(() => {\n const gate = gateRef.current!\n return () => {\n const controller = prefetchControllerRef.current\n controller?.abortController.abort()\n controller?.cleanup?.()\n controller?.hydrationListeners.clear()\n releaseGate(gate)\n }\n }, [])\n\n React.useEffect(() => {\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (isServer ?? typeof window === 'undefined') ||\n !latestRef.current.prefetch\n ) {\n return\n }\n\n const controller = prefetchControllerRef.current!\n if (controller.started) return\n controller.started = true\n\n const onHydrate = (listener: () => void) => {\n if (controller.hydrationRequested) {\n listener()\n return () => {}\n }\n\n controller.hydrationListeners.add(listener)\n return () => {\n controller.hydrationListeners.delete(listener)\n }\n }\n\n const preload = () => latestRef.current.preload?.() ?? Promise.resolve()\n const prefetchInput = latestRef.current.prefetch\n\n if (typeof prefetchInput === 'function') {\n const promise = Promise.resolve()\n .then(() =>\n prefetchInput({\n element: markerElementRef.current,\n signal: controller.abortController.signal,\n preload,\n waitFor: (strategy) =>\n waitForHydrationPrefetchStrategy(strategy, {\n element: markerElementRef.current,\n signal: controller.abortController.signal,\n onHydrate,\n }),\n }),\n )\n .then(() => undefined)\n\n controller.promise = promise\n promise.catch((error) => {\n if (!controller.abortController.signal.aborted) {\n setPrefetchError(error)\n }\n })\n return\n }\n\n if (!latestRef.current.preload) return\n\n const prefetch = () => {\n if (didPrefetchRef.current) return\n didPrefetchRef.current = true\n void preload()\n }\n\n controller.cleanup = runHydrationStrategyCleanup(\n prefetchInput._s?.({\n element: markerElementRef.current,\n prefetch,\n }),\n )\n }, [hydrateStrategy, latestRef])\n\n useLayoutEffect(() => {\n const gate = gateRef.current!\n if (\n !shouldDeferInitialHydrationRef.current ||\n hydrateStrategy._t === 'never'\n ) {\n return\n }\n\n if (gate.resolved) {\n return\n }\n\n const cleanups: Array<() => void> = []\n let removeResolveListener = () => {}\n let disposed = false\n const resolveGate = gate.resolve\n\n const cleanup = () => {\n if (disposed) return\n disposed = true\n if (gate.resolve === requestHydration) {\n gate.resolve = resolveGate\n }\n removeResolveListener()\n cleanups.forEach((fn) => fn())\n }\n\n const addCleanup = (fn: void | (() => void)) => {\n if (!fn) return\n if (disposed || gate.resolved) {\n fn()\n return\n }\n cleanups.push(fn)\n }\n\n const requestHydration = () => {\n const controller = prefetchControllerRef.current!\n if (!controller.hydrationRequested) {\n controller.hydrationRequested = true\n controller.hydrationListeners.forEach((listener) => listener())\n controller.hydrationListeners.clear()\n }\n\n if (!controller.promise) {\n resolveGate()\n return\n }\n if (controller.hydrationResolvePending) return\n controller.hydrationResolvePending = true\n\n controller.promise.then(\n () => resolveGate(),\n (error) => {\n if (!controller.abortController.signal.aborted) {\n setPrefetchError(error)\n }\n },\n )\n }\n\n gate.resolve = requestHydration\n removeResolveListener = onGateResolve(gate, cleanup)\n\n const context: HydrationRuntimeContext = {\n element: markerElementRef.current,\n gate,\n }\n addCleanup(runHydrationStrategyCleanup(hydrateStrategy._s?.(context)))\n\n if (hydrateStrategy._t !== 'interaction') {\n addCleanup(\n runHydrationStrategyCleanup(\n markerElementRef.current\n ? listenForDelegatedHydrationIntent(\n markerElementRef.current,\n context,\n )\n : undefined,\n ),\n )\n }\n\n return cleanup\n }, [hydrateStrategy, latestRef])\n\n return {\n gate: gateRef.current,\n markerRef,\n markerElementRef,\n hydrateStrategy,\n markerHydrateType,\n prefetchError,\n shouldPreserveServerHTML: shouldPreserveServerHTMLRef.current,\n }\n}\n\nfunction HydrationGate(props: { gate: Gate; children: React.ReactNode }) {\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n isServer ??\n typeof window === 'undefined'\n ) {\n return props.children as React.JSX.Element\n }\n\n if (props.gate.resolved) {\n return props.children as React.JSX.Element\n }\n\n if (!reactUse) {\n throw props.gate.promise\n }\n\n reactUse(props.gate.promise)\n\n return props.children as React.JSX.Element\n}\n\nfunction HydratedBoundary(props: {\n id: string\n onHydrated?: () => void\n onStrategyHydrated?: (id: string) => void\n children: React.ReactNode\n}) {\n const { id, onHydrated, onStrategyHydrated } = props\n const didHydrateRef = React.useRef(false)\n\n React.useEffect(() => {\n if (didHydrateRef.current) return\n didHydrateRef.current = true\n onHydrated?.()\n onStrategyHydrated?.(id)\n }, [id, onHydrated, onStrategyHydrated])\n\n return props.children as React.JSX.Element\n}\n\nexport function GenericHydrate(props: HydrateProps): React.JSX.Element {\n const internalProps = props as InternalHydrateProps\n const {\n gate,\n hydrateStrategy,\n markerHydrateType,\n markerElementRef,\n markerRef,\n prefetchError,\n shouldPreserveServerHTML,\n } = useHydrationGate(internalProps)\n if (prefetchError) throw prefetchError\n\n const fallback = shouldPreserveServerHTML\n ? (() => {\n const html = getFallbackHtml(gate.id)\n return html ? (\n <div\n style={{ display: 'contents' }}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n ) : null\n })()\n : (props.fallback ?? null)\n const markerAttributes =\n markerHydrateType === dynamicType ? undefined : hydrateStrategy._a?.()\n\n const hydrateType = hydrateStrategy._t!\n\n if (hydrateType === 'never' && !shouldPreserveServerHTML) {\n return (\n <div\n ref={markerRef}\n {...{\n [hydrateIdAttribute]: gate.id,\n [hydrateWhenAttribute]: markerHydrateType,\n ...markerAttributes,\n }}\n >\n {props.fallback ?? null}\n </div>\n )\n }\n\n return (\n <div\n ref={markerRef}\n {...{\n [hydrateIdAttribute]: gate.id,\n [hydrateWhenAttribute]: markerHydrateType,\n ...markerAttributes,\n }}\n >\n <React.Suspense fallback={fallback}>\n <HydrationGate gate={gate}>\n <HydratedBoundary\n id={gate.id}\n onHydrated={props.onHydrated}\n onStrategyHydrated={(id) => {\n markerElementRef.current?.removeAttribute(hydrateWhenAttribute)\n hydrateStrategy._o?.(id)\n }}\n >\n {props.children}\n </HydratedBoundary>\n </HydrationGate>\n </React.Suspense>\n </div>\n )\n}\n"],"mappings":"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"} |
| import { HydrationStrategy as CoreHydrationStrategy, HydrationPrefetchFunction, HydrationPrefetchStrategy, HydrationWhen } from '@tanstack/start-client-core/hydration'; | ||
| import * as React from 'react'; | ||
| export type { HydrationInteractionEvent, HydrationInteractionEvents, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationWhen, } from '@tanstack/start-client-core/hydration'; | ||
| export type ReactHydrationStrategy<TWhen extends HydrationWhen = HydrationWhen, TCanPrefetch extends boolean = boolean> = CoreHydrationStrategy<TWhen, TCanPrefetch> & { | ||
| _h: (this: ReactHydrationStrategy, props: HydrateProps) => React.JSX.Element; | ||
| }; | ||
| export type HydrationStrategy<TWhen extends HydrationWhen = HydrationWhen, TCanPrefetch extends boolean = boolean> = ReactHydrationStrategy<TWhen, TCanPrefetch>; | ||
| export type HydrateWhen = ReactHydrationStrategy | (() => ReactHydrationStrategy); | ||
| type HydrateCommonOptions = { | ||
| when: HydrateWhen; | ||
| fallback?: React.ReactNode; | ||
| onHydrated?: () => void; | ||
| }; | ||
| export type HydrateOptions = (HydrateCommonOptions & { | ||
| prefetch?: never; | ||
| split?: boolean; | ||
| }) | (HydrateCommonOptions & { | ||
| prefetch: HydrationPrefetchStrategy; | ||
| split?: true; | ||
| }) | (HydrateCommonOptions & { | ||
| prefetch: HydrationPrefetchFunction; | ||
| split?: boolean; | ||
| }); | ||
| export type HydrateProps = HydrateOptions & { | ||
| children: React.ReactNode; | ||
| }; | ||
| export type InternalHydrateProps = HydrateProps & { | ||
| h?: string; | ||
| p?: () => Promise<void>; | ||
| }; | ||
| export declare function Hydrate(props: HydrateProps): React.JSX.Element; |
| "use client"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| import * as React from "react"; | ||
| import { isServer } from "@tanstack/router-core/isServer"; | ||
| //#region src/Hydrate.tsx | ||
| var dynamicType = "dynamic"; | ||
| var hydrateIdAttribute = "data-ts-hydrate-id"; | ||
| var hydrateWhenAttribute = "data-ts-hydrate-when"; | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function ServerDynamicHydrate(props) { | ||
| const internalProps = props; | ||
| const reactId = React.useId(); | ||
| const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId; | ||
| return /* @__PURE__ */ jsx("div", { | ||
| [hydrateIdAttribute]: id, | ||
| [hydrateWhenAttribute]: dynamicType, | ||
| children: /* @__PURE__ */ jsx(React.Suspense, { | ||
| fallback: props.fallback ?? null, | ||
| children: props.children | ||
| }) | ||
| }); | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function Hydrate(props) { | ||
| if (typeof props.when === "function") { | ||
| if (isServer ?? typeof window === "undefined") return /* @__PURE__ */ jsx(ServerDynamicHydrate, { ...props }); | ||
| return props.when()._h(props); | ||
| } | ||
| return props.when._h(props); | ||
| } | ||
| //#endregion | ||
| export { Hydrate }; | ||
| //# sourceMappingURL=Hydrate.js.map |
| {"version":3,"file":"Hydrate.js","names":[],"sources":["../../src/Hydrate.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type {\n HydrationStrategy as CoreHydrationStrategy,\n HydrationPrefetchFunction,\n HydrationPrefetchStrategy,\n HydrationWhen,\n} from '@tanstack/start-client-core/hydration'\n\nexport type {\n HydrationInteractionEvent,\n HydrationInteractionEvents,\n HydrationPrefetchContext,\n HydrationPrefetchFunction,\n HydrationPrefetchStrategy,\n HydrationPrefetchWaitReason,\n HydrationWhen,\n} from '@tanstack/start-client-core/hydration'\n\nexport type ReactHydrationStrategy<\n TWhen extends HydrationWhen = HydrationWhen,\n TCanPrefetch extends boolean = boolean,\n> = CoreHydrationStrategy<TWhen, TCanPrefetch> & {\n _h: (this: ReactHydrationStrategy, props: HydrateProps) => React.JSX.Element\n}\n\nexport type HydrationStrategy<\n TWhen extends HydrationWhen = HydrationWhen,\n TCanPrefetch extends boolean = boolean,\n> = ReactHydrationStrategy<TWhen, TCanPrefetch>\n\nexport type HydrateWhen =\n | ReactHydrationStrategy\n | (() => ReactHydrationStrategy)\n\ntype HydrateCommonOptions = {\n when: HydrateWhen\n fallback?: React.ReactNode\n onHydrated?: () => void\n}\n\nexport type HydrateOptions =\n | (HydrateCommonOptions & {\n prefetch?: never\n split?: boolean\n })\n | (HydrateCommonOptions & {\n prefetch: HydrationPrefetchStrategy\n split?: true\n })\n | (HydrateCommonOptions & {\n prefetch: HydrationPrefetchFunction\n split?: boolean\n })\n\nexport type HydrateProps = HydrateOptions & {\n children: React.ReactNode\n}\n\nexport type InternalHydrateProps = HydrateProps & {\n h?: string\n p?: () => Promise<void>\n}\n\nconst dynamicType = 'dynamic'\nconst hydrateIdAttribute = 'data-ts-hydrate-id'\nconst hydrateWhenAttribute = 'data-ts-hydrate-when'\n\n/* @__NO_SIDE_EFFECTS__ */\nfunction ServerDynamicHydrate(props: HydrateProps): React.JSX.Element {\n const internalProps = props as InternalHydrateProps\n const reactId = React.useId()\n const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId\n\n return (\n <div\n {...{\n [hydrateIdAttribute]: id,\n [hydrateWhenAttribute]: dynamicType,\n }}\n >\n <React.Suspense fallback={props.fallback ?? null}>\n {props.children}\n </React.Suspense>\n </div>\n )\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function Hydrate(props: HydrateProps): React.JSX.Element {\n if (typeof props.when === 'function') {\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n isServer ??\n typeof window === 'undefined'\n ) {\n return <ServerDynamicHydrate {...props} />\n }\n\n return props.when()._h(props)\n }\n\n return props.when._h(props)\n}\n"],"mappings":";;;;;AAmEA,IAAM,cAAc;AACpB,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;;AAG7B,SAAS,qBAAqB,OAAwC;CACpE,MAAM,gBAAgB;CACtB,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,KAAK,cAAc,IAAI,GAAG,cAAc,IAAI,YAAY;AAE9D,QACE,oBAAC,OAAD;GAEK,qBAAqB;GACrB,uBAAuB;YAG1B,oBAAC,MAAM,UAAP;GAAgB,UAAU,MAAM,YAAY;aACzC,MAAM;GACQ,CAAA;EACb,CAAA;;;AAKV,SAAgB,QAAQ,OAAwC;AAC9D,KAAI,OAAO,MAAM,SAAS,YAAY;AACpC,MAEE,YACA,OAAO,WAAW,YAElB,QAAO,oBAAC,sBAAD,EAAsB,GAAI,OAAS,CAAA;AAG5C,SAAO,MAAM,MAAM,CAAC,GAAG,MAAM;;AAG/B,QAAO,MAAM,KAAK,GAAG,MAAM"} |
| export { condition, interaction, media } from './hydration/generic.js'; | ||
| export { idle } from './hydration/idle.js'; | ||
| export { load } from './hydration/load.js'; | ||
| export { never } from './hydration/never.js'; | ||
| export { visible } from './hydration/visible.js'; | ||
| export type { HydrationCondition, HydrationInteractionEvent, HydrationInteractionEvents, IdleHydrationOptions, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchWhen, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationStrategyTypes, HydrationWhen, VisibleHydrationOptions, } from '@tanstack/start-client-core/hydration'; | ||
| export type { HydrationStrategy, ReactHydrationStrategy } from './Hydrate.js'; |
| "use client"; | ||
| import { condition, interaction, media } from "./hydration/generic.js"; | ||
| import { idle } from "./hydration/idle.js"; | ||
| import { load } from "./hydration/load.js"; | ||
| import { never } from "./hydration/never.js"; | ||
| import { visible } from "./hydration/visible.js"; | ||
| export { condition, idle, interaction, load, media, never, visible }; |
| import { HydrationCondition, HydrationInteractionEvents, HydrationPrefetchStrategy } from '@tanstack/start-client-core/hydration'; | ||
| import { ReactHydrationStrategy } from '../Hydrate.js'; | ||
| export declare function media(query: string): ReactHydrationStrategy<'media', true> & HydrationPrefetchStrategy<'media'>; | ||
| export declare function condition(condition: HydrationCondition): ReactHydrationStrategy<'condition', false>; | ||
| export declare function interaction(options?: { | ||
| events?: HydrationInteractionEvents; | ||
| }): ReactHydrationStrategy<'interaction', true> & HydrationPrefetchStrategy<'interaction'>; |
| "use client"; | ||
| import { GenericHydrate } from "../GenericHydrate.js"; | ||
| import { condition, interaction, media, withHydrationRenderer } from "@tanstack/start-client-core/hydration"; | ||
| //#region src/hydration/generic.ts | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function media$1(query) { | ||
| return /* @__PURE__ */ withHydrationRenderer(media(query), GenericHydrate); | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function condition$1(condition$2) { | ||
| return /* @__PURE__ */ withHydrationRenderer(condition(condition$2), GenericHydrate); | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function interaction$1(options) { | ||
| return /* @__PURE__ */ withHydrationRenderer(interaction(options), GenericHydrate); | ||
| } | ||
| //#endregion | ||
| export { condition$1 as condition, interaction$1 as interaction, media$1 as media }; | ||
| //# sourceMappingURL=generic.js.map |
| {"version":3,"file":"generic.js","names":[],"sources":["../../../src/hydration/generic.ts"],"sourcesContent":["'use client'\n\nimport {\n condition as coreCondition,\n interaction as coreInteraction,\n media as coreMedia,\n withHydrationRenderer,\n} from '@tanstack/start-client-core/hydration'\nimport { GenericHydrate } from '../GenericHydrate'\nimport type {\n HydrationCondition,\n HydrationInteractionEvents,\n HydrationPrefetchStrategy,\n} from '@tanstack/start-client-core/hydration'\nimport type { ReactHydrationStrategy } from '../Hydrate'\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function media(\n query: string,\n): ReactHydrationStrategy<'media', true> & HydrationPrefetchStrategy<'media'> {\n return /* @__PURE__ */ withHydrationRenderer(coreMedia(query), GenericHydrate)\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function condition(\n condition: HydrationCondition,\n): ReactHydrationStrategy<'condition', false> {\n return /* @__PURE__ */ withHydrationRenderer(\n coreCondition(condition),\n GenericHydrate,\n )\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function interaction(options?: {\n events?: HydrationInteractionEvents\n}): ReactHydrationStrategy<'interaction', true> &\n HydrationPrefetchStrategy<'interaction'> {\n return /* @__PURE__ */ withHydrationRenderer(\n coreInteraction(options),\n GenericHydrate,\n )\n}\n"],"mappings":";;;;;AAiBA,SAAgB,QACd,OAC4E;AAC5E,QAAuB,sCAAsB,MAAU,MAAM,EAAE,eAAe;;;AAIhF,SAAgB,YACd,aAC4C;AAC5C,QAAuB,sCACrB,UAAc,YAAU,EACxB,eACD;;;AAIH,SAAgB,cAAY,SAGe;AACzC,QAAuB,sCACrB,YAAgB,QAAQ,EACxB,eACD"} |
| import { HydrationPrefetchStrategy, IdleHydrationOptions } from '@tanstack/start-client-core/hydration'; | ||
| import { ReactHydrationStrategy } from '../Hydrate.js'; | ||
| export declare function idle(options?: IdleHydrationOptions): ReactHydrationStrategy<'idle', true> & HydrationPrefetchStrategy<'idle'>; |
| "use client"; | ||
| import { GenericHydrate } from "../GenericHydrate.js"; | ||
| import { idle, withHydrationRenderer } from "@tanstack/start-client-core/hydration"; | ||
| //#region src/hydration/idle.ts | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function idle$1(options = {}) { | ||
| return /* @__PURE__ */ withHydrationRenderer(idle(options), GenericHydrate); | ||
| } | ||
| //#endregion | ||
| export { idle$1 as idle }; | ||
| //# sourceMappingURL=idle.js.map |
| {"version":3,"file":"idle.js","names":[],"sources":["../../../src/hydration/idle.ts"],"sourcesContent":["'use client'\n\nimport {\n idle as coreIdle,\n withHydrationRenderer,\n} from '@tanstack/start-client-core/hydration'\nimport { GenericHydrate } from '../GenericHydrate'\nimport type {\n HydrationPrefetchStrategy,\n IdleHydrationOptions,\n} from '@tanstack/start-client-core/hydration'\nimport type { ReactHydrationStrategy } from '../Hydrate'\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function idle(\n options: IdleHydrationOptions = {},\n): ReactHydrationStrategy<'idle', true> & HydrationPrefetchStrategy<'idle'> {\n return /* @__PURE__ */ withHydrationRenderer(\n coreIdle(options),\n GenericHydrate,\n )\n}\n"],"mappings":";;;;;AAcA,SAAgB,OACd,UAAgC,EAAE,EACwC;AAC1E,QAAuB,sCACrB,KAAS,QAAQ,EACjB,eACD"} |
| import { HydrationPrefetchStrategy } from '@tanstack/start-client-core/hydration'; | ||
| import { HydrateProps, ReactHydrationStrategy } from '../Hydrate.js'; | ||
| import * as React from 'react'; | ||
| export declare function LoadHydrate(props: HydrateProps): React.JSX.Element; | ||
| export declare function load(): ReactHydrationStrategy<'load', true> & HydrationPrefetchStrategy<'load'>; |
| "use client"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| import * as React from "react"; | ||
| import { load, withHydrationRenderer } from "@tanstack/start-client-core/hydration"; | ||
| //#region src/hydration/load.tsx | ||
| function HydratedBoundary(props) { | ||
| const { onHydrated, children } = props; | ||
| const didHydrateRef = React.useRef(false); | ||
| React.useEffect(() => { | ||
| if (didHydrateRef.current) return; | ||
| didHydrateRef.current = true; | ||
| onHydrated?.(); | ||
| }, [onHydrated]); | ||
| return children; | ||
| } | ||
| function LoadHydrate(props) { | ||
| return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(React.Suspense, { | ||
| fallback: props.fallback ?? null, | ||
| children: /* @__PURE__ */ jsx(HydratedBoundary, { | ||
| onHydrated: props.onHydrated, | ||
| children: props.children | ||
| }) | ||
| }) }); | ||
| } | ||
| var loadStrategy = /* @__PURE__ */ withHydrationRenderer(load(), LoadHydrate); | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function load$1() { | ||
| return loadStrategy; | ||
| } | ||
| //#endregion | ||
| export { load$1 as load }; | ||
| //# sourceMappingURL=load.js.map |
| {"version":3,"file":"load.js","names":[],"sources":["../../../src/hydration/load.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport {\n load as coreLoad,\n withHydrationRenderer,\n} from '@tanstack/start-client-core/hydration'\nimport type { HydrationPrefetchStrategy } from '@tanstack/start-client-core/hydration'\nimport type { HydrateProps, ReactHydrationStrategy } from '../Hydrate'\n\nfunction HydratedBoundary(props: {\n onHydrated?: () => void\n children: React.ReactNode\n}) {\n const { onHydrated, children } = props\n const didHydrateRef = React.useRef(false)\n\n React.useEffect(() => {\n if (didHydrateRef.current) return\n didHydrateRef.current = true\n onHydrated?.()\n }, [onHydrated])\n\n return children as React.JSX.Element\n}\n\nexport function LoadHydrate(props: HydrateProps): React.JSX.Element {\n return (\n <div>\n <React.Suspense fallback={props.fallback ?? null}>\n <HydratedBoundary onHydrated={props.onHydrated}>\n {props.children}\n </HydratedBoundary>\n </React.Suspense>\n </div>\n )\n}\n\nconst loadStrategy = /* @__PURE__ */ withHydrationRenderer(\n coreLoad(),\n LoadHydrate,\n) as ReactHydrationStrategy<'load', true> & HydrationPrefetchStrategy<'load'>\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function load(): ReactHydrationStrategy<'load', true> &\n HydrationPrefetchStrategy<'load'> {\n return loadStrategy\n}\n"],"mappings":";;;;;AAWA,SAAS,iBAAiB,OAGvB;CACD,MAAM,EAAE,YAAY,aAAa;CACjC,MAAM,gBAAgB,MAAM,OAAO,MAAM;AAEzC,OAAM,gBAAgB;AACpB,MAAI,cAAc,QAAS;AAC3B,gBAAc,UAAU;AACxB,gBAAc;IACb,CAAC,WAAW,CAAC;AAEhB,QAAO;;AAGT,SAAgB,YAAY,OAAwC;AAClE,QACE,oBAAC,OAAD,EAAA,UACE,oBAAC,MAAM,UAAP;EAAgB,UAAU,MAAM,YAAY;YAC1C,oBAAC,kBAAD;GAAkB,YAAY,MAAM;aACjC,MAAM;GACU,CAAA;EACJ,CAAA,EACb,CAAA;;AAIV,IAAM,eAA+B,sCACnC,MAAU,EACV,YACD;;AAGD,SAAgB,SACoB;AAClC,QAAO"} |
| import { HydrateProps, ReactHydrationStrategy } from '../Hydrate.js'; | ||
| import * as React from 'react'; | ||
| export declare function NeverHydrate(props: HydrateProps): React.JSX.Element; | ||
| export declare function never(): ReactHydrationStrategy<'never', false>; |
| "use client"; | ||
| import { reactUse, useHydrated } from "@tanstack/react-router"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| import * as React from "react"; | ||
| import { isServer } from "@tanstack/router-core/isServer"; | ||
| import { never, withHydrationRenderer } from "@tanstack/start-client-core/hydration"; | ||
| import { hydrateIdAttribute, hydrateWhenAttribute } from "@tanstack/start-client-core/hydration/constants"; | ||
| import { getFallbackHtml, saveFallbackHtml } from "@tanstack/start-client-core/hydration/runtime"; | ||
| //#region src/hydration/never.tsx | ||
| var neverType = "never"; | ||
| var neverPromise = new Promise(() => {}); | ||
| function NeverGate(props) { | ||
| if (isServer ?? typeof window === "undefined") return props.children; | ||
| if (!reactUse) throw neverPromise; | ||
| reactUse(neverPromise); | ||
| return props.children; | ||
| } | ||
| function NeverHydrate(props) { | ||
| const internalProps = props; | ||
| const hydrated = useHydrated(); | ||
| const reactId = React.useId(); | ||
| const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId; | ||
| const shouldPreserveServerHTMLRef = React.useRef(void 0); | ||
| shouldPreserveServerHTMLRef.current ??= (isServer ?? typeof window === "undefined") || !hydrated; | ||
| const markerProps = { | ||
| ref: React.useCallback((element) => { | ||
| if (!element) return; | ||
| if (!shouldPreserveServerHTMLRef.current) element.replaceChildren(); | ||
| else saveFallbackHtml(id, element); | ||
| }, [id]), | ||
| [hydrateIdAttribute]: id, | ||
| [hydrateWhenAttribute]: neverType | ||
| }; | ||
| const fallback = (() => { | ||
| const html = getFallbackHtml(id); | ||
| return html ? /* @__PURE__ */ jsx("div", { | ||
| style: { display: "contents" }, | ||
| dangerouslySetInnerHTML: { __html: html } | ||
| }) : props.fallback ?? null; | ||
| })(); | ||
| return /* @__PURE__ */ jsx("div", { | ||
| ...markerProps, | ||
| children: /* @__PURE__ */ jsx(React.Suspense, { | ||
| fallback, | ||
| children: /* @__PURE__ */ jsx(NeverGate, { children: props.children }) | ||
| }) | ||
| }); | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function never$1() { | ||
| return /* @__PURE__ */ withHydrationRenderer(never(), NeverHydrate); | ||
| } | ||
| //#endregion | ||
| export { never$1 as never }; | ||
| //# sourceMappingURL=never.js.map |
| {"version":3,"file":"never.js","names":[],"sources":["../../../src/hydration/never.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { reactUse, useHydrated } from '@tanstack/react-router'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport {\n never as coreNever,\n withHydrationRenderer,\n} from '@tanstack/start-client-core/hydration'\nimport {\n hydrateIdAttribute,\n hydrateWhenAttribute,\n} from '@tanstack/start-client-core/hydration/constants'\nimport {\n getFallbackHtml,\n saveFallbackHtml,\n} from '@tanstack/start-client-core/hydration/runtime'\nimport type {\n HydrateProps,\n InternalHydrateProps,\n ReactHydrationStrategy,\n} from '../Hydrate'\n\nconst neverType = 'never'\nconst neverPromise = new Promise<void>(() => {})\n\nfunction NeverGate(props: { children: React.ReactNode }) {\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n isServer ??\n typeof window === 'undefined'\n ) {\n return props.children as React.JSX.Element\n }\n\n if (!reactUse) {\n throw neverPromise\n }\n\n reactUse(neverPromise)\n\n return props.children as React.JSX.Element\n}\n\nexport function NeverHydrate(props: HydrateProps): React.JSX.Element {\n const internalProps = props as InternalHydrateProps\n const hydrated = useHydrated()\n const reactId = React.useId()\n const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId\n const shouldPreserveServerHTMLRef = React.useRef<boolean | undefined>(\n undefined,\n )\n shouldPreserveServerHTMLRef.current ??=\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (isServer ?? typeof window === 'undefined') || !hydrated\n const markerRef = React.useCallback(\n (element: HTMLDivElement | null) => {\n if (!element) return\n if (!shouldPreserveServerHTMLRef.current) {\n element.replaceChildren()\n } else {\n saveFallbackHtml(id, element)\n }\n },\n [id],\n )\n const markerProps = {\n ref: markerRef,\n [hydrateIdAttribute]: id,\n [hydrateWhenAttribute]: neverType,\n }\n const fallback = (() => {\n const html = getFallbackHtml(id)\n return html ? (\n <div\n style={{ display: 'contents' }}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n ) : (\n (props.fallback ?? null)\n )\n })()\n\n return (\n <div {...markerProps}>\n <React.Suspense fallback={fallback}>\n <NeverGate>{props.children}</NeverGate>\n </React.Suspense>\n </div>\n )\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function never(): ReactHydrationStrategy<'never', false> {\n return /* @__PURE__ */ withHydrationRenderer(coreNever(), NeverHydrate)\n}\n"],"mappings":";;;;;;;;;AAwBA,IAAM,YAAY;AAClB,IAAM,eAAe,IAAI,cAAoB,GAAG;AAEhD,SAAS,UAAU,OAAsC;AACvD,KAEE,YACA,OAAO,WAAW,YAElB,QAAO,MAAM;AAGf,KAAI,CAAC,SACH,OAAM;AAGR,UAAS,aAAa;AAEtB,QAAO,MAAM;;AAGf,SAAgB,aAAa,OAAwC;CACnE,MAAM,gBAAgB;CACtB,MAAM,WAAW,aAAa;CAC9B,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,KAAK,cAAc,IAAI,GAAG,cAAc,IAAI,YAAY;CAC9D,MAAM,8BAA8B,MAAM,OACxC,KAAA,EACD;AACD,6BAA4B,aAEzB,YAAY,OAAO,WAAW,gBAAgB,CAAC;CAYlD,MAAM,cAAc;EAClB,KAZgB,MAAM,aACrB,YAAmC;AAClC,OAAI,CAAC,QAAS;AACd,OAAI,CAAC,4BAA4B,QAC/B,SAAQ,iBAAiB;OAEzB,kBAAiB,IAAI,QAAQ;KAGjC,CAAC,GAAG,CACL;GAGE,qBAAqB;GACrB,uBAAuB;EACzB;CACD,MAAM,kBAAkB;EACtB,MAAM,OAAO,gBAAgB,GAAG;AAChC,SAAO,OACL,oBAAC,OAAD;GACE,OAAO,EAAE,SAAS,YAAY;GAC9B,yBAAyB,EAAE,QAAQ,MAAM;GACzC,CAAA,GAED,MAAM,YAAY;KAEnB;AAEJ,QACE,oBAAC,OAAD;EAAK,GAAI;YACP,oBAAC,MAAM,UAAP;GAA0B;aACxB,oBAAC,WAAD,EAAA,UAAY,MAAM,UAAqB,CAAA;GACxB,CAAA;EACb,CAAA;;;AAKV,SAAgB,UAAgD;AAC9D,QAAuB,sCAAsB,OAAW,EAAE,aAAa"} |
| import { HydrationPrefetchStrategy, VisibleHydrationOptions } from '@tanstack/start-client-core/hydration'; | ||
| import { HydrateProps, ReactHydrationStrategy } from '../Hydrate.js'; | ||
| import * as React from 'react'; | ||
| export declare function VisibleHydrate(this: ReactHydrationStrategy, props: HydrateProps): React.JSX.Element; | ||
| export declare function visible(options?: VisibleHydrationOptions): ReactHydrationStrategy<'visible', true> & HydrationPrefetchStrategy<'visible'>; |
| "use client"; | ||
| import { reactUse } from "@tanstack/react-router"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| import * as React from "react"; | ||
| import { isServer } from "@tanstack/router-core/isServer"; | ||
| //#region src/hydration/visible.tsx | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function HydrationBoundary(props) { | ||
| const { g, o } = props; | ||
| if (!g.r) { | ||
| if (!reactUse) throw g.p; | ||
| reactUse(g.p); | ||
| } | ||
| React.useEffect(() => { | ||
| o?.(); | ||
| }, [o]); | ||
| return props.children; | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function VisibleHydrate(props) { | ||
| const strategy = this; | ||
| const prefetchStrategy = props.prefetch; | ||
| const preload = props.p; | ||
| const markerRef = React.useRef(null); | ||
| const [gate] = React.useState(() => { | ||
| let resolvePromise; | ||
| const nextGate = { | ||
| p: new Promise((resolve) => { | ||
| resolvePromise = resolve; | ||
| }), | ||
| r: false, | ||
| s: () => { | ||
| nextGate.r = true; | ||
| resolvePromise(); | ||
| } | ||
| }; | ||
| if (isServer ?? typeof window === "undefined") nextGate.s(); | ||
| return nextGate; | ||
| }); | ||
| React.useEffect(() => { | ||
| if (!preload || typeof prefetchStrategy === "function") return; | ||
| return prefetchStrategy?._s?.({ | ||
| element: markerRef.current, | ||
| prefetch: preload | ||
| }); | ||
| }, [prefetchStrategy, preload]); | ||
| React.useEffect(() => { | ||
| if (gate.r) return; | ||
| return strategy._s?.({ | ||
| element: markerRef.current, | ||
| gate | ||
| }); | ||
| }, [gate, strategy]); | ||
| return /* @__PURE__ */ jsx("div", { | ||
| ref: markerRef, | ||
| children: /* @__PURE__ */ jsx(React.Suspense, { | ||
| fallback: props.fallback, | ||
| children: /* @__PURE__ */ jsx(HydrationBoundary, { | ||
| g: gate, | ||
| o: props.onHydrated, | ||
| children: props.children | ||
| }) | ||
| }) | ||
| }); | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function visible(options) { | ||
| const rootMargin = options?.rootMargin ?? "600px"; | ||
| const threshold = options?.threshold ?? 0; | ||
| return { | ||
| _s: ({ element, gate, prefetch }) => { | ||
| const callback = prefetch || gate.s; | ||
| if (!element) { | ||
| callback(); | ||
| return; | ||
| } | ||
| const observer = new IntersectionObserver((entries) => { | ||
| if (!entries[0].isIntersecting) return; | ||
| observer.disconnect(); | ||
| callback(); | ||
| }, { | ||
| rootMargin, | ||
| threshold | ||
| }); | ||
| observer.observe(element); | ||
| return () => observer.disconnect(); | ||
| }, | ||
| _h: VisibleHydrate | ||
| }; | ||
| } | ||
| //#endregion | ||
| export { visible }; | ||
| //# sourceMappingURL=visible.js.map |
| {"version":3,"file":"visible.js","names":[],"sources":["../../../src/hydration/visible.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { reactUse } from '@tanstack/react-router'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type {\n HydrationPrefetchStrategy,\n VisibleHydrationOptions,\n} from '@tanstack/start-client-core/hydration'\nimport type {\n HydrateProps,\n InternalHydrateProps,\n ReactHydrationStrategy,\n} from '../Hydrate'\n\ntype VisibleGate = {\n p: Promise<void>\n r: boolean\n s: () => void\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nfunction HydrationBoundary(props: {\n g: VisibleGate\n o?: () => void\n children?: React.ReactNode\n}) {\n const { g, o } = props\n\n if (!g.r) {\n if (!reactUse) {\n throw g.p\n }\n\n reactUse(g.p)\n }\n\n React.useEffect(() => {\n o?.()\n }, [o])\n\n return props.children as React.JSX.Element\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function VisibleHydrate(\n this: ReactHydrationStrategy,\n props: HydrateProps,\n): React.JSX.Element {\n const strategy = this as ReactHydrationStrategy<'visible', true>\n const prefetchStrategy = props.prefetch\n const preload = (props as InternalHydrateProps).p\n const markerRef = React.useRef<HTMLDivElement | null>(null)\n const [gate] = React.useState<VisibleGate>(() => {\n let resolvePromise!: () => void\n const nextGate: VisibleGate = {\n p: new Promise<void>((resolve) => {\n resolvePromise = resolve\n }),\n r: false,\n s: () => {\n nextGate.r = true\n resolvePromise()\n },\n }\n if (\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n isServer ??\n typeof window === 'undefined'\n ) {\n nextGate.s()\n }\n\n return nextGate\n })\n\n React.useEffect(() => {\n if (!preload || typeof prefetchStrategy === 'function') {\n return\n }\n\n return prefetchStrategy?._s?.({\n element: markerRef.current,\n prefetch: preload,\n })\n }, [prefetchStrategy, preload])\n\n React.useEffect(() => {\n if (gate.r) return\n\n return strategy._s?.({\n element: markerRef.current,\n gate: gate as never,\n })\n }, [gate, strategy])\n\n return (\n <div ref={markerRef}>\n <React.Suspense fallback={props.fallback}>\n <HydrationBoundary g={gate} o={props.onHydrated}>\n {props.children}\n </HydrationBoundary>\n </React.Suspense>\n </div>\n )\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function visible(\n options?: VisibleHydrationOptions,\n): ReactHydrationStrategy<'visible', true> &\n HydrationPrefetchStrategy<'visible'> {\n const rootMargin = options?.rootMargin ?? '600px'\n const threshold = options?.threshold ?? 0\n\n return {\n _s: ({ element, gate, prefetch }) => {\n const callback = prefetch || (gate as never as VisibleGate).s\n\n if (!element) {\n callback()\n return\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (!entries[0]!.isIntersecting) return\n observer.disconnect()\n callback()\n },\n { rootMargin, threshold },\n )\n observer.observe(element)\n return () => observer.disconnect()\n },\n _h: VisibleHydrate,\n }\n}\n"],"mappings":";;;;;;;AAuBA,SAAS,kBAAkB,OAIxB;CACD,MAAM,EAAE,GAAG,MAAM;AAEjB,KAAI,CAAC,EAAE,GAAG;AACR,MAAI,CAAC,SACH,OAAM,EAAE;AAGV,WAAS,EAAE,EAAE;;AAGf,OAAM,gBAAgB;AACpB,OAAK;IACJ,CAAC,EAAE,CAAC;AAEP,QAAO,MAAM;;;AAIf,SAAgB,eAEd,OACmB;CACnB,MAAM,WAAW;CACjB,MAAM,mBAAmB,MAAM;CAC/B,MAAM,UAAW,MAA+B;CAChD,MAAM,YAAY,MAAM,OAA8B,KAAK;CAC3D,MAAM,CAAC,QAAQ,MAAM,eAA4B;EAC/C,IAAI;EACJ,MAAM,WAAwB;GAC5B,GAAG,IAAI,SAAe,YAAY;AAChC,qBAAiB;KACjB;GACF,GAAG;GACH,SAAS;AACP,aAAS,IAAI;AACb,oBAAgB;;GAEnB;AACD,MAEE,YACA,OAAO,WAAW,YAElB,UAAS,GAAG;AAGd,SAAO;GACP;AAEF,OAAM,gBAAgB;AACpB,MAAI,CAAC,WAAW,OAAO,qBAAqB,WAC1C;AAGF,SAAO,kBAAkB,KAAK;GAC5B,SAAS,UAAU;GACnB,UAAU;GACX,CAAC;IACD,CAAC,kBAAkB,QAAQ,CAAC;AAE/B,OAAM,gBAAgB;AACpB,MAAI,KAAK,EAAG;AAEZ,SAAO,SAAS,KAAK;GACnB,SAAS,UAAU;GACb;GACP,CAAC;IACD,CAAC,MAAM,SAAS,CAAC;AAEpB,QACE,oBAAC,OAAD;EAAK,KAAK;YACR,oBAAC,MAAM,UAAP;GAAgB,UAAU,MAAM;aAC9B,oBAAC,mBAAD;IAAmB,GAAG;IAAM,GAAG,MAAM;cAClC,MAAM;IACW,CAAA;GACL,CAAA;EACb,CAAA;;;AAKV,SAAgB,QACd,SAEqC;CACrC,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,YAAY,SAAS,aAAa;AAExC,QAAO;EACL,KAAK,EAAE,SAAS,MAAM,eAAe;GACnC,MAAM,WAAW,YAAa,KAA8B;AAE5D,OAAI,CAAC,SAAS;AACZ,cAAU;AACV;;GAGF,MAAM,WAAW,IAAI,sBAClB,YAAY;AACX,QAAI,CAAC,QAAQ,GAAI,eAAgB;AACjC,aAAS,YAAY;AACrB,cAAU;MAEZ;IAAE;IAAY;IAAW,CAC1B;AACD,YAAS,QAAQ,QAAQ;AACzB,gBAAa,SAAS,YAAY;;EAEpC,IAAI;EACL"} |
| export {}; |
| export {}; |
| 'use client' | ||
| import * as React from 'react' | ||
| import { reactUse, useHydrated, useLayoutEffect } from '@tanstack/react-router' | ||
| import { isServer } from '@tanstack/router-core/isServer' | ||
| import { | ||
| hydrateIdAttribute, | ||
| hydrateWhenAttribute, | ||
| } from '@tanstack/start-client-core/hydration/constants' | ||
| import { | ||
| createResolvedGate, | ||
| getFallbackHtml, | ||
| getOrCreateGate, | ||
| onGateResolve, | ||
| releaseGate, | ||
| runHydrationStrategyCleanup, | ||
| saveFallbackHtml, | ||
| waitForHydrationPrefetchStrategy, | ||
| } from '@tanstack/start-client-core/hydration/runtime' | ||
| import { listenForDelegatedHydrationIntent } from '@tanstack/start-client-core/hydration' | ||
| import type { | ||
| HydrationRuntimeContext, | ||
| HydrationStrategy, | ||
| HydrationWhen, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import type { HydrationGateRecord } from '@tanstack/start-client-core/hydration/runtime' | ||
| import type { HydrateProps, InternalHydrateProps } from './Hydrate' | ||
| type Gate = HydrationGateRecord & { promise: Promise<void> } | ||
| type PrefetchController = { | ||
| abortController: AbortController | ||
| hydrationRequested: boolean | ||
| hydrationListeners: Set<() => void> | ||
| hydrationResolvePending: boolean | ||
| started: boolean | ||
| promise?: Promise<void> | ||
| cleanup?: () => void | ||
| } | ||
| const dynamicType = 'dynamic' | ||
| const dynamicHydrateStrategy = { | ||
| _t: dynamicType, | ||
| _d: () => true, | ||
| } satisfies HydrationStrategy<typeof dynamicType, false> | ||
| function shouldDeferHydration(strategy: HydrationStrategy) { | ||
| return strategy._d ? strategy._d() : strategy._t !== 'load' | ||
| } | ||
| function useLatest<T>(value: T) { | ||
| const ref = React.useRef(value) | ||
| ref.current = value | ||
| return ref | ||
| } | ||
| function useHydrationGate(props: InternalHydrateProps) { | ||
| const hydrated = useHydrated() | ||
| const reactId = React.useId() | ||
| const id = props.h ? `${props.h}${reactId}` : reactId | ||
| const when = props.when | ||
| const isDynamicHydrate = typeof when === 'function' | ||
| const dynamicHydrateStrategyRef = React.useRef<HydrationStrategy | undefined>( | ||
| undefined, | ||
| ) | ||
| if (isDynamicHydrate) { | ||
| dynamicHydrateStrategyRef.current ??= | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| (isServer ?? typeof window === 'undefined') | ||
| ? dynamicHydrateStrategy | ||
| : when() | ||
| } | ||
| const hydrateStrategy = isDynamicHydrate | ||
| ? dynamicHydrateStrategyRef.current! | ||
| : when | ||
| const markerHydrateType: HydrationWhen = isDynamicHydrate | ||
| ? dynamicType | ||
| : hydrateStrategy._t! | ||
| const [prefetchError, setPrefetchError] = React.useState<unknown>() | ||
| const latestRef = useLatest({ | ||
| prefetch: props.prefetch, | ||
| preload: props.p, | ||
| }) | ||
| const gateRef = React.useRef<HydrationGateRecord | undefined>(undefined) | ||
| const markerElementRef = React.useRef<HTMLDivElement | null>(null) | ||
| const shouldPreserveServerHTMLRef = React.useRef<boolean | undefined>( | ||
| undefined, | ||
| ) | ||
| const shouldDeferInitialHydrationRef = React.useRef<boolean | undefined>( | ||
| undefined, | ||
| ) | ||
| const didPrefetchRef = React.useRef(false) | ||
| const prefetchControllerRef = React.useRef<PrefetchController | undefined>( | ||
| undefined, | ||
| ) | ||
| prefetchControllerRef.current ??= { | ||
| abortController: new AbortController(), | ||
| hydrationRequested: false, | ||
| hydrationListeners: new Set<() => void>(), | ||
| hydrationResolvePending: false, | ||
| started: false, | ||
| } | ||
| shouldPreserveServerHTMLRef.current ??= | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| (isServer ?? typeof window === 'undefined') || !hydrated | ||
| shouldDeferInitialHydrationRef.current ??= | ||
| !hydrated && shouldDeferHydration(hydrateStrategy) | ||
| if (!gateRef.current) { | ||
| gateRef.current = | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| (isServer ?? typeof window === 'undefined') | ||
| ? createResolvedGate(id, hydrateStrategy._t!) | ||
| : getOrCreateGate(id, hydrateStrategy._t!) | ||
| } | ||
| gateRef.current.when = hydrateStrategy._t! | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| !(isServer ?? typeof window === 'undefined') && | ||
| hydrateStrategy._t !== 'never' && | ||
| (!shouldDeferInitialHydrationRef.current || | ||
| !shouldDeferHydration(hydrateStrategy)) | ||
| ) { | ||
| gateRef.current.resolve() | ||
| } | ||
| const markerRef = React.useCallback( | ||
| (element: HTMLDivElement | null) => { | ||
| markerElementRef.current = element | ||
| if (element) { | ||
| if ( | ||
| hydrateStrategy._t === 'never' && | ||
| !shouldPreserveServerHTMLRef.current | ||
| ) { | ||
| element.replaceChildren() | ||
| } | ||
| saveFallbackHtml(id, element) | ||
| } | ||
| }, | ||
| [hydrateStrategy._t, id], | ||
| ) | ||
| React.useEffect(() => { | ||
| const gate = gateRef.current! | ||
| return () => { | ||
| const controller = prefetchControllerRef.current | ||
| controller?.abortController.abort() | ||
| controller?.cleanup?.() | ||
| controller?.hydrationListeners.clear() | ||
| releaseGate(gate) | ||
| } | ||
| }, []) | ||
| React.useEffect(() => { | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| (isServer ?? typeof window === 'undefined') || | ||
| !latestRef.current.prefetch | ||
| ) { | ||
| return | ||
| } | ||
| const controller = prefetchControllerRef.current! | ||
| if (controller.started) return | ||
| controller.started = true | ||
| const onHydrate = (listener: () => void) => { | ||
| if (controller.hydrationRequested) { | ||
| listener() | ||
| return () => {} | ||
| } | ||
| controller.hydrationListeners.add(listener) | ||
| return () => { | ||
| controller.hydrationListeners.delete(listener) | ||
| } | ||
| } | ||
| const preload = () => latestRef.current.preload?.() ?? Promise.resolve() | ||
| const prefetchInput = latestRef.current.prefetch | ||
| if (typeof prefetchInput === 'function') { | ||
| const promise = Promise.resolve() | ||
| .then(() => | ||
| prefetchInput({ | ||
| element: markerElementRef.current, | ||
| signal: controller.abortController.signal, | ||
| preload, | ||
| waitFor: (strategy) => | ||
| waitForHydrationPrefetchStrategy(strategy, { | ||
| element: markerElementRef.current, | ||
| signal: controller.abortController.signal, | ||
| onHydrate, | ||
| }), | ||
| }), | ||
| ) | ||
| .then(() => undefined) | ||
| controller.promise = promise | ||
| promise.catch((error) => { | ||
| if (!controller.abortController.signal.aborted) { | ||
| setPrefetchError(error) | ||
| } | ||
| }) | ||
| return | ||
| } | ||
| if (!latestRef.current.preload) return | ||
| const prefetch = () => { | ||
| if (didPrefetchRef.current) return | ||
| didPrefetchRef.current = true | ||
| void preload() | ||
| } | ||
| controller.cleanup = runHydrationStrategyCleanup( | ||
| prefetchInput._s?.({ | ||
| element: markerElementRef.current, | ||
| prefetch, | ||
| }), | ||
| ) | ||
| }, [hydrateStrategy, latestRef]) | ||
| useLayoutEffect(() => { | ||
| const gate = gateRef.current! | ||
| if ( | ||
| !shouldDeferInitialHydrationRef.current || | ||
| hydrateStrategy._t === 'never' | ||
| ) { | ||
| return | ||
| } | ||
| if (gate.resolved) { | ||
| return | ||
| } | ||
| const cleanups: Array<() => void> = [] | ||
| let removeResolveListener = () => {} | ||
| let disposed = false | ||
| const resolveGate = gate.resolve | ||
| const cleanup = () => { | ||
| if (disposed) return | ||
| disposed = true | ||
| if (gate.resolve === requestHydration) { | ||
| gate.resolve = resolveGate | ||
| } | ||
| removeResolveListener() | ||
| cleanups.forEach((fn) => fn()) | ||
| } | ||
| const addCleanup = (fn: void | (() => void)) => { | ||
| if (!fn) return | ||
| if (disposed || gate.resolved) { | ||
| fn() | ||
| return | ||
| } | ||
| cleanups.push(fn) | ||
| } | ||
| const requestHydration = () => { | ||
| const controller = prefetchControllerRef.current! | ||
| if (!controller.hydrationRequested) { | ||
| controller.hydrationRequested = true | ||
| controller.hydrationListeners.forEach((listener) => listener()) | ||
| controller.hydrationListeners.clear() | ||
| } | ||
| if (!controller.promise) { | ||
| resolveGate() | ||
| return | ||
| } | ||
| if (controller.hydrationResolvePending) return | ||
| controller.hydrationResolvePending = true | ||
| controller.promise.then( | ||
| () => resolveGate(), | ||
| (error) => { | ||
| if (!controller.abortController.signal.aborted) { | ||
| setPrefetchError(error) | ||
| } | ||
| }, | ||
| ) | ||
| } | ||
| gate.resolve = requestHydration | ||
| removeResolveListener = onGateResolve(gate, cleanup) | ||
| const context: HydrationRuntimeContext = { | ||
| element: markerElementRef.current, | ||
| gate, | ||
| } | ||
| addCleanup(runHydrationStrategyCleanup(hydrateStrategy._s?.(context))) | ||
| if (hydrateStrategy._t !== 'interaction') { | ||
| addCleanup( | ||
| runHydrationStrategyCleanup( | ||
| markerElementRef.current | ||
| ? listenForDelegatedHydrationIntent( | ||
| markerElementRef.current, | ||
| context, | ||
| ) | ||
| : undefined, | ||
| ), | ||
| ) | ||
| } | ||
| return cleanup | ||
| }, [hydrateStrategy, latestRef]) | ||
| return { | ||
| gate: gateRef.current, | ||
| markerRef, | ||
| markerElementRef, | ||
| hydrateStrategy, | ||
| markerHydrateType, | ||
| prefetchError, | ||
| shouldPreserveServerHTML: shouldPreserveServerHTMLRef.current, | ||
| } | ||
| } | ||
| function HydrationGate(props: { gate: Gate; children: React.ReactNode }) { | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| isServer ?? | ||
| typeof window === 'undefined' | ||
| ) { | ||
| return props.children as React.JSX.Element | ||
| } | ||
| if (props.gate.resolved) { | ||
| return props.children as React.JSX.Element | ||
| } | ||
| if (!reactUse) { | ||
| throw props.gate.promise | ||
| } | ||
| reactUse(props.gate.promise) | ||
| return props.children as React.JSX.Element | ||
| } | ||
| function HydratedBoundary(props: { | ||
| id: string | ||
| onHydrated?: () => void | ||
| onStrategyHydrated?: (id: string) => void | ||
| children: React.ReactNode | ||
| }) { | ||
| const { id, onHydrated, onStrategyHydrated } = props | ||
| const didHydrateRef = React.useRef(false) | ||
| React.useEffect(() => { | ||
| if (didHydrateRef.current) return | ||
| didHydrateRef.current = true | ||
| onHydrated?.() | ||
| onStrategyHydrated?.(id) | ||
| }, [id, onHydrated, onStrategyHydrated]) | ||
| return props.children as React.JSX.Element | ||
| } | ||
| export function GenericHydrate(props: HydrateProps): React.JSX.Element { | ||
| const internalProps = props as InternalHydrateProps | ||
| const { | ||
| gate, | ||
| hydrateStrategy, | ||
| markerHydrateType, | ||
| markerElementRef, | ||
| markerRef, | ||
| prefetchError, | ||
| shouldPreserveServerHTML, | ||
| } = useHydrationGate(internalProps) | ||
| if (prefetchError) throw prefetchError | ||
| const fallback = shouldPreserveServerHTML | ||
| ? (() => { | ||
| const html = getFallbackHtml(gate.id) | ||
| return html ? ( | ||
| <div | ||
| style={{ display: 'contents' }} | ||
| dangerouslySetInnerHTML={{ __html: html }} | ||
| /> | ||
| ) : null | ||
| })() | ||
| : (props.fallback ?? null) | ||
| const markerAttributes = | ||
| markerHydrateType === dynamicType ? undefined : hydrateStrategy._a?.() | ||
| const hydrateType = hydrateStrategy._t! | ||
| if (hydrateType === 'never' && !shouldPreserveServerHTML) { | ||
| return ( | ||
| <div | ||
| ref={markerRef} | ||
| {...{ | ||
| [hydrateIdAttribute]: gate.id, | ||
| [hydrateWhenAttribute]: markerHydrateType, | ||
| ...markerAttributes, | ||
| }} | ||
| > | ||
| {props.fallback ?? null} | ||
| </div> | ||
| ) | ||
| } | ||
| return ( | ||
| <div | ||
| ref={markerRef} | ||
| {...{ | ||
| [hydrateIdAttribute]: gate.id, | ||
| [hydrateWhenAttribute]: markerHydrateType, | ||
| ...markerAttributes, | ||
| }} | ||
| > | ||
| <React.Suspense fallback={fallback}> | ||
| <HydrationGate gate={gate}> | ||
| <HydratedBoundary | ||
| id={gate.id} | ||
| onHydrated={props.onHydrated} | ||
| onStrategyHydrated={(id) => { | ||
| markerElementRef.current?.removeAttribute(hydrateWhenAttribute) | ||
| hydrateStrategy._o?.(id) | ||
| }} | ||
| > | ||
| {props.children} | ||
| </HydratedBoundary> | ||
| </HydrationGate> | ||
| </React.Suspense> | ||
| </div> | ||
| ) | ||
| } |
+107
| 'use client' | ||
| import * as React from 'react' | ||
| import { isServer } from '@tanstack/router-core/isServer' | ||
| import type { | ||
| HydrationStrategy as CoreHydrationStrategy, | ||
| HydrationPrefetchFunction, | ||
| HydrationPrefetchStrategy, | ||
| HydrationWhen, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| export type { | ||
| HydrationInteractionEvent, | ||
| HydrationInteractionEvents, | ||
| HydrationPrefetchContext, | ||
| HydrationPrefetchFunction, | ||
| HydrationPrefetchStrategy, | ||
| HydrationPrefetchWaitReason, | ||
| HydrationWhen, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| export type ReactHydrationStrategy< | ||
| TWhen extends HydrationWhen = HydrationWhen, | ||
| TCanPrefetch extends boolean = boolean, | ||
| > = CoreHydrationStrategy<TWhen, TCanPrefetch> & { | ||
| _h: (this: ReactHydrationStrategy, props: HydrateProps) => React.JSX.Element | ||
| } | ||
| export type HydrationStrategy< | ||
| TWhen extends HydrationWhen = HydrationWhen, | ||
| TCanPrefetch extends boolean = boolean, | ||
| > = ReactHydrationStrategy<TWhen, TCanPrefetch> | ||
| export type HydrateWhen = | ||
| | ReactHydrationStrategy | ||
| | (() => ReactHydrationStrategy) | ||
| type HydrateCommonOptions = { | ||
| when: HydrateWhen | ||
| fallback?: React.ReactNode | ||
| onHydrated?: () => void | ||
| } | ||
| export type HydrateOptions = | ||
| | (HydrateCommonOptions & { | ||
| prefetch?: never | ||
| split?: boolean | ||
| }) | ||
| | (HydrateCommonOptions & { | ||
| prefetch: HydrationPrefetchStrategy | ||
| split?: true | ||
| }) | ||
| | (HydrateCommonOptions & { | ||
| prefetch: HydrationPrefetchFunction | ||
| split?: boolean | ||
| }) | ||
| export type HydrateProps = HydrateOptions & { | ||
| children: React.ReactNode | ||
| } | ||
| export type InternalHydrateProps = HydrateProps & { | ||
| h?: string | ||
| p?: () => Promise<void> | ||
| } | ||
| const dynamicType = 'dynamic' | ||
| const hydrateIdAttribute = 'data-ts-hydrate-id' | ||
| const hydrateWhenAttribute = 'data-ts-hydrate-when' | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function ServerDynamicHydrate(props: HydrateProps): React.JSX.Element { | ||
| const internalProps = props as InternalHydrateProps | ||
| const reactId = React.useId() | ||
| const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId | ||
| return ( | ||
| <div | ||
| {...{ | ||
| [hydrateIdAttribute]: id, | ||
| [hydrateWhenAttribute]: dynamicType, | ||
| }} | ||
| > | ||
| <React.Suspense fallback={props.fallback ?? null}> | ||
| {props.children} | ||
| </React.Suspense> | ||
| </div> | ||
| ) | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function Hydrate(props: HydrateProps): React.JSX.Element { | ||
| if (typeof props.when === 'function') { | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| isServer ?? | ||
| typeof window === 'undefined' | ||
| ) { | ||
| return <ServerDynamicHydrate {...props} /> | ||
| } | ||
| return props.when()._h(props) | ||
| } | ||
| return props.when._h(props) | ||
| } |
| 'use client' | ||
| export { condition, interaction, media } from './hydration/generic' | ||
| export { idle } from './hydration/idle' | ||
| export { load } from './hydration/load' | ||
| export { never } from './hydration/never' | ||
| export { visible } from './hydration/visible' | ||
| export type { | ||
| HydrationCondition, | ||
| HydrationInteractionEvent, | ||
| HydrationInteractionEvents, | ||
| IdleHydrationOptions, | ||
| HydrationPrefetchContext, | ||
| HydrationPrefetchFunction, | ||
| HydrationPrefetchWhen, | ||
| HydrationPrefetchStrategy, | ||
| HydrationPrefetchWaitReason, | ||
| HydrationStrategyTypes, | ||
| HydrationWhen, | ||
| VisibleHydrationOptions, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| export type { HydrationStrategy, ReactHydrationStrategy } from './Hydrate' |
| 'use client' | ||
| import { | ||
| condition as coreCondition, | ||
| interaction as coreInteraction, | ||
| media as coreMedia, | ||
| withHydrationRenderer, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import { GenericHydrate } from '../GenericHydrate' | ||
| import type { | ||
| HydrationCondition, | ||
| HydrationInteractionEvents, | ||
| HydrationPrefetchStrategy, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import type { ReactHydrationStrategy } from '../Hydrate' | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function media( | ||
| query: string, | ||
| ): ReactHydrationStrategy<'media', true> & HydrationPrefetchStrategy<'media'> { | ||
| return /* @__PURE__ */ withHydrationRenderer(coreMedia(query), GenericHydrate) | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function condition( | ||
| condition: HydrationCondition, | ||
| ): ReactHydrationStrategy<'condition', false> { | ||
| return /* @__PURE__ */ withHydrationRenderer( | ||
| coreCondition(condition), | ||
| GenericHydrate, | ||
| ) | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function interaction(options?: { | ||
| events?: HydrationInteractionEvents | ||
| }): ReactHydrationStrategy<'interaction', true> & | ||
| HydrationPrefetchStrategy<'interaction'> { | ||
| return /* @__PURE__ */ withHydrationRenderer( | ||
| coreInteraction(options), | ||
| GenericHydrate, | ||
| ) | ||
| } |
| 'use client' | ||
| import { | ||
| idle as coreIdle, | ||
| withHydrationRenderer, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import { GenericHydrate } from '../GenericHydrate' | ||
| import type { | ||
| HydrationPrefetchStrategy, | ||
| IdleHydrationOptions, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import type { ReactHydrationStrategy } from '../Hydrate' | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function idle( | ||
| options: IdleHydrationOptions = {}, | ||
| ): ReactHydrationStrategy<'idle', true> & HydrationPrefetchStrategy<'idle'> { | ||
| return /* @__PURE__ */ withHydrationRenderer( | ||
| coreIdle(options), | ||
| GenericHydrate, | ||
| ) | ||
| } |
| 'use client' | ||
| import * as React from 'react' | ||
| import { | ||
| load as coreLoad, | ||
| withHydrationRenderer, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import type { HydrationPrefetchStrategy } from '@tanstack/start-client-core/hydration' | ||
| import type { HydrateProps, ReactHydrationStrategy } from '../Hydrate' | ||
| function HydratedBoundary(props: { | ||
| onHydrated?: () => void | ||
| children: React.ReactNode | ||
| }) { | ||
| const { onHydrated, children } = props | ||
| const didHydrateRef = React.useRef(false) | ||
| React.useEffect(() => { | ||
| if (didHydrateRef.current) return | ||
| didHydrateRef.current = true | ||
| onHydrated?.() | ||
| }, [onHydrated]) | ||
| return children as React.JSX.Element | ||
| } | ||
| export function LoadHydrate(props: HydrateProps): React.JSX.Element { | ||
| return ( | ||
| <div> | ||
| <React.Suspense fallback={props.fallback ?? null}> | ||
| <HydratedBoundary onHydrated={props.onHydrated}> | ||
| {props.children} | ||
| </HydratedBoundary> | ||
| </React.Suspense> | ||
| </div> | ||
| ) | ||
| } | ||
| const loadStrategy = /* @__PURE__ */ withHydrationRenderer( | ||
| coreLoad(), | ||
| LoadHydrate, | ||
| ) as ReactHydrationStrategy<'load', true> & HydrationPrefetchStrategy<'load'> | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function load(): ReactHydrationStrategy<'load', true> & | ||
| HydrationPrefetchStrategy<'load'> { | ||
| return loadStrategy | ||
| } |
| 'use client' | ||
| import * as React from 'react' | ||
| import { reactUse, useHydrated } from '@tanstack/react-router' | ||
| import { isServer } from '@tanstack/router-core/isServer' | ||
| import { | ||
| never as coreNever, | ||
| withHydrationRenderer, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import { | ||
| hydrateIdAttribute, | ||
| hydrateWhenAttribute, | ||
| } from '@tanstack/start-client-core/hydration/constants' | ||
| import { | ||
| getFallbackHtml, | ||
| saveFallbackHtml, | ||
| } from '@tanstack/start-client-core/hydration/runtime' | ||
| import type { | ||
| HydrateProps, | ||
| InternalHydrateProps, | ||
| ReactHydrationStrategy, | ||
| } from '../Hydrate' | ||
| const neverType = 'never' | ||
| const neverPromise = new Promise<void>(() => {}) | ||
| function NeverGate(props: { children: React.ReactNode }) { | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| isServer ?? | ||
| typeof window === 'undefined' | ||
| ) { | ||
| return props.children as React.JSX.Element | ||
| } | ||
| if (!reactUse) { | ||
| throw neverPromise | ||
| } | ||
| reactUse(neverPromise) | ||
| return props.children as React.JSX.Element | ||
| } | ||
| export function NeverHydrate(props: HydrateProps): React.JSX.Element { | ||
| const internalProps = props as InternalHydrateProps | ||
| const hydrated = useHydrated() | ||
| const reactId = React.useId() | ||
| const id = internalProps.h ? `${internalProps.h}${reactId}` : reactId | ||
| const shouldPreserveServerHTMLRef = React.useRef<boolean | undefined>( | ||
| undefined, | ||
| ) | ||
| shouldPreserveServerHTMLRef.current ??= | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| (isServer ?? typeof window === 'undefined') || !hydrated | ||
| const markerRef = React.useCallback( | ||
| (element: HTMLDivElement | null) => { | ||
| if (!element) return | ||
| if (!shouldPreserveServerHTMLRef.current) { | ||
| element.replaceChildren() | ||
| } else { | ||
| saveFallbackHtml(id, element) | ||
| } | ||
| }, | ||
| [id], | ||
| ) | ||
| const markerProps = { | ||
| ref: markerRef, | ||
| [hydrateIdAttribute]: id, | ||
| [hydrateWhenAttribute]: neverType, | ||
| } | ||
| const fallback = (() => { | ||
| const html = getFallbackHtml(id) | ||
| return html ? ( | ||
| <div | ||
| style={{ display: 'contents' }} | ||
| dangerouslySetInnerHTML={{ __html: html }} | ||
| /> | ||
| ) : ( | ||
| (props.fallback ?? null) | ||
| ) | ||
| })() | ||
| return ( | ||
| <div {...markerProps}> | ||
| <React.Suspense fallback={fallback}> | ||
| <NeverGate>{props.children}</NeverGate> | ||
| </React.Suspense> | ||
| </div> | ||
| ) | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function never(): ReactHydrationStrategy<'never', false> { | ||
| return /* @__PURE__ */ withHydrationRenderer(coreNever(), NeverHydrate) | ||
| } |
| 'use client' | ||
| import * as React from 'react' | ||
| import { reactUse } from '@tanstack/react-router' | ||
| import { isServer } from '@tanstack/router-core/isServer' | ||
| import type { | ||
| HydrationPrefetchStrategy, | ||
| VisibleHydrationOptions, | ||
| } from '@tanstack/start-client-core/hydration' | ||
| import type { | ||
| HydrateProps, | ||
| InternalHydrateProps, | ||
| ReactHydrationStrategy, | ||
| } from '../Hydrate' | ||
| type VisibleGate = { | ||
| p: Promise<void> | ||
| r: boolean | ||
| s: () => void | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| function HydrationBoundary(props: { | ||
| g: VisibleGate | ||
| o?: () => void | ||
| children?: React.ReactNode | ||
| }) { | ||
| const { g, o } = props | ||
| if (!g.r) { | ||
| if (!reactUse) { | ||
| throw g.p | ||
| } | ||
| reactUse(g.p) | ||
| } | ||
| React.useEffect(() => { | ||
| o?.() | ||
| }, [o]) | ||
| return props.children as React.JSX.Element | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function VisibleHydrate( | ||
| this: ReactHydrationStrategy, | ||
| props: HydrateProps, | ||
| ): React.JSX.Element { | ||
| const strategy = this as ReactHydrationStrategy<'visible', true> | ||
| const prefetchStrategy = props.prefetch | ||
| const preload = (props as InternalHydrateProps).p | ||
| const markerRef = React.useRef<HTMLDivElement | null>(null) | ||
| const [gate] = React.useState<VisibleGate>(() => { | ||
| let resolvePromise!: () => void | ||
| const nextGate: VisibleGate = { | ||
| p: new Promise<void>((resolve) => { | ||
| resolvePromise = resolve | ||
| }), | ||
| r: false, | ||
| s: () => { | ||
| nextGate.r = true | ||
| resolvePromise() | ||
| }, | ||
| } | ||
| if ( | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| isServer ?? | ||
| typeof window === 'undefined' | ||
| ) { | ||
| nextGate.s() | ||
| } | ||
| return nextGate | ||
| }) | ||
| React.useEffect(() => { | ||
| if (!preload || typeof prefetchStrategy === 'function') { | ||
| return | ||
| } | ||
| return prefetchStrategy?._s?.({ | ||
| element: markerRef.current, | ||
| prefetch: preload, | ||
| }) | ||
| }, [prefetchStrategy, preload]) | ||
| React.useEffect(() => { | ||
| if (gate.r) return | ||
| return strategy._s?.({ | ||
| element: markerRef.current, | ||
| gate: gate as never, | ||
| }) | ||
| }, [gate, strategy]) | ||
| return ( | ||
| <div ref={markerRef}> | ||
| <React.Suspense fallback={props.fallback}> | ||
| <HydrationBoundary g={gate} o={props.onHydrated}> | ||
| {props.children} | ||
| </HydrationBoundary> | ||
| </React.Suspense> | ||
| </div> | ||
| ) | ||
| } | ||
| /* @__NO_SIDE_EFFECTS__ */ | ||
| export function visible( | ||
| options?: VisibleHydrationOptions, | ||
| ): ReactHydrationStrategy<'visible', true> & | ||
| HydrationPrefetchStrategy<'visible'> { | ||
| const rootMargin = options?.rootMargin ?? '600px' | ||
| const threshold = options?.threshold ?? 0 | ||
| return { | ||
| _s: ({ element, gate, prefetch }) => { | ||
| const callback = prefetch || (gate as never as VisibleGate).s | ||
| if (!element) { | ||
| callback() | ||
| return | ||
| } | ||
| const observer = new IntersectionObserver( | ||
| (entries) => { | ||
| if (!entries[0]!.isIntersecting) return | ||
| observer.disconnect() | ||
| callback() | ||
| }, | ||
| { rootMargin, threshold }, | ||
| ) | ||
| observer.observe(element) | ||
| return () => observer.disconnect() | ||
| }, | ||
| _h: VisibleHydrate, | ||
| } | ||
| } |
| import { expectTypeOf, test } from 'vitest' | ||
| import { visible } from '../hydration' | ||
| import { Hydrate } from '../Hydrate' | ||
| import type { | ||
| HydrateOptions, | ||
| HydrateProps, | ||
| HydrationPrefetchFunction, | ||
| HydrationPrefetchStrategy, | ||
| HydrationStrategy, | ||
| } from '../Hydrate' | ||
| import type { HydrationStrategy as CoreHydrationStrategy } from '@tanstack/start-client-core/hydration' | ||
| import type { ReactNode } from 'react' | ||
| type CommonHydrateProps = { | ||
| fallback?: ReactNode | ||
| onHydrated?: () => void | ||
| children: ReactNode | ||
| } | ||
| type SplitHydrateProps = CommonHydrateProps & { | ||
| when: HydrationStrategy | (() => HydrationStrategy) | ||
| prefetch?: never | ||
| split?: boolean | ||
| } | ||
| type PrefetchHydrateProps = CommonHydrateProps & { | ||
| when: HydrationStrategy | (() => HydrationStrategy) | ||
| prefetch: HydrationPrefetchStrategy | ||
| split?: true | ||
| } | ||
| type FunctionPrefetchHydrateProps = CommonHydrateProps & { | ||
| when: HydrationStrategy | (() => HydrationStrategy) | ||
| prefetch: HydrationPrefetchFunction | ||
| split?: boolean | ||
| } | ||
| test('Hydrate component accepts the public HydrateProps type', () => { | ||
| expectTypeOf(Hydrate).toBeFunction() | ||
| expectTypeOf(Hydrate).parameter(0).branded.toEqualTypeOf<HydrateProps>() | ||
| }) | ||
| test('HydrateOptions supports reusable spread props', () => { | ||
| const belowFoldProps = { | ||
| when: () => visible({ rootMargin: '800px' }), | ||
| } satisfies HydrateOptions | ||
| expectTypeOf(belowFoldProps).toMatchTypeOf<HydrateOptions>() | ||
| const withFunctionPrefetch = { | ||
| when: visible(), | ||
| split: false, | ||
| prefetch: (ctx) => { | ||
| expectTypeOf(ctx.element).toEqualTypeOf<Element | null>() | ||
| expectTypeOf(ctx.signal).toEqualTypeOf<AbortSignal>() | ||
| expectTypeOf(ctx.preload).returns.toEqualTypeOf<Promise<void>>() | ||
| expectTypeOf(ctx.waitFor).returns.toEqualTypeOf< | ||
| Promise<'prefetch' | 'hydrate' | 'abort'> | ||
| >() | ||
| }, | ||
| } satisfies HydrateOptions | ||
| expectTypeOf(withFunctionPrefetch).toMatchTypeOf<HydrateOptions>() | ||
| }) | ||
| test('Hydrate props are exact for strategy and prefetch forms', () => { | ||
| expectTypeOf< | ||
| Extract<HydrateProps, { prefetch?: never }> | ||
| >().branded.toEqualTypeOf<SplitHydrateProps>() | ||
| expectTypeOf< | ||
| Extract<HydrateProps, { prefetch: HydrationPrefetchStrategy }> | ||
| >().branded.toEqualTypeOf<PrefetchHydrateProps>() | ||
| expectTypeOf< | ||
| Extract<HydrateProps, { prefetch: HydrationPrefetchFunction }> | ||
| >().branded.toEqualTypeOf<FunctionPrefetchHydrateProps>() | ||
| }) | ||
| test('Hydrate requires a strategy', () => { | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: () => HydrationStrategy | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| children: ReactNode | ||
| }>().not.toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: () => true | ||
| children: ReactNode | ||
| }>().not.toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: false | ||
| children: ReactNode | ||
| }>().not.toMatchTypeOf<HydrateProps>() | ||
| }) | ||
| test('Hydrate requires a framework-renderable strategy', () => { | ||
| expectTypeOf<CoreHydrationStrategy>().not.toMatchTypeOf<HydrationStrategy>() | ||
| expectTypeOf<ReturnType<typeof visible>>().toMatchTypeOf<HydrationStrategy>() | ||
| expectTypeOf<{ | ||
| when: CoreHydrationStrategy | ||
| children: ReactNode | ||
| }>().not.toMatchTypeOf<HydrateProps>() | ||
| }) | ||
| test('Hydrate enforces prefetch only with split boundaries', () => { | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| prefetch: HydrationPrefetchStrategy | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| prefetch: HydrationPrefetchStrategy | ||
| split: true | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| prefetch: HydrationPrefetchStrategy | ||
| split: false | ||
| children: ReactNode | ||
| }>().not.toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| prefetch: HydrationPrefetchFunction | ||
| split: false | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| expectTypeOf<{ | ||
| when: HydrationStrategy | ||
| prefetch: HydrationPrefetchFunction | ||
| children: ReactNode | ||
| }>().toMatchTypeOf<HydrateProps>() | ||
| }) |
| import * as React from 'react' | ||
| import { renderToString } from 'react-dom/server' | ||
| import { hydrateRoot } from 'react-dom/client' | ||
| import { | ||
| act, | ||
| cleanup, | ||
| fireEvent, | ||
| render, | ||
| screen, | ||
| waitFor, | ||
| } from '@testing-library/react' | ||
| import { afterEach, describe, expect, it, vi } from 'vitest' | ||
| import { hydrateIdAttribute } from '@tanstack/start-client-core/hydration/constants' | ||
| import { Hydrate } from '../Hydrate' | ||
| import { condition, idle, interaction, load, never } from '../hydration' | ||
| import type { HydrateProps, HydrationPrefetchStrategy } from '../Hydrate' | ||
| const InternalHydrate = Hydrate as React.ComponentType< | ||
| HydrateProps & { p?: () => Promise<void>; h?: string } | ||
| > | ||
| const hydrateIdSelector = `[${hydrateIdAttribute}]` | ||
| function getMarker() { | ||
| const marker = document.querySelector(hydrateIdSelector) | ||
| if (!marker) { | ||
| throw new Error('Expected Hydrate marker to exist') | ||
| } | ||
| return marker | ||
| } | ||
| function InteractiveChild() { | ||
| const [count, setCount] = React.useState(0) | ||
| const [hydrated, setHydrated] = React.useState(false) | ||
| React.useEffect(() => { | ||
| setHydrated(true) | ||
| }, []) | ||
| return ( | ||
| <button | ||
| data-testid="child" | ||
| data-hydrated={hydrated ? 'true' : 'false'} | ||
| onClick={() => setCount((prev) => prev + 1)} | ||
| > | ||
| {count} | ||
| </button> | ||
| ) | ||
| } | ||
| function NamedInteractiveChild(props: { id: string }) { | ||
| const [hydrated, setHydrated] = React.useState(false) | ||
| React.useEffect(() => { | ||
| setHydrated(true) | ||
| }, []) | ||
| return ( | ||
| <button | ||
| data-testid={`child-${props.id}`} | ||
| data-hydrated={hydrated ? 'true' : 'false'} | ||
| > | ||
| {props.id} | ||
| </button> | ||
| ) | ||
| } | ||
| function createSuspendingChild() { | ||
| let resolve!: () => void | ||
| let resolved = false | ||
| const promise = new Promise<void>((resolvePromise) => { | ||
| resolve = () => { | ||
| resolved = true | ||
| resolvePromise() | ||
| } | ||
| }) | ||
| function SuspendingChild() { | ||
| if (!resolved) { | ||
| throw promise | ||
| } | ||
| return <div data-testid="child">child</div> | ||
| } | ||
| return { resolve, SuspendingChild } | ||
| } | ||
| async function expectNoHydrationAfterDefaultIntentEvents() { | ||
| const marker = getMarker() | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| await act(async () => { | ||
| fireEvent.pointerEnter(marker) | ||
| fireEvent.focusIn(marker) | ||
| fireEvent.pointerDown(marker) | ||
| fireEvent.click(marker) | ||
| await new Promise((resolve) => setTimeout(resolve, 20)) | ||
| }) | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| } | ||
| async function fireIntent(event: () => void) { | ||
| await act(async () => { | ||
| event() | ||
| await Promise.resolve() | ||
| }) | ||
| } | ||
| async function renderAsync(ui: React.ReactElement) { | ||
| await act(async () => { | ||
| render(ui) | ||
| await Promise.resolve() | ||
| }) | ||
| } | ||
| async function hydrateFromServer(ui: React.ReactElement) { | ||
| vi.stubGlobal('window', undefined) | ||
| const html = renderToString(ui) | ||
| vi.unstubAllGlobals() | ||
| const container = document.createElement('div') | ||
| document.body.append(container) | ||
| container.innerHTML = html | ||
| let root!: ReturnType<typeof hydrateRoot> | ||
| await act(async () => { | ||
| root = hydrateRoot(container, ui) | ||
| await Promise.resolve() | ||
| }) | ||
| return { container, html, root } | ||
| } | ||
| async function unmountHydratedRoot( | ||
| root: ReturnType<typeof hydrateRoot>, | ||
| container: Element, | ||
| ) { | ||
| await act(async () => { | ||
| root.unmount() | ||
| }) | ||
| container.remove() | ||
| } | ||
| afterEach(() => { | ||
| cleanup() | ||
| vi.unstubAllGlobals() | ||
| }) | ||
| describe('Hydrate', () => { | ||
| it('uses a single custom interaction event instead of the default intent events', async () => { | ||
| const { container, html, root } = await hydrateFromServer( | ||
| <Hydrate | ||
| when={interaction({ events: 'dblclick' })} | ||
| fallback={<div data-testid="fallback">fallback</div>} | ||
| > | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| try { | ||
| expect(html).toContain('data-testid="child"') | ||
| expect(html).not.toContain('data-testid="fallback"') | ||
| expect(screen.queryByTestId('fallback')).toBeNull() | ||
| await expectNoHydrationAfterDefaultIntentEvents() | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('dblclick', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('uses every event in a custom interaction event list', async () => { | ||
| const { container, root } = await hydrateFromServer( | ||
| <Hydrate | ||
| when={interaction({ events: ['contextmenu', 'dblclick'] })} | ||
| fallback={<div data-testid="fallback">fallback</div>} | ||
| > | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| try { | ||
| expect(screen.queryByTestId('fallback')).toBeNull() | ||
| await expectNoHydrationAfterDefaultIntentEvents() | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('contextmenu', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('omits never content when mounted after the app is already hydrated', async () => { | ||
| await renderAsync( | ||
| <Hydrate when={never()}> | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| expect(screen.queryByTestId('child')).toBeNull() | ||
| }) | ||
| it('shows fallback for a client-only mount while children suspend', async () => { | ||
| const { resolve, SuspendingChild } = createSuspendingChild() | ||
| await renderAsync( | ||
| <Hydrate | ||
| when={load()} | ||
| fallback={<div data-testid="fallback">fallback</div>} | ||
| > | ||
| <SuspendingChild /> | ||
| </Hydrate>, | ||
| ) | ||
| expect(screen.getByTestId('fallback').textContent).toBe('fallback') | ||
| expect(screen.queryByTestId('child')).toBeNull() | ||
| await act(async () => { | ||
| resolve() | ||
| await Promise.resolve() | ||
| }) | ||
| await screen.findByTestId('child') | ||
| expect(screen.queryByTestId('fallback')).toBeNull() | ||
| }) | ||
| it('does not use fallback for an initial never boundary', async () => { | ||
| const { container, html, root } = await hydrateFromServer( | ||
| <Hydrate | ||
| when={never()} | ||
| fallback={<div data-testid="fallback">fallback</div>} | ||
| > | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| try { | ||
| expect(html).toContain('data-testid="child"') | ||
| expect(html).not.toContain('data-testid="fallback"') | ||
| expect(screen.queryByTestId('fallback')).toBeNull() | ||
| fireEvent.click(screen.getByTestId('child')) | ||
| await new Promise((resolve) => setTimeout(resolve, 20)) | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| expect(screen.getByTestId('child').textContent).toBe('0') | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('keeps repeated split boundaries independently gated', async () => { | ||
| const { container, root } = await hydrateFromServer( | ||
| <> | ||
| <InternalHydrate when={interaction()} h="shared-boundary"> | ||
| <NamedInteractiveChild id="one" /> | ||
| </InternalHydrate> | ||
| <InternalHydrate when={interaction()} h="shared-boundary"> | ||
| <NamedInteractiveChild id="two" /> | ||
| </InternalHydrate> | ||
| </>, | ||
| ) | ||
| try { | ||
| const markers = container.querySelectorAll(hydrateIdSelector) | ||
| expect(markers).toHaveLength(2) | ||
| expect(markers[0]!.getAttribute(hydrateIdAttribute)).not.toBe( | ||
| markers[1]!.getAttribute(hydrateIdAttribute), | ||
| ) | ||
| expect( | ||
| screen.getByTestId('child-one').getAttribute('data-hydrated'), | ||
| ).toBe('false') | ||
| expect( | ||
| screen.getByTestId('child-two').getAttribute('data-hydrated'), | ||
| ).toBe('false') | ||
| await fireIntent(() => | ||
| markers[0]!.dispatchEvent( | ||
| new MouseEvent('click', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect( | ||
| screen.getByTestId('child-one').getAttribute('data-hydrated'), | ||
| ).toBe('true'), | ||
| ) | ||
| expect( | ||
| screen.getByTestId('child-two').getAttribute('data-hydrated'), | ||
| ).toBe('false') | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('fires onHydrated once after the client hydration commit', async () => { | ||
| const onHydrated = vi.fn() | ||
| const app = ( | ||
| <Hydrate when={load()} onHydrated={onHydrated}> | ||
| <div data-testid="child">child</div> | ||
| </Hydrate> | ||
| ) | ||
| vi.stubGlobal('window', undefined) | ||
| const html = renderToString(app) | ||
| expect(html).toContain('child') | ||
| expect(onHydrated).not.toHaveBeenCalled() | ||
| vi.unstubAllGlobals() | ||
| const container = document.createElement('div') | ||
| document.body.append(container) | ||
| container.innerHTML = html | ||
| let root!: ReturnType<typeof hydrateRoot> | ||
| await act(async () => { | ||
| root = hydrateRoot(container, app) | ||
| }) | ||
| await waitFor(() => expect(onHydrated).toHaveBeenCalledTimes(1)) | ||
| fireEvent.click(screen.getByTestId('child')) | ||
| await new Promise((resolve) => setTimeout(resolve, 20)) | ||
| expect(onHydrated).toHaveBeenCalledTimes(1) | ||
| await act(async () => { | ||
| root.unmount() | ||
| }) | ||
| container.remove() | ||
| }) | ||
| it('prefetches split children without hydrating the boundary', async () => { | ||
| const preload = vi.fn(() => Promise.resolve()) | ||
| const { container, root } = await hydrateFromServer( | ||
| <InternalHydrate | ||
| when={interaction()} | ||
| prefetch={idle({ timeout: 1 })} | ||
| p={preload} | ||
| > | ||
| <InteractiveChild /> | ||
| </InternalHydrate>, | ||
| ) | ||
| try { | ||
| await waitFor(() => expect(preload).toHaveBeenCalledTimes(1)) | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('click', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| expect(preload).toHaveBeenCalledTimes(1) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('does not evaluate dynamic when callbacks on the server', async () => { | ||
| const when = vi.fn(() => interaction({ events: 'dblclick' })) | ||
| vi.stubGlobal('window', undefined) | ||
| const html = renderToString( | ||
| <Hydrate when={when}> | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| vi.unstubAllGlobals() | ||
| expect(when).not.toHaveBeenCalled() | ||
| expect(html).toContain('data-ts-hydrate-when="dynamic"') | ||
| const container = document.createElement('div') | ||
| document.body.append(container) | ||
| container.innerHTML = html | ||
| let root!: ReturnType<typeof hydrateRoot> | ||
| try { | ||
| await act(async () => { | ||
| root = hydrateRoot( | ||
| container, | ||
| <Hydrate when={when}> | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| await Promise.resolve() | ||
| }) | ||
| expect(when).toHaveBeenCalled() | ||
| await expectNoHydrationAfterDefaultIntentEvents() | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('dblclick', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('replays an interaction captured before the Hydrate component hydrates', async () => { | ||
| const when = () => interaction({ events: 'click' }) | ||
| vi.stubGlobal('window', undefined) | ||
| const html = renderToString( | ||
| <Hydrate when={when}> | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| vi.unstubAllGlobals() | ||
| const container = document.createElement('div') | ||
| document.body.append(container) | ||
| container.innerHTML = html | ||
| const button = container.querySelector('[data-testid="child"]') | ||
| if (!button) { | ||
| throw new Error('Expected server-rendered child button') | ||
| } | ||
| button.dispatchEvent( | ||
| new MouseEvent('click', { bubbles: true, cancelable: true }), | ||
| ) | ||
| let root!: ReturnType<typeof hydrateRoot> | ||
| try { | ||
| await act(async () => { | ||
| root = hydrateRoot( | ||
| container, | ||
| <Hydrate when={when}> | ||
| <InteractiveChild /> | ||
| </Hydrate>, | ||
| ) | ||
| await Promise.resolve() | ||
| }) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').textContent).toBe('1'), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('blocks hydration on awaited procedural prefetch work', async () => { | ||
| const preload = vi.fn(() => Promise.resolve()) | ||
| let resolvePrefetch!: () => void | ||
| const prefetchBlocker = new Promise<void>((resolve) => { | ||
| resolvePrefetch = resolve | ||
| }) | ||
| const waitReasons: Array<string> = [] | ||
| const neverPrefetches = { | ||
| _t: 'idle', | ||
| _s: () => () => {}, | ||
| } as HydrationPrefetchStrategy<'idle'> | ||
| const { container, root } = await hydrateFromServer( | ||
| <InternalHydrate | ||
| when={interaction()} | ||
| prefetch={async ({ waitFor, preload }) => { | ||
| waitReasons.push(await waitFor(neverPrefetches)) | ||
| await preload() | ||
| await prefetchBlocker | ||
| }} | ||
| p={preload} | ||
| > | ||
| <InteractiveChild /> | ||
| </InternalHydrate>, | ||
| ) | ||
| try { | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('click', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => expect(waitReasons).toEqual(['hydrate'])) | ||
| expect(preload).toHaveBeenCalledTimes(1) | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| await act(async () => { | ||
| resolvePrefetch() | ||
| await prefetchBlocker | ||
| await Promise.resolve() | ||
| }) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('hydrates when a condition strategy changes after the initial render', async () => { | ||
| function ConditionHarness() { | ||
| const [ready, setReady] = React.useState(false) | ||
| return ( | ||
| <> | ||
| <button data-testid="ready" onClick={() => setReady(true)}> | ||
| ready | ||
| </button> | ||
| <Hydrate when={condition(ready)}> | ||
| <InteractiveChild /> | ||
| </Hydrate> | ||
| </> | ||
| ) | ||
| } | ||
| const { container, root } = await hydrateFromServer(<ConditionHarness />) | ||
| try { | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| await act(async () => { | ||
| fireEvent.click(screen.getByTestId('ready')) | ||
| await Promise.resolve() | ||
| }) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('does not block hydration on fire-and-forget procedural prefetch work', async () => { | ||
| let resolvePrefetch!: () => void | ||
| const prefetchBlocker = new Promise<void>((resolve) => { | ||
| resolvePrefetch = resolve | ||
| }) | ||
| const { container, root } = await hydrateFromServer( | ||
| <InternalHydrate | ||
| when={interaction()} | ||
| prefetch={() => { | ||
| void prefetchBlocker | ||
| }} | ||
| > | ||
| <InteractiveChild /> | ||
| </InternalHydrate>, | ||
| ) | ||
| try { | ||
| await fireIntent(() => | ||
| getMarker().dispatchEvent( | ||
| new MouseEvent('click', { bubbles: true, cancelable: true }), | ||
| ), | ||
| ) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| await act(async () => { | ||
| resolvePrefetch() | ||
| await prefetchBlocker | ||
| }) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| it('aborts procedural prefetch when the boundary unmounts', async () => { | ||
| const signals: Array<AbortSignal> = [] | ||
| const { container, root } = await hydrateFromServer( | ||
| <InternalHydrate | ||
| when={interaction()} | ||
| prefetch={({ signal }) => { | ||
| signals.push(signal) | ||
| return new Promise<void>(() => {}) | ||
| }} | ||
| > | ||
| <InteractiveChild /> | ||
| </InternalHydrate>, | ||
| ) | ||
| expect(signals).toHaveLength(1) | ||
| expect(signals[0]!.aborted).toBe(false) | ||
| await unmountHydratedRoot(root, container) | ||
| expect(signals[0]!.aborted).toBe(true) | ||
| }) | ||
| it('delegates nested interaction boundaries at runtime', async () => { | ||
| const { container, root } = await hydrateFromServer( | ||
| <Hydrate when={idle({ timeout: 1000 })}> | ||
| <Hydrate when={interaction()}> | ||
| <InteractiveChild /> | ||
| </Hydrate> | ||
| </Hydrate>, | ||
| ) | ||
| try { | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'false', | ||
| ) | ||
| await fireIntent(() => { | ||
| fireEvent.click(screen.getByTestId('child')) | ||
| }) | ||
| await waitFor(() => | ||
| expect(screen.getByTestId('child').getAttribute('data-hydrated')).toBe( | ||
| 'true', | ||
| ), | ||
| ) | ||
| } finally { | ||
| await unmountHydratedRoot(root, container) | ||
| } | ||
| }) | ||
| }) |
| export { StartClient } from './StartClient.js'; | ||
| export { hydrateStart } from './hydrateStart.js'; | ||
| export { Hydrate } from './Hydrate.js'; | ||
| export type { HydrateOptions, HydrateProps, HydrateWhen, HydrationInteractionEvent, HydrationInteractionEvents, HydrationPrefetchContext, HydrationPrefetchFunction, HydrationPrefetchStrategy, HydrationPrefetchWaitReason, HydrationStrategy, HydrationWhen, } from './Hydrate.js'; |
@@ -0,3 +1,5 @@ | ||
| "use client"; | ||
| import { hydrateStart } from "./hydrateStart.js"; | ||
| import { StartClient } from "./StartClient.js"; | ||
| export { StartClient, hydrateStart }; | ||
| import { Hydrate } from "./Hydrate.js"; | ||
| export { Hydrate, StartClient, hydrateStart }; |
+10
-4
| { | ||
| "name": "@tanstack/react-start-client", | ||
| "version": "1.167.4", | ||
| "version": "1.168.0", | ||
| "description": "Modern and scalable routing for React applications", | ||
@@ -35,2 +35,8 @@ "author": "Tanner Linsley", | ||
| }, | ||
| "./hydration": { | ||
| "import": { | ||
| "types": "./dist/esm/hydration.d.ts", | ||
| "default": "./dist/esm/hydration.js" | ||
| } | ||
| }, | ||
| "./package.json": "./package.json" | ||
@@ -47,5 +53,5 @@ }, | ||
| "dependencies": { | ||
| "@tanstack/react-router": "1.170.4", | ||
| "@tanstack/router-core": "1.171.2", | ||
| "@tanstack/start-client-core": "1.169.4" | ||
| "@tanstack/react-router": "1.170.5", | ||
| "@tanstack/router-core": "1.171.3", | ||
| "@tanstack/start-client-core": "1.170.0" | ||
| }, | ||
@@ -52,0 +58,0 @@ "devDependencies": { |
+16
-0
@@ -0,2 +1,18 @@ | ||
| 'use client' | ||
| export { StartClient } from './StartClient' | ||
| export { hydrateStart } from './hydrateStart' | ||
| export { Hydrate } from './Hydrate' | ||
| export type { | ||
| HydrateOptions, | ||
| HydrateProps, | ||
| HydrateWhen, | ||
| HydrationInteractionEvent, | ||
| HydrationInteractionEvents, | ||
| HydrationPrefetchContext, | ||
| HydrationPrefetchFunction, | ||
| HydrationPrefetchStrategy, | ||
| HydrationPrefetchWaitReason, | ||
| HydrationStrategy, | ||
| HydrationWhen, | ||
| } from './Hydrate' |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
110742
1084.91%53
194.44%2197
1730.83%1
Infinity%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed