@real-router/react 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -56,10 +56,6 @@ declare const Link: FC<Omit<BaseLinkProps, "router">>;
56
56
 
57
57
  declare const ConnectedLink: FC<Omit<BaseLinkProps, "router" | "route" | "previousRoute">>;
58
58
 
59
- /**
60
- * Hook that subscribes to a specific route node with optimizations.
61
- * Provides the current and previous route when the node is affected.
62
- */
63
59
  declare function useRouteNode(nodeName: string): RouteContext$1;
64
60
 
65
61
  declare const useRoute: () => RouteContext$1;
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var e=require("react"),t=require("@real-router/route-utils"),r=require("react/jsx-runtime"),u=require("@real-router/core"),o=Object.freeze({}),s=Object.freeze({});function n(t,r,u){const o=e.useRef(void 0),s=e.useRef(r),n=e.useRef(u);if(s.current=r,n.current=u,void 0===o.current){const e=t.getState(),r=!n.current||e&&n.current(e);o.current=s.current(r&&e?{route:e,previousRoute:void 0}:void 0)}const i=e.useCallback(()=>o.current,[]),a=e.useCallback(e=>t.subscribe(t=>{let r=!0;if(n.current&&(r=n.current(t.route,t.previousRoute)),!r)return;const u=s.current(t);Object.is(o.current,u)||(o.current=u,e())}),[t]);return e.useSyncExternalStore(a,i,i)}function i(t){const r=JSON.stringify(t);return e.useMemo(()=>t,[r])}var a=new WeakMap;var c=e.memo(({routeName:u,routeParams:a=o,routeOptions:c=s,className:l,activeClassName:v="active",activeStrict:m=!1,ignoreQueryParams:d=!0,onClick:p,target:R,router:g,children:f,...x})=>{const b=i(a),N=i(c),h=function(r,u,s=o,a=!1,c=!0){const l=i(s),v=e.useMemo(()=>function(e,t,r,u){return JSON.stringify({routeName:e,routeParams:t,activeStrict:r,ignoreQueryParams:u})}(u,l,a,c),[u,l,a,c]),m=e.useRef(void 0),d=e.useRef(void 0);d.current!==v&&(m.current=void 0,d.current=v);const p=e.useCallback((e,r)=>{const o=t.areRoutesRelated(u,e.name),s=r&&t.areRoutesRelated(u,r.name);return!(!o&&!s)},[u]),R=e.useCallback(e=>{const o=e?.route??r.getState();if(!o)return m.current=!1,!1;if(!t.areRoutesRelated(u,o.name))return m.current=!1,!1;const s=r.isActiveRoute(u,l,a,c);return m.current=s,s},[r,u,l,a,c]);return n(r,R,p)}(g,u,b,m,d),C=e.useMemo(()=>"function"==typeof g.buildUrl?g.buildUrl(u,b):g.buildPath(u,b),[g,u,b]),S=e.useCallback(e=>{p&&(p(e),e.defaultPrevented)||function(e){return!(0!==e.button||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e)&&"_blank"!==R&&(e.preventDefault(),g.navigate(u,b,N).catch(()=>{}))},[p,R,g,u,b,N]),y=e.useMemo(()=>h&&v?l?`${l} ${v}`.trim():v:l??void 0,[h,l,v]),{previousRoute:k,...P}=x;return r.jsx("a",{...P,href:C,className:y,onClick:S,"data-route":u,"data-active":h,children:f})},(e,t)=>e.router===t.router&&e.routeName===t.routeName&&JSON.stringify(e.routeParams)===JSON.stringify(t.routeParams)&&JSON.stringify(e.routeOptions)===JSON.stringify(t.routeOptions)&&e.className===t.className&&e.activeClassName===t.activeClassName&&e.activeStrict===t.activeStrict&&e.ignoreQueryParams===t.ignoreQueryParams&&e.onClick===t.onClick&&e.target===t.target&&e.children===t.children);c.displayName="BaseLink";var l=e.createContext(null),v=e.createContext(null),m=e.createContext(null),d=()=>{const t=e.use(v);if(!t)throw new Error("useRouter must be used within a RouterProvider");return t},p=()=>{const t=e.use(l);if(!t)throw new Error("useRoute must be used within a RouteProvider");return t};exports.BaseLink=c,exports.ConnectedLink=e=>{const t=d(),{route:u}=p(),{routeOptions:o,...s}=e;return r.jsx(c,{router:t,route:u,...s})},exports.Link=e=>{const t=d(),{route:u,previousRoute:o,routeOptions:s,...n}=e;return r.jsx(c,{router:t,...n})},exports.NavigatorContext=m,exports.RouteContext=l,exports.RouterContext=v,exports.RouterProvider=({router:t,children:o})=>{const s=e.useMemo(()=>u.getNavigator(t),[t]),n=e.useMemo(()=>{let e={route:t.getState(),previousRoute:void 0};return{getSnapshot:()=>e,subscribe:r=>t.subscribe(({route:t,previousRoute:u})=>{e={route:t,previousRoute:u},r()})}},[t]),i=e.useSyncExternalStore(n.subscribe,n.getSnapshot),a=e.useMemo(()=>({navigator:s,...i}),[s,i]);return r.jsx(v,{value:t,children:r.jsx(m,{value:s,children:r.jsx(l,{value:a,children:o})})})},exports.useNavigator=()=>{const t=e.use(m);if(!t)throw new Error("useNavigator must be used within a RouterProvider");return t},exports.useRoute=p,exports.useRouteNode=function(t){const r=d(),o=e.useMemo(()=>function(e,t){let r=a.get(e);r||(r=new Map,a.set(e,r));let u=r.get(t);if(!u){u=e.shouldUpdateNode(t);const o=u;u=(e,t)=>o(e,t),r.set(t,u)}return u}(r,t),[r,t]),s=e.useCallback(e=>{const u=e?.route??r.getState();return u&&""!==t&&u.name!==t&&!u.name.startsWith(`${t}.`)?{route:void 0,previousRoute:e?.previousRoute}:{route:u,previousRoute:e?.previousRoute}},[r,t]),i=n(r,s,o),c=e.useMemo(()=>u.getNavigator(r),[r]);return e.useMemo(()=>({navigator:c,route:i.route,previousRoute:i.previousRoute}),[c,i.route,i.previousRoute])},exports.useRouteUtils=()=>{const e=d();return t.getRouteUtils(u.getPluginApi(e).getTree())},exports.useRouter=d;//# sourceMappingURL=index.js.map
1
+ var e=require("react"),t=require("@real-router/sources"),r=require("react/jsx-runtime"),o=require("@real-router/core"),u=require("@real-router/route-utils"),s=Object.freeze({}),a=Object.freeze({}),i=e.createContext(null),n=e.createContext(null),c=e.createContext(null),l=()=>{const t=e.use(n);if(!t)throw new Error("useRouter must be used within a RouterProvider");return t};function m(t){const r=JSON.stringify(t);return e.useMemo(()=>t,[r])}var v=e.memo(({routeName:o,routeParams:u=s,routeOptions:i=a,className:n,activeClassName:c="active",activeStrict:v=!1,ignoreQueryParams:p=!0,onClick:g,target:x,router:d,children:h,...N})=>{const S=m(u),R=m(i),f=function(r,o,u=!1,s=!0){const a=l(),i=m(o),n=e.useMemo(()=>t.createActiveRouteSource(a,r,i,{strict:u,ignoreQueryParams:s}),[a,r,i,u,s]);return e.useSyncExternalStore(n.subscribe,n.getSnapshot,n.getSnapshot)}(o,S,v,p),y=e.useMemo(()=>"function"==typeof d.buildUrl?d.buildUrl(o,S):d.buildPath(o,S),[d,o,S]),b=e.useCallback(e=>{g&&(g(e),e.defaultPrevented)||function(e){return!(0!==e.button||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e)&&"_blank"!==x&&(e.preventDefault(),d.navigate(o,S,R).catch(()=>{}))},[g,x,d,o,S,R]),C=e.useMemo(()=>f&&c?n?`${n} ${c}`.trim():c:n??void 0,[f,n,c]),{previousRoute:P,...O}=N;return r.jsx("a",{...O,href:y,className:C,onClick:b,"data-route":o,"data-active":f,children:h})},(e,t)=>e.router===t.router&&e.routeName===t.routeName&&JSON.stringify(e.routeParams)===JSON.stringify(t.routeParams)&&JSON.stringify(e.routeOptions)===JSON.stringify(t.routeOptions)&&e.className===t.className&&e.activeClassName===t.activeClassName&&e.activeStrict===t.activeStrict&&e.ignoreQueryParams===t.ignoreQueryParams&&e.onClick===t.onClick&&e.target===t.target&&e.children===t.children);v.displayName="BaseLink";var p=()=>{const t=e.use(i);if(!t)throw new Error("useRoute must be used within a RouteProvider");return t};exports.BaseLink=v,exports.ConnectedLink=e=>{const t=l(),{route:o}=p(),{routeOptions:u,...s}=e;return r.jsx(v,{router:t,route:o,...s})},exports.Link=e=>{const t=l(),{route:o,previousRoute:u,routeOptions:s,...a}=e;return r.jsx(v,{router:t,...a})},exports.NavigatorContext=c,exports.RouteContext=i,exports.RouterContext=n,exports.RouterProvider=({router:u,children:s})=>{const a=e.useMemo(()=>o.getNavigator(u),[u]),l=e.useMemo(()=>t.createRouteSource(u),[u]),{route:m,previousRoute:v}=e.useSyncExternalStore(l.subscribe,l.getSnapshot,l.getSnapshot),p=e.useMemo(()=>({navigator:a,route:m,previousRoute:v}),[a,m,v]);return r.jsx(n,{value:u,children:r.jsx(c,{value:a,children:r.jsx(i,{value:p,children:s})})})},exports.useNavigator=()=>{const t=e.use(c);if(!t)throw new Error("useNavigator must be used within a RouterProvider");return t},exports.useRoute=p,exports.useRouteNode=function(r){const u=l(),s=e.useMemo(()=>t.createRouteNodeSource(u,r),[u,r]),{route:a,previousRoute:i}=e.useSyncExternalStore(s.subscribe,s.getSnapshot,s.getSnapshot),n=e.useMemo(()=>o.getNavigator(u),[u]);return e.useMemo(()=>({navigator:n,route:a,previousRoute:i}),[n,a,i])},exports.useRouteUtils=()=>{const e=l();return u.getRouteUtils(o.getPluginApi(e).getTree())},exports.useRouter=l;//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/hooks/useRouterSubscription.tsx","../../src/hooks/useStableValue.tsx","../../src/utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/BaseLink.tsx","../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/components/Link.tsx","../../src/hooks/useRoute.tsx","../../src/components/ConnectedLink.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useNavigator.tsx","../../src/RouterProvider.tsx","../../src/hooks/useRouteUtils.tsx"],"names":["useRef","useCallback","useSyncExternalStore","useMemo","areRoutesRelated","memo","jsx","createContext","use","getNavigator","getRouteUtils","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/react/modules/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","// packages/react/modules/hooks/useRouterSubscription.tsx\n\nimport { useCallback, useRef, useSyncExternalStore } from \"react\";\n\nimport type { Router, State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Generic hook for subscribing to router changes with optimization.\n *\n * @param router - Real Router instance\n * @param selector - Function to derive state from router subscription\n * @param shouldUpdate - Optional predicate to filter updates\n */\nexport function useRouterSubscription<T>(\n router: Router,\n selector: (sub?: SubscribeState) => T,\n shouldUpdate?: (newRoute: State, prevRoute?: State) => boolean,\n): T {\n // Store current value\n const stateRef = useRef<T | undefined>(undefined);\n const selectorRef = useRef(selector);\n const shouldUpdateRef = useRef(shouldUpdate);\n\n // Update refs to avoid stale closures\n selectorRef.current = selector;\n shouldUpdateRef.current = shouldUpdate;\n\n // Lazy initialization\n if (stateRef.current === undefined) {\n // Get initial state from router\n const currentState = router.getState();\n\n // Check if initial state is relevant for this subscription\n const shouldInitialize =\n !shouldUpdateRef.current ||\n (currentState && shouldUpdateRef.current(currentState));\n\n stateRef.current = selectorRef.current(\n shouldInitialize && currentState\n ? { route: currentState, previousRoute: undefined }\n : undefined,\n );\n }\n\n // Stable snapshot getter\n const getSnapshot = useCallback(() => stateRef.current as T, []);\n\n // Subscribe function with optimization\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return router.subscribe((next) => {\n // Check if we should process this update\n let shouldProcess = true;\n\n if (shouldUpdateRef.current) {\n shouldProcess = shouldUpdateRef.current(\n next.route,\n next.previousRoute,\n );\n }\n\n if (!shouldProcess) {\n return;\n }\n\n // Calculate new value\n const newValue = selectorRef.current(next);\n\n // Only trigger update if value actually changed\n if (!Object.is(stateRef.current, newValue)) {\n stateRef.current = newValue;\n\n onStoreChange();\n }\n });\n },\n [router],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","// packages/react/modules/utils.ts\n\nimport type { Params, Router, State } from \"@real-router/core\";\nimport type { MouseEvent } from \"react\";\n\n/**\n * Cache for shouldUpdateNode functions to avoid recreating them\n */\nexport const shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\n/**\n * Get cached shouldUpdateNode function for a router and nodeName\n */\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let cache = shouldUpdateCache.get(router);\n\n if (!cache) {\n cache = new Map();\n shouldUpdateCache.set(router, cache);\n }\n\n let fn = cache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n\n const originalFn = fn;\n\n fn = (toState: State, fromState?: State) => originalFn(toState, fromState);\n\n cache.set(nodeName, fn);\n }\n\n return fn;\n}\n\n/**\n * Check if navigation should be handled by router\n */\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 && // left click\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\n/**\n * Create cache key for route active check\n */\nexport function createActiveCheckKey(\n routeName: string,\n routeParams: Params,\n activeStrict: boolean,\n ignoreQueryParams: boolean,\n): string {\n return JSON.stringify({\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n });\n}\n","// packages/react/modules/hooks/useIsActiveRoute.tsx\n\nimport { areRoutesRelated } from \"@real-router/route-utils\";\nimport { useCallback, useMemo, useRef } from \"react\";\n\nimport { useRouterSubscription } from \"./useRouterSubscription\";\nimport { useStableValue } from \"./useStableValue\";\nimport { EMPTY_PARAMS } from \"../constants\";\nimport { createActiveCheckKey } from \"../utils\";\n\nimport type { Params, Router, State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Optimized hook to check if a route is active.\n * Minimizes unnecessary recalculations and re-renders.\n */\nexport function useIsActiveRoute(\n router: Router,\n routeName: string,\n routeParams: Params = EMPTY_PARAMS,\n activeStrict = false,\n ignoreQueryParams = true,\n): boolean {\n // Stabilize params reference to prevent unnecessary recalculations\n const stableParams = useStableValue(routeParams);\n\n // Create stable cache key\n const cacheKey = useMemo(\n () =>\n createActiveCheckKey(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n ),\n [routeName, stableParams, activeStrict, ignoreQueryParams],\n );\n\n // Cache the active state\n const isActiveRef = useRef<boolean | undefined>(undefined);\n const lastCacheKeyRef = useRef<string | undefined>(undefined);\n\n if (lastCacheKeyRef.current !== cacheKey) {\n isActiveRef.current = undefined;\n lastCacheKeyRef.current = cacheKey;\n }\n\n // Optimize shouldUpdate to skip unrelated routes\n const shouldUpdate = useCallback(\n (newRoute: State, prevRoute?: State) => {\n const isNewRelated = areRoutesRelated(routeName, newRoute.name);\n const isPrevRelated =\n prevRoute && areRoutesRelated(routeName, prevRoute.name);\n\n return !!(isNewRelated || isPrevRelated);\n },\n [routeName],\n );\n\n // Selector that performs active check\n const selector = useCallback(\n (sub?: SubscribeState): boolean => {\n const currentRoute = sub?.route ?? router.getState();\n\n // Fast path: if no current route, not active\n if (!currentRoute) {\n isActiveRef.current = false;\n\n return false;\n }\n\n // Fast path: skip unrelated routes\n if (!areRoutesRelated(routeName, currentRoute.name)) {\n isActiveRef.current = false;\n\n return false;\n }\n\n // Full check for related routes\n const isActive = router.isActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n isActiveRef.current = isActive;\n\n return isActive;\n },\n [router, routeName, stableParams, activeStrict, ignoreQueryParams],\n );\n\n return useRouterSubscription(router, selector, shouldUpdate);\n}\n","// packages/react/modules/components/BaseLink.tsx\n\nimport { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useStableValue } from \"../hooks/useStableValue\";\nimport { shouldNavigate } from \"../utils\";\n\nimport type { BaseLinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\n/**\n * Optimized BaseLink component with memoization and performance improvements\n */\nexport const BaseLink: FC<BaseLinkProps> = memo(\n ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n router,\n children,\n ...props\n }) => {\n // Stabilize object references to prevent unnecessary re-renders\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n // Use optimized hook for active state checking\n const isActive = useIsActiveRoute(\n router,\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n // Build URL with memoization\n const href = useMemo(() => {\n // Use buildUrl if available (browser plugin installed)\n // Otherwise fall back to buildPath (e.g., in SSR or without browser plugin)\n if (typeof router.buildUrl === \"function\") {\n return router.buildUrl(routeName, stableParams);\n }\n\n return router.buildPath(routeName, stableParams);\n }, [router, routeName, stableParams]);\n\n // Optimized click handler\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n // Call custom onClick if provided\n if (onClick) {\n onClick(evt);\n // Respect preventDefault from custom handler\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n // Check if we should handle navigation\n if (!shouldNavigate(evt) || target === \"_blank\") {\n return;\n }\n\n // Prevent default link behavior\n evt.preventDefault();\n\n // Perform navigation (fire-and-forget)\n router.navigate(routeName, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n // Build className efficiently\n const finalClassName = useMemo(() => {\n if (isActive && activeClassName) {\n return className\n ? `${className} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return className ?? undefined;\n }, [isActive, className, activeClassName]);\n\n // Filter out previousRoute from props\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { previousRoute, ...restProps } = props;\n\n return (\n <a\n {...restProps}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n data-route={routeName} // For event delegation if needed\n data-active={isActive} // For CSS selectors if needed\n >\n {children}\n </a>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for better memoization\n // Check if props that affect rendering have changed\n return (\n prevProps.router === nextProps.router &&\n prevProps.routeName === nextProps.routeName &&\n JSON.stringify(prevProps.routeParams) ===\n JSON.stringify(nextProps.routeParams) &&\n JSON.stringify(prevProps.routeOptions) ===\n JSON.stringify(nextProps.routeOptions) &&\n prevProps.className === nextProps.className &&\n prevProps.activeClassName === nextProps.activeClassName &&\n prevProps.activeStrict === nextProps.activeStrict &&\n prevProps.ignoreQueryParams === nextProps.ignoreQueryParams &&\n prevProps.onClick === nextProps.onClick &&\n prevProps.target === nextProps.target &&\n prevProps.children === nextProps.children\n );\n },\n);\n\nBaseLink.displayName = \"BaseLink\";\n","// packages/react/modules/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/modules/hooks/useRouter.tsx\n\nimport { use } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = use(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","// packages/react/modules/components/Link.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const Link: FC<Omit<BaseLinkProps, \"router\">> = (props) => {\n const router = useRouter();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { route, previousRoute, routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRoute.tsx\n\nimport { use } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = use(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","// packages/react/modules/components/ConnectedLink.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRoute } from \"../hooks/useRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const ConnectedLink: FC<\n Omit<BaseLinkProps, \"router\" | \"route\" | \"previousRoute\">\n> = (props) => {\n const router = useRouter();\n const { route } = useRoute();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} route={route} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRouteNode.tsx\n\nimport { getNavigator } from \"@real-router/core\";\nimport { useCallback, useMemo } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useRouterSubscription } from \"./useRouterSubscription\";\nimport { getCachedShouldUpdate } from \"../utils\";\n\nimport type { RouteContext, RouteState } from \"../types\";\nimport type { State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Hook that subscribes to a specific route node with optimizations.\n * Provides the current and previous route when the node is affected.\n */\nexport function useRouteNode(nodeName: string): RouteContext {\n // Get router from context with error handling\n const router = useRouter();\n\n // Get cached shouldUpdate function to avoid recreation\n const shouldUpdate = useMemo(\n () => getCachedShouldUpdate(router, nodeName),\n [router, nodeName],\n );\n\n // Stable state factory\n // useRouteNode.tsx\n const stateFactory = useCallback(\n (sub?: SubscribeState): RouteState => {\n const currentRoute = sub?.route ?? router.getState();\n\n // Проверяем, активен ли узел\n if (currentRoute && nodeName !== \"\") {\n // Корневой узел всегда активен\n const isNodeActive =\n currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`);\n\n if (!isNodeActive) {\n return {\n route: undefined,\n previousRoute: sub?.previousRoute,\n };\n }\n }\n\n return {\n route: currentRoute,\n previousRoute: sub?.previousRoute,\n };\n },\n [router, nodeName],\n );\n\n // Subscribe to router with optimization\n const state = useRouterSubscription<RouteState>(\n router,\n stateFactory,\n shouldUpdate as (newRoute: State, prevRoute?: State) => boolean,\n );\n\n // Return memoized context - useMemo ensures stable reference when deps unchanged\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: state.route,\n previousRoute: state.previousRoute,\n }),\n [navigator, state.route, state.previousRoute],\n );\n}\n","// packages/react/modules/hooks/useNavigator.tsx\n\nimport { use } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = use(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","// packages/react/modules/RouterProvider.tsx\n\nimport { getNavigator } from \"@real-router/core\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\n\nimport type { RouteState } from \"./types\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n}) => {\n // Get navigator instance from router\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // Local store state to hold route information\n const store = useMemo(() => {\n let currentState: RouteState = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n // This will be called to return the current state snapshot\n const getSnapshot = () => currentState;\n\n // Subscribe to router updates and notify React when state changes\n const subscribe = (callback: () => void) => {\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n currentState = { route, previousRoute };\n callback(); // Notify React to trigger re-render\n });\n\n // Note: router.subscribe() always returns a function, no need to check\n return unsubscribe;\n };\n\n return { getSnapshot, subscribe };\n }, [router]);\n\n // Using useSyncExternalStore to manage subscription and state updates\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot);\n\n // Memoize RouteContext value to prevent unnecessary re-renders\n const routeContextValue = useMemo(\n () => ({ navigator, ...state }),\n [navigator, state],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useStableValue.tsx","../../src/hooks/useIsActiveRoute.tsx","../../src/utils.ts","../../src/components/BaseLink.tsx","../../src/components/Link.tsx","../../src/hooks/useRoute.tsx","../../src/components/ConnectedLink.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useNavigator.tsx","../../src/RouterProvider.tsx","../../src/hooks/useRouteUtils.tsx"],"names":["createContext","use","useMemo","createActiveRouteSource","useSyncExternalStore","memo","useCallback","jsx","createRouteNodeSource","getNavigator","createRouteSource","getRouteUtils","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/react/modules/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","// packages/react/modules/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/modules/hooks/useRouter.tsx\n\nimport { use } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = use(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useStableValue } from \"./useStableValue\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n): boolean {\n const router = useRouter();\n\n // useStableValue: JSON.stringify memoization of params object.\n // Without it, every render with a new params reference (e.g.,\n // <Link routeParams={{ id: '123' }} />) would recreate the store.\n const stableParams = useStableValue(params);\n\n const store = useMemo(\n () =>\n createActiveRouteSource(router, routeName, stableParams, {\n strict,\n ignoreQueryParams,\n }),\n [router, routeName, stableParams, strict, ignoreQueryParams],\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n}\n","import type { MouseEvent } from \"react\";\n\n/**\n * Check if navigation should be handled by router\n */\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 && // left click\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n","// packages/react/modules/components/BaseLink.tsx\n\nimport { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useStableValue } from \"../hooks/useStableValue\";\nimport { shouldNavigate } from \"../utils\";\n\nimport type { BaseLinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\n/**\n * Optimized BaseLink component with memoization and performance improvements\n */\nexport const BaseLink: FC<BaseLinkProps> = memo(\n ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n router,\n children,\n ...props\n }) => {\n // Stabilize object references to prevent unnecessary re-renders\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n // Use optimized hook for active state checking\n const isActive = useIsActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n // Build URL with memoization\n const href = useMemo(() => {\n // Use buildUrl if available (browser plugin installed)\n // Otherwise fall back to buildPath (e.g., in SSR or without browser plugin)\n if (typeof router.buildUrl === \"function\") {\n return router.buildUrl(routeName, stableParams);\n }\n\n return router.buildPath(routeName, stableParams);\n }, [router, routeName, stableParams]);\n\n // Optimized click handler\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n // Call custom onClick if provided\n if (onClick) {\n onClick(evt);\n // Respect preventDefault from custom handler\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n // Check if we should handle navigation\n if (!shouldNavigate(evt) || target === \"_blank\") {\n return;\n }\n\n // Prevent default link behavior\n evt.preventDefault();\n\n // Perform navigation (fire-and-forget)\n router.navigate(routeName, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n // Build className efficiently\n const finalClassName = useMemo(() => {\n if (isActive && activeClassName) {\n return className\n ? `${className} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return className ?? undefined;\n }, [isActive, className, activeClassName]);\n\n // Filter out previousRoute from props\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { previousRoute, ...restProps } = props;\n\n return (\n <a\n {...restProps}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n data-route={routeName} // For event delegation if needed\n data-active={isActive} // For CSS selectors if needed\n >\n {children}\n </a>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for better memoization\n // Check if props that affect rendering have changed\n return (\n prevProps.router === nextProps.router &&\n prevProps.routeName === nextProps.routeName &&\n JSON.stringify(prevProps.routeParams) ===\n JSON.stringify(nextProps.routeParams) &&\n JSON.stringify(prevProps.routeOptions) ===\n JSON.stringify(nextProps.routeOptions) &&\n prevProps.className === nextProps.className &&\n prevProps.activeClassName === nextProps.activeClassName &&\n prevProps.activeStrict === nextProps.activeStrict &&\n prevProps.ignoreQueryParams === nextProps.ignoreQueryParams &&\n prevProps.onClick === nextProps.onClick &&\n prevProps.target === nextProps.target &&\n prevProps.children === nextProps.children\n );\n },\n);\n\nBaseLink.displayName = \"BaseLink\";\n","// packages/react/modules/components/Link.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const Link: FC<Omit<BaseLinkProps, \"router\">> = (props) => {\n const router = useRouter();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { route, previousRoute, routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRoute.tsx\n\nimport { use } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = use(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","// packages/react/modules/components/ConnectedLink.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRoute } from \"../hooks/useRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const ConnectedLink: FC<\n Omit<BaseLinkProps, \"router\" | \"route\" | \"previousRoute\">\n> = (props) => {\n const router = useRouter();\n const { route } = useRoute();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} route={route} {...linkProps} />;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n}\n","// packages/react/modules/hooks/useNavigator.tsx\n\nimport { use } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = use(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n}) => {\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":225,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouterSubscription.tsx":{"bytes":2370,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useStableValue.tsx":{"bytes":976,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/utils.ts":{"bytes":1483,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useIsActiveRoute.tsx":{"bytes":2692,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouterSubscription.tsx","kind":"import-statement","original":"./useRouterSubscription"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"./useStableValue"},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/components/BaseLink.tsx":{"bytes":4067,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/hooks/useIsActiveRoute.tsx","kind":"import-statement","original":"../hooks/useIsActiveRoute"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"../hooks/useStableValue"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/context.ts":{"bytes":416,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouter.tsx":{"bytes":364,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/components/Link.tsx":{"bytes":511,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRoute.tsx":{"bytes":403,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/components/ConnectedLink.tsx":{"bytes":631,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"../hooks/useRoute"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteNode.tsx":{"bytes":2212,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"src/hooks/useRouterSubscription.tsx","kind":"import-statement","original":"./useRouterSubscription"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useNavigator.tsx":{"bytes":394,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/RouterProvider.tsx":{"bytes":1958,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteUtils.tsx":{"bytes":977,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":719,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./components/BaseLink"},{"path":"src/components/Link.tsx","kind":"import-statement","original":"./components/Link"},{"path":"src/components/ConnectedLink.tsx","kind":"import-statement","original":"./components/ConnectedLink"},{"path":"src/hooks/useRouteNode.tsx","kind":"import-statement","original":"./hooks/useRouteNode"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"./hooks/useRoute"},{"path":"src/hooks/useNavigator.tsx","kind":"import-statement","original":"./hooks/useNavigator"},{"path":"src/RouterProvider.tsx","kind":"import-statement","original":"./RouterProvider"},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./hooks/useRouter"},{"path":"src/hooks/useRouteUtils.tsx","kind":"import-statement","original":"./hooks/useRouteUtils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":28546},"dist/cjs/index.js":{"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["BaseLink","ConnectedLink","Link","NavigatorContext","RouteContext","RouterContext","RouterProvider","useNavigator","useRoute","useRouteNode","useRouteUtils","useRouter"],"entryPoint":"src/index.ts","inputs":{"src/components/BaseLink.tsx":{"bytesInOutput":2638},"src/constants.ts":{"bytesInOutput":77},"src/hooks/useIsActiveRoute.tsx":{"bytesInOutput":1736},"src/hooks/useRouterSubscription.tsx":{"bytesInOutput":1386},"src/hooks/useStableValue.tsx":{"bytesInOutput":157},"src/utils.ts":{"bytesInOutput":816},"src/index.ts":{"bytesInOutput":0},"src/hooks/useRouter.tsx":{"bytesInOutput":202},"src/context.ts":{"bytesInOutput":164},"src/components/Link.tsx":{"bytesInOutput":243},"src/hooks/useRoute.tsx":{"bytesInOutput":225},"src/components/ConnectedLink.tsx":{"bytesInOutput":269},"src/hooks/useRouteNode.tsx":{"bytesInOutput":1188},"src/hooks/useNavigator.tsx":{"bytesInOutput":229},"src/RouterProvider.tsx":{"bytesInOutput":1183},"src/hooks/useRouteUtils.tsx":{"bytesInOutput":225}},"bytes":11463}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":225,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/context.ts":{"bytes":416,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouter.tsx":{"bytes":364,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useStableValue.tsx":{"bytes":976,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useIsActiveRoute.tsx":{"bytes":1060,"imports":[{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"./useStableValue"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/utils.ts":{"bytes":293,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/components/BaseLink.tsx":{"bytes":4053,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/hooks/useIsActiveRoute.tsx","kind":"import-statement","original":"../hooks/useIsActiveRoute"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"../hooks/useStableValue"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/components/Link.tsx":{"bytes":511,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRoute.tsx":{"bytes":403,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/components/ConnectedLink.tsx":{"bytes":631,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"../hooks/useRoute"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteNode.tsx":{"bytes":837,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useNavigator.tsx":{"bytes":394,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/RouterProvider.tsx":{"bytes":1380,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteUtils.tsx":{"bytes":977,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":719,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./components/BaseLink"},{"path":"src/components/Link.tsx","kind":"import-statement","original":"./components/Link"},{"path":"src/components/ConnectedLink.tsx","kind":"import-statement","original":"./components/ConnectedLink"},{"path":"src/hooks/useRouteNode.tsx","kind":"import-statement","original":"./hooks/useRouteNode"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"./hooks/useRoute"},{"path":"src/hooks/useNavigator.tsx","kind":"import-statement","original":"./hooks/useNavigator"},{"path":"src/RouterProvider.tsx","kind":"import-statement","original":"./RouterProvider"},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./hooks/useRouter"},{"path":"src/hooks/useRouteUtils.tsx","kind":"import-statement","original":"./hooks/useRouteUtils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":18254},"dist/cjs/index.js":{"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["BaseLink","ConnectedLink","Link","NavigatorContext","RouteContext","RouterContext","RouterProvider","useNavigator","useRoute","useRouteNode","useRouteUtils","useRouter"],"entryPoint":"src/index.ts","inputs":{"src/components/BaseLink.tsx":{"bytesInOutput":2607},"src/constants.ts":{"bytesInOutput":77},"src/hooks/useIsActiveRoute.tsx":{"bytesInOutput":671},"src/hooks/useRouter.tsx":{"bytesInOutput":202},"src/context.ts":{"bytesInOutput":164},"src/hooks/useStableValue.tsx":{"bytesInOutput":157},"src/utils.ts":{"bytesInOutput":140},"src/index.ts":{"bytesInOutput":0},"src/components/Link.tsx":{"bytesInOutput":243},"src/hooks/useRoute.tsx":{"bytesInOutput":225},"src/components/ConnectedLink.tsx":{"bytesInOutput":269},"src/hooks/useRouteNode.tsx":{"bytesInOutput":742},"src/hooks/useNavigator.tsx":{"bytesInOutput":229},"src/RouterProvider.tsx":{"bytesInOutput":1001},"src/hooks/useRouteUtils.tsx":{"bytesInOutput":225}},"bytes":7637}}}
@@ -56,10 +56,6 @@ declare const Link: FC<Omit<BaseLinkProps, "router">>;
56
56
 
57
57
  declare const ConnectedLink: FC<Omit<BaseLinkProps, "router" | "route" | "previousRoute">>;
58
58
 
59
- /**
60
- * Hook that subscribes to a specific route node with optimizations.
61
- * Provides the current and previous route when the node is affected.
62
- */
63
59
  declare function useRouteNode(nodeName: string): RouteContext$1;
64
60
 
65
61
  declare const useRoute: () => RouteContext$1;
@@ -1 +1 @@
1
- import{memo as e,useMemo as r,useCallback as t,createContext as u,useRef as o,useSyncExternalStore as n,use as i}from"react";import{areRoutesRelated as a,getRouteUtils as s}from"@real-router/route-utils";import{jsx as c}from"react/jsx-runtime";import{getNavigator as v,getPluginApi as l}from"@real-router/core";var m=Object.freeze({}),d=Object.freeze({});function f(e,r,u){const i=o(void 0),a=o(r),s=o(u);if(a.current=r,s.current=u,void 0===i.current){const r=e.getState(),t=!s.current||r&&s.current(r);i.current=a.current(t&&r?{route:r,previousRoute:void 0}:void 0)}const c=t(()=>i.current,[]),v=t(r=>e.subscribe(e=>{let t=!0;if(s.current&&(t=s.current(e.route,e.previousRoute)),!t)return;const u=a.current(e);Object.is(i.current,u)||(i.current=u,r())}),[e]);return n(v,c,c)}function p(e){const t=JSON.stringify(e);return r(()=>e,[t])}var g=new WeakMap;var h=e(({routeName:e,routeParams:u=m,routeOptions:n=d,className:i,activeClassName:s="active",activeStrict:v=!1,ignoreQueryParams:l=!0,onClick:g,target:h,router:N,children:R,...b})=>{const y=p(u),S=p(n),O=function(e,u,n=m,i=!1,s=!0){const c=p(n),v=r(()=>function(e,r,t,u){return JSON.stringify({routeName:e,routeParams:r,activeStrict:t,ignoreQueryParams:u})}(u,c,i,s),[u,c,i,s]),l=o(void 0),d=o(void 0);d.current!==v&&(l.current=void 0,d.current=v);const g=t((e,r)=>{const t=a(u,e.name),o=r&&a(u,r.name);return!(!t&&!o)},[u]),h=t(r=>{const t=r?.route??e.getState();if(!t)return l.current=!1,!1;if(!a(u,t.name))return l.current=!1,!1;const o=e.isActiveRoute(u,c,i,s);return l.current=o,o},[e,u,c,i,s]);return f(e,h,g)}(N,e,y,v,l),P=r(()=>"function"==typeof N.buildUrl?N.buildUrl(e,y):N.buildPath(e,y),[N,e,y]),w=t(r=>{g&&(g(r),r.defaultPrevented)||function(e){return!(0!==e.button||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(r)&&"_blank"!==h&&(r.preventDefault(),N.navigate(e,y,S).catch(()=>{}))},[g,h,N,e,y,S]),k=r(()=>O&&s?i?`${i} ${s}`.trim():s:i??void 0,[O,i,s]),{previousRoute:C,...J}=b;return c("a",{...J,href:P,className:k,onClick:w,"data-route":e,"data-active":O,children:R})},(e,r)=>e.router===r.router&&e.routeName===r.routeName&&JSON.stringify(e.routeParams)===JSON.stringify(r.routeParams)&&JSON.stringify(e.routeOptions)===JSON.stringify(r.routeOptions)&&e.className===r.className&&e.activeClassName===r.activeClassName&&e.activeStrict===r.activeStrict&&e.ignoreQueryParams===r.ignoreQueryParams&&e.onClick===r.onClick&&e.target===r.target&&e.children===r.children);h.displayName="BaseLink";var N=u(null),R=u(null),b=u(null),y=()=>{const e=i(R);if(!e)throw new Error("useRouter must be used within a RouterProvider");return e},S=e=>{const r=y(),{route:t,previousRoute:u,routeOptions:o,...n}=e;return c(h,{router:r,...n})},O=()=>{const e=i(N);if(!e)throw new Error("useRoute must be used within a RouteProvider");return e},P=e=>{const r=y(),{route:t}=O(),{routeOptions:u,...o}=e;return c(h,{router:r,route:t,...o})};function w(e){const u=y(),o=r(()=>function(e,r){let t=g.get(e);t||(t=new Map,g.set(e,t));let u=t.get(r);if(!u){u=e.shouldUpdateNode(r);const o=u;u=(e,r)=>o(e,r),t.set(r,u)}return u}(u,e),[u,e]),n=t(r=>{const t=r?.route??u.getState();return t&&""!==e&&t.name!==e&&!t.name.startsWith(`${e}.`)?{route:void 0,previousRoute:r?.previousRoute}:{route:t,previousRoute:r?.previousRoute}},[u,e]),i=f(u,n,o),a=r(()=>v(u),[u]);return r(()=>({navigator:a,route:i.route,previousRoute:i.previousRoute}),[a,i.route,i.previousRoute])}var k=()=>{const e=i(b);if(!e)throw new Error("useNavigator must be used within a RouterProvider");return e},C=({router:e,children:t})=>{const u=r(()=>v(e),[e]),o=r(()=>{let r={route:e.getState(),previousRoute:void 0};return{getSnapshot:()=>r,subscribe:t=>e.subscribe(({route:e,previousRoute:u})=>{r={route:e,previousRoute:u},t()})}},[e]),i=n(o.subscribe,o.getSnapshot),a=r(()=>({navigator:u,...i}),[u,i]);return c(R,{value:e,children:c(b,{value:u,children:c(N,{value:a,children:t})})})},J=()=>{const e=y();return s(l(e).getTree())};export{h as BaseLink,P as ConnectedLink,S as Link,b as NavigatorContext,N as RouteContext,R as RouterContext,C as RouterProvider,k as useNavigator,O as useRoute,w as useRouteNode,J as useRouteUtils,y as useRouter};//# sourceMappingURL=index.mjs.map
1
+ import{createContext as r,memo as e,useMemo as t,useCallback as o,useSyncExternalStore as u,use as i}from"react";import{createActiveRouteSource as a,createRouteNodeSource as n,createRouteSource as s}from"@real-router/sources";import{jsx as c}from"react/jsx-runtime";import{getNavigator as l,getPluginApi as m}from"@real-router/core";import{getRouteUtils as v}from"@real-router/route-utils";var p=Object.freeze({}),f=Object.freeze({}),h=r(null),d=r(null),g=r(null),N=()=>{const r=i(d);if(!r)throw new Error("useRouter must be used within a RouterProvider");return r};function b(r){const e=JSON.stringify(r);return t(()=>r,[e])}var y=e(({routeName:r,routeParams:e=p,routeOptions:i=f,className:n,activeClassName:s="active",activeStrict:l=!1,ignoreQueryParams:m=!0,onClick:v,target:h,router:d,children:g,...y})=>{const S=b(e),O=b(i),P=function(r,e,o=!1,i=!0){const n=N(),s=b(e),c=t(()=>a(n,r,s,{strict:o,ignoreQueryParams:i}),[n,r,s,o,i]);return u(c.subscribe,c.getSnapshot,c.getSnapshot)}(r,S,l,m),R=t(()=>"function"==typeof d.buildUrl?d.buildUrl(r,S):d.buildPath(r,S),[d,r,S]),w=o(e=>{v&&(v(e),e.defaultPrevented)||function(r){return!(0!==r.button||r.metaKey||r.altKey||r.ctrlKey||r.shiftKey)}(e)&&"_blank"!==h&&(e.preventDefault(),d.navigate(r,S,O).catch(()=>{}))},[v,h,d,r,S,O]),C=t(()=>P&&s?n?`${n} ${s}`.trim():s:n??void 0,[P,n,s]),{previousRoute:k,...J}=y;return c("a",{...J,href:R,className:C,onClick:w,"data-route":r,"data-active":P,children:g})},(r,e)=>r.router===e.router&&r.routeName===e.routeName&&JSON.stringify(r.routeParams)===JSON.stringify(e.routeParams)&&JSON.stringify(r.routeOptions)===JSON.stringify(e.routeOptions)&&r.className===e.className&&r.activeClassName===e.activeClassName&&r.activeStrict===e.activeStrict&&r.ignoreQueryParams===e.ignoreQueryParams&&r.onClick===e.onClick&&r.target===e.target&&r.children===e.children);y.displayName="BaseLink";var S=r=>{const e=N(),{route:t,previousRoute:o,routeOptions:u,...i}=r;return c(y,{router:e,...i})},O=()=>{const r=i(h);if(!r)throw new Error("useRoute must be used within a RouteProvider");return r},P=r=>{const e=N(),{route:t}=O(),{routeOptions:o,...u}=r;return c(y,{router:e,route:t,...u})};function R(r){const e=N(),o=t(()=>n(e,r),[e,r]),{route:i,previousRoute:a}=u(o.subscribe,o.getSnapshot,o.getSnapshot),s=t(()=>l(e),[e]);return t(()=>({navigator:s,route:i,previousRoute:a}),[s,i,a])}var w=()=>{const r=i(g);if(!r)throw new Error("useNavigator must be used within a RouterProvider");return r},C=({router:r,children:e})=>{const o=t(()=>l(r),[r]),i=t(()=>s(r),[r]),{route:a,previousRoute:n}=u(i.subscribe,i.getSnapshot,i.getSnapshot),m=t(()=>({navigator:o,route:a,previousRoute:n}),[o,a,n]);return c(d,{value:r,children:c(g,{value:o,children:c(h,{value:m,children:e})})})},k=()=>{const r=N();return v(m(r).getTree())};export{y as BaseLink,P as ConnectedLink,S as Link,g as NavigatorContext,h as RouteContext,d as RouterContext,C as RouterProvider,w as useNavigator,O as useRoute,R as useRouteNode,k as useRouteUtils,N as useRouter};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/hooks/useRouterSubscription.tsx","../../src/hooks/useStableValue.tsx","../../src/utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/BaseLink.tsx","../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/components/Link.tsx","../../src/hooks/useRoute.tsx","../../src/components/ConnectedLink.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useNavigator.tsx","../../src/RouterProvider.tsx","../../src/hooks/useRouteUtils.tsx"],"names":["useMemo","useRef","useCallback","jsx","use","getNavigator","useSyncExternalStore"],"mappings":";;;AAKO,IAAM,YAAA,GAAe,MAAA,CAAO,MAAA,CAAO,EAAE,CAAA;AAKrC,IAAM,aAAA,GAAgB,MAAA,CAAO,MAAA,CAAO,EAAE,CAAA;ACGtC,SAAS,qBAAA,CACd,MAAA,EACA,QAAA,EACA,YAAA,EACG;AAEH,EAAA,MAAM,QAAA,GAAW,OAAsB,MAAS,CAAA;AAChD,EAAA,MAAM,WAAA,GAAc,OAAO,QAAQ,CAAA;AACnC,EAAA,MAAM,eAAA,GAAkB,OAAO,YAAY,CAAA;AAG3C,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,EAAA,eAAA,CAAgB,OAAA,GAAU,YAAA;AAG1B,EAAA,IAAI,QAAA,CAAS,YAAY,MAAA,EAAW;AAElC,IAAA,MAAM,YAAA,GAAe,OAAO,QAAA,EAAS;AAGrC,IAAA,MAAM,mBACJ,CAAC,eAAA,CAAgB,WAChB,YAAA,IAAgB,eAAA,CAAgB,QAAQ,YAAY,CAAA;AAEvD,IAAA,QAAA,CAAS,UAAU,WAAA,CAAY,OAAA;AAAA,MAC7B,oBAAoB,YAAA,GAChB,EAAE,OAAO,YAAA,EAAc,aAAA,EAAe,QAAU,GAChD;AAAA,KACN;AAAA,EACF;AAGA,EAAA,MAAM,cAAc,WAAA,CAAY,MAAM,QAAA,CAAS,OAAA,EAAc,EAAE,CAAA;AAG/D,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,aAAA,KAA8B;AAC7B,MAAA,OAAO,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAEhC,QAAA,IAAI,aAAA,GAAgB,IAAA;AAEpB,QAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,UAAA,aAAA,GAAgB,eAAA,CAAgB,OAAA;AAAA,YAC9B,IAAA,CAAK,KAAA;AAAA,YACL,IAAA,CAAK;AAAA,WACP;AAAA,QACF;AAEA,QAAA,IAAI,CAAC,aAAA,EAAe;AAClB,UAAA;AAAA,QACF;AAGA,QAAA,MAAM,QAAA,GAAW,WAAA,CAAY,OAAA,CAAQ,IAAI,CAAA;AAGzC,QAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,QAAA,CAAS,OAAA,EAAS,QAAQ,CAAA,EAAG;AAC1C,UAAA,QAAA,CAAS,OAAA,GAAU,QAAA;AAEnB,UAAA,aAAA,EAAc;AAAA,QAChB;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,WAAW,CAAA;AACjE;AC1DO,SAAS,eAAkB,KAAA,EAAa;AAC7C,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAIvC,EAAA,OAAO,OAAA,CAAQ,MAAM,KAAA,EAAO,CAAC,UAAU,CAAC,CAAA;AAC1C;;;ACpBO,IAAM,iBAAA,uBAAwB,OAAA,EAGnC;AAKK,SAAS,qBAAA,CACd,QACA,QAAA,EACgD;AAChD,EAAA,IAAI,KAAA,GAAQ,iBAAA,CAAkB,GAAA,CAAI,MAAM,CAAA;AAExC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,uBAAY,GAAA,EAAI;AAChB,IAAA,iBAAA,CAAkB,GAAA,CAAI,QAAQ,KAAK,CAAA;AAAA,EACrC;AAEA,EAAA,IAAI,EAAA,GAAK,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA;AAE3B,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,EAAA,GAAK,MAAA,CAAO,iBAAiB,QAAQ,CAAA;AAErC,IAAA,MAAM,UAAA,GAAa,EAAA;AAEnB,IAAA,EAAA,GAAK,CAAC,OAAA,EAAgB,SAAA,KAAsB,UAAA,CAAW,SAAS,SAAS,CAAA;AAEzE,IAAA,KAAA,CAAM,GAAA,CAAI,UAAU,EAAE,CAAA;AAAA,EACxB;AAEA,EAAA,OAAO,EAAA;AACT;AAKO,SAAS,eAAe,GAAA,EAA0B;AACvD,EAAA,OACE,IAAI,MAAA,KAAW,CAAA;AAAA,EACf,CAAC,GAAA,CAAI,OAAA,IACL,CAAC,GAAA,CAAI,UACL,CAAC,GAAA,CAAI,OAAA,IACL,CAAC,GAAA,CAAI,QAAA;AAET;AAKO,SAAS,oBAAA,CACd,SAAA,EACA,WAAA,EACA,YAAA,EACA,iBAAA,EACQ;AACR,EAAA,OAAO,KAAK,SAAA,CAAU;AAAA,IACpB,SAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;ACtDO,SAAS,gBAAA,CACd,QACA,SAAA,EACA,WAAA,GAAsB,cACtB,YAAA,GAAe,KAAA,EACf,oBAAoB,IAAA,EACX;AAET,EAAA,MAAM,YAAA,GAAe,eAAe,WAAW,CAAA;AAG/C,EAAA,MAAM,QAAA,GAAWA,OAAAA;AAAA,IACf,MACE,oBAAA;AAAA,MACE,SAAA;AAAA,MACA,YAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACF;AAAA,IACF,CAAC,SAAA,EAAW,YAAA,EAAc,YAAA,EAAc,iBAAiB;AAAA,GAC3D;AAGA,EAAA,MAAM,WAAA,GAAcC,OAA4B,MAAS,CAAA;AACzD,EAAA,MAAM,eAAA,GAAkBA,OAA2B,MAAS,CAAA;AAE5D,EAAA,IAAI,eAAA,CAAgB,YAAY,QAAA,EAAU;AACxC,IAAA,WAAA,CAAY,OAAA,GAAU,MAAA;AACtB,IAAA,eAAA,CAAgB,OAAA,GAAU,QAAA;AAAA,EAC5B;AAGA,EAAA,MAAM,YAAA,GAAeC,WAAAA;AAAA,IACnB,CAAC,UAAiB,SAAA,KAAsB;AACtC,MAAA,MAAM,YAAA,GAAe,gBAAA,CAAiB,SAAA,EAAW,QAAA,CAAS,IAAI,CAAA;AAC9D,MAAA,MAAM,aAAA,GACJ,SAAA,IAAa,gBAAA,CAAiB,SAAA,EAAW,UAAU,IAAI,CAAA;AAEzD,MAAA,OAAO,CAAC,EAAE,YAAA,IAAgB,aAAA,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAGA,EAAA,MAAM,QAAA,GAAWA,WAAAA;AAAA,IACf,CAAC,GAAA,KAAkC;AACjC,MAAA,MAAM,YAAA,GAAe,GAAA,EAAK,KAAA,IAAS,MAAA,CAAO,QAAA,EAAS;AAGnD,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AAEtB,QAAA,OAAO,KAAA;AAAA,MACT;AAGA,MAAA,IAAI,CAAC,gBAAA,CAAiB,SAAA,EAAW,YAAA,CAAa,IAAI,CAAA,EAAG;AACnD,QAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AAEtB,QAAA,OAAO,KAAA;AAAA,MACT;AAGA,MAAA,MAAM,WAAW,MAAA,CAAO,aAAA;AAAA,QACtB,SAAA;AAAA,QACA,YAAA;AAAA,QACA,YAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAEtB,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,SAAA,EAAW,YAAA,EAAc,cAAc,iBAAiB;AAAA,GACnE;AAEA,EAAA,OAAO,qBAAA,CAAsB,MAAA,EAAQ,QAAA,EAAU,YAAY,CAAA;AAC7D;AC/EO,IAAM,QAAA,GAA8B,IAAA;AAAA,EACzC,CAAC;AAAA,IACC,SAAA;AAAA,IACA,WAAA,GAAc,YAAA;AAAA,IACd,YAAA,GAAe,aAAA;AAAA,IACf,SAAA;AAAA,IACA,eAAA,GAAkB,QAAA;AAAA,IAClB,YAAA,GAAe,KAAA;AAAA,IACf,iBAAA,GAAoB,IAAA;AAAA,IACpB,OAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG;AAAA,GACL,KAAM;AAEJ,IAAA,MAAM,YAAA,GAAe,eAAe,WAAW,CAAA;AAC/C,IAAA,MAAM,aAAA,GAAgB,eAAe,YAAY,CAAA;AAGjD,IAAA,MAAM,QAAA,GAAW,gBAAA;AAAA,MACf,MAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACF;AAGA,IAAA,MAAM,IAAA,GAAOF,QAAQ,MAAM;AAGzB,MAAA,IAAI,OAAO,MAAA,CAAO,QAAA,KAAa,UAAA,EAAY;AACzC,QAAA,OAAO,MAAA,CAAO,QAAA,CAAS,SAAA,EAAW,YAAY,CAAA;AAAA,MAChD;AAEA,MAAA,OAAO,MAAA,CAAO,SAAA,CAAU,SAAA,EAAW,YAAY,CAAA;AAAA,IACjD,CAAA,EAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,YAAY,CAAC,CAAA;AAGpC,IAAA,MAAM,WAAA,GAAcE,WAAAA;AAAA,MAClB,CAAC,GAAA,KAAuC;AAEtC,QAAA,IAAI,OAAA,EAAS;AACX,UAAA,OAAA,CAAQ,GAAG,CAAA;AAEX,UAAA,IAAI,IAAI,gBAAA,EAAkB;AACxB,YAAA;AAAA,UACF;AAAA,QACF;AAGA,QAAA,IAAI,CAAC,cAAA,CAAe,GAAG,CAAA,IAAK,WAAW,QAAA,EAAU;AAC/C,UAAA;AAAA,QACF;AAGA,QAAA,GAAA,CAAI,cAAA,EAAe;AAGnB,QAAA,MAAA,CAAO,SAAS,SAAA,EAAW,YAAA,EAAc,aAAa,CAAA,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACxE,CAAA;AAAA,MACA,CAAC,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,cAAc,aAAa;AAAA,KAClE;AAGA,IAAA,MAAM,cAAA,GAAiBF,QAAQ,MAAM;AACnC,MAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,QAAA,OAAO,YACH,CAAA,EAAG,SAAS,IAAI,eAAe,CAAA,CAAA,CAAG,MAAK,GACvC,eAAA;AAAA,MACN;AAEA,MAAA,OAAO,SAAA,IAAa,MAAA;AAAA,IACtB,CAAA,EAAG,CAAC,QAAA,EAAU,SAAA,EAAW,eAAe,CAAC,CAAA;AAIzC,IAAA,MAAM,EAAE,aAAA,EAAe,GAAG,SAAA,EAAU,GAAI,KAAA;AAExC,IAAA,uBACE,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACE,GAAG,SAAA;AAAA,QACJ,IAAA;AAAA,QACA,SAAA,EAAW,cAAA;AAAA,QACX,OAAA,EAAS,WAAA;AAAA,QACT,YAAA,EAAY,SAAA;AAAA,QACZ,aAAA,EAAa,QAAA;AAAA,QAEZ;AAAA;AAAA,KACH;AAAA,EAEJ,CAAA;AAAA,EACA,CAAC,WAAW,SAAA,KAAc;AAGxB,IAAA,OACE,SAAA,CAAU,MAAA,KAAW,SAAA,CAAU,MAAA,IAC/B,SAAA,CAAU,cAAc,SAAA,CAAU,SAAA,IAClC,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,WAAW,MAClC,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,WAAW,CAAA,IACtC,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,YAAY,CAAA,KACnC,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,YAAY,KACvC,SAAA,CAAU,SAAA,KAAc,SAAA,CAAU,SAAA,IAClC,SAAA,CAAU,eAAA,KAAoB,UAAU,eAAA,IACxC,SAAA,CAAU,YAAA,KAAiB,SAAA,CAAU,YAAA,IACrC,SAAA,CAAU,sBAAsB,SAAA,CAAU,iBAAA,IAC1C,SAAA,CAAU,OAAA,KAAY,SAAA,CAAU,OAAA,IAChC,SAAA,CAAU,MAAA,KAAW,SAAA,CAAU,MAAA,IAC/B,SAAA,CAAU,QAAA,KAAa,SAAA,CAAU,QAAA;AAAA,EAErC;AACF;AAEA,QAAA,CAAS,WAAA,GAAc,UAAA;AC1HhB,IAAM,YAAA,GAAe,cAAuC,IAAI;AAEhE,IAAM,aAAA,GAAgB,cAA6B,IAAI;AAEvD,IAAM,gBAAA,GAAmB,cAAgC,IAAI;;;ACH7D,IAAM,YAAY,MAAc;AACrC,EAAA,MAAM,MAAA,GAAS,IAAI,aAAa,CAAA;AAEhC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,MAAA;AACT;ACRO,IAAM,IAAA,GAA0C,CAAC,KAAA,KAAU;AAChE,EAAA,MAAM,SAAS,SAAA,EAAU;AAGzB,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,YAAA,EAAc,GAAG,WAAU,GAAI,KAAA;AAE7D,EAAA,uBAAOG,GAAAA,CAAC,QAAA,EAAA,EAAS,MAAA,EAAiB,GAAG,SAAA,EAAW,CAAA;AAClD;ACPO,IAAM,WAAW,MAAwB;AAC9C,EAAA,MAAM,YAAA,GAAeC,IAAI,YAAY,CAAA;AAErC,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAChE;AAEA,EAAA,OAAO,YAAA;AACT;ACPO,IAAM,aAAA,GAET,CAAC,KAAA,KAAU;AACb,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,QAAA,EAAS;AAG3B,EAAA,MAAM,EAAE,YAAA,EAAc,GAAG,SAAA,EAAU,GAAI,KAAA;AAEvC,EAAA,uBAAOD,GAAAA,CAAC,QAAA,EAAA,EAAS,MAAA,EAAgB,KAAA,EAAe,GAAG,SAAA,EAAW,CAAA;AAChE;ACHO,SAAS,aAAa,QAAA,EAAgC;AAE3D,EAAA,MAAM,SAAS,SAAA,EAAU;AAGzB,EAAA,MAAM,YAAA,GAAeH,OAAAA;AAAA,IACnB,MAAM,qBAAA,CAAsB,MAAA,EAAQ,QAAQ,CAAA;AAAA,IAC5C,CAAC,QAAQ,QAAQ;AAAA,GACnB;AAIA,EAAA,MAAM,YAAA,GAAeE,WAAAA;AAAA,IACnB,CAAC,GAAA,KAAqC;AACpC,MAAA,MAAM,YAAA,GAAe,GAAA,EAAK,KAAA,IAAS,MAAA,CAAO,QAAA,EAAS;AAGnD,MAAA,IAAI,YAAA,IAAgB,aAAa,EAAA,EAAI;AAEnC,QAAA,MAAM,YAAA,GACJ,aAAa,IAAA,KAAS,QAAA,IACtB,aAAa,IAAA,CAAK,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA;AAE7C,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,OAAO;AAAA,YACL,KAAA,EAAO,MAAA;AAAA,YACP,eAAe,GAAA,EAAK;AAAA,WACtB;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,YAAA;AAAA,QACP,eAAe,GAAA,EAAK;AAAA,OACtB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,GACnB;AAGA,EAAA,MAAM,KAAA,GAAQ,qBAAA;AAAA,IACZ,MAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,MAAM,SAAA,GAAYF,QAAQ,MAAM,YAAA,CAAa,MAAM,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAE9D,EAAA,OAAOA,OAAAA;AAAA,IACL,OAAqB;AAAA,MACnB,SAAA;AAAA,MACA,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,eAAe,KAAA,CAAM;AAAA,KACvB,CAAA;AAAA,IACA,CAAC,SAAA,EAAW,KAAA,CAAM,KAAA,EAAO,MAAM,aAAa;AAAA,GAC9C;AACF;ACjEO,IAAM,eAAe,MAAiB;AAC3C,EAAA,MAAM,SAAA,GAAYI,IAAI,gBAAgB,CAAA;AAEtC,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,EACrE;AAEA,EAAA,OAAO,SAAA;AACT;ACAO,IAAM,iBAAyC,CAAC;AAAA,EACrD,MAAA;AAAA,EACA;AACF,CAAA,KAAM;AAEJ,EAAA,MAAM,SAAA,GAAYJ,QAAQ,MAAMK,YAAAA,CAAa,MAAM,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAG9D,EAAA,MAAM,KAAA,GAAQL,QAAQ,MAAM;AAC1B,IAAA,IAAI,YAAA,GAA2B;AAAA,MAC7B,KAAA,EAAO,OAAO,QAAA,EAAS;AAAA,MACvB,aAAA,EAAe;AAAA,KACjB;AAGA,IAAA,MAAM,cAAc,MAAM,YAAA;AAG1B,IAAA,MAAM,SAAA,GAAY,CAAC,QAAA,KAAyB;AAC1C,MAAA,MAAM,cAAc,MAAA,CAAO,SAAA,CAAU,CAAC,EAAE,KAAA,EAAO,eAAc,KAAM;AACjE,QAAA,YAAA,GAAe,EAAE,OAAO,aAAA,EAAc;AACtC,QAAA,QAAA,EAAS;AAAA,MACX,CAAC,CAAA;AAGD,MAAA,OAAO,WAAA;AAAA,IACT,CAAA;AAEA,IAAA,OAAO,EAAE,aAAa,SAAA,EAAU;AAAA,EAClC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,MAAM,KAAA,GAAQM,oBAAAA,CAAqB,KAAA,CAAM,SAAA,EAAW,MAAM,WAAW,CAAA;AAGrE,EAAA,MAAM,iBAAA,GAAoBN,OAAAA;AAAA,IACxB,OAAO,EAAE,SAAA,EAAW,GAAG,KAAA,EAAM,CAAA;AAAA,IAC7B,CAAC,WAAW,KAAK;AAAA,GACnB;AAEA,EAAA,uBACEG,GAAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,MAAA,EACpB,0BAAAA,GAAAA,CAAC,gBAAA,EAAA,EAAiB,KAAA,EAAO,SAAA,EACvB,0BAAAA,GAAAA,CAAC,YAAA,EAAA,EAAa,OAAO,iBAAA,EAAoB,QAAA,EAAS,GACpD,CAAA,EACF,CAAA;AAEJ;AChCO,IAAM,gBAAgB,MAAkB;AAC7C,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,OAAO,aAAA,CAAc,YAAA,CAAa,MAAM,CAAA,CAAE,SAAS,CAAA;AACrD","file":"index.mjs","sourcesContent":["// packages/react/modules/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","// packages/react/modules/hooks/useRouterSubscription.tsx\n\nimport { useCallback, useRef, useSyncExternalStore } from \"react\";\n\nimport type { Router, State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Generic hook for subscribing to router changes with optimization.\n *\n * @param router - Real Router instance\n * @param selector - Function to derive state from router subscription\n * @param shouldUpdate - Optional predicate to filter updates\n */\nexport function useRouterSubscription<T>(\n router: Router,\n selector: (sub?: SubscribeState) => T,\n shouldUpdate?: (newRoute: State, prevRoute?: State) => boolean,\n): T {\n // Store current value\n const stateRef = useRef<T | undefined>(undefined);\n const selectorRef = useRef(selector);\n const shouldUpdateRef = useRef(shouldUpdate);\n\n // Update refs to avoid stale closures\n selectorRef.current = selector;\n shouldUpdateRef.current = shouldUpdate;\n\n // Lazy initialization\n if (stateRef.current === undefined) {\n // Get initial state from router\n const currentState = router.getState();\n\n // Check if initial state is relevant for this subscription\n const shouldInitialize =\n !shouldUpdateRef.current ||\n (currentState && shouldUpdateRef.current(currentState));\n\n stateRef.current = selectorRef.current(\n shouldInitialize && currentState\n ? { route: currentState, previousRoute: undefined }\n : undefined,\n );\n }\n\n // Stable snapshot getter\n const getSnapshot = useCallback(() => stateRef.current as T, []);\n\n // Subscribe function with optimization\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return router.subscribe((next) => {\n // Check if we should process this update\n let shouldProcess = true;\n\n if (shouldUpdateRef.current) {\n shouldProcess = shouldUpdateRef.current(\n next.route,\n next.previousRoute,\n );\n }\n\n if (!shouldProcess) {\n return;\n }\n\n // Calculate new value\n const newValue = selectorRef.current(next);\n\n // Only trigger update if value actually changed\n if (!Object.is(stateRef.current, newValue)) {\n stateRef.current = newValue;\n\n onStoreChange();\n }\n });\n },\n [router],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","// packages/react/modules/utils.ts\n\nimport type { Params, Router, State } from \"@real-router/core\";\nimport type { MouseEvent } from \"react\";\n\n/**\n * Cache for shouldUpdateNode functions to avoid recreating them\n */\nexport const shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\n/**\n * Get cached shouldUpdateNode function for a router and nodeName\n */\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let cache = shouldUpdateCache.get(router);\n\n if (!cache) {\n cache = new Map();\n shouldUpdateCache.set(router, cache);\n }\n\n let fn = cache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n\n const originalFn = fn;\n\n fn = (toState: State, fromState?: State) => originalFn(toState, fromState);\n\n cache.set(nodeName, fn);\n }\n\n return fn;\n}\n\n/**\n * Check if navigation should be handled by router\n */\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 && // left click\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\n/**\n * Create cache key for route active check\n */\nexport function createActiveCheckKey(\n routeName: string,\n routeParams: Params,\n activeStrict: boolean,\n ignoreQueryParams: boolean,\n): string {\n return JSON.stringify({\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n });\n}\n","// packages/react/modules/hooks/useIsActiveRoute.tsx\n\nimport { areRoutesRelated } from \"@real-router/route-utils\";\nimport { useCallback, useMemo, useRef } from \"react\";\n\nimport { useRouterSubscription } from \"./useRouterSubscription\";\nimport { useStableValue } from \"./useStableValue\";\nimport { EMPTY_PARAMS } from \"../constants\";\nimport { createActiveCheckKey } from \"../utils\";\n\nimport type { Params, Router, State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Optimized hook to check if a route is active.\n * Minimizes unnecessary recalculations and re-renders.\n */\nexport function useIsActiveRoute(\n router: Router,\n routeName: string,\n routeParams: Params = EMPTY_PARAMS,\n activeStrict = false,\n ignoreQueryParams = true,\n): boolean {\n // Stabilize params reference to prevent unnecessary recalculations\n const stableParams = useStableValue(routeParams);\n\n // Create stable cache key\n const cacheKey = useMemo(\n () =>\n createActiveCheckKey(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n ),\n [routeName, stableParams, activeStrict, ignoreQueryParams],\n );\n\n // Cache the active state\n const isActiveRef = useRef<boolean | undefined>(undefined);\n const lastCacheKeyRef = useRef<string | undefined>(undefined);\n\n if (lastCacheKeyRef.current !== cacheKey) {\n isActiveRef.current = undefined;\n lastCacheKeyRef.current = cacheKey;\n }\n\n // Optimize shouldUpdate to skip unrelated routes\n const shouldUpdate = useCallback(\n (newRoute: State, prevRoute?: State) => {\n const isNewRelated = areRoutesRelated(routeName, newRoute.name);\n const isPrevRelated =\n prevRoute && areRoutesRelated(routeName, prevRoute.name);\n\n return !!(isNewRelated || isPrevRelated);\n },\n [routeName],\n );\n\n // Selector that performs active check\n const selector = useCallback(\n (sub?: SubscribeState): boolean => {\n const currentRoute = sub?.route ?? router.getState();\n\n // Fast path: if no current route, not active\n if (!currentRoute) {\n isActiveRef.current = false;\n\n return false;\n }\n\n // Fast path: skip unrelated routes\n if (!areRoutesRelated(routeName, currentRoute.name)) {\n isActiveRef.current = false;\n\n return false;\n }\n\n // Full check for related routes\n const isActive = router.isActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n isActiveRef.current = isActive;\n\n return isActive;\n },\n [router, routeName, stableParams, activeStrict, ignoreQueryParams],\n );\n\n return useRouterSubscription(router, selector, shouldUpdate);\n}\n","// packages/react/modules/components/BaseLink.tsx\n\nimport { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useStableValue } from \"../hooks/useStableValue\";\nimport { shouldNavigate } from \"../utils\";\n\nimport type { BaseLinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\n/**\n * Optimized BaseLink component with memoization and performance improvements\n */\nexport const BaseLink: FC<BaseLinkProps> = memo(\n ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n router,\n children,\n ...props\n }) => {\n // Stabilize object references to prevent unnecessary re-renders\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n // Use optimized hook for active state checking\n const isActive = useIsActiveRoute(\n router,\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n // Build URL with memoization\n const href = useMemo(() => {\n // Use buildUrl if available (browser plugin installed)\n // Otherwise fall back to buildPath (e.g., in SSR or without browser plugin)\n if (typeof router.buildUrl === \"function\") {\n return router.buildUrl(routeName, stableParams);\n }\n\n return router.buildPath(routeName, stableParams);\n }, [router, routeName, stableParams]);\n\n // Optimized click handler\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n // Call custom onClick if provided\n if (onClick) {\n onClick(evt);\n // Respect preventDefault from custom handler\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n // Check if we should handle navigation\n if (!shouldNavigate(evt) || target === \"_blank\") {\n return;\n }\n\n // Prevent default link behavior\n evt.preventDefault();\n\n // Perform navigation (fire-and-forget)\n router.navigate(routeName, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n // Build className efficiently\n const finalClassName = useMemo(() => {\n if (isActive && activeClassName) {\n return className\n ? `${className} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return className ?? undefined;\n }, [isActive, className, activeClassName]);\n\n // Filter out previousRoute from props\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { previousRoute, ...restProps } = props;\n\n return (\n <a\n {...restProps}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n data-route={routeName} // For event delegation if needed\n data-active={isActive} // For CSS selectors if needed\n >\n {children}\n </a>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for better memoization\n // Check if props that affect rendering have changed\n return (\n prevProps.router === nextProps.router &&\n prevProps.routeName === nextProps.routeName &&\n JSON.stringify(prevProps.routeParams) ===\n JSON.stringify(nextProps.routeParams) &&\n JSON.stringify(prevProps.routeOptions) ===\n JSON.stringify(nextProps.routeOptions) &&\n prevProps.className === nextProps.className &&\n prevProps.activeClassName === nextProps.activeClassName &&\n prevProps.activeStrict === nextProps.activeStrict &&\n prevProps.ignoreQueryParams === nextProps.ignoreQueryParams &&\n prevProps.onClick === nextProps.onClick &&\n prevProps.target === nextProps.target &&\n prevProps.children === nextProps.children\n );\n },\n);\n\nBaseLink.displayName = \"BaseLink\";\n","// packages/react/modules/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/modules/hooks/useRouter.tsx\n\nimport { use } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = use(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","// packages/react/modules/components/Link.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const Link: FC<Omit<BaseLinkProps, \"router\">> = (props) => {\n const router = useRouter();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { route, previousRoute, routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRoute.tsx\n\nimport { use } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = use(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","// packages/react/modules/components/ConnectedLink.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRoute } from \"../hooks/useRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const ConnectedLink: FC<\n Omit<BaseLinkProps, \"router\" | \"route\" | \"previousRoute\">\n> = (props) => {\n const router = useRouter();\n const { route } = useRoute();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} route={route} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRouteNode.tsx\n\nimport { getNavigator } from \"@real-router/core\";\nimport { useCallback, useMemo } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useRouterSubscription } from \"./useRouterSubscription\";\nimport { getCachedShouldUpdate } from \"../utils\";\n\nimport type { RouteContext, RouteState } from \"../types\";\nimport type { State, SubscribeState } from \"@real-router/core\";\n\n/**\n * Hook that subscribes to a specific route node with optimizations.\n * Provides the current and previous route when the node is affected.\n */\nexport function useRouteNode(nodeName: string): RouteContext {\n // Get router from context with error handling\n const router = useRouter();\n\n // Get cached shouldUpdate function to avoid recreation\n const shouldUpdate = useMemo(\n () => getCachedShouldUpdate(router, nodeName),\n [router, nodeName],\n );\n\n // Stable state factory\n // useRouteNode.tsx\n const stateFactory = useCallback(\n (sub?: SubscribeState): RouteState => {\n const currentRoute = sub?.route ?? router.getState();\n\n // Проверяем, активен ли узел\n if (currentRoute && nodeName !== \"\") {\n // Корневой узел всегда активен\n const isNodeActive =\n currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`);\n\n if (!isNodeActive) {\n return {\n route: undefined,\n previousRoute: sub?.previousRoute,\n };\n }\n }\n\n return {\n route: currentRoute,\n previousRoute: sub?.previousRoute,\n };\n },\n [router, nodeName],\n );\n\n // Subscribe to router with optimization\n const state = useRouterSubscription<RouteState>(\n router,\n stateFactory,\n shouldUpdate as (newRoute: State, prevRoute?: State) => boolean,\n );\n\n // Return memoized context - useMemo ensures stable reference when deps unchanged\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: state.route,\n previousRoute: state.previousRoute,\n }),\n [navigator, state.route, state.previousRoute],\n );\n}\n","// packages/react/modules/hooks/useNavigator.tsx\n\nimport { use } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = use(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","// packages/react/modules/RouterProvider.tsx\n\nimport { getNavigator } from \"@real-router/core\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\n\nimport type { RouteState } from \"./types\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n}) => {\n // Get navigator instance from router\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // Local store state to hold route information\n const store = useMemo(() => {\n let currentState: RouteState = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n // This will be called to return the current state snapshot\n const getSnapshot = () => currentState;\n\n // Subscribe to router updates and notify React when state changes\n const subscribe = (callback: () => void) => {\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n currentState = { route, previousRoute };\n callback(); // Notify React to trigger re-render\n });\n\n // Note: router.subscribe() always returns a function, no need to check\n return unsubscribe;\n };\n\n return { getSnapshot, subscribe };\n }, [router]);\n\n // Using useSyncExternalStore to manage subscription and state updates\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot);\n\n // Memoize RouteContext value to prevent unnecessary re-renders\n const routeContextValue = useMemo(\n () => ({ navigator, ...state }),\n [navigator, state],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useStableValue.tsx","../../src/hooks/useIsActiveRoute.tsx","../../src/utils.ts","../../src/components/BaseLink.tsx","../../src/components/Link.tsx","../../src/hooks/useRoute.tsx","../../src/components/ConnectedLink.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useNavigator.tsx","../../src/RouterProvider.tsx","../../src/hooks/useRouteUtils.tsx"],"names":["useMemo","jsx","use","useSyncExternalStore","getNavigator"],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/react/modules/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","// packages/react/modules/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/modules/hooks/useRouter.tsx\n\nimport { use } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = use(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useStableValue } from \"./useStableValue\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n): boolean {\n const router = useRouter();\n\n // useStableValue: JSON.stringify memoization of params object.\n // Without it, every render with a new params reference (e.g.,\n // <Link routeParams={{ id: '123' }} />) would recreate the store.\n const stableParams = useStableValue(params);\n\n const store = useMemo(\n () =>\n createActiveRouteSource(router, routeName, stableParams, {\n strict,\n ignoreQueryParams,\n }),\n [router, routeName, stableParams, strict, ignoreQueryParams],\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n}\n","import type { MouseEvent } from \"react\";\n\n/**\n * Check if navigation should be handled by router\n */\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 && // left click\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n","// packages/react/modules/components/BaseLink.tsx\n\nimport { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useStableValue } from \"../hooks/useStableValue\";\nimport { shouldNavigate } from \"../utils\";\n\nimport type { BaseLinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\n/**\n * Optimized BaseLink component with memoization and performance improvements\n */\nexport const BaseLink: FC<BaseLinkProps> = memo(\n ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n router,\n children,\n ...props\n }) => {\n // Stabilize object references to prevent unnecessary re-renders\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n // Use optimized hook for active state checking\n const isActive = useIsActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n // Build URL with memoization\n const href = useMemo(() => {\n // Use buildUrl if available (browser plugin installed)\n // Otherwise fall back to buildPath (e.g., in SSR or without browser plugin)\n if (typeof router.buildUrl === \"function\") {\n return router.buildUrl(routeName, stableParams);\n }\n\n return router.buildPath(routeName, stableParams);\n }, [router, routeName, stableParams]);\n\n // Optimized click handler\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n // Call custom onClick if provided\n if (onClick) {\n onClick(evt);\n // Respect preventDefault from custom handler\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n // Check if we should handle navigation\n if (!shouldNavigate(evt) || target === \"_blank\") {\n return;\n }\n\n // Prevent default link behavior\n evt.preventDefault();\n\n // Perform navigation (fire-and-forget)\n router.navigate(routeName, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n // Build className efficiently\n const finalClassName = useMemo(() => {\n if (isActive && activeClassName) {\n return className\n ? `${className} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return className ?? undefined;\n }, [isActive, className, activeClassName]);\n\n // Filter out previousRoute from props\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { previousRoute, ...restProps } = props;\n\n return (\n <a\n {...restProps}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n data-route={routeName} // For event delegation if needed\n data-active={isActive} // For CSS selectors if needed\n >\n {children}\n </a>\n );\n },\n (prevProps, nextProps) => {\n // Custom comparison for better memoization\n // Check if props that affect rendering have changed\n return (\n prevProps.router === nextProps.router &&\n prevProps.routeName === nextProps.routeName &&\n JSON.stringify(prevProps.routeParams) ===\n JSON.stringify(nextProps.routeParams) &&\n JSON.stringify(prevProps.routeOptions) ===\n JSON.stringify(nextProps.routeOptions) &&\n prevProps.className === nextProps.className &&\n prevProps.activeClassName === nextProps.activeClassName &&\n prevProps.activeStrict === nextProps.activeStrict &&\n prevProps.ignoreQueryParams === nextProps.ignoreQueryParams &&\n prevProps.onClick === nextProps.onClick &&\n prevProps.target === nextProps.target &&\n prevProps.children === nextProps.children\n );\n },\n);\n\nBaseLink.displayName = \"BaseLink\";\n","// packages/react/modules/components/Link.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const Link: FC<Omit<BaseLinkProps, \"router\">> = (props) => {\n const router = useRouter();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { route, previousRoute, routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} {...linkProps} />;\n};\n","// packages/react/modules/hooks/useRoute.tsx\n\nimport { use } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = use(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","// packages/react/modules/components/ConnectedLink.tsx\n\nimport { BaseLink } from \"./BaseLink\";\nimport { useRoute } from \"../hooks/useRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { BaseLinkProps } from \"./interfaces\";\nimport type { FC } from \"react\";\n\nexport const ConnectedLink: FC<\n Omit<BaseLinkProps, \"router\" | \"route\" | \"previousRoute\">\n> = (props) => {\n const router = useRouter();\n const { route } = useRoute();\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { routeOptions, ...linkProps } = props;\n\n return <BaseLink router={router} route={route} {...linkProps} />;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n}\n","// packages/react/modules/hooks/useNavigator.tsx\n\nimport { use } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = use(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n}) => {\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n"]}
@@ -1 +1 @@
1
- {"inputs":{"src/constants.ts":{"bytes":225,"imports":[],"format":"esm"},"src/hooks/useRouterSubscription.tsx":{"bytes":2370,"imports":[{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useStableValue.tsx":{"bytes":976,"imports":[{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/utils.ts":{"bytes":1483,"imports":[],"format":"esm"},"src/hooks/useIsActiveRoute.tsx":{"bytes":2692,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouterSubscription.tsx","kind":"import-statement","original":"./useRouterSubscription"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"./useStableValue"},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"}],"format":"esm"},"src/components/BaseLink.tsx":{"bytes":4067,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/hooks/useIsActiveRoute.tsx","kind":"import-statement","original":"../hooks/useIsActiveRoute"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"../hooks/useStableValue"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/context.ts":{"bytes":416,"imports":[{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouter.tsx":{"bytes":364,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/components/Link.tsx":{"bytes":511,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRoute.tsx":{"bytes":403,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/components/ConnectedLink.tsx":{"bytes":631,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"../hooks/useRoute"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteNode.tsx":{"bytes":2212,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"src/hooks/useRouterSubscription.tsx","kind":"import-statement","original":"./useRouterSubscription"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"}],"format":"esm"},"src/hooks/useNavigator.tsx":{"bytes":394,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/RouterProvider.tsx":{"bytes":1958,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteUtils.tsx":{"bytes":977,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"}],"format":"esm"},"src/index.ts":{"bytes":719,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./components/BaseLink"},{"path":"src/components/Link.tsx","kind":"import-statement","original":"./components/Link"},{"path":"src/components/ConnectedLink.tsx","kind":"import-statement","original":"./components/ConnectedLink"},{"path":"src/hooks/useRouteNode.tsx","kind":"import-statement","original":"./hooks/useRouteNode"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"./hooks/useRoute"},{"path":"src/hooks/useNavigator.tsx","kind":"import-statement","original":"./hooks/useNavigator"},{"path":"src/RouterProvider.tsx","kind":"import-statement","original":"./RouterProvider"},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./hooks/useRouter"},{"path":"src/hooks/useRouteUtils.tsx","kind":"import-statement","original":"./hooks/useRouteUtils"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":28546},"dist/esm/index.mjs":{"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["BaseLink","ConnectedLink","Link","NavigatorContext","RouteContext","RouterContext","RouterProvider","useNavigator","useRoute","useRouteNode","useRouteUtils","useRouter"],"entryPoint":"src/index.ts","inputs":{"src/components/BaseLink.tsx":{"bytesInOutput":2638},"src/constants.ts":{"bytesInOutput":77},"src/hooks/useIsActiveRoute.tsx":{"bytesInOutput":1736},"src/hooks/useRouterSubscription.tsx":{"bytesInOutput":1386},"src/hooks/useStableValue.tsx":{"bytesInOutput":157},"src/utils.ts":{"bytesInOutput":816},"src/index.ts":{"bytesInOutput":0},"src/hooks/useRouter.tsx":{"bytesInOutput":202},"src/context.ts":{"bytesInOutput":164},"src/components/Link.tsx":{"bytesInOutput":243},"src/hooks/useRoute.tsx":{"bytesInOutput":225},"src/components/ConnectedLink.tsx":{"bytesInOutput":269},"src/hooks/useRouteNode.tsx":{"bytesInOutput":1188},"src/hooks/useNavigator.tsx":{"bytesInOutput":229},"src/RouterProvider.tsx":{"bytesInOutput":1183},"src/hooks/useRouteUtils.tsx":{"bytesInOutput":225}},"bytes":11463}}}
1
+ {"inputs":{"src/constants.ts":{"bytes":225,"imports":[],"format":"esm"},"src/context.ts":{"bytes":416,"imports":[{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouter.tsx":{"bytes":364,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/hooks/useStableValue.tsx":{"bytes":976,"imports":[{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useIsActiveRoute.tsx":{"bytes":1060,"imports":[{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"./useStableValue"}],"format":"esm"},"src/utils.ts":{"bytes":293,"imports":[],"format":"esm"},"src/components/BaseLink.tsx":{"bytes":4053,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"../constants"},{"path":"src/hooks/useIsActiveRoute.tsx","kind":"import-statement","original":"../hooks/useIsActiveRoute"},{"path":"src/hooks/useStableValue.tsx","kind":"import-statement","original":"../hooks/useStableValue"},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/components/Link.tsx":{"bytes":511,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRoute.tsx":{"bytes":403,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/components/ConnectedLink.tsx":{"bytes":631,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./BaseLink"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"../hooks/useRoute"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"../hooks/useRouter"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteNode.tsx":{"bytes":837,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"}],"format":"esm"},"src/hooks/useNavigator.tsx":{"bytes":394,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"../context"}],"format":"esm"},"src/RouterProvider.tsx":{"bytes":1380,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useRouteUtils.tsx":{"bytes":977,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./useRouter"}],"format":"esm"},"src/index.ts":{"bytes":719,"imports":[{"path":"src/components/BaseLink.tsx","kind":"import-statement","original":"./components/BaseLink"},{"path":"src/components/Link.tsx","kind":"import-statement","original":"./components/Link"},{"path":"src/components/ConnectedLink.tsx","kind":"import-statement","original":"./components/ConnectedLink"},{"path":"src/hooks/useRouteNode.tsx","kind":"import-statement","original":"./hooks/useRouteNode"},{"path":"src/hooks/useRoute.tsx","kind":"import-statement","original":"./hooks/useRoute"},{"path":"src/hooks/useNavigator.tsx","kind":"import-statement","original":"./hooks/useNavigator"},{"path":"src/RouterProvider.tsx","kind":"import-statement","original":"./RouterProvider"},{"path":"src/context.ts","kind":"import-statement","original":"./context"},{"path":"src/hooks/useRouter.tsx","kind":"import-statement","original":"./hooks/useRouter"},{"path":"src/hooks/useRouteUtils.tsx","kind":"import-statement","original":"./hooks/useRouteUtils"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18254},"dist/esm/index.mjs":{"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/sources","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["BaseLink","ConnectedLink","Link","NavigatorContext","RouteContext","RouterContext","RouterProvider","useNavigator","useRoute","useRouteNode","useRouteUtils","useRouter"],"entryPoint":"src/index.ts","inputs":{"src/components/BaseLink.tsx":{"bytesInOutput":2607},"src/constants.ts":{"bytesInOutput":77},"src/hooks/useIsActiveRoute.tsx":{"bytesInOutput":671},"src/hooks/useRouter.tsx":{"bytesInOutput":202},"src/context.ts":{"bytesInOutput":164},"src/hooks/useStableValue.tsx":{"bytesInOutput":157},"src/utils.ts":{"bytesInOutput":140},"src/index.ts":{"bytesInOutput":0},"src/components/Link.tsx":{"bytesInOutput":243},"src/hooks/useRoute.tsx":{"bytesInOutput":225},"src/components/ConnectedLink.tsx":{"bytesInOutput":269},"src/hooks/useRouteNode.tsx":{"bytesInOutput":742},"src/hooks/useNavigator.tsx":{"bytesInOutput":229},"src/RouterProvider.tsx":{"bytesInOutput":1001},"src/hooks/useRouteUtils.tsx":{"bytesInOutput":225}},"bytes":7637}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/react",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "commonjs",
5
5
  "description": "React integration for Real-Router",
6
6
  "main": "./dist/cjs/index.js",
@@ -48,8 +48,9 @@
48
48
  "license": "MIT",
49
49
  "sideEffects": false,
50
50
  "dependencies": {
51
- "@real-router/core": "^0.31.0",
52
- "@real-router/route-utils": "^0.1.0"
51
+ "@real-router/sources": "^0.1.0",
52
+ "@real-router/route-utils": "^0.1.0",
53
+ "@real-router/core": "^0.31.0"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@testing-library/dom": "10.4.1",
@@ -1,11 +1,9 @@
1
- // packages/react/modules/RouterProvider.tsx
2
-
3
1
  import { getNavigator } from "@real-router/core";
2
+ import { createRouteSource } from "@real-router/sources";
4
3
  import { useMemo, useSyncExternalStore } from "react";
5
4
 
6
5
  import { NavigatorContext, RouteContext, RouterContext } from "./context";
7
6
 
8
- import type { RouteState } from "./types";
9
7
  import type { Router } from "@real-router/core";
10
8
  import type { FC, ReactNode } from "react";
11
9
 
@@ -18,40 +16,21 @@ export const RouterProvider: FC<RouteProviderProps> = ({
18
16
  router,
19
17
  children,
20
18
  }) => {
21
- // Get navigator instance from router
22
19
  const navigator = useMemo(() => getNavigator(router), [router]);
23
20
 
24
- // Local store state to hold route information
25
- const store = useMemo(() => {
26
- let currentState: RouteState = {
27
- route: router.getState(),
28
- previousRoute: undefined,
29
- };
30
-
31
- // This will be called to return the current state snapshot
32
- const getSnapshot = () => currentState;
33
-
34
- // Subscribe to router updates and notify React when state changes
35
- const subscribe = (callback: () => void) => {
36
- const unsubscribe = router.subscribe(({ route, previousRoute }) => {
37
- currentState = { route, previousRoute };
38
- callback(); // Notify React to trigger re-render
39
- });
40
-
41
- // Note: router.subscribe() always returns a function, no need to check
42
- return unsubscribe;
43
- };
44
-
45
- return { getSnapshot, subscribe };
46
- }, [router]);
47
-
48
- // Using useSyncExternalStore to manage subscription and state updates
49
- const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
21
+ // useSyncExternalStore manages the router subscription lifecycle:
22
+ // subscribe connects to router on first listener, unsubscribes on last.
23
+ // This is Strict Mode safe — no useEffect cleanup needed.
24
+ const store = useMemo(() => createRouteSource(router), [router]);
25
+ const { route, previousRoute } = useSyncExternalStore(
26
+ store.subscribe,
27
+ store.getSnapshot,
28
+ store.getSnapshot, // SSR: router returns same state on server and client
29
+ );
50
30
 
51
- // Memoize RouteContext value to prevent unnecessary re-renders
52
31
  const routeContextValue = useMemo(
53
- () => ({ navigator, ...state }),
54
- [navigator, state],
32
+ () => ({ navigator, route, previousRoute }),
33
+ [navigator, route, previousRoute],
55
34
  );
56
35
 
57
36
  return (
@@ -34,7 +34,6 @@ export const BaseLink: FC<BaseLinkProps> = memo(
34
34
 
35
35
  // Use optimized hook for active state checking
36
36
  const isActive = useIsActiveRoute(
37
- router,
38
37
  routeName,
39
38
  stableParams,
40
39
  activeStrict,
@@ -1,95 +1,36 @@
1
- // packages/react/modules/hooks/useIsActiveRoute.tsx
1
+ import { createActiveRouteSource } from "@real-router/sources";
2
+ import { useMemo, useSyncExternalStore } from "react";
2
3
 
3
- import { areRoutesRelated } from "@real-router/route-utils";
4
- import { useCallback, useMemo, useRef } from "react";
5
-
6
- import { useRouterSubscription } from "./useRouterSubscription";
4
+ import { useRouter } from "./useRouter";
7
5
  import { useStableValue } from "./useStableValue";
8
- import { EMPTY_PARAMS } from "../constants";
9
- import { createActiveCheckKey } from "../utils";
10
6
 
11
- import type { Params, Router, State, SubscribeState } from "@real-router/core";
7
+ import type { Params } from "@real-router/core";
12
8
 
13
- /**
14
- * Optimized hook to check if a route is active.
15
- * Minimizes unnecessary recalculations and re-renders.
16
- */
17
9
  export function useIsActiveRoute(
18
- router: Router,
19
10
  routeName: string,
20
- routeParams: Params = EMPTY_PARAMS,
21
- activeStrict = false,
11
+ params?: Params,
12
+ strict = false,
22
13
  ignoreQueryParams = true,
23
14
  ): boolean {
24
- // Stabilize params reference to prevent unnecessary recalculations
25
- const stableParams = useStableValue(routeParams);
15
+ const router = useRouter();
16
+
17
+ // useStableValue: JSON.stringify memoization of params object.
18
+ // Without it, every render with a new params reference (e.g.,
19
+ // <Link routeParams={{ id: '123' }} />) would recreate the store.
20
+ const stableParams = useStableValue(params);
26
21
 
27
- // Create stable cache key
28
- const cacheKey = useMemo(
22
+ const store = useMemo(
29
23
  () =>
30
- createActiveCheckKey(
31
- routeName,
32
- stableParams,
33
- activeStrict,
24
+ createActiveRouteSource(router, routeName, stableParams, {
25
+ strict,
34
26
  ignoreQueryParams,
35
- ),
36
- [routeName, stableParams, activeStrict, ignoreQueryParams],
37
- );
38
-
39
- // Cache the active state
40
- const isActiveRef = useRef<boolean | undefined>(undefined);
41
- const lastCacheKeyRef = useRef<string | undefined>(undefined);
42
-
43
- if (lastCacheKeyRef.current !== cacheKey) {
44
- isActiveRef.current = undefined;
45
- lastCacheKeyRef.current = cacheKey;
46
- }
47
-
48
- // Optimize shouldUpdate to skip unrelated routes
49
- const shouldUpdate = useCallback(
50
- (newRoute: State, prevRoute?: State) => {
51
- const isNewRelated = areRoutesRelated(routeName, newRoute.name);
52
- const isPrevRelated =
53
- prevRoute && areRoutesRelated(routeName, prevRoute.name);
54
-
55
- return !!(isNewRelated || isPrevRelated);
56
- },
57
- [routeName],
27
+ }),
28
+ [router, routeName, stableParams, strict, ignoreQueryParams],
58
29
  );
59
30
 
60
- // Selector that performs active check
61
- const selector = useCallback(
62
- (sub?: SubscribeState): boolean => {
63
- const currentRoute = sub?.route ?? router.getState();
64
-
65
- // Fast path: if no current route, not active
66
- if (!currentRoute) {
67
- isActiveRef.current = false;
68
-
69
- return false;
70
- }
71
-
72
- // Fast path: skip unrelated routes
73
- if (!areRoutesRelated(routeName, currentRoute.name)) {
74
- isActiveRef.current = false;
75
-
76
- return false;
77
- }
78
-
79
- // Full check for related routes
80
- const isActive = router.isActiveRoute(
81
- routeName,
82
- stableParams,
83
- activeStrict,
84
- ignoreQueryParams,
85
- );
86
-
87
- isActiveRef.current = isActive;
88
-
89
- return isActive;
90
- },
91
- [router, routeName, stableParams, activeStrict, ignoreQueryParams],
31
+ return useSyncExternalStore(
32
+ store.subscribe,
33
+ store.getSnapshot,
34
+ store.getSnapshot, // SSR: router returns same state on server and client
92
35
  );
93
-
94
- return useRouterSubscription(router, selector, shouldUpdate);
95
36
  }
@@ -1,74 +1,29 @@
1
- // packages/react/modules/hooks/useRouteNode.tsx
2
-
3
1
  import { getNavigator } from "@real-router/core";
4
- import { useCallback, useMemo } from "react";
2
+ import { createRouteNodeSource } from "@real-router/sources";
3
+ import { useMemo, useSyncExternalStore } from "react";
5
4
 
6
5
  import { useRouter } from "./useRouter";
7
- import { useRouterSubscription } from "./useRouterSubscription";
8
- import { getCachedShouldUpdate } from "../utils";
9
6
 
10
- import type { RouteContext, RouteState } from "../types";
11
- import type { State, SubscribeState } from "@real-router/core";
7
+ import type { RouteContext } from "../types";
12
8
 
13
- /**
14
- * Hook that subscribes to a specific route node with optimizations.
15
- * Provides the current and previous route when the node is affected.
16
- */
17
9
  export function useRouteNode(nodeName: string): RouteContext {
18
- // Get router from context with error handling
19
10
  const router = useRouter();
20
11
 
21
- // Get cached shouldUpdate function to avoid recreation
22
- const shouldUpdate = useMemo(
23
- () => getCachedShouldUpdate(router, nodeName),
24
- [router, nodeName],
25
- );
26
-
27
- // Stable state factory
28
- // useRouteNode.tsx
29
- const stateFactory = useCallback(
30
- (sub?: SubscribeState): RouteState => {
31
- const currentRoute = sub?.route ?? router.getState();
32
-
33
- // Проверяем, активен ли узел
34
- if (currentRoute && nodeName !== "") {
35
- // Корневой узел всегда активен
36
- const isNodeActive =
37
- currentRoute.name === nodeName ||
38
- currentRoute.name.startsWith(`${nodeName}.`);
39
-
40
- if (!isNodeActive) {
41
- return {
42
- route: undefined,
43
- previousRoute: sub?.previousRoute,
44
- };
45
- }
46
- }
47
-
48
- return {
49
- route: currentRoute,
50
- previousRoute: sub?.previousRoute,
51
- };
52
- },
12
+ const store = useMemo(
13
+ () => createRouteNodeSource(router, nodeName),
53
14
  [router, nodeName],
54
15
  );
55
16
 
56
- // Subscribe to router with optimization
57
- const state = useRouterSubscription<RouteState>(
58
- router,
59
- stateFactory,
60
- shouldUpdate as (newRoute: State, prevRoute?: State) => boolean,
17
+ const { route, previousRoute } = useSyncExternalStore(
18
+ store.subscribe,
19
+ store.getSnapshot,
20
+ store.getSnapshot, // SSR: router returns same state on server and client
61
21
  );
62
22
 
63
- // Return memoized context - useMemo ensures stable reference when deps unchanged
64
23
  const navigator = useMemo(() => getNavigator(router), [router]);
65
24
 
66
25
  return useMemo(
67
- (): RouteContext => ({
68
- navigator,
69
- route: state.route,
70
- previousRoute: state.previousRoute,
71
- }),
72
- [navigator, state.route, state.previousRoute],
26
+ (): RouteContext => ({ navigator, route, previousRoute }),
27
+ [navigator, route, previousRoute],
73
28
  );
74
29
  }
package/src/utils.ts CHANGED
@@ -1,45 +1,5 @@
1
- // packages/react/modules/utils.ts
2
-
3
- import type { Params, Router, State } from "@real-router/core";
4
1
  import type { MouseEvent } from "react";
5
2
 
6
- /**
7
- * Cache for shouldUpdateNode functions to avoid recreating them
8
- */
9
- export const shouldUpdateCache = new WeakMap<
10
- Router,
11
- Map<string, (toState: State, fromState?: State) => boolean>
12
- >();
13
-
14
- /**
15
- * Get cached shouldUpdateNode function for a router and nodeName
16
- */
17
- export function getCachedShouldUpdate(
18
- router: Router,
19
- nodeName: string,
20
- ): (toState: State, fromState?: State) => boolean {
21
- let cache = shouldUpdateCache.get(router);
22
-
23
- if (!cache) {
24
- cache = new Map();
25
- shouldUpdateCache.set(router, cache);
26
- }
27
-
28
- let fn = cache.get(nodeName);
29
-
30
- if (!fn) {
31
- fn = router.shouldUpdateNode(nodeName);
32
-
33
- const originalFn = fn;
34
-
35
- fn = (toState: State, fromState?: State) => originalFn(toState, fromState);
36
-
37
- cache.set(nodeName, fn);
38
- }
39
-
40
- return fn;
41
- }
42
-
43
3
  /**
44
4
  * Check if navigation should be handled by router
45
5
  */
@@ -52,20 +12,3 @@ export function shouldNavigate(evt: MouseEvent): boolean {
52
12
  !evt.shiftKey
53
13
  );
54
14
  }
55
-
56
- /**
57
- * Create cache key for route active check
58
- */
59
- export function createActiveCheckKey(
60
- routeName: string,
61
- routeParams: Params,
62
- activeStrict: boolean,
63
- ignoreQueryParams: boolean,
64
- ): string {
65
- return JSON.stringify({
66
- routeName,
67
- routeParams,
68
- activeStrict,
69
- ignoreQueryParams,
70
- });
71
- }
@@ -1,81 +0,0 @@
1
- // packages/react/modules/hooks/useRouterSubscription.tsx
2
-
3
- import { useCallback, useRef, useSyncExternalStore } from "react";
4
-
5
- import type { Router, State, SubscribeState } from "@real-router/core";
6
-
7
- /**
8
- * Generic hook for subscribing to router changes with optimization.
9
- *
10
- * @param router - Real Router instance
11
- * @param selector - Function to derive state from router subscription
12
- * @param shouldUpdate - Optional predicate to filter updates
13
- */
14
- export function useRouterSubscription<T>(
15
- router: Router,
16
- selector: (sub?: SubscribeState) => T,
17
- shouldUpdate?: (newRoute: State, prevRoute?: State) => boolean,
18
- ): T {
19
- // Store current value
20
- const stateRef = useRef<T | undefined>(undefined);
21
- const selectorRef = useRef(selector);
22
- const shouldUpdateRef = useRef(shouldUpdate);
23
-
24
- // Update refs to avoid stale closures
25
- selectorRef.current = selector;
26
- shouldUpdateRef.current = shouldUpdate;
27
-
28
- // Lazy initialization
29
- if (stateRef.current === undefined) {
30
- // Get initial state from router
31
- const currentState = router.getState();
32
-
33
- // Check if initial state is relevant for this subscription
34
- const shouldInitialize =
35
- !shouldUpdateRef.current ||
36
- (currentState && shouldUpdateRef.current(currentState));
37
-
38
- stateRef.current = selectorRef.current(
39
- shouldInitialize && currentState
40
- ? { route: currentState, previousRoute: undefined }
41
- : undefined,
42
- );
43
- }
44
-
45
- // Stable snapshot getter
46
- const getSnapshot = useCallback(() => stateRef.current as T, []);
47
-
48
- // Subscribe function with optimization
49
- const subscribe = useCallback(
50
- (onStoreChange: () => void) => {
51
- return router.subscribe((next) => {
52
- // Check if we should process this update
53
- let shouldProcess = true;
54
-
55
- if (shouldUpdateRef.current) {
56
- shouldProcess = shouldUpdateRef.current(
57
- next.route,
58
- next.previousRoute,
59
- );
60
- }
61
-
62
- if (!shouldProcess) {
63
- return;
64
- }
65
-
66
- // Calculate new value
67
- const newValue = selectorRef.current(next);
68
-
69
- // Only trigger update if value actually changed
70
- if (!Object.is(stateRef.current, newValue)) {
71
- stateRef.current = newValue;
72
-
73
- onStoreChange();
74
- }
75
- });
76
- },
77
- [router],
78
- );
79
-
80
- return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
81
- }