| Server IP : 159.203.156.69 / Your IP : 216.73.217.172 Web Server : nginx/1.24.0 System : Linux main-ubuntu 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64 User : root ( 0) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/tanviranik.com/node_modules/vinext/dist/shims/ |
Upload File : |
"use client";
import { stripBasePath } from "../utils/base-path.js";
import { createRouteTrieCache, matchRouteWithTrie } from "../routing/route-matching.js";
import { VINEXT_MOUNTED_SLOTS_HEADER } from "../server/headers.js";
import { isDangerousScheme } from "./url-safety.js";
import { resolveRelativeHref, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
import { addLocalePrefix, getDomainLocaleUrl } from "../utils/domain-locale.js";
import { appendSearchParamsToUrl, urlQueryToSearchParams } from "../utils/query.js";
import { AppElementsWire } from "../server/app-elements-wire.js";
import "../server/app-elements.js";
import { createRscRequestHeaders, createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
import { getCurrentInterceptionContext, getMountedSlotsHeader, getPrefetchedUrls, navigateClientSide, prefetchRscResponse } from "./navigation.js";
import { getI18nContext } from "./i18n-context.js";
import { canLinkPrefetch, getLinkPrefetchHref } from "./link-prefetch.js";
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";
//#region src/shims/link.tsx
/**
* next/link shim
*
* Renders an <a> tag with client-side navigation support.
* On click, prevents full page reload and triggers client-side
* page swap via the router's navigation system.
*/
const LinkStatusContext = createContext({ pending: false });
/**
* useLinkStatus returns the pending state of the enclosing <Link>.
* In Next.js, this is used to show loading indicators while a
* prefetch-triggered navigation is in progress.
*/
function useLinkStatus() {
return useContext(LinkStatusContext);
}
/** basePath from next.config.js, injected by the plugin at build time */
const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
const linkPrefetchRouteTrieCache = createRouteTrieCache();
function resolveHref(href) {
if (typeof href === "string") return href;
let url = href.pathname ?? "/";
if (href.query) {
const params = urlQueryToSearchParams(href.query);
url = appendSearchParamsToUrl(url, params);
}
return url;
}
function resolveLinkPrefetchMode(prefetchProp, isDangerous) {
if (isDangerous || prefetchProp === false) return "disabled";
if (prefetchProp === true) return "full";
return "auto";
}
function toSameOriginRouteHref(href) {
if (typeof window === "undefined") return null;
let url;
try {
url = new URL(href, window.location.href);
} catch {
return null;
}
if (url.origin !== window.location.origin) return null;
return `${stripBasePath(url.pathname, __basePath)}${url.search}`;
}
function canAutoPrefetchFullAppRoute(href) {
if (typeof window === "undefined") return false;
const routes = window.__VINEXT_LINK_PREFETCH_ROUTES__;
if (!routes) return false;
const routeHref = toSameOriginRouteHref(href);
if (routeHref === null) return false;
const match = matchRouteWithTrie(routeHref, routes, linkPrefetchRouteTrieCache);
if (!match) return false;
return !match.route.isDynamic;
}
/**
* Prefetch a URL for faster navigation.
*
* For App Router (RSC): fetches the .rsc payload in the background and
* stores it in an in-memory cache for instant use during navigation.
* For Pages Router: injects a <link rel="prefetch"> for the page module.
*
* Uses `requestIdleCallback` (or `setTimeout` fallback) to avoid blocking
* the main thread during initial page load.
*/
function prefetchUrl(href, mode, priority = "low") {
if (typeof window === "undefined") return;
const prefetchHref = getLinkPrefetchHref({
href,
basePath: __basePath,
currentOrigin: window.location.origin
});
if (prefetchHref == null) return;
const fullHref = toBrowserNavigationHref(prefetchHref, window.location.href, __basePath);
(window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(() => {
(async () => {
if (typeof window.__VINEXT_RSC_NAVIGATE__ === "function") {
if (mode === "auto" && !canAutoPrefetchFullAppRoute(prefetchHref)) return;
const interceptionContext = getCurrentInterceptionContext();
const mountedSlotsHeader = getMountedSlotsHeader();
const headers = createRscRequestHeaders({ interceptionContext });
if (mountedSlotsHeader) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
const rscUrl = await createRscRequestUrl(fullHref, headers);
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
const prefetched = getPrefetchedUrls();
if (prefetched.has(cacheKey)) return;
prefetched.add(cacheKey);
prefetchRscResponse(rscUrl, fetch(rscUrl, {
headers,
credentials: "include",
priority,
purpose: "prefetch"
}), interceptionContext, mountedSlotsHeader);
} else if (window.__NEXT_DATA__?.__vinext?.pageModuleUrl) {
const link = document.createElement("link");
link.rel = "prefetch";
link.href = fullHref;
link.as = "document";
document.head.appendChild(link);
}
})().catch((error) => {
console.error("[vinext] RSC prefetch setup error:", error);
});
});
}
/**
* Shared IntersectionObserver for viewport-based prefetching.
* All Link elements use the same observer to minimize resource usage.
*/
let sharedObserver = null;
const observerCallbacks = /* @__PURE__ */ new WeakMap();
function getSharedObserver() {
if (typeof window === "undefined" || typeof IntersectionObserver === "undefined") return null;
if (sharedObserver) return sharedObserver;
sharedObserver = new IntersectionObserver((entries) => {
for (const entry of entries) if (entry.isIntersecting) {
const callback = observerCallbacks.get(entry.target);
if (callback) {
callback();
sharedObserver?.unobserve(entry.target);
observerCallbacks.delete(entry.target);
}
}
}, { rootMargin: "250px" });
return sharedObserver;
}
function getDefaultLocale() {
if (typeof window !== "undefined") return window.__VINEXT_DEFAULT_LOCALE__;
return getI18nContext()?.defaultLocale;
}
function getDomainLocales() {
if (typeof window !== "undefined") return window.__NEXT_DATA__?.domainLocales;
return getI18nContext()?.domainLocales;
}
function getCurrentHostname() {
if (typeof window !== "undefined") return window.location.hostname;
return getI18nContext()?.hostname;
}
function getDomainLocaleHref(href, locale) {
return getDomainLocaleUrl(href, locale, {
basePath: __basePath,
currentHostname: getCurrentHostname(),
domainItems: getDomainLocales()
});
}
/**
* Apply locale prefix to a URL path based on the locale prop.
* - locale="fr" → prepend /fr (unless it already has a locale prefix)
* - locale={false} → use the href as-is (no locale prefix, link to default)
* - locale=undefined → use current locale (href as-is in most cases)
*/
function applyLocaleToHref(href, locale) {
if (locale === false) return href;
if (locale === void 0) return href;
if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) return href;
const domainLocaleHref = getDomainLocaleHref(href, locale);
if (domainLocaleHref) return domainLocaleHref;
return addLocalePrefix(href, locale, getDefaultLocale() ?? "");
}
const Link = forwardRef(function Link({ href, as, replace = false, prefetch: prefetchProp, scroll = true, children, onClick, onMouseEnter, onTouchStart, onNavigate, ...rest }, forwardedRef) {
const { locale, ...restWithoutLocale } = rest;
const resolvedHref = as ?? resolveHref(href);
const isDangerous = typeof resolvedHref === "string" && isDangerousScheme(resolvedHref);
const localizedHref = applyLocaleToHref(isDangerous ? "/" : resolvedHref, locale);
const fullHref = withBasePath(localizedHref, __basePath);
const [pending, setPending] = useState(false);
const mountedRef = useRef(true);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
const internalRef = useRef(null);
const prefetchMode = resolveLinkPrefetchMode(prefetchProp, isDangerous);
const shouldPrefetch = canLinkPrefetch({
nodeEnv: process.env.NODE_ENV,
prefetch: prefetchProp,
isDangerous
});
const setRefs = useCallback((node) => {
internalRef.current = node;
if (typeof forwardedRef === "function") forwardedRef(node);
else if (forwardedRef) forwardedRef.current = node;
}, [forwardedRef]);
useEffect(() => {
if (!shouldPrefetch || typeof window === "undefined") return;
const node = internalRef.current;
if (!node) return;
const hrefToPrefetch = getLinkPrefetchHref({
href: localizedHref,
basePath: __basePath,
currentOrigin: window.location.origin
});
if (hrefToPrefetch == null) return;
const observer = getSharedObserver();
if (!observer) return;
observerCallbacks.set(node, () => prefetchUrl(hrefToPrefetch, prefetchMode, "low"));
observer.observe(node);
return () => {
observer.unobserve(node);
observerCallbacks.delete(node);
};
}, [
shouldPrefetch,
prefetchMode,
localizedHref
]);
const prefetchOnIntent = useCallback(() => {
if (!shouldPrefetch) return;
prefetchUrl(localizedHref, prefetchMode, "high");
}, [
shouldPrefetch,
prefetchMode,
localizedHref
]);
const handleMouseEnter = useCallback((e) => {
onMouseEnter?.(e);
prefetchOnIntent();
}, [onMouseEnter, prefetchOnIntent]);
const handleTouchStart = useCallback((e) => {
onTouchStart?.(e);
prefetchOnIntent();
}, [onTouchStart, prefetchOnIntent]);
const handleClick = async (e) => {
if (onClick) onClick(e);
if (e.defaultPrevented) return;
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
if (e.currentTarget.target && e.currentTarget.target !== "_self") return;
let navigateHref = localizedHref;
if (resolvedHref.startsWith("http://") || resolvedHref.startsWith("https://") || resolvedHref.startsWith("//")) {
const localPath = toSameOriginAppPath(resolvedHref, __basePath);
if (localPath == null) return;
navigateHref = localPath;
}
e.preventDefault();
const absoluteHref = resolveRelativeHref(navigateHref, window.location.href, __basePath);
const absoluteFullHref = toBrowserNavigationHref(navigateHref, window.location.href, __basePath);
if (onNavigate) try {
const navUrl = new URL(absoluteFullHref, window.location.origin);
let prevented = false;
const navEvent = {
url: navUrl,
preventDefault() {
prevented = true;
},
get defaultPrevented() {
return prevented;
}
};
onNavigate(navEvent);
if (navEvent.defaultPrevented) return;
} catch {}
if (typeof window.__VINEXT_RSC_NAVIGATE__ === "function") {
setPending(true);
React.startTransition(() => {
navigateClientSide(navigateHref, replace ? "replace" : "push", scroll, true).finally(() => {
if (mountedRef.current) setPending(false);
});
});
return;
} else try {
const Router = (await import("next/router.js")).default;
if (replace) await Router.replace(absoluteHref, void 0, { scroll });
else await Router.push(absoluteHref, void 0, { scroll });
} catch {
if (replace) window.history.replaceState({}, "", absoluteFullHref);
else window.history.pushState({}, "", absoluteFullHref);
window.dispatchEvent(new PopStateEvent("popstate"));
}
};
const { passHref: _p, ...anchorProps } = restWithoutLocale;
const linkStatusValue = React.useMemo(() => ({ pending }), [pending]);
if (isDangerous) {
if (process.env.NODE_ENV !== "production") console.warn(`<Link> blocked dangerous href: ${resolvedHref}`);
return /* @__PURE__ */ jsx("a", {
...anchorProps,
onMouseEnter: handleMouseEnter,
onTouchStart: handleTouchStart,
children
});
}
return /* @__PURE__ */ jsx(LinkStatusContext.Provider, {
value: linkStatusValue,
children: /* @__PURE__ */ jsx("a", {
ref: setRefs,
href: fullHref,
onClick: (event) => {
handleClick(event);
},
onMouseEnter: handleMouseEnter,
onTouchStart: handleTouchStart,
...anchorProps,
children
})
});
});
//#endregion
export { canAutoPrefetchFullAppRoute, Link as default, resolveLinkPrefetchMode, useLinkStatus };
//# sourceMappingURL=link.js.map