nuxt
Version:
135 lines (134 loc) • 4.77 kB
JavaScript
import { defineNuxtPlugin } from "../nuxt.js";
import { useRouter } from "../composables/router.js";
import { injectHead } from "../../head/runtime/composables.js";
import "../composables/head.js";
import { onNuxtReady } from "../composables/ready.js";
import { stateDiagnostics } from "../diagnostics/state.js";
import { canPrefetch, prefetchGroup } from "../internal/prefetch-util.js";
import { getAppManifest } from "../composables/manifest.js";
import { isCachedPayloadRoute, loadPayload } from "../composables/payload.js";
import { usePrefetchScheduler } from "../internal/prefetch-scheduler.js";
import { appManifest, prefetchPreloadTags, purgeCachedData } from "#build/nuxt.config.mjs";
import { withoutFragment } from "ufo";
//#region src/app/plugins/payload.client.ts
const forwardedHintEntries = /* @__PURE__ */ new Set();
const forwardedHintHrefs = /* @__PURE__ */ new Set();
const MAX_HINTS_PER_ROUTE = 2;
const FORWARDED_HINT_TIMEOUT_MS = 3e4;
function disposeHint(entry) {
if (forwardedHintEntries.delete(entry)) entry.dispose();
}
function documentHrefs() {
const hrefs = /* @__PURE__ */ new Set();
for (const link of document.head.querySelectorAll("link[href]")) hrefs.add(link.href);
return hrefs;
}
function selectHints(prefetchLinks) {
const existingHrefs = documentHrefs();
const selected = [];
for (const link of prefetchLinks) {
if (selected.length >= MAX_HINTS_PER_ROUTE) break;
if (typeof link.href !== "string") continue;
const href = new URL(link.href, window.location.href).href;
if (existingHrefs.has(href) || forwardedHintHrefs.has(href)) continue;
forwardedHintHrefs.add(href);
if (link.as === "image") {
const { fetchpriority: _fetchpriority, ...rest } = link;
selected.push({
href,
link: rest
});
} else {
const { rel: _rel, ...rest } = link;
selected.push({
href,
link: {
...rest,
rel: "prefetch"
}
});
}
}
return selected;
}
const plugin = /* @__PURE__ */ defineNuxtPlugin({
name: "nuxt:payload",
setup(nuxtApp) {
const staticKeysToRemove = /* @__PURE__ */ new Set();
const router = useRouter();
const { schedule } = usePrefetchScheduler(nuxtApp);
if (prefetchPreloadTags) router.afterEach(() => {
for (const entry of [...forwardedHintEntries]) disposeHint(entry);
forwardedHintHrefs.clear();
});
router.beforeResolve(async (to, from) => {
const queryAware = isCachedPayloadRoute(to.path);
const toURL = queryAware ? withoutFragment(to.fullPath) : to.path;
if (toURL === (queryAware ? withoutFragment(from.fullPath) : from.path)) return;
const payload = await loadPayload(toURL);
if (!payload) return;
if (purgeCachedData) for (const key of staticKeysToRemove) delete nuxtApp.static.data[key];
for (const key in payload.data) {
if (purgeCachedData) {
if (!(key in nuxtApp.static.data)) staticKeysToRemove.add(key);
}
nuxtApp.static.data[key] = payload.data[key];
}
});
const head = prefetchPreloadTags ? injectHead(nuxtApp) : null;
const forwardHint = (link) => (signal) => new Promise((resolve) => {
if (signal.aborted) return resolve();
const hint = {};
const complete = (dispose) => {
clearTimeout(hint.timeout);
signal.removeEventListener("abort", onAbort);
if (dispose && hint.entry) disposeHint(hint.entry);
resolve();
};
const onAbort = () => complete(true);
signal.addEventListener("abort", onAbort);
hint.entry = head.push({ link: [{
...link,
onerror: () => complete(true),
onload: () => complete(false)
}] });
forwardedHintEntries.add(hint.entry);
hint.timeout = setTimeout(() => complete(true), FORWARDED_HINT_TIMEOUT_MS);
});
nuxtApp.hooks.hook("link:prefetch", (url) => {
onNuxtReady(() => {
const { hostname } = new URL(url, window.location.href);
if (hostname !== window.location.hostname) return;
const group = prefetchGroup(url);
schedule({
key: `payload:${url}`,
priority: "payload",
scope: "navigation",
group,
run: async (signal, promoted) => {
if (signal.aborted) return;
const payload = await loadPayload(url, {
signal,
promoted
}).catch(() => {
stateDiagnostics.NUXT_E7003({ url });
});
if (signal.aborted || !head || !payload?.prefetchLinks?.length || group === prefetchGroup(router.currentRoute.value.fullPath)) return;
schedule(selectHints(payload.prefetchLinks).map(({ href, link }) => ({
key: `hint:${href}`,
priority: "hint",
scope: "navigation",
group,
run: forwardHint(link)
})));
}
});
});
});
onNuxtReady(() => {
if (appManifest && canPrefetch()) setTimeout(getAppManifest, 1e3);
});
}
});
//#endregion
export { plugin as default };